summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNguyen Hung Phuong <phuongnh@vn.fujitsu.com>2017-01-06 17:44:02 +0700
committerNguyen Hung Phuong <phuongnh@vn.fujitsu.com>2017-11-21 17:49:42 +0700
commit7adbf11bb843fc1e80d6c9dc62ac200561c3a9f8 (patch)
treeb18713f0c7576af053f97a9e288990a173fc4faa
parent459d1c2d3ea202f9fcbf6e4e9fd1c306f99dab89 (diff)
downloadironic-7adbf11bb843fc1e80d6c9dc62ac200561c3a9f8.tar.gz
Add additional capabilities discovery for iRMC driver
This update enhances iRMC out-of-band hardware inspection for FUJITSU PRIMERGY bare metal nodes having iRMC S4 and beyond. The capabilities are server_model, rom_firmware_version, pci_gpu_devices, trusted_boot and irmc_firmware_version. Co-authored-By: Nguyen Van Trung <trungnv@vn.fujitsu.com> Change-Id: I1958e18a5b9d933e2aa405b200bac7717f146611 Closes-Bug: #1637422
-rw-r--r--doc/source/admin/drivers/irmc.rst97
-rw-r--r--driver-requirements.txt2
-rw-r--r--etc/ironic/ironic.conf.sample9
-rw-r--r--ironic/conf/irmc.py9
-rw-r--r--ironic/drivers/modules/irmc/inspect.py82
-rw-r--r--ironic/tests/unit/drivers/modules/irmc/test_inspect.py348
-rw-r--r--ironic/tests/unit/drivers/third_party_driver_mock_specs.py1
-rw-r--r--releasenotes/notes/irmc-additional-capabilities-4fd72ba50d05676c.yaml9
8 files changed, 534 insertions, 23 deletions
diff --git a/doc/source/admin/drivers/irmc.rst b/doc/source/admin/drivers/irmc.rst
index 88d7a01a8..4ab9aa492 100644
--- a/doc/source/admin/drivers/irmc.rst
+++ b/doc/source/admin/drivers/irmc.rst
@@ -26,7 +26,7 @@ Prerequisites
* Install `python-scciclient <https://pypi.python.org/pypi/python-scciclient>`_
and `pysnmp <https://pypi.python.org/pypi/pysnmp>`_ packages::
- $ pip install "python-scciclient>=0.5.0" pysnmp
+ $ pip install "python-scciclient>=0.6.0" pysnmp
Hardware Type
=============
@@ -62,10 +62,10 @@ hardware interfaces:
Supports ``irmc``, ``inspector``, and ``no-inspect``.
The default is ``irmc``.
- .. note::
- `Ironic Inspector <https://docs.openstack.org/ironic-inspector/latest/>`_
- needs to be present and configured to use ``inspector`` as the
- inspect interface.
+.. note::
+ `Ironic Inspector <https://docs.openstack.org/ironic-inspector/latest/>`_
+ needs to be present and configured to use ``inspector`` as the
+ inspect interface.
* management
Supports only ``irmc``.
@@ -491,6 +491,93 @@ The drivers support the PCI controllers, Fibrechannel Cards, Converged Network
Adapters supported by
`Fujitsu ServerView Virtual-IO Manager <http://www.fujitsu.com/fts/products/computing/servers/primergy/management/primergy-blade-server-io-virtualization.html>`_.
+Hardware Inspection Support
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``irmc`` hardware type (only ``irmc`` inspect interface is supported) and
+the following iRMC classic drivers support Hardware Inspection:
+
+* ``pxe_irmc``
+* ``iscsi_irmc``
+* ``agent_irmc``
+
+.. note::
+ SNMP requires being enabled in ServerView® iRMC S4 Web Server(Network
+ Settings\SNMP section).
+
+Configuration
+~~~~~~~~~~~~~
+
+The Hardware Inspection Support in the iRMC drivers requires the following
+configuration:
+
+* It is necessary to set ironic configuration with ``gpu_ids`` option
+ in ``[irmc]`` section.
+
+ ``gpu_ids`` is a list of ``<vendorID>/<deviceID>`` where:
+
+ - ``<vendorID>``: 4 hexadecimal digits starts with '0x'.
+ - ``<deviceID>``: 4 hexadecimal digits starts with '0x'.
+
+ Here is a sample value for gpu_ids::
+
+ gpu_ids = 0x1000/0x0079,0x2100/0x0080
+
+* It is necessary that pyghmi version >= 1.0.22 and pysnmp version >= 4.2.3
+ are used on the conductor. The latest version of pyghmi can
+ be downloaded from `here <https://pypi.python.org/pypi/pyghmi/>`__
+ and pysnmp can be downloaded from `here
+ <https://pypi.python.org/pypi/pysnmp/>`__.
+
+Supported properties
+~~~~~~~~~~~~~~~~~~~~
+
+The inspection process will discover the following essential properties
+(properties required for scheduling deployment):
+
+* ``memory_mb``: memory size
+
+* ``cpus``: number of cpus
+
+* ``cpu_arch``: cpu architecture
+
+* ``local_gb``: disk size
+
+Inspection can also discover the following extra capabilities for iRMC
+drivers:
+
+* ``irmc_firmware_version``: iRMC firmware version
+
+* ``rom_firmware_version``: ROM firmware version
+
+* ``trusted_boot``: The flag whether TPM(Trusted Platform Module) is
+ supported by the server. The possible values are 'True' or 'False'.
+
+* ``server_model``: server model
+
+* ``pci_gpu_devices``: number of gpu devices connected to the bare metal.
+
+.. note::
+
+ * The disk size is returned only when eLCM License for FUJITSU PRIMERGY
+ servers is activated. If the license is not activated, then Hardware
+ Inspection will fail to get this value.
+ * Before inspecting, if the server is power-off, it will be turned on
+ automatically. System will wait for a few second before start
+ inspecting. After inspection, power status will be restored to the
+ previous state.
+
+The operator can specify these capabilities in compute service flavor, for
+example::
+
+ openstack flavor set baremetal-flavor-name --property capabilities:irmc_firmware_version="iRMC S4-8.64F"
+
+ openstack flavor set baremetal-flavor-name --property capabilities:server_model="TX2540M1F5"
+
+ openstack flavor set baremetal-flavor-name --property capabilities:pci_gpu_devices="1"
+
+See :ref:`capabilities-discovery` for more details and examples.
+
Supported platforms
===================
This driver supports FUJITSU PRIMERGY BX S4 or RX S8 servers and above.
diff --git a/driver-requirements.txt b/driver-requirements.txt
index a344bd667..c643d2e96 100644
--- a/driver-requirements.txt
+++ b/driver-requirements.txt
@@ -8,7 +8,7 @@ proliantutils>=2.4.1
pysnmp
python-ironic-inspector-client>=1.5.0
python-oneviewclient<3.0.0,>=2.5.2
-python-scciclient>=0.5.0
+python-scciclient>=0.6.0
UcsSdk==0.8.2.2
python-dracclient>=1.3.0
diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample
index c11b6cd0b..7c61ed8b5 100644
--- a/etc/ironic/ironic.conf.sample
+++ b/etc/ironic/ironic.conf.sample
@@ -1963,6 +1963,15 @@
# value)
#clean_priority_restore_irmc_bios_config = 0
+# List of vendor IDs and device IDs for GPU device to inspect.
+# List items are in format vendorID/deviceID and separated by
+# commas. GPU inspection will use this value to count the
+# number of GPU device in a node. If this option is not
+# defined, then leave out pci_gpu_devices in capabilities
+# property. Sample gpu_ids value: 0x1000/0x0079,0x2100/0x0080
+# (list value)
+#gpu_ids =
+
[ironic_lib]
diff --git a/ironic/conf/irmc.py b/ironic/conf/irmc.py
index bc558d03c..ce0a0d53c 100644
--- a/ironic/conf/irmc.py
+++ b/ironic/conf/irmc.py
@@ -72,6 +72,15 @@ opts = [
cfg.IntOpt('clean_priority_restore_irmc_bios_config',
default=0,
help=_('Priority for restore_irmc_bios_config clean step.')),
+ cfg.ListOpt('gpu_ids',
+ default=[],
+ help=_('List of vendor IDs and device IDs for GPU device to '
+ 'inspect. List items are in format vendorID/deviceID '
+ 'and separated by commas. GPU inspection will use this '
+ 'value to count the number of GPU device in a node. If '
+ 'this option is not defined, then leave out '
+ 'pci_gpu_devices in capabilities property. '
+ 'Sample gpu_ids value: 0x1000/0x0079,0x2100/0x0080')),
]
diff --git a/ironic/drivers/modules/irmc/inspect.py b/ironic/drivers/modules/irmc/inspect.py
index 582eb0b85..012fac5e8 100644
--- a/ironic/drivers/modules/irmc/inspect.py
+++ b/ironic/drivers/modules/irmc/inspect.py
@@ -14,13 +14,19 @@
"""
iRMC Inspect Interface
"""
+import re
+
from ironic_lib import metrics_utils
from oslo_log import log as logging
from oslo_utils import importutils
+from ironic.common import boot_devices
from ironic.common import exception
from ironic.common.i18n import _
from ironic.common import states
+from ironic.common import utils
+from ironic.conductor import utils as manager_utils
+from ironic.conf import CONF
from ironic.drivers import base
from ironic.drivers.modules.irmc import common as irmc_common
from ironic.drivers.modules import snmp
@@ -84,6 +90,9 @@ sc2UnitNodeMacAddress OBJECT-TYPE
"""
MAC_ADDRESS_OID = '1.3.6.1.4.1.231.2.10.2.2.10.3.1.1.9.1'
+CAPABILITIES_PROPERTIES = {'trusted_boot', 'irmc_firmware_version',
+ 'rom_firmware_version', 'server_model',
+ 'pci_gpu_devices'}
def _get_mac_addresses(node):
@@ -108,20 +117,55 @@ def _get_mac_addresses(node):
if c == NODE_CLASS_OID_VALUE['primary']]
-def _inspect_hardware(node):
+def _inspect_hardware(node, **kwargs):
"""Inspect the node and get hardware information.
:param node: node object.
+ :param kwargs: the dictionary of additional parameters.
:raises: HardwareInspectionFailure, if unable to get essential
hardware properties.
:returns: a pair of dictionary and list, the dictionary contains
keys as in IRMCInspect.ESSENTIAL_PROPERTIES and its inspected
values, the list contains mac addresses.
"""
+ capabilities_props = set(CAPABILITIES_PROPERTIES)
+
+ # Remove all capabilities item which will be inspected in the existing
+ # capabilities of node
+ if 'capabilities' in node.properties:
+ existing_cap = node.properties['capabilities'].split(',')
+ for item in capabilities_props:
+ for prop in existing_cap:
+ if item == prop.split(':')[0]:
+ existing_cap.remove(prop)
+ node.properties['capabilities'] = ",".join(existing_cap)
+
+ # get gpu_ids in ironic configuration
+ values = [gpu_id.lower() for gpu_id in CONF.irmc.gpu_ids]
+
+ # if gpu_ids = [], pci_gpu_devices will not be inspected
+ if len(values) == 0:
+ capabilities_props.remove('pci_gpu_devices')
+
try:
report = irmc_common.get_irmc_report(node)
props = scci.get_essential_properties(
report, IRMCInspect.ESSENTIAL_PROPERTIES)
+ d_info = irmc_common.parse_driver_info(node)
+ capabilities = scci.get_capabilities_properties(
+ d_info,
+ capabilities_props,
+ values,
+ **kwargs)
+ if capabilities:
+ if capabilities.get('pci_gpu_devices') == 0:
+ capabilities.pop('pci_gpu_devices')
+ if capabilities.get('trusted_boot') is False:
+ capabilities.pop('trusted_boot')
+ capabilities = utils.get_updated_capabilities(
+ node.properties.get('capabilities'), capabilities)
+ if capabilities:
+ props['capabilities'] = capabilities
macs = _get_mac_addresses(node)
except (scci.SCCIInvalidInputError,
scci.SCCIClientError,
@@ -137,6 +181,19 @@ def _inspect_hardware(node):
class IRMCInspect(base.InspectInterface):
"""Interface for out of band inspection."""
+ def __init__(self):
+ """Validate the driver-specific inspection information.
+
+ This action will validate gpu_ids value along with starting
+ ironic-conductor service.
+ """
+ for gpu_id in CONF.irmc.gpu_ids:
+ if not re.match('^0x[0-9a-f]{4}/0x[0-9a-f]{4}$', gpu_id.lower()):
+ raise exception.InvalidParameterValue(_(
+ "Invalid [irmc]/gpu_ids configuration option."))
+
+ super(IRMCInspect, self).__init__()
+
def get_properties(self):
"""Return the properties of the interface.
@@ -170,7 +227,20 @@ class IRMCInspect(base.InspectInterface):
:returns: states.MANAGEABLE, if hardware inspection succeeded.
"""
node = task.node
- (props, macs) = _inspect_hardware(node)
+ kwargs = {}
+ # Inspect additional capabilities task requires node with power on
+ # status
+ old_power_state = task.driver.power.get_power_state(task)
+ if old_power_state == states.POWER_OFF:
+ manager_utils.node_set_boot_device(task, boot_devices.BIOS, False)
+ manager_utils.node_power_action(task, states.POWER_ON)
+
+ LOG.info("The Node %(node_uuid)s being powered on for inspection",
+ {'node_uuid': task.node.uuid})
+
+ kwargs['sleep_flag'] = True
+
+ (props, macs) = _inspect_hardware(node, **kwargs)
node.properties = dict(node.properties, **props)
node.save()
@@ -189,4 +259,12 @@ class IRMCInspect(base.InspectInterface):
{'address': mac, 'node_uuid': node.uuid})
LOG.info("Node %s inspected", node.uuid)
+ # restore old power state
+ if old_power_state == states.POWER_OFF:
+ manager_utils.node_power_action(task, states.POWER_OFF)
+
+ LOG.info("The Node %(node_uuid)s being powered off after "
+ "inspection",
+ {'node_uuid': task.node.uuid})
+
return states.MANAGEABLE
diff --git a/ironic/tests/unit/drivers/modules/irmc/test_inspect.py b/ironic/tests/unit/drivers/modules/irmc/test_inspect.py
index c74f51edb..92ce437f6 100644
--- a/ironic/tests/unit/drivers/modules/irmc/test_inspect.py
+++ b/ironic/tests/unit/drivers/modules/irmc/test_inspect.py
@@ -20,15 +20,19 @@ 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 manager_utils
from ironic.drivers.modules.irmc import common as irmc_common
from ironic.drivers.modules.irmc import inspect as irmc_inspect
+from ironic.drivers.modules.irmc import power as irmc_power
from ironic import objects
from ironic.tests.unit.conductor import mgr_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.drivers import third_party_driver_mock_specs \
- as mock_specs
+from ironic.tests.unit.drivers import (
+ third_party_driver_mock_specs as mock_specs
+)
from ironic.tests.unit.objects import utils as obj_utils
INFO_DICT = db_utils.get_test_irmc_info()
@@ -66,24 +70,45 @@ class IRMCInspectInternalMethodsTestCase(db_base.DbTestCase):
autospec=True)
def test__inspect_hardware(
self, get_irmc_report_mock, scci_mock, _get_mac_addresses_mock):
+ # Set config flags
+ gpu_ids = ['0x1000/0x0079', '0x2100/0x0080']
+
+ self.config(gpu_ids=gpu_ids, group='irmc')
+ kwargs = {'sleep_flag': False}
+
inspected_props = {
'memory_mb': '1024',
'local_gb': 10,
'cpus': 2,
'cpu_arch': 'x86_64'}
+ inspected_capabilities = {
+ 'trusted_boot': False,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 1}
+
inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb']
report = 'fake_report'
get_irmc_report_mock.return_value = report
scci_mock.get_essential_properties.return_value = inspected_props
+ scci_mock.get_capabilities_properties.return_value = (
+ inspected_capabilities)
_get_mac_addresses_mock.return_value = inspected_macs
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
- result = irmc_inspect._inspect_hardware(task.node)
-
+ result = irmc_inspect._inspect_hardware(task.node, **kwargs)
get_irmc_report_mock.assert_called_once_with(task.node)
scci_mock.get_essential_properties.assert_called_once_with(
report, irmc_inspect.IRMCInspect.ESSENTIAL_PROPERTIES)
- self.assertEqual((inspected_props, inspected_macs), result)
+ scci_mock.get_capabilities_properties.assert_called_once_with(
+ mock.ANY, irmc_inspect.CAPABILITIES_PROPERTIES,
+ gpu_ids, **kwargs)
+ expected_props = dict(inspected_props)
+ inspected_capabilities = utils.get_updated_capabilities(
+ '', inspected_capabilities)
+ expected_props['capabilities'] = inspected_capabilities
+ self.assertEqual((expected_props, inspected_macs), result)
@mock.patch.object(irmc_inspect, '_get_mac_addresses', spec_set=True,
autospec=True)
@@ -94,6 +119,7 @@ class IRMCInspectInternalMethodsTestCase(db_base.DbTestCase):
def test__inspect_hardware_exception(
self, get_irmc_report_mock, scci_mock, _get_mac_addresses_mock):
report = 'fake_report'
+ kwargs = {'sleep_flag': False}
get_irmc_report_mock.return_value = report
side_effect = exception.SNMPFailure("fake exception")
scci_mock.get_essential_properties.side_effect = side_effect
@@ -104,7 +130,7 @@ class IRMCInspectInternalMethodsTestCase(db_base.DbTestCase):
shared=True) as task:
self.assertRaises(exception.HardwareInspectionFailure,
irmc_inspect._inspect_hardware,
- task.node)
+ task.node, **kwargs)
get_irmc_report_mock.assert_called_once_with(task.node)
self.assertFalse(_get_mac_addresses_mock.called)
@@ -131,7 +157,7 @@ class IRMCInspectTestCase(db_base.DbTestCase):
def test_validate(self, parse_driver_info_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
- task.driver.power.validate(task)
+ task.driver.inspect.validate(task)
parse_driver_info_mock.assert_called_once_with(task.node)
@mock.patch.object(irmc_common, 'parse_driver_info', spec_set=True,
@@ -143,22 +169,31 @@ class IRMCInspectTestCase(db_base.DbTestCase):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertRaises(exception.InvalidParameterValue,
- task.driver.power.validate,
+ task.driver.inspect.validate,
task)
+ def test__init_fail_invalid_input(self):
+ # Set config flags
+ self.config(gpu_ids='100/x079,0x20/', group='irmc')
+ self.assertRaises(exception.InvalidParameterValue,
+ irmc_inspect.IRMCInspect)
+
@mock.patch.object(irmc_inspect.LOG, 'info', spec_set=True, autospec=True)
@mock.patch('ironic.drivers.modules.irmc.inspect.objects.Port',
spec_set=True, autospec=True)
@mock.patch.object(irmc_inspect, '_inspect_hardware', spec_set=True,
autospec=True)
- def test_inspect_hardware(self, _inspect_hardware_mock, port_mock,
- info_mock):
+ @mock.patch.object(irmc_power.IRMCPower, 'get_power_state', spec_set=True,
+ autospec=True)
+ def test_inspect_hardware(self, power_state_mock, _inspect_hardware_mock,
+ port_mock, info_mock):
inspected_props = {
'memory_mb': '1024',
'local_gb': 10,
'cpus': 2,
'cpu_arch': 'x86_64'}
inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb']
+ power_state_mock.return_value = states.POWER_ON
_inspect_hardware_mock.return_value = (inspected_props,
inspected_macs)
new_port_mock1 = mock.MagicMock(spec=objects.Port)
@@ -167,7 +202,7 @@ class IRMCInspectTestCase(db_base.DbTestCase):
port_mock.side_effect = [new_port_mock1, new_port_mock2]
with task_manager.acquire(self.context, self.node.uuid,
- shared=True) as task:
+ shared=False) as task:
result = task.driver.inspect.inspect_hardware(task)
node_id = task.node.id
@@ -204,16 +239,73 @@ class IRMCInspectTestCase(db_base.DbTestCase):
self.assertEqual(inspected_props, task.node.properties)
self.assertEqual(states.MANAGEABLE, result)
+ @mock.patch.object(manager_utils, 'node_power_action', spec_set=True,
+ autospec=True)
+ @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True,
+ autospec=True)
+ @mock.patch.object(irmc_inspect.LOG, 'info', spec_set=True, autospec=True)
+ @mock.patch.object(irmc_inspect.objects, 'Port',
+ spec_set=True, autospec=True)
+ @mock.patch.object(irmc_inspect, '_inspect_hardware', spec_set=True,
+ autospec=True)
+ @mock.patch.object(irmc_power.IRMCPower, 'get_power_state', spec_set=True,
+ autospec=True)
+ def test_inspect_hardware_with_power_off(self, power_state_mock,
+ _inspect_hardware_mock,
+ port_mock, info_mock,
+ set_boot_device_mock,
+ power_action_mock):
+ inspected_props = {
+ 'memory_mb': '1024',
+ 'local_gb': 10,
+ 'cpus': 2,
+ 'cpu_arch': 'x86_64'}
+ inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb']
+ power_state_mock.return_value = states.POWER_OFF
+ _inspect_hardware_mock.return_value = (inspected_props,
+ inspected_macs)
+ new_port_mock1 = mock.MagicMock(spec=objects.Port)
+ new_port_mock2 = mock.MagicMock(spec=objects.Port)
+
+ port_mock.side_effect = [new_port_mock1, new_port_mock2]
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ result = task.driver.inspect.inspect_hardware(task)
+
+ node_id = task.node.id
+ _inspect_hardware_mock.assert_called_once_with(task.node,
+ sleep_flag=True)
+
+ port_mock.assert_has_calls([
+ mock.call(task.context, address=inspected_macs[0],
+ node_id=node_id),
+ mock.call(task.context, address=inspected_macs[1],
+ node_id=node_id)
+ ])
+ new_port_mock1.create.assert_called_once_with()
+ new_port_mock2.create.assert_called_once_with()
+
+ self.assertTrue(info_mock.called)
+ task.node.refresh()
+ self.assertEqual(inspected_props, task.node.properties)
+ self.assertEqual(states.MANAGEABLE, result)
+ self.assertEqual(power_action_mock.called, True)
+ self.assertEqual(power_action_mock.call_count, 2)
+
@mock.patch('ironic.objects.Port', spec_set=True, autospec=True)
@mock.patch.object(irmc_inspect, '_inspect_hardware', spec_set=True,
autospec=True)
+ @mock.patch.object(irmc_power.IRMCPower, 'get_power_state', spec_set=True,
+ autospec=True)
def test_inspect_hardware_inspect_exception(
- self, _inspect_hardware_mock, port_mock):
+ self, power_state_mock, _inspect_hardware_mock, port_mock):
side_effect = exception.HardwareInspectionFailure("fake exception")
_inspect_hardware_mock.side_effect = side_effect
+ power_state_mock.return_value = states.POWER_ON
with task_manager.acquire(self.context, self.node.uuid,
- shared=True) as task:
+ shared=False) as task:
self.assertRaises(exception.HardwareInspectionFailure,
task.driver.inspect.inspect_hardware,
task)
@@ -223,8 +315,11 @@ class IRMCInspectTestCase(db_base.DbTestCase):
@mock.patch('ironic.objects.Port', spec_set=True, autospec=True)
@mock.patch.object(irmc_inspect, '_inspect_hardware', spec_set=True,
autospec=True)
+ @mock.patch.object(irmc_power.IRMCPower, 'get_power_state', spec_set=True,
+ autospec=True)
def test_inspect_hardware_mac_already_exist(
- self, _inspect_hardware_mock, port_mock, warn_mock):
+ self, power_state_mock, _inspect_hardware_mock,
+ port_mock, warn_mock):
inspected_props = {
'memory_mb': '1024',
'local_gb': 10,
@@ -233,12 +328,13 @@ class IRMCInspectTestCase(db_base.DbTestCase):
inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb']
_inspect_hardware_mock.return_value = (inspected_props,
inspected_macs)
+ power_state_mock.return_value = states.POWER_ON
side_effect = exception.MACAlreadyExists("fake exception")
new_port_mock = port_mock.return_value
new_port_mock.create.side_effect = side_effect
with task_manager.acquire(self.context, self.node.uuid,
- shared=True) as task:
+ shared=False) as task:
result = task.driver.inspect.inspect_hardware(task)
_inspect_hardware_mock.assert_called_once_with(task.node)
@@ -246,3 +342,225 @@ class IRMCInspectTestCase(db_base.DbTestCase):
task.node.refresh()
self.assertEqual(inspected_props, task.node.properties)
self.assertEqual(states.MANAGEABLE, result)
+
+ @mock.patch.object(irmc_inspect, '_get_mac_addresses', spec_set=True,
+ autospec=True)
+ @mock.patch.object(irmc_inspect, 'scci',
+ spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
+ @mock.patch.object(irmc_common, 'get_irmc_report', spec_set=True,
+ autospec=True)
+ def _test_inspect_hardware_props(self, gpu_ids,
+ existed_capabilities,
+ inspected_capabilities,
+ expected_capabilities,
+ get_irmc_report_mock,
+ scci_mock,
+ _get_mac_addresses_mock):
+ capabilities_props = set(irmc_inspect.CAPABILITIES_PROPERTIES)
+
+ # if gpu_ids = [], pci_gpu_devices will not be inspected
+ if len(gpu_ids) == 0:
+ capabilities_props.remove('pci_gpu_devices')
+
+ self.config(gpu_ids=gpu_ids, group='irmc')
+ kwargs = {'sleep_flag': False}
+
+ inspected_props = {
+ 'memory_mb': '1024',
+ 'local_gb': 10,
+ 'cpus': 2,
+ 'cpu_arch': 'x86_64'}
+
+ inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb']
+ report = 'fake_report'
+ get_irmc_report_mock.return_value = report
+ scci_mock.get_essential_properties.return_value = inspected_props
+ scci_mock.get_capabilities_properties.return_value = \
+ inspected_capabilities
+ _get_mac_addresses_mock.return_value = inspected_macs
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ task.node.properties[u'capabilities'] =\
+ ",".join('%(k)s:%(v)s' % {'k': k, 'v': v}
+ for k, v in existed_capabilities.items())
+ result = irmc_inspect._inspect_hardware(task.node, **kwargs)
+ get_irmc_report_mock.assert_called_once_with(task.node)
+ scci_mock.get_essential_properties.assert_called_once_with(
+ report, irmc_inspect.IRMCInspect.ESSENTIAL_PROPERTIES)
+ scci_mock.get_capabilities_properties.assert_called_once_with(
+ mock.ANY, capabilities_props,
+ gpu_ids, **kwargs)
+ expected_capabilities = utils.get_updated_capabilities(
+ '', expected_capabilities)
+
+ set1 = set(expected_capabilities.split(','))
+ set2 = set(result[0]['capabilities'].split(','))
+ self.assertEqual(set1, set2)
+
+ def test_inspect_hardware_existing_cap_in_props(self):
+ # Set config flags
+ gpu_ids = ['0x1000/0x0079', '0x2100/0x0080']
+ existed_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 1}
+ inspected_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 1}
+ expected_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 1}
+
+ self._test_inspect_hardware_props(gpu_ids,
+ existed_capabilities,
+ inspected_capabilities,
+ expected_capabilities)
+
+ def test_inspect_hardware_props_empty_gpu_ids(self):
+ # Set config flags
+ gpu_ids = []
+ existed_capabilities = {}
+ inspected_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x'}
+ expected_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x'}
+
+ self._test_inspect_hardware_props(gpu_ids,
+ existed_capabilities,
+ inspected_capabilities,
+ expected_capabilities)
+
+ def test_inspect_hardware_props_pci_gpu_devices_return_zero(self):
+ # Set config flags
+ gpu_ids = ['0x1000/0x0079', '0x2100/0x0080']
+
+ existed_capabilities = {}
+ inspected_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 0}
+ expected_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x'}
+
+ self._test_inspect_hardware_props(gpu_ids,
+ existed_capabilities,
+ inspected_capabilities,
+ expected_capabilities)
+
+ def test_inspect_hardware_props_empty_gpu_ids_and_existing_cap(self):
+ # Set config flags
+ gpu_ids = []
+ existed_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 1}
+ inspected_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x'}
+ expected_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x'}
+
+ self._test_inspect_hardware_props(gpu_ids,
+ existed_capabilities,
+ inspected_capabilities,
+ expected_capabilities)
+
+ def test_inspect_hardware_props_pci_gpu_devices_zero_and_existing_cap(
+ self):
+ # Set config flags
+ gpu_ids = ['0x1000/0x0079', '0x2100/0x0080']
+ existed_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 1}
+ inspected_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 0}
+ expected_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x'}
+
+ self._test_inspect_hardware_props(gpu_ids,
+ existed_capabilities,
+ inspected_capabilities,
+ expected_capabilities)
+
+ def test_inspect_hardware_props_trusted_boot_is_false(self):
+ # Set config flags
+ gpu_ids = ['0x1000/0x0079', '0x2100/0x0080']
+ existed_capabilities = {}
+ inspected_capabilities = {
+ 'trusted_boot': False,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 1}
+ expected_capabilities = {
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 1}
+
+ self._test_inspect_hardware_props(gpu_ids,
+ existed_capabilities,
+ inspected_capabilities,
+ expected_capabilities)
+
+ def test_inspect_hardware_props_trusted_boot_is_false_and_existing_cap(
+ self):
+ # Set config flags
+ gpu_ids = ['0x1000/0x0079', '0x2100/0x0080']
+ existed_capabilities = {
+ 'trusted_boot': True,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 1}
+ inspected_capabilities = {
+ 'trusted_boot': False,
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 1}
+ expected_capabilities = {
+ 'irmc_firmware_version': 'iRMC S4-7.82F',
+ 'server_model': 'TX2540M1F5',
+ 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x',
+ 'pci_gpu_devices': 1}
+ self._test_inspect_hardware_props(gpu_ids,
+ existed_capabilities,
+ inspected_capabilities,
+ expected_capabilities)
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 8f59f1072..f21adafd4 100644
--- a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py
+++ b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py
@@ -102,6 +102,7 @@ SCCICLIENT_IRMC_SCCI_SPEC = (
'get_virtual_cd_set_params_cmd',
'get_virtual_fd_set_params_cmd',
'get_essential_properties',
+ 'get_capabilities_properties',
)
SCCICLIENT_IRMC_ELCM_SPEC = (
'backup_bios_config',
diff --git a/releasenotes/notes/irmc-additional-capabilities-4fd72ba50d05676c.yaml b/releasenotes/notes/irmc-additional-capabilities-4fd72ba50d05676c.yaml
new file mode 100644
index 000000000..a47571e35
--- /dev/null
+++ b/releasenotes/notes/irmc-additional-capabilities-4fd72ba50d05676c.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ Adds new capabilities ("server_model", "rom_firmware_version", "pci_gpu_devices",
+ "trusted_boot" and "irmc_firmware_version") to the iRMC out-of-band hardware inspection
+ for FUJITSU PRIMERGY bare metal nodes with firmware iRMC S4 and beyond.
+ Before inspecting, if a server is power-off, it will be turned on automatically.
+ System will wait for a few second before start inspecting. After inspection,
+ power status will be restored to the previous state.