# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.


"""Test class for Management Interface used by iLO modules."""

from unittest import mock

from ironic.common import exception
from ironic.common import states
from ironic.common import utils
from ironic.conductor import task_manager
from ironic.conductor import utils as conductor_utils
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers.modules.ilo import inspect as ilo_inspect
from ironic.drivers.modules.ilo import power as ilo_power
from ironic.drivers.modules import inspect_utils
from ironic.tests.unit.drivers.modules.ilo import test_common


class IloInspectTestCase(test_common.BaseIloTest):

    def test_get_properties(self):
        with task_manager.acquire(self.context, self.node.uuid,
                                  shared=False) as task:
            properties = ilo_common.REQUIRED_PROPERTIES.copy()
            properties.update(ilo_common.SNMP_PROPERTIES)
            properties.update(ilo_common.SNMP_OPTIONAL_PROPERTIES)
            self.assertEqual(properties,
                             task.driver.inspect.get_properties())

    @mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True,
                       autospec=True)
    def test_validate(self, driver_info_mock):
        with task_manager.acquire(self.context, self.node.uuid,
                                  shared=False) as task:
            task.driver.inspect.validate(task)
            driver_info_mock.assert_called_once_with(task.node)

    @mock.patch.object(ilo_inspect, '_get_capabilities', spec_set=True,
                       autospec=True)
    @mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
                       spec_set=True, autospec=True)
    @mock.patch.object(ilo_inspect, '_get_essential_properties', spec_set=True,
                       autospec=True)
    @mock.patch.object(ilo_power.IloPower, 'get_power_state', spec_set=True,
                       autospec=True)
    @mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
                       autospec=True)
    def test_inspect_essential_ok(self, get_ilo_object_mock,
                                  power_mock,
                                  get_essential_mock,
                                  create_port_mock,
                                  get_capabilities_mock):
        ilo_object_mock = get_ilo_object_mock.return_value
        properties = {'memory_mb': '512', 'local_gb': '10',
                      'cpus': '1', 'cpu_arch': 'x86_64'}
        macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
        capabilities = {}
        result = {'properties': properties, 'macs': macs}
        get_essential_mock.return_value = result
        get_capabilities_mock.return_value = capabilities
        power_mock.return_value = states.POWER_ON
        with task_manager.acquire(self.context, self.node.uuid,
                                  shared=False) as task:
            task.driver.inspect.inspect_hardware(task)
            self.assertEqual(properties, task.node.properties)
            power_mock.assert_called_once_with(mock.ANY, task)
            get_essential_mock.assert_called_once_with(task.node,
                                                       ilo_object_mock)
            get_capabilities_mock.assert_called_once_with(task.node,
                                                          ilo_object_mock)
            create_port_mock.assert_called_once_with(
                task, ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'])

    @mock.patch.object(ilo_inspect.LOG, 'warning',
                       spec_set=True, autospec=True)
    @mock.patch.object(ilo_inspect, '_get_capabilities', spec_set=True,
                       autospec=True)
    @mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
                       spec_set=True, autospec=True)
    @mock.patch.object(ilo_inspect, '_get_essential_properties', spec_set=True,
                       autospec=True)
    @mock.patch.object(ilo_power.IloPower, 'get_power_state', spec_set=True,
                       autospec=True)
    @mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
                       autospec=True)
    def test_inspect_essential_ok_local_gb_zero(self, get_ilo_object_mock,
                                                power_mock,
                                                get_essential_mock,
                                                create_port_mock,
                                                get_capabilities_mock,
                                                log_mock):
        ilo_object_mock = get_ilo_object_mock.return_value
        properties = {'memory_mb': '512', 'local_gb': 0,
                      'cpus': '1', 'cpu_arch': 'x86_64'}
        macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
        capabilities = {}
        result = {'properties': properties, 'macs': macs}
        get_essential_mock.return_value = result
        get_capabilities_mock.return_value = capabilities
        power_mock.return_value = states.POWER_ON
        with task_manager.acquire(self.context, self.node.uuid,
                                  shared=False) as task:
            properties = task.node.properties
            properties['local_gb'] = 10
            task.node.properties = properties
            task.node.save()
            expected_properties = {'memory_mb': '512', 'local_gb': 10,
                                   'cpus': '1', 'cpu_arch': 'x86_64'}
            task.driver.inspect.inspect_hardware(task)
            self.assertEqual(expected_properties, task.node.properties)
            power_mock.assert_called_once_with(mock.ANY, task)
            get_essential_mock.assert_called_once_with(task.node,
                                                       ilo_object_mock)
            self.assertTrue(log_mock.called)
            get_capabilities_mock.assert_called_once_with(task.node,
                                                          ilo_object_mock)
            create_port_mock.assert_called_once_with(
                task, ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'])

    @mock.patch.object(ilo_inspect.LOG, 'warning',
                       spec_set=True, autospec=True)
    @mock.patch.object(ilo_inspect, '_get_capabilities', spec_set=True,
                       autospec=True)
    @mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
                       spec_set=True, autospec=True)
    @mock.patch.object(ilo_inspect, '_get_essential_properties', spec_set=True,
                       autospec=True)
    @mock.patch.object(ilo_power.IloPower, 'get_power_state', spec_set=True,
                       autospec=True)
    @mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
                       autospec=True)
    def test_inspect_ok_gen8(self, get_ilo_object_mock,
                             power_mock,
                             get_essential_mock,
                             create_port_mock,
                             get_capabilities_mock,
                             log_mock):
        ilo_object_mock = get_ilo_object_mock.return_value
        properties = {'memory_mb': '512', 'local_gb': 10,
                      'cpus': '1', 'cpu_arch': 'x86_64'}
        macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
        capabilities = {'server_model': 'Gen8'}
        result = {'properties': properties, 'macs': macs}
        get_essential_mock.return_value = result
        get_capabilities_mock.return_value = capabilities
        power_mock.return_value = states.POWER_ON
        with task_manager.acquire(self.context, self.node.uuid,
                                  shared=False) as task:
            expected_properties = {'memory_mb': '512', 'local_gb': 10,
                                   'cpus': '1', 'cpu_arch': 'x86_64',
                                   'capabilities': 'server_model:Gen8'}
            task.driver.inspect.inspect_hardware(task)
            self.assertEqual(expected_properties, task.node.properties)
            power_mock.assert_called_once_with(mock.ANY, task)
            get_essential_mock.assert_called_once_with(task.node,
                                                       ilo_object_mock)
            self.assertTrue(log_mock.called)
            get_capabilities_mock.assert_called_once_with(task.node,
                                                          ilo_object_mock)
            create_port_mock.assert_called_once_with(
                task, ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'])

    @mock.patch.object(ilo_inspect.LOG, 'warning',
                       spec_set=True, autospec=True)
    @mock.patch.object(ilo_inspect, '_get_security_parameters',
                       spec_set=True, autospec=True)
    @mock.patch.object(ilo_inspect, '_get_capabilities', spec_set=True,
                       autospec=True)
    @mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
                       spec_set=True, autospec=True)
    @mock.patch.object(ilo_inspect, '_get_essential_properties', spec_set=True,
                       autospec=True)
    @mock.patch.object(ilo_power.IloPower, 'get_power_state', spec_set=True,
                       autospec=True)
    @mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
                       autospec=True)
    def test_inspect_ok_gen10(self, get_ilo_object_mock,
                              power_mock,
                              get_essential_mock,
                              create_port_mock,
                              get_capabilities_mock,
                              get_security_params_mock,
                              log_mock):
        ilo_object_mock = get_ilo_object_mock.return_value
        properties = {'memory_mb': '512', 'local_gb': 10,
                      'cpus': '1', 'cpu_arch': 'x86_64'}
        macs = {'NIC.LOM.1.1': 'aa:aa:aa:aa:aa:aa'}
        capabilities = {'server_model': 'Gen10'}
        security_params = (
            {'security_parameters': {'Password Complexity': 'ok'}})
        result = {'properties': properties, 'macs': macs}
        get_essential_mock.return_value = result
        get_capabilities_mock.return_value = capabilities
        get_security_params_mock.return_value = security_params
        power_mock.return_value = states.POWER_ON
        with task_manager.acquire(self.context, self.node.uuid,
                                  shared=False) as task:
            expected_properties = {
                'memory_mb': '512', 'local_gb': 10, 'cpus': '1',
                'cpu_arch': 'x86_64', 'capabilities': 'server_model:Gen10',
                'security_parameters': {'Password Complexity': 'ok'}}
            task.driver.inspect.inspect_hardware(task)
            self.assertEqual(expected_properties, task.node.properties)
            power_mock.assert_called_once_with(mock.ANY, task)
            get_essential_mock.assert_called_once_with(task.node,
                                                       ilo_object_mock)
            self.assertFalse(log_mock.called)
            get_capabilities_mock.assert_called_once_with(task.node,
                                                          ilo_object_mock)
            create_port_mock.assert_called_once_with(
                task, ['aa:aa:aa:aa:aa:aa'])

    @mock.patch.object(ilo_inspect, '_get_capabilities', spec_set=True,
                       autospec=True)
    @mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
                       spec_set=True, autospec=True)
    @mock.patch.object(ilo_inspect, '_get_essential_properties', spec_set=True,
                       autospec=True)
    @mock.patch.object(conductor_utils, 'node_power_action', spec_set=True,
                       autospec=True)
    @mock.patch.object(ilo_power.IloPower, 'get_power_state', spec_set=True,
                       autospec=True)
    @mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
                       autospec=True)
    def test_inspect_essential_ok_power_off(self, get_ilo_object_mock,
                                            power_mock,
                                            set_power_mock,
                                            get_essential_mock,
                                            create_port_mock,
                                            get_capabilities_mock):
        ilo_object_mock = get_ilo_object_mock.return_value
        properties = {'memory_mb': '512', 'local_gb': '10',
                      'cpus': '1', 'cpu_arch': 'x86_64'}
        macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
        capabilities = {}
        result = {'properties': properties, 'macs': macs}
        get_essential_mock.return_value = result
        get_capabilities_mock.return_value = capabilities
        power_mock.return_value = states.POWER_OFF
        with task_manager.acquire(self.context, self.node.uuid,
                                  shared=False) as task:
            task.driver.inspect.inspect_hardware(task)
            self.assertEqual(properties, task.node.properties)
            power_mock.assert_called_once_with(mock.ANY, task)
            set_power_mock.assert_any_call(task, states.POWER_ON)
            get_essential_mock.assert_called_once_with(task.node,
                                                       ilo_object_mock)
            get_capabilities_mock.assert_called_once_with(task.node,
                                                          ilo_object_mock)
            create_port_mock.assert_called_once_with(
                task, ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'])

    @mock.patch.object(ilo_inspect, '_get_capabilities', spec_set=True,
                       autospec=True)
    @mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
                       spec_set=True, autospec=True)
    @mock.patch.object(ilo_inspect, '_get_essential_properties', spec_set=True,
                       autospec=True)
    @mock.patch.object(ilo_power.IloPower, 'get_power_state', spec_set=True,
                       autospec=True)
    @mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
                       autospec=True)
    def test_inspect_essential_capabilities_ok(self, get_ilo_object_mock,
                                               power_mock,
                                               get_essential_mock,
                                               create_port_mock,
                                               get_capabilities_mock):
        ilo_object_mock = get_ilo_object_mock.return_value
        properties = {'memory_mb': '512', 'local_gb': '10',
                      'cpus': '1', 'cpu_arch': 'x86_64'}
        macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
        capability_str = 'sriov_enabled:true'
        capabilities = {'sriov_enabled': 'true'}
        result = {'properties': properties, 'macs': macs}
        get_essential_mock.return_value = result
        get_capabilities_mock.return_value = capabilities
        power_mock.return_value = states.POWER_ON
        with task_manager.acquire(self.context, self.node.uuid,
                                  shared=False) as task:
            task.driver.inspect.inspect_hardware(task)
            expected_properties = {'memory_mb': '512', 'local_gb': '10',
                                   'cpus': '1', 'cpu_arch': 'x86_64',
                                   'capabilities': capability_str}
            self.assertEqual(expected_properties, task.node.properties)
            power_mock.assert_called_once_with(mock.ANY, task)
            get_essential_mock.assert_called_once_with(task.node,
                                                       ilo_object_mock)
            get_capabilities_mock.assert_called_once_with(task.node,
                                                          ilo_object_mock)
            create_port_mock.assert_called_once_with(
                task, ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'])

    @mock.patch.object(ilo_inspect, '_get_capabilities', spec_set=True,