summaryrefslogtreecommitdiff
path: root/ironic
diff options
context:
space:
mode:
authorNisha Agarwal <agarwalnisha1980@gmail.com>2017-03-22 00:38:44 -0700
committerIlya Etingof <etingof@gmail.com>2018-11-03 09:09:56 +0100
commit41d3356571eafd7f3192d05d7c7523ac295ff5c0 (patch)
tree54daaeb8536e8d230e35c44c69eea89608b70933 /ironic
parent325b0a6bd42d9eda531ace1185ecdc5e2ceb8614 (diff)
downloadironic-41d3356571eafd7f3192d05d7c7523ac295ff5c0.tar.gz
Add Redfish inspect interface
Add the InspectInterface to the `redfish` hardware type. This enables OOB inspection in ironic. Story: 1668487 Task: 10571 Co-Authored-By: Ilya Etingof <etingof@gmail.com> Depends-On: I3a79f2afe6c838636df554ee468f8f2e0cf0859e Depends-On: Ieb374f8cabb0418bb2680fdab690446346fc354f Change-Id: Ie3167569db060620fe0b9784bc7d7a854f2ef754
Diffstat (limited to 'ironic')
-rw-r--r--ironic/drivers/modules/deploy_utils.py28
-rw-r--r--ironic/drivers/modules/ilo/inspect.py30
-rw-r--r--ironic/drivers/modules/redfish/inspect.py192
-rw-r--r--ironic/drivers/redfish.py9
-rw-r--r--ironic/tests/unit/drivers/modules/ilo/test_inspect.py44
-rw-r--r--ironic/tests/unit/drivers/modules/redfish/test_inspect.py189
-rw-r--r--ironic/tests/unit/drivers/modules/redfish/test_management.py3
-rw-r--r--ironic/tests/unit/drivers/modules/redfish/test_power.py3
-rw-r--r--ironic/tests/unit/drivers/modules/test_deploy_utils.py33
-rw-r--r--ironic/tests/unit/drivers/test_redfish.py6
-rw-r--r--ironic/tests/unit/drivers/third_party_driver_mock_specs.py5
-rw-r--r--ironic/tests/unit/drivers/third_party_driver_mocks.py7
12 files changed, 479 insertions, 70 deletions
diff --git a/ironic/drivers/modules/deploy_utils.py b/ironic/drivers/modules/deploy_utils.py
index ba7990117..21cb895eb 100644
--- a/ironic/drivers/modules/deploy_utils.py
+++ b/ironic/drivers/modules/deploy_utils.py
@@ -579,6 +579,34 @@ def get_single_nic_with_vif_port_id(task):
return port.address
+def create_ports_if_not_exist(task, macs):
+ """Create ironic ports for the mac addresses.
+
+ Creates ironic ports for the mac addresses returned with inspection
+ or as requested by operator.
+
+ :param task: a TaskManager instance.
+ :param macs: A dictionary of port numbers to mac addresses
+ returned by node inspection.
+
+ """
+ node = task.node
+ for port_num, mac in macs.items():
+ port_dict = {'address': mac, 'node_id': node.id}
+ port = objects.Port(task.context, **port_dict)
+
+ try:
+ port.create()
+ LOG.info(_("Port %(port_num)s created for MAC address %(address)s "
+ "for node %(node)s"),
+ {'address': mac, 'node': node.uuid, 'port_num': port_num})
+ except exception.MACAlreadyExists:
+ LOG.warning(_("Port %(port_num)s already exists for MAC address"
+ "%(address)s for node %(node)s"),
+ {'address': mac, 'node': node.uuid,
+ 'port_num': port_num})
+
+
def agent_get_clean_steps(task, interface=None, override_priorities=None):
"""Get the list of cached clean steps from the agent.
diff --git a/ironic/drivers/modules/ilo/inspect.py b/ironic/drivers/modules/ilo/inspect.py
index 6884a675e..231a0d90b 100644
--- a/ironic/drivers/modules/ilo/inspect.py
+++ b/ironic/drivers/modules/ilo/inspect.py
@@ -22,8 +22,8 @@ from ironic.common import states
from ironic.common import utils
from ironic.conductor import utils as conductor_utils
from ironic.drivers import base
+from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.ilo import common as ilo_common
-from ironic import objects
ilo_error = importutils.try_import('proliantutils.exception')
@@ -48,32 +48,6 @@ CAPABILITIES_KEYS = {'secure_boot', 'rom_firmware_version',
'nvdimm_n', 'logical_nvdimm_n', 'persistent_memory'}
-def _create_ports_if_not_exist(task, macs):
- """Create ironic ports for the mac addresses.
-
- Creates ironic ports for the mac addresses returned with inspection
- or as requested by operator.
-
- :param task: a TaskManager instance.
- :param macs: A dictionary of port numbers to mac addresses
- returned by node inspection.
-
- """
- node = task.node
- for mac in macs.values():
- port_dict = {'address': mac, 'node_id': node.id}
- port = objects.Port(task.context, **port_dict)
-
- try:
- port.create()
- LOG.info("Port created for MAC address %(address)s for node "
- "%(node)s", {'address': mac, 'node': node.uuid})
- except exception.MACAlreadyExists:
- LOG.warning("Port already exists for MAC address %(address)s "
- "for node %(node)s",
- {'address': mac, 'node': node.uuid})
-
-
def _get_essential_properties(node, ilo_object):
"""Inspects the node and get essential scheduling properties
@@ -270,7 +244,7 @@ class IloInspect(base.InspectInterface):
task.node.save()
# Create ports for the nics detected.
- _create_ports_if_not_exist(task, result['macs'])
+ deploy_utils.create_ports_if_not_exist(task, result['macs'])
LOG.debug("Node properties for %(node)s are updated as "
"%(properties)s",
diff --git a/ironic/drivers/modules/redfish/inspect.py b/ironic/drivers/modules/redfish/inspect.py
new file mode 100644
index 000000000..8fc92a15e
--- /dev/null
+++ b/ironic/drivers/modules/redfish/inspect.py
@@ -0,0 +1,192 @@
+# 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.
+"""
+Redfish Inspect Interface
+"""
+
+from oslo_log import log
+from oslo_utils import importutils
+from oslo_utils import units
+
+from ironic.common import exception
+from ironic.common.i18n import _
+from ironic.common import states
+from ironic.drivers import base
+from ironic.drivers.modules import deploy_utils
+from ironic.drivers.modules.redfish import utils as redfish_utils
+
+LOG = log.getLogger(__name__)
+
+sushy = importutils.try_import('sushy')
+
+if sushy:
+ CPU_ARCH_MAP = {
+ sushy.PROCESSOR_ARCH_x86: 'x86_64',
+ sushy.PROCESSOR_ARCH_IA_64: 'ia64',
+ sushy.PROCESSOR_ARCH_ARM: 'arm',
+ sushy.PROCESSOR_ARCH_MIPS: 'mips',
+ sushy.PROCESSOR_ARCH_OEM: 'oem'
+ }
+
+
+class RedfishInspect(base.InspectInterface):
+
+ def __init__(self):
+ """Initialize the Redfish inspection interface.
+
+ :raises: DriverLoadError if the driver can't be loaded due to
+ missing dependencies
+ """
+ super(RedfishInspect, self).__init__()
+ if not sushy:
+ raise exception.DriverLoadError(
+ driver='redfish',
+ reason=_('Unable to import the sushy library'))
+
+ def get_properties(self):
+ """Return the properties of the interface.
+
+ :returns: dictionary of <property name>:<property description> entries.
+ """
+ return redfish_utils.COMMON_PROPERTIES.copy()
+
+ def validate(self, task):
+ """Validate the driver-specific Node deployment info.
+
+ This method validates whether the 'driver_info' properties of
+ the task's node contains the required information for this
+ interface to function.
+
+ This method is often executed synchronously in API requests, so it
+ should not conduct long-running checks.
+
+ :param task: A TaskManager instance containing the node to act on.
+ :raises: InvalidParameterValue on malformed parameter(s)
+ :raises: MissingParameterValue on missing parameter(s)
+ """
+ redfish_utils.parse_driver_info(task.node)
+
+ def inspect_hardware(self, task):
+ """Inspect hardware to get the hardware properties.
+
+ Inspects hardware to get the essential properties.
+ It fails if any of the essential properties
+ are not received from the node.
+
+ :param task: a TaskManager instance.
+ :raises: HardwareInspectionFailure if essential properties
+ could not be retrieved successfully.
+ :returns: The resulting state of inspection.
+
+ """
+ system = redfish_utils.get_system(task.node)
+
+ # get the essential properties and update the node properties
+ # with it.
+ inspected_properties = task.node.properties
+
+ if system.memory_summary and system.memory_summary.size_gib:
+ inspected_properties['memory_mb'] = str(
+ system.memory_summary.size_gib * units.Ki)
+
+ if system.processors and system.processors.summary:
+ cpus, arch = system.processors.summary
+ if cpus:
+ inspected_properties['cpus'] = cpus
+
+ if arch:
+ try:
+ inspected_properties['cpu_arch'] = CPU_ARCH_MAP[arch]
+
+ except KeyError:
+ LOG.warning(
+ _("Unknown CPU arch %(arch)s discovered "
+ "for Node %(node)s"), {'node': task.node.uuid,
+ 'arch': arch})
+
+ simple_storage_size = 0
+
+ try:
+ if (system.simple_storage and
+ system.simple_storage.disks_sizes_bytes):
+ simple_storage_size = [
+ size for size in system.simple_storage.disks_sizes_bytes
+ if size >= 4 * units.Gi
+ ] or [0]
+
+ simple_storage_size = simple_storage_size[0]
+
+ except sushy.SushyError:
+ LOG.info(
+ _("No simple storage information discovered "
+ "for Node %(node)s"), {'node': task.node.uuid})
+
+ storage_size = 0
+
+ try:
+ if system.storage and system.storage.volumes_sizes_bytes:
+ storage_size = [
+ size for size in system.storage.volumes_sizes_bytes
+ if size >= 4 * units.Gi
+ ] or [0]
+
+ storage_size = storage_size[0]
+
+ except sushy.SushyError:
+ LOG.info(_("No storage volume information discovered "
+ "for Node %(node)s"), {'node': task.node.uuid})
+
+ local_gb = max(simple_storage_size, storage_size)
+
+ # Note(deray): Convert the received size to GiB and reduce the
+ # value by 1 GB as consumers like Ironic requires the ``local_gb``
+ # to be returned 1 less than actual size.
+ local_gb = max(0, int(local_gb / units.Gi - 1))
+
+ if local_gb:
+ inspected_properties['local_gb'] = str(local_gb)
+
+ else:
+ LOG.warning(_("Could not provide a valid storage size configured "
+ "for Node %(node)s"), {'node': task.node.uuid})
+
+ valid_keys = self.ESSENTIAL_PROPERTIES
+ missing_keys = valid_keys - set(inspected_properties)
+ if missing_keys:
+ error = (_('Failed to discover the following properties: '
+ '%(missing_keys)s on node %(node)s'),
+ {'missing_keys': ', '.join(missing_keys),
+ 'node': task.node.uuid})
+ raise exception.HardwareInspectionFailure(error=error)
+
+ task.node.properties = inspected_properties
+ task.node.save()
+
+ LOG.debug(_("Node properties for %(node)s are updated as "
+ "%(properties)s"),
+ {'properties': inspected_properties,
+ 'node': task.node.uuid})
+
+ if (system.ethernet_interfaces and
+ system.ethernet_interfaces.eth_summary):
+ macs = system.ethernet_interfaces.eth_summary
+
+ # Create ports for the nics detected.
+ deploy_utils.create_ports_if_not_exist(task, macs)
+
+ else:
+ LOG.info(_("No NIC information discovered "
+ "for Node %(node)s"), {'node': task.node.uuid})
+
+ LOG.info(_("Node %(node)s inspected."), {'node': task.node.uuid})
+
+ return states.MANAGEABLE
diff --git a/ironic/drivers/redfish.py b/ironic/drivers/redfish.py
index ecf7c789a..9e5c93331 100644
--- a/ironic/drivers/redfish.py
+++ b/ironic/drivers/redfish.py
@@ -14,6 +14,9 @@
# under the License.
from ironic.drivers import generic
+from ironic.drivers.modules import inspector
+from ironic.drivers.modules import noop
+from ironic.drivers.modules.redfish import inspect as redfish_inspect
from ironic.drivers.modules.redfish import management as redfish_mgmt
from ironic.drivers.modules.redfish import power as redfish_power
@@ -30,3 +33,9 @@ class RedfishHardware(generic.GenericHardware):
def supported_power_interfaces(self):
"""List of supported power interfaces."""
return [redfish_power.RedfishPower]
+
+ @property
+ def supported_inspect_interfaces(self):
+ """List of supported power interfaces."""
+ return [redfish_inspect.RedfishInspect, inspector.Inspector,
+ noop.NoInspect]
diff --git a/ironic/tests/unit/drivers/modules/ilo/test_inspect.py b/ironic/tests/unit/drivers/modules/ilo/test_inspect.py
index 8c73d5c76..2efc104f0 100644
--- a/ironic/tests/unit/drivers/modules/ilo/test_inspect.py
+++ b/ironic/tests/unit/drivers/modules/ilo/test_inspect.py
@@ -23,10 +23,10 @@ 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 import deploy_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 import objects
from ironic.tests.unit.drivers.modules.ilo import test_common
@@ -51,7 +51,7 @@ class IloInspectTestCase(test_common.BaseIloTest):
@mock.patch.object(ilo_inspect, '_get_capabilities', spec_set=True,
autospec=True)
- @mock.patch.object(ilo_inspect, '_create_ports_if_not_exist',
+ @mock.patch.object(deploy_utils, 'create_ports_if_not_exist',
spec_set=True, autospec=True)
@mock.patch.object(ilo_inspect, '_get_essential_properties', spec_set=True,
autospec=True)
@@ -88,7 +88,7 @@ class IloInspectTestCase(test_common.BaseIloTest):
spec_set=True, autospec=True)
@mock.patch.object(ilo_inspect, '_get_capabilities', spec_set=True,
autospec=True)
- @mock.patch.object(ilo_inspect, '_create_ports_if_not_exist',
+ @mock.patch.object(deploy_utils, 'create_ports_if_not_exist',
spec_set=True, autospec=True)
@mock.patch.object(ilo_inspect, '_get_essential_properties', spec_set=True,
autospec=True)
@@ -131,7 +131,7 @@ class IloInspectTestCase(test_common.BaseIloTest):
@mock.patch.object(ilo_inspect, '_get_capabilities', spec_set=True,
autospec=True)
- @mock.patch.object(ilo_inspect, '_create_ports_if_not_exist',
+ @mock.patch.object(deploy_utils, 'create_ports_if_not_exist',
spec_set=True, autospec=True)
@mock.patch.object(ilo_inspect, '_get_essential_properties', spec_set=True,
autospec=True)
@@ -170,7 +170,7 @@ class IloInspectTestCase(test_common.BaseIloTest):
@mock.patch.object(ilo_inspect, '_get_capabilities', spec_set=True,
autospec=True)
- @mock.patch.object(ilo_inspect, '_create_ports_if_not_exist',
+ @mock.patch.object(deploy_utils, 'create_ports_if_not_exist',
spec_set=True, autospec=True)
@mock.patch.object(ilo_inspect, '_get_essential_properties', spec_set=True,
autospec=True)
@@ -209,7 +209,7 @@ class IloInspectTestCase(test_common.BaseIloTest):
@mock.patch.object(ilo_inspect, '_get_capabilities', spec_set=True,
autospec=True)
- @mock.patch.object(ilo_inspect, '_create_ports_if_not_exist',
+ @mock.patch.object(deploy_utils, 'create_ports_if_not_exist',
spec_set=True, autospec=True)
@mock.patch.object(ilo_inspect, '_get_essential_properties', spec_set=True,
autospec=True)
@@ -256,38 +256,6 @@ class IloInspectTestCase(test_common.BaseIloTest):
class TestInspectPrivateMethods(test_common.BaseIloTest):
- @mock.patch.object(ilo_inspect.LOG, 'info', spec_set=True, autospec=True)
- @mock.patch.object(objects, 'Port', spec_set=True, autospec=True)
- def test__create_ports_if_not_exist(self, port_mock, log_mock):
- macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
- node_id = self.node.id
- port_dict1 = {'address': 'aa:aa:aa:aa:aa:aa', 'node_id': node_id}
- port_dict2 = {'address': 'bb:bb:bb:bb:bb:bb', 'node_id': node_id}
- port_obj1, port_obj2 = mock.MagicMock(), mock.MagicMock()
- port_mock.side_effect = [port_obj1, port_obj2]
- with task_manager.acquire(self.context, self.node.uuid,
- shared=False) as task:
- ilo_inspect._create_ports_if_not_exist(task, macs)
- self.assertTrue(log_mock.called)
- expected_calls = [mock.call(task.context, **port_dict1),
- mock.call(task.context, **port_dict2)]
- port_mock.assert_has_calls(expected_calls, any_order=True)
- port_obj1.create.assert_called_once_with()
- port_obj2.create.assert_called_once_with()
-
- @mock.patch.object(ilo_inspect.LOG, 'warning',
- spec_set=True, autospec=True)
- @mock.patch.object(objects.Port, 'create', spec_set=True, autospec=True)
- def test__create_ports_if_not_exist_mac_exception(self,
- create_mock,
- log_mock):
- create_mock.side_effect = exception.MACAlreadyExists('f')
- macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
- with task_manager.acquire(self.context, self.node.uuid,
- shared=False) as task:
- ilo_inspect._create_ports_if_not_exist(task, macs)
- self.assertEqual(2, log_mock.call_count)
-
def test__get_essential_properties_ok(self):
ilo_mock = mock.MagicMock(spec=['get_essential_properties'])
properties = {'memory_mb': '512', 'local_gb': '10',
diff --git a/ironic/tests/unit/drivers/modules/redfish/test_inspect.py b/ironic/tests/unit/drivers/modules/redfish/test_inspect.py
new file mode 100644
index 000000000..b70585638
--- /dev/null
+++ b/ironic/tests/unit/drivers/modules/redfish/test_inspect.py
@@ -0,0 +1,189 @@
+# Copyright 2017 Red Hat, Inc.
+# All Rights Reserved.
+#
+# 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.
+
+import mock
+from oslo_utils import importutils
+from oslo_utils import units
+
+from ironic.common import exception
+from ironic.conductor import task_manager
+from ironic.drivers.modules import deploy_utils
+from ironic.drivers.modules.redfish import utils as redfish_utils
+from ironic.tests.unit.db import base as db_base
+from ironic.tests.unit.db import utils as db_utils
+from ironic.tests.unit.objects import utils as obj_utils
+
+sushy = importutils.try_import('sushy')
+
+INFO_DICT = db_utils.get_test_redfish_info()
+
+
+class MockedSushyError(Exception):
+ pass
+
+
+class RedfishInspectTestCase(db_base.DbTestCase):
+
+ def setUp(self):
+ super(RedfishInspectTestCase, self).setUp()
+ self.config(enabled_hardware_types=['redfish'],
+ enabled_power_interfaces=['redfish'],
+ enabled_management_interfaces=['redfish'],
+ enabled_inspect_interfaces=['redfish'])
+ self.node = obj_utils.create_test_node(
+ self.context, driver='redfish', driver_info=INFO_DICT)
+
+ def init_system_mock(self, system_mock, **properties):
+
+ system_mock.reset()
+
+ system_mock.memory_summary.size_gib = 2
+
+ system_mock.processors.summary = '8', 'MIPS'
+
+ system_mock.simple_storage.disks_sizes_bytes = (
+ 1 * units.Gi, units.Gi * 3, units.Gi * 5)
+ system_mock.storage.volumes_sizes_bytes = (
+ 2 * units.Gi, units.Gi * 4, units.Gi * 6)
+
+ system_mock.ethernet_interfaces.eth_summary = {
+ '1': '00:11:22:33:44:55', '2': '66:77:88:99:AA:BB'
+ }
+
+ return system_mock
+
+ def test_get_properties(self):
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ properties = task.driver.get_properties()
+ for prop in redfish_utils.COMMON_PROPERTIES:
+ self.assertIn(prop, properties)
+
+ @mock.patch.object(redfish_utils, 'parse_driver_info', autospec=True)
+ def test_validate(self, mock_parse_driver_info):
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ task.driver.management.validate(task)
+ mock_parse_driver_info.assert_called_once_with(task.node)
+
+ @mock.patch.object(redfish_utils, 'get_system', autospec=True)
+ @mock.patch.object(deploy_utils, 'create_ports_if_not_exist',
+ autospec=True)
+ def test_inspect_hardware_ok(self, mock_create_ports_if_not_exist,
+ mock_get_system):
+ expected_properties = {
+ 'cpu_arch': 'mips', 'cpus': '8',
+ 'local_gb': '4', 'memory_mb': '2048'
+ }
+
+ system = self.init_system_mock(mock_get_system.return_value)
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ task.driver.inspect.inspect_hardware(task)
+ mock_create_ports_if_not_exist.assert_called_once_with(
+ task, system.ethernet_interfaces.eth_summary)
+ mock_get_system.assert_called_once_with(task.node)
+ self.assertEqual(expected_properties, task.node.properties)
+
+ @mock.patch.object(redfish_utils, 'get_system', autospec=True)
+ def test_inspect_hardware_fail_missing_cpu(self, mock_get_system):
+ system_mock = self.init_system_mock(mock_get_system.return_value)
+ system_mock.processors.summary = None, None
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ task.node.properties.pop('cpu_arch')
+ self.assertRaises(exception.HardwareInspectionFailure,
+ task.driver.inspect.inspect_hardware, task)
+
+ @mock.patch.object(redfish_utils, 'get_system', autospec=True)
+ def test_inspect_hardware_ignore_missing_cpu(self, mock_get_system):
+ system_mock = self.init_system_mock(mock_get_system.return_value)
+ system_mock.processors.summary = None, None
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ expected_properties = {
+ 'cpu_arch': 'x86_64', 'cpus': '8',
+ 'local_gb': '4', 'memory_mb': '2048'
+ }
+ task.driver.inspect.inspect_hardware(task)
+ self.assertEqual(expected_properties, task.node.properties)
+
+ @mock.patch.object(redfish_utils, 'get_system', autospec=True)
+ def test_inspect_hardware_fail_missing_local_gb(self, mock_get_system):
+ system_mock = self.init_system_mock(mock_get_system.return_value)
+ system_mock.simple_storage.disks_sizes_bytes = None
+ system_mock.storage.volumes_sizes_bytes = None
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ task.node.properties.pop('local_gb')
+ self.assertRaises(exception.HardwareInspectionFailure,
+ task.driver.inspect.inspect_hardware, task)
+
+ @mock.patch.object(redfish_utils, 'get_system', autospec=True)
+ def test_inspect_hardware_ignore_missing_local_gb(self, mock_get_system):
+ system_mock = self.init_system_mock(mock_get_system.return_value)
+ system_mock.simple_storage.disks_sizes_bytes = None
+ system_mock.storage.volumes_sizes_bytes = None
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ expected_properties = {
+ 'cpu_arch': 'mips', 'cpus': '8',
+ 'local_gb': '10', 'memory_mb': '2048'
+ }
+ task.driver.inspect.inspect_hardware(task)
+ self.assertEqual(expected_properties, task.node.properties)
+
+ @mock.patch.object(redfish_utils, 'get_system', autospec=True)
+ def test_inspect_hardware_fail_missing_memory_mb(self, mock_get_system):
+ system_mock = self.init_system_mock(mock_get_system.return_value)
+ system_mock.memory_summary.size_gib = None
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ task.node.properties.pop('memory_mb')
+ self.assertRaises(exception.HardwareInspectionFailure,
+ task.driver.inspect.inspect_hardware, task)
+
+ @mock.patch.object(redfish_utils, 'get_system', autospec=True)
+ def test_inspect_hardware_ignore_missing_memory_mb(self, mock_get_system):
+ system_mock = self.init_system_mock(mock_get_system.return_value)
+ system_mock.memory_summary.size_gib = None
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ expected_properties = {
+ 'cpu_arch': 'mips', 'cpus': '8',
+ 'local_gb': '4', 'memory_mb': '4096'
+ }
+ task.driver.inspect.inspect_hardware(task)
+ self.assertEqual(expected_properties, task.node.properties)
+
+ @mock.patch.object(redfish_utils, 'get_system', autospec=True)
+ @mock.patch.object(deploy_utils, 'create_ports_if_not_exist',
+ autospec=True)
+ def test_inspect_hardware_ignore_missing_nics(
+ self, mock_create_ports_if_not_exist, mock_get_system):
+ system_mock = self.init_system_mock(mock_get_system.return_value)
+ system_mock.ethernet_interfaces.eth_summary = None
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ task.driver.inspect.inspect_hardware(task)
+ self.assertFalse(mock_create_ports_if_not_exist.called)
diff --git a/ironic/tests/unit/drivers/modules/redfish/test_management.py b/ironic/tests/unit/drivers/modules/redfish/test_management.py
index d42e4a4e7..f35ffce5e 100644
--- a/ironic/tests/unit/drivers/modules/redfish/test_management.py
+++ b/ironic/tests/unit/drivers/modules/redfish/test_management.py
@@ -41,7 +41,8 @@ class RedfishManagementTestCase(db_base.DbTestCase):
super(RedfishManagementTestCase, self).setUp()
self.config(enabled_hardware_types=['redfish'],
enabled_power_interfaces=['redfish'],
- enabled_management_interfaces=['redfish'])
+ enabled_management_interfaces=['redfish'],
+ enabled_inspect_interfaces=['redfish'])
self.node = obj_utils.create_test_node(
self.context, driver='redfish', driver_info=INFO_DICT)
diff --git a/ironic/tests/unit/drivers/modules/redfish/test_power.py b/ironic/tests/unit/drivers/modules/redfish/test_power.py
index 2c8b964f6..2318b6217 100644
--- a/ironic/tests/unit/drivers/modules/redfish/test_power.py
+++ b/ironic/tests/unit/drivers/modules/redfish/test_power.py
@@ -41,7 +41,8 @@ class RedfishPowerTestCase(db_base.DbTestCase):
super(RedfishPowerTestCase, self).setUp()
self.config(enabled_hardware_types=['redfish'],
enabled_power_interfaces=['redfish'],
- enabled_management_interfaces=['redfish'])
+ enabled_management_interfaces=['redfish'],
+ enabled_inspect_interfaces=['redfish'])
self.node = obj_utils.create_test_node(
self.context, driver='redfish', driver_info=INFO_DICT)
diff --git a/ironic/tests/unit/drivers/modules/test_deploy_utils.py b/ironic/tests/unit/drivers/modules/test_deploy_utils.py
index 3cecc5ec1..6f9354856 100644
--- a/ironic/tests/unit/drivers/modules/test_deploy_utils.py
+++ b/ironic/tests/unit/drivers/modules/test_deploy_utils.py
@@ -44,6 +44,7 @@ from ironic.drivers.modules import image_cache
from ironic.drivers.modules import pxe
from ironic.drivers.modules.storage import cinder
from ironic.drivers import utils as driver_utils
+from ironic import objects
from ironic.tests import base as tests_base
from ironic.tests.unit.db import base as db_base
from ironic.tests.unit.db import utils as db_utils
@@ -1307,6 +1308,38 @@ class OtherFunctionTestCase(db_base.DbTestCase):
self.assertRaises(exception.InvalidParameterValue,
utils.get_ironic_api_url)
+ @mock.patch.object(utils.LOG, 'info', spec_set=True, autospec=True)
+ @mock.patch.object(objects, 'Port', spec_set=True, autospec=True)
+ def test_create_ports_if_not_exist(self, port_mock, log_mock):
+ macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
+ node_id = self.node.id
+ port_dict1 = {'address': 'aa:aa:aa:aa:aa:aa', 'node_id': node_id}
+ port_dict2 = {'address': 'bb:bb:bb:bb:bb:bb', 'node_id': node_id}
+ port_obj1, port_obj2 = mock.MagicMock(), mock.MagicMock()
+ port_mock.side_effect = [port_obj1, port_obj2]
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ utils.create_ports_if_not_exist(task, macs)
+ self.assertTrue(log_mock.called)
+ expected_calls = [mock.call(task.context, **port_dict1),
+ mock.call(task.context, **port_dict2)]
+ port_mock.assert_has_calls(expected_calls, any_order=True)
+ port_obj1.create.assert_called_once_with()
+ port_obj2.create.assert_called_once_with()
+
+ @mock.patch.object(utils.LOG, 'warning',
+ spec_set=True, autospec=True)
+ @mock.patch.object(objects.Port, 'create', spec_set=True, autospec=True)
+ def test_create_ports_if_not_exist_mac_exception(self,
+ create_mock,
+ log_mock):
+ create_mock.side_effect = exception.MACAlreadyExists('f')
+ macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ utils.create_ports_if_not_exist(task, macs)
+ self.assertEqual(2, log_mock.call_count)
+
class GetSingleNicTestCase(db_base.DbTestCase):
diff --git a/ironic/tests/unit/drivers/test_redfish.py b/ironic/tests/unit/drivers/test_redfish.py
index bddd63ce4..073df7d77 100644
--- a/ironic/tests/unit/drivers/test_redfish.py
+++ b/ironic/tests/unit/drivers/test_redfish.py
@@ -17,6 +17,7 @@ from ironic.conductor import task_manager
from ironic.drivers.modules import iscsi_deploy
from ironic.drivers.modules import noop
from ironic.drivers.modules import pxe
+from ironic.drivers.modules.redfish import inspect as redfish_inspect
from ironic.drivers.modules.redfish import management as redfish_mgmt
from ironic.drivers.modules.redfish import power as redfish_power
from ironic.tests.unit.db import base as db_base
@@ -29,11 +30,14 @@ class RedfishHardwareTestCase(db_base.DbTestCase):
super(RedfishHardwareTestCase, self).setUp()
self.config(enabled_hardware_types=['redfish'],
enabled_power_interfaces=['redfish'],
- enabled_management_interfaces=['redfish'])
+ enabled_management_interfaces=['redfish'],
+ enabled_inspect_interfaces=['redfish'])
def test_default_interfaces(self):
node = obj_utils.create_test_node(self.context, driver='redfish')
with task_manager.acquire(self.context, node.id) as task:
+ self.assertIsInstance(task.driver.inspect,
+ redfish_inspect.RedfishInspect)
self.assertIsInstance(task.driver.management,
redfish_mgmt.RedfishManagement)
self.assertIsInstance(task.driver.power,
diff --git a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py
index a5b1c6b5a..11fc9646d 100644
--- a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py
+++ b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py
@@ -146,6 +146,11 @@ SUSHY_CONSTANTS_SPEC = (
'BOOT_SOURCE_ENABLED_ONCE',
'BOOT_SOURCE_MODE_BIOS',
'BOOT_SOURCE_MODE_UEFI',
+ 'PROCESSOR_ARCH_x86',
+ 'PROCESSOR_ARCH_IA_64',
+ 'PROCESSOR_ARCH_ARM',
+ 'PROCESSOR_ARCH_MIPS',
+ 'PROCESSOR_ARCH_OEM',
)
XCLARITY_SPEC = (
diff --git a/ironic/tests/unit/drivers/third_party_driver_mocks.py b/ironic/tests/unit/drivers/third_party_driver_mocks.py
index fe955ba39..fa6031291 100644
--- a/ironic/tests/unit/drivers/third_party_driver_mocks.py
+++ b/ironic/tests/unit/drivers/third_party_driver_mocks.py
@@ -219,7 +219,12 @@ if not sushy:
BOOT_SOURCE_ENABLED_CONTINUOUS='continuous',
BOOT_SOURCE_ENABLED_ONCE='once',
BOOT_SOURCE_MODE_BIOS='bios',
- BOOT_SOURCE_MODE_UEFI='uefi'
+ BOOT_SOURCE_MODE_UEFI='uefi',
+ PROCESSOR_ARCH_x86='x86 or x86-64',
+ PROCESSOR_ARCH_IA_64='Intel Itanium',
+ PROCESSOR_ARCH_ARM='ARM',
+ PROCESSOR_ARCH_MIPS='MIPS',
+ PROCESSOR_ARCH_OEM='OEM-defined',
)
sys.modules['sushy'] = sushy