A simple standalone test for in-band inspection
Redfish is chosen because it is virtually guaranteed to support managed
inspection, unlike IPMI which may require a separate PXE setup.
Adds support for setting enabled interfaces, which is already relied
upon by the iDRAC tests but is not actually implemented.
Depends-On: https://review.opendev.org/c/openstack/ironic/+/927265
Change-Id: Ib66ac41c2919bade7c0c1ca3d8bb4fdfd2acf858
diff --git a/ironic_tempest_plugin/config.py b/ironic_tempest_plugin/config.py
index a728bef..9e96863 100644
--- a/ironic_tempest_plugin/config.py
+++ b/ironic_tempest_plugin/config.py
@@ -98,7 +98,7 @@
help="Timeout for association of Nova instance and Ironic "
"node"),
cfg.IntOpt('inspect_timeout',
- default=10,
+ default=300,
help="Timeout for inspecting an Ironic node."),
cfg.IntOpt('power_timeout',
default=60,
@@ -203,6 +203,9 @@
cfg.ListOpt('enabled_power_interfaces',
default=['fake', 'ipmitool'],
help="List of Ironic enabled power interfaces."),
+ cfg.ListOpt('enabled_inspect_interfaces',
+ default=['no-inspect'],
+ help="List of Ironic enabled inspect interfaces."),
cfg.StrOpt('default_rescue_interface',
help="Ironic default rescue interface."),
cfg.StrOpt('firmware_image_url',
diff --git a/ironic_tempest_plugin/services/baremetal/base.py b/ironic_tempest_plugin/services/baremetal/base.py
index f23310f..f2cffd5 100644
--- a/ironic_tempest_plugin/services/baremetal/base.py
+++ b/ironic_tempest_plugin/services/baremetal/base.py
@@ -23,6 +23,11 @@
# separate processes so global variables are not shared among them.
BAREMETAL_MICROVERSION = None
+# Interfaces that can be set via the baremetal client and by logic in scenario
+# managers.
+SUPPORTED_INTERFACES = ['bios', 'deploy', 'rescue', 'boot', 'raid',
+ 'management', 'power', 'inspect']
+
def set_baremetal_api_microversion(baremetal_microversion):
global BAREMETAL_MICROVERSION
diff --git a/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py b/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py
index 5715609..988dc66 100644
--- a/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py
+++ b/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py
@@ -20,6 +20,26 @@
version = '1'
uri_prefix = 'v1'
+ node_attributes = (
+ 'properties/cpu_arch',
+ 'properties/cpus',
+ 'properties/local_gb',
+ 'properties/memory_mb',
+ 'driver',
+ 'instance_uuid',
+ 'resource_class',
+ 'protected',
+ 'protected_reason',
+ # TODO(dtantsur): maintenance is set differently
+ # in newer API versions.
+ 'maintenance',
+ 'description',
+ 'shard'
+ ) + tuple(
+ f'{iface}_interface'
+ for iface in base.SUPPORTED_INTERFACES
+ )
+
@staticmethod
def _get_headers(api_version):
"""Return headers for a request.
@@ -507,26 +527,8 @@
else:
params = {}
- node_attributes = ('properties/cpu_arch',
- 'properties/cpus',
- 'properties/local_gb',
- 'properties/memory_mb',
- 'driver',
- 'bios_interface',
- 'deploy_interface',
- 'raid_interface',
- 'rescue_interface',
- 'instance_uuid',
- 'resource_class',
- 'protected',
- 'protected_reason',
- # TODO(dtantsur): maintenance is set differently
- # in newer API versions.
- 'maintenance',
- 'description',
- 'shard')
if not patch:
- patch = self._make_patch(node_attributes, **kwargs)
+ patch = self._make_patch(self.node_attributes, **kwargs)
return self._patch_request('nodes', uuid, patch, params=params)
diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
index 01289ce..d0cfa06 100644
--- a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
+++ b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
@@ -607,6 +607,12 @@
# set via a different test).
power_interface = None
+ # The inspect interface to use by the HW type. The inspect interface of the
+ # node used in the test will be set to this value. If set to None, the
+ # node will retain its existing inspect_interface value (which may have
+ # been set via a different test).
+ inspect_interface = None
+
# Boolean value specify if image is wholedisk or not.
wholedisk_image = None
@@ -632,55 +638,18 @@
'driver': cls.driver,
'enabled_drivers': CONF.baremetal.enabled_drivers,
'enabled_hw_types': CONF.baremetal.enabled_hardware_types})
- if (cls.bios_interface and cls.bios_interface not in
- CONF.baremetal.enabled_bios_interfaces):
- raise cls.skipException(
- "Bios interface %(iface)s required by the test is not in the "
- "list of enabled bios interfaces %(enabled)s" % {
- 'iface': cls.bios_interface,
- 'enabled': CONF.baremetal.enabled_bios_interfaces})
- if (cls.deploy_interface and cls.deploy_interface not in
- CONF.baremetal.enabled_deploy_interfaces):
- raise cls.skipException(
- "Deploy interface %(iface)s required by test is not "
- "in the list of enabled deploy interfaces %(enabled)s" % {
- 'iface': cls.deploy_interface,
- 'enabled': CONF.baremetal.enabled_deploy_interfaces})
- if (cls.rescue_interface and cls.rescue_interface not in
- CONF.baremetal.enabled_rescue_interfaces):
- raise cls.skipException(
- "Rescue interface %(iface)s required by test is not "
- "in the list of enabled rescue interfaces %(enabled)s" % {
- 'iface': cls.rescue_interface,
- 'enabled': CONF.baremetal.enabled_rescue_interfaces})
- if (cls.boot_interface and cls.boot_interface not in
- CONF.baremetal.enabled_boot_interfaces):
- raise cls.skipException(
- "Boot interface %(iface)s required by test is not "
- "in the list of enabled boot interfaces %(enabled)s" % {
- 'iface': cls.boot_interface,
- 'enabled': CONF.baremetal.enabled_boot_interfaces})
- if (cls.raid_interface and cls.raid_interface not in
- CONF.baremetal.enabled_raid_interfaces):
- raise cls.skipException(
- "RAID interface %(iface)s required by test is not "
- "in the list of enabled RAID interfaces %(enabled)s" % {
- 'iface': cls.raid_interface,
- 'enabled': CONF.baremetal.enabled_raid_interfaces})
- if (cls.management_interface and cls.management_interface not in
- CONF.baremetal.enabled_management_interfaces):
- raise cls.skipException(
- "Management interface %(iface)s required by test is not "
- "in the list of enabled management interfaces %(enabled)s" % {
- 'iface': cls.management_interface,
- 'enabled': CONF.baremetal.enabled_management_interfaces})
- if (cls.power_interface and cls.power_interface not in
- CONF.baremetal.enabled_power_interfaces):
- raise cls.skipException(
- "Power interface %(iface)s required by test is not "
- "in the list of enabled power interfaces %(enabled)s" % {
- 'iface': cls.power_interface,
- 'enabled': CONF.baremetal.enabled_power_interfaces})
+ for iface in base.SUPPORTED_INTERFACES:
+ requested = getattr(cls, f'{iface}_interface')
+ enabled = getattr(CONF.baremetal, f'enabled_{iface}_interfaces')
+ if requested and requested not in enabled:
+ raise cls.skipException(
+ "%(type)s interface %(iface)s required by the test is not "
+ "in the list of enabled %(type)s interfaces "
+ "%(enabled)s" % {
+ 'iface': requested,
+ 'type': iface,
+ 'enabled': ', '.join(enabled),
+ })
if (cls.wholedisk_image is not None
and not cls.wholedisk_image
and CONF.baremetal.use_provision_network):
@@ -720,20 +689,9 @@
if not uuidutils.is_uuid_like(cls.image_ref):
image_checksum = cls.image_checksum
boot_kwargs = {'image_checksum': image_checksum}
- if cls.bios_interface:
- boot_kwargs['bios_interface'] = cls.bios_interface
- if cls.deploy_interface:
- boot_kwargs['deploy_interface'] = cls.deploy_interface
- if cls.rescue_interface:
- boot_kwargs['rescue_interface'] = cls.rescue_interface
- if cls.boot_interface:
- boot_kwargs['boot_interface'] = cls.boot_interface
- if cls.raid_interface:
- boot_kwargs['raid_interface'] = cls.raid_interface
- if cls.management_interface:
- boot_kwargs['management_interface'] = cls.management_interface
- if cls.power_interface:
- boot_kwargs['power_interface'] = cls.power_interface
+ for iface in base.SUPPORTED_INTERFACES:
+ if requested := getattr(cls, f'{iface}_interface'):
+ boot_kwargs[f'{iface}_interface'] = requested
# just get an available node
cls.node = cls.get_and_reserve_node()
diff --git a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_inspection_basic.py b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_inspection_basic.py
index 97f0fa3..2f367b1 100644
--- a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_inspection_basic.py
+++ b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_inspection_basic.py
@@ -21,23 +21,16 @@
CONF = config.CONF
-class BaremetalIdracInspect(bsm.BaremetalStandaloneScenarioTest):
+class BaremetalInspectBase:
- driver = 'idrac'
mandatory_attr = ['driver', 'inspect_interface']
- # The test cases clean up at the end by detaching the VIF.
- # Support for VIFs was introduced by version 1.28
- # (# v1.28: Add vifs subcontroller to node).
- api_microversion = '1.28'
+ # (# v1.31: Support for updating inspect_interface).
+ api_microversion = '1.31'
delete_node = False
+ wait_provisioning_state_interval = 1
- def _verify_node_inspection_data(self):
- _, node = self.baremetal_client.show_node(self.node['uuid'])
-
- self.assertEqual(node['properties']['cpu_arch'], 'x86_64')
- self.assertGreater(int(node['properties']['memory_mb']), 0)
- self.assertGreater(int(node['properties']['cpus']), 0)
- self.assertGreater(int(node['properties']['local_gb']), 0)
+ def _verify_node_inspection_data(self, node):
+ self.assertIn(node['properties']['cpu_arch'], ['x86_64', 'aarch64'])
@decorators.idempotent_id('47ea4487-4720-43e8-a024-53ae82f8c264')
def test_baremetal_inspect(self):
@@ -50,19 +43,48 @@
"""
self.baremetal_client.set_node_provision_state(self.node['uuid'],
'manage')
+ _, node = self.baremetal_client.show_node(self.node['uuid'])
+ if 'cpu_arch' in node['properties']:
+ new_properties = node['properties'].copy()
+ new_properties.pop('cpu_arch')
+ self.baremetal_client.update_node(self.node['uuid'],
+ properties=new_properties)
+
self.baremetal_client.set_node_provision_state(self.node['uuid'],
'inspect')
+ self.wait_provisioning_state(
+ self.node['uuid'], 'manageable',
+ timeout=CONF.baremetal.inspect_timeout,
+ interval=self.wait_provisioning_state_interval)
- self.wait_provisioning_state(self.node['uuid'], 'manageable',
- timeout=CONF.baremetal.inspect_timeout)
-
- self._verify_node_inspection_data()
+ _, node = self.baremetal_client.show_node(self.node['uuid'])
+ self._verify_node_inspection_data(node)
self.baremetal_client.set_node_provision_state(self.node['uuid'],
'provide')
self.wait_provisioning_state(self.node['uuid'], 'available')
+class BaremetalRedfishAgentInspect(BaremetalInspectBase,
+ bsm.BaremetalStandaloneScenarioTest):
+ driver = 'redfish'
+ inspect_interface = 'agent'
+ wait_provisioning_state_interval = 15
+
+ # TODO(dtantsur): test aborting inspection and fetching inspection data
+
+
+class BaremetalIdracInspect(BaremetalInspectBase,
+ bsm.BaremetalStandaloneScenarioTest):
+ driver = 'idrac'
+
+ def _verify_node_inspection_data(self, node):
+ super()._verify_node_inspection_data(node)
+ self.assertGreater(int(node['properties']['memory_mb']), 0)
+ self.assertGreater(int(node['properties']['cpus']), 0)
+ self.assertGreater(int(node['properties']['local_gb']), 0)
+
+
class BaremetalIdracRedfishInspect(BaremetalIdracInspect):
inspect_interface = 'idrac-redfish'