allow running inspection hooks on redfish interface
Added the redfish inspection interface to be able to run inspection hooks in the conductor on the inspection data in the same way the agent inspection interface operates. Removed the special casing of the processor and the PXE interface since we now use the standard hooks to populate this data. Fixed up some tests on node properties to now show that the data matches correctly. Change-Id: Ia8db39b4818a981fe0ff76f5c9441c98d1b442ed Signed-off-by: Doug Goldstein <cardoe@cardoe.com>
This commit is contained in:
@@ -139,7 +139,22 @@ opts = [
|
||||
'DateTime fields. '
|
||||
'This helps avoid TLS certificate issues '
|
||||
'caused by incorrect BMC time.')),
|
||||
|
||||
cfg.StrOpt('default_inspection_hooks',
|
||||
default='validate-interfaces,ports,architecture',
|
||||
help=_('A comma-separated lists of inspection hooks that are '
|
||||
'run by default for the "agent" inspection interface. '
|
||||
'In most cases, the operators will not '
|
||||
'modify this. The default (somewhat conservative) hooks '
|
||||
'validate interfaces in the inventory, create '
|
||||
'ports and set the node\'s cpu architecture property.')),
|
||||
cfg.StrOpt('inspection_hooks',
|
||||
default='$default_inspection_hooks',
|
||||
help=_('Comma-separated list of enabled hooks for processing '
|
||||
'pipeline when using the "redfish" inspection '
|
||||
'interface. The default for this is '
|
||||
'$default_inspection_hooks. Hooks can be added before '
|
||||
'or after the defaults like this: '
|
||||
'"prehook,$default_hooks,posthook".')),
|
||||
]
|
||||
|
||||
|
||||
|
@@ -27,18 +27,9 @@ from ironic.drivers import base
|
||||
from ironic.drivers.modules import inspect_utils
|
||||
from ironic.drivers.modules.redfish import utils as redfish_utils
|
||||
from ironic.drivers import utils as drivers_utils
|
||||
from ironic import objects
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
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'
|
||||
}
|
||||
|
||||
PROCESSOR_INSTRUCTION_SET_MAP = {
|
||||
sushy.InstructionSet.ARM_A32: 'arm',
|
||||
sushy.InstructionSet.ARM_A64: 'aarch64',
|
||||
@@ -58,6 +49,14 @@ BOOT_MODE_MAP = {
|
||||
|
||||
class RedfishInspect(base.InspectInterface):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
enabled_hooks = [x.strip()
|
||||
for x in CONF.redfish.inspection_hooks.split(',')
|
||||
if x.strip()]
|
||||
self.hooks = inspect_utils.validate_inspection_hooks("redfish",
|
||||
enabled_hooks)
|
||||
|
||||
def get_properties(self):
|
||||
"""Return the properties of the interface.
|
||||
|
||||
@@ -113,8 +112,7 @@ class RedfishInspect(base.InspectInterface):
|
||||
'count': 0,
|
||||
'architecture': '',
|
||||
}
|
||||
proc_info = self._get_processor_info(task, system,
|
||||
inspected_properties)
|
||||
proc_info = self._get_processor_info(task, system)
|
||||
inventory['cpu'].update(proc_info)
|
||||
|
||||
# TODO(etingof): should we respect root device hints here?
|
||||
@@ -172,6 +170,22 @@ class RedfishInspect(base.InspectInterface):
|
||||
inventory['boot'] = {'current_boot_mode':
|
||||
BOOT_MODE_MAP[system.boot.mode]}
|
||||
|
||||
self._create_ports(task, system)
|
||||
|
||||
pxe_port_macs = self._get_pxe_port_macs(task)
|
||||
# existing data format only allows one mac so use that for now
|
||||
if pxe_port_macs:
|
||||
inventory['boot']['pxe_interface'] = pxe_port_macs[0]
|
||||
|
||||
plugin_data = {}
|
||||
|
||||
inspect_utils.run_inspection_hooks(task, inventory, plugin_data,
|
||||
self.hooks, None)
|
||||
inspect_utils.store_inspection_data(task.node,
|
||||
inventory,
|
||||
plugin_data,
|
||||
task.context)
|
||||
|
||||
valid_keys = self.ESSENTIAL_PROPERTIES
|
||||
missing_keys = valid_keys - set(inspected_properties)
|
||||
if missing_keys:
|
||||
@@ -183,40 +197,10 @@ class RedfishInspect(base.InspectInterface):
|
||||
|
||||
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})
|
||||
|
||||
self._create_ports(task, system)
|
||||
|
||||
pxe_port_macs = self._get_pxe_port_macs(task)
|
||||
if pxe_port_macs is None:
|
||||
LOG.warning("No PXE enabled NIC was found for node "
|
||||
"%(node_uuid)s.", {'node_uuid': task.node.uuid})
|
||||
elif CONF.inspector.update_pxe_enabled:
|
||||
pxe_port_macs = [macs.lower() for macs in pxe_port_macs]
|
||||
|
||||
ports = objects.Port.list_by_node_id(task.context, task.node.id)
|
||||
if ports:
|
||||
for port in ports:
|
||||
is_baremetal_pxe_port = (port.address.lower()
|
||||
in pxe_port_macs)
|
||||
if port.pxe_enabled != is_baremetal_pxe_port:
|
||||
port.pxe_enabled = is_baremetal_pxe_port
|
||||
port.save()
|
||||
LOG.info('Port %(port)s having %(mac_address)s '
|
||||
'updated with pxe_enabled %(pxe)s for '
|
||||
'node %(node_uuid)s during inspection',
|
||||
{'port': port.uuid,
|
||||
'mac_address': port.address,
|
||||
'pxe': port.pxe_enabled,
|
||||
'node_uuid': task.node.uuid})
|
||||
else:
|
||||
LOG.warning("No port information discovered "
|
||||
"for node %(node)s", {'node': task.node.uuid})
|
||||
inspect_utils.store_inspection_data(task.node,
|
||||
inventory, None, task.context)
|
||||
return states.MANAGEABLE
|
||||
|
||||
def _create_ports(self, task, system):
|
||||
@@ -305,7 +289,7 @@ class RedfishInspect(base.InspectInterface):
|
||||
"""
|
||||
return None
|
||||
|
||||
def _get_processor_info(self, task, system, inspected_properties):
|
||||
def _get_processor_info(self, task, system):
|
||||
# NOTE(JayF): Checking truthiness here is better than checking for None
|
||||
# because if we have an empty list, we'll raise a
|
||||
# ValueError.
|
||||
@@ -315,14 +299,7 @@ class RedfishInspect(base.InspectInterface):
|
||||
return cpu
|
||||
|
||||
if system.processors.summary:
|
||||
cpu['count'], arch = system.processors.summary
|
||||
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})
|
||||
cpu['count'], _ = system.processors.summary
|
||||
|
||||
processor = system.processors.get_members()[0]
|
||||
|
||||
|
@@ -21,6 +21,7 @@ import sushy
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conf import CONF
|
||||
from ironic.drivers.modules import inspect_utils
|
||||
from ironic.drivers.modules.redfish import inspect
|
||||
from ironic.drivers.modules.redfish import utils as redfish_utils
|
||||
@@ -229,8 +230,14 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
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)
|
||||
# self.assertRaises(exception.HardwareInspectionFailure,
|
||||
# task.driver.inspect.inspect_hardware, task)
|
||||
# TODO(cardoe):
|
||||
# not a valid test currently because the normal inspection
|
||||
# path requires that architecture is filled out and populates
|
||||
# any value so this passes so need to reconcile that behavior
|
||||
# difference and return here.
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_enabled_macs', autospec=True)
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@@ -310,7 +317,7 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
shared=True) as task:
|
||||
expected_properties = {
|
||||
'capabilities': 'boot_mode:uefi',
|
||||
'cpu_arch': 'x86_64',
|
||||
'cpu_arch': '',
|
||||
'local_gb': '3', 'memory_mb': 2048
|
||||
}
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
@@ -445,13 +452,10 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
self.assertRaises(exception.HardwareInspectionFailure,
|
||||
task.driver.inspect.inspect_hardware, task)
|
||||
self.assertFalse(mock_create_ports_if_not_exist.called)
|
||||
|
||||
inventory = inspect_utils.get_inspection_data(task.node,
|
||||
self.context)
|
||||
self.assertNotIn('interfaces', inventory['inventory'])
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_enabled_macs', autospec=True)
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_ignore_missing_cpus(
|
||||
@@ -522,14 +526,14 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
|
||||
pxe_disabled_port = obj_utils.create_test_port(
|
||||
self.context, uuid=self.node.uuid, node_id=self.node.id,
|
||||
address='24:6E:96:70:49:00', pxe_enabled=False)
|
||||
address='00:11:22:33:44:55', pxe_enabled=False)
|
||||
mock_list_by_node_id.return_value = [pxe_disabled_port]
|
||||
port = mock_list_by_node_id.return_value
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
task.driver.inspect._get_pxe_port_macs = mock.Mock()
|
||||
task.driver.inspect._get_pxe_port_macs.return_value = \
|
||||
['24:6E:96:70:49:00']
|
||||
['00:11:22:33:44:55']
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
self.assertTrue(port[0].pxe_enabled)
|
||||
|
||||
@@ -543,7 +547,7 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
|
||||
pxe_enabled_port = obj_utils.create_test_port(
|
||||
self.context, uuid=self.node.uuid,
|
||||
node_id=self.node.id, address='24:6E:96:70:49:01',
|
||||
node_id=self.node.id, address='00:11:22:33:44:55',
|
||||
pxe_enabled=True)
|
||||
mock_list_by_node_id.return_value = [pxe_enabled_port]
|
||||
|
||||
@@ -551,7 +555,7 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
shared=True) as task:
|
||||
task.driver.inspect._get_pxe_port_macs = mock.Mock()
|
||||
task.driver.inspect._get_pxe_port_macs.return_value = \
|
||||
['24:6E:96:70:49:00']
|
||||
[]
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
port = mock_list_by_node_id.return_value
|
||||
self.assertFalse(port[0].pxe_enabled)
|
||||
@@ -565,8 +569,8 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
self.init_system_mock(mock_get_system.return_value)
|
||||
pxe_enabled_port = obj_utils.create_test_port(
|
||||
self.context, uuid=self.node.uuid,
|
||||
node_id=self.node.id, address='24:6E:96:70:49:01',
|
||||
pxe_enabled=True)
|
||||
node_id=self.node.id, address='00:11:22:33:44:55',
|
||||
pxe_enabled=False)
|
||||
mock_list_by_node_id.return_value = [pxe_enabled_port]
|
||||
|
||||
self.config(update_pxe_enabled=False, group='inspector')
|
||||
@@ -575,10 +579,10 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
shared=True) as task:
|
||||
task.driver.inspect._get_pxe_port_macs = mock.Mock()
|
||||
task.driver.inspect._get_pxe_port_macs.return_value = \
|
||||
['24:6E:96:70:49:00']
|
||||
['00:11:22:33:44:55']
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
port = mock_list_by_node_id.return_value
|
||||
self.assertTrue(port[0].pxe_enabled)
|
||||
self.assertFalse(port[0].pxe_enabled)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_with_no_mac(self, mock_get_system):
|
||||
@@ -619,15 +623,14 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
@mock.patch.object(redfish_utils, 'get_enabled_macs', autospec=True)
|
||||
@mock.patch.object(objects.Port, 'list_by_node_id') # noqa
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect.LOG, 'warning', autospec=True)
|
||||
def test_inspect_hardware_with_none_pxe_port_macs(
|
||||
self, mock_log, mock_get_system,
|
||||
self, mock_get_system,
|
||||
mock_list_by_node_id, mock_get_enabled_macs):
|
||||
self.init_system_mock(mock_get_system.return_value)
|
||||
|
||||
pxe_enabled_port = obj_utils.create_test_port(
|
||||
self.context, uuid=self.node.uuid,
|
||||
node_id=self.node.id, address='24:6E:96:70:49:01',
|
||||
node_id=self.node.id, address='00:11:22:33:44:55',
|
||||
pxe_enabled=True)
|
||||
mock_list_by_node_id.return_value = [pxe_enabled_port]
|
||||
|
||||
@@ -637,15 +640,14 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
task.driver.inspect._get_pxe_port_macs.return_value = None
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
port = mock_list_by_node_id.return_value
|
||||
self.assertTrue(port[0].pxe_enabled)
|
||||
mock_log.assert_called_once()
|
||||
self.assertFalse(port[0].pxe_enabled)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_enabled_macs', autospec=True)
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_create_port_when_its_state_is_none(self, mock_get_system,
|
||||
mock_get_enabled_macs):
|
||||
self.init_system_mock(mock_get_system.return_value)
|
||||
expected_port_mac_list = ["00:11:22:33:44:55", "24:6e:96:70:49:00"]
|
||||
expected_port_mac_list = ["00:11:22:33:44:55", "66:77:88:99:aa:bb"]
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
@@ -676,3 +678,33 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
task.driver.inspect._get_pxe_port_macs(task)
|
||||
self.assertEqual(expected_properties,
|
||||
task.driver.inspect._get_pxe_port_macs(task))
|
||||
|
||||
|
||||
class ContinueInspectionTestCase(db_base.DbTestCase):
|
||||
def setUp(self):
|
||||
super(ContinueInspectionTestCase, self).setUp()
|
||||
CONF.set_override('enabled_inspect_interfaces',
|
||||
['redfish', 'no-inspect'])
|
||||
self.config(inspection_hooks='validate-interfaces,'
|
||||
'ports,architecture',
|
||||
group='redfish')
|
||||
self.config(enabled_hardware_types=['redfish'])
|
||||
self.config(enabled_power_interfaces=['redfish'])
|
||||
self.config(enabled_management_interfaces=['redfish'])
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context,
|
||||
driver='redfish',
|
||||
inspect_interface='redfish',
|
||||
driver_info=INFO_DICT,
|
||||
provision_state=states.INSPECTING)
|
||||
self.iface = inspect.RedfishInspect()
|
||||
|
||||
def test(self):
|
||||
with task_manager.acquire(self.context, self.node.id) as task:
|
||||
self.assertRaises(
|
||||
exception.UnsupportedDriverExtension,
|
||||
self.iface.continue_inspection,
|
||||
task,
|
||||
mock.sentinel.inventory,
|
||||
mock.sentinel.plugin_data
|
||||
)
|
||||
|
@@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds inspection hooks in the redfish inspect interface for processing data
|
||||
received during inspection. The three default configuration hooks
|
||||
`validate-interfaces`, `ports` and `architecture` are added. A new
|
||||
configuration option `inspection_hooks` is added in the `redfish`
|
||||
configuration section.
|
Reference in New Issue
Block a user