Rescue mode standalone tests
Adds standalone scenario test for rescue mode.
Change-Id: Iac2dfb1adab6b9ef53b61046e45740c2b94f2bfb
Partial-Bug: 1526449
diff --git a/ironic_tempest_plugin/config.py b/ironic_tempest_plugin/config.py
index f9fefc4..3e8bc52 100644
--- a/ironic_tempest_plugin/config.py
+++ b/ironic_tempest_plugin/config.py
@@ -80,6 +80,12 @@
help="Timeout for unprovisioning an Ironic node. "
"Takes longer since Kilo as Ironic performs an extra "
"step in Node cleaning."),
+ cfg.IntOpt('rescue_timeout',
+ default=300,
+ help="Timeout for rescuing an Ironic node."),
+ cfg.IntOpt('unrescue_timeout',
+ default=300,
+ help="Timeout for unrescuing an Ironic node."),
cfg.StrOpt('min_microversion',
help="Lower version of the test target microversion range. "
"The format is 'X.Y', where 'X' and 'Y' are int values. "
@@ -116,6 +122,9 @@
cfg.ListOpt('enabled_deploy_interfaces',
default=['iscsi', 'direct'],
help="List of Ironic enabled deploy interfaces."),
+ cfg.ListOpt('enabled_rescue_interfaces',
+ default=['no-rescue'],
+ help="List of Ironic enabled rescue interfaces."),
cfg.IntOpt('adjusted_root_disk_size_gb',
min=0,
help="Ironic adjusted disk size to use in the standalone tests "
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 550128a..7e41dd8 100644
--- a/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py
+++ b/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py
@@ -388,6 +388,7 @@
'properties/memory_mb',
'driver',
'deploy_interface',
+ 'rescue_interface',
'instance_uuid',
'resource_class')
if not patch:
@@ -460,7 +461,7 @@
@base.handle_errors
def set_node_provision_state(self, node_uuid, state, configdrive=None,
- clean_steps=None):
+ clean_steps=None, rescue_password=None):
"""Set provision state of the specified node.
:param node_uuid: The unique identifier of the node.
@@ -469,6 +470,7 @@
:param configdrive: A gzipped, base64-encoded
configuration drive string.
:param clean_steps: A list with clean steps to execute.
+ :param rescue_password: user password used to rescue.
"""
data = {'target': state}
# NOTE (vsaienk0): Add both here if specified, do not check anything.
@@ -477,6 +479,8 @@
data['configdrive'] = configdrive
if clean_steps is not None:
data['clean_steps'] = clean_steps
+ if rescue_password is not None:
+ data['rescue_password'] = rescue_password
return self._put_request('nodes/%s/states/provision' % node_uuid,
data)
diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
index b4a8169..53ac60f 100644
--- a/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
+++ b/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
@@ -69,6 +69,7 @@
DELETED = 'deleted'
ERROR = 'error'
MANAGEABLE = 'manageable'
+ RESCUE = 'rescue'
class BaremetalScenarioTest(manager.ScenarioTest):
@@ -153,9 +154,10 @@
@classmethod
@retry_on_conflict
def set_node_provision_state(cls, node_id, state, configdrive=None,
- clean_steps=None):
+ clean_steps=None, rescue_password=None):
cls.baremetal_client.set_node_provision_state(
- node_id, state, configdrive=configdrive, clean_steps=clean_steps)
+ node_id, state, configdrive=configdrive,
+ clean_steps=clean_steps, rescue_password=rescue_password)
def verify_connectivity(self, ip=None):
if ip:
diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
index 0137db8..5356c5d 100644
--- a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
+++ b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
@@ -272,6 +272,27 @@
timeout=CONF.baremetal.unprovision_timeout,
interval=1)
+ @classmethod
+ def rescue_node(cls, node_id, rescue_password):
+ """Rescue the node."""
+ cls.set_node_provision_state(node_id, 'rescue',
+ rescue_password=rescue_password)
+ cls.wait_provisioning_state(
+ node_id,
+ bm.BaremetalProvisionStates.RESCUE,
+ timeout=CONF.baremetal.rescue_timeout,
+ interval=1)
+
+ @classmethod
+ def unrescue_node(cls, node_id):
+ """Unrescue the node."""
+ cls.set_node_provision_state(node_id, 'unrescue')
+ cls.wait_provisioning_state(
+ node_id,
+ bm.BaremetalProvisionStates.ACTIVE,
+ timeout=CONF.baremetal.unrescue_timeout,
+ interval=1)
+
class BaremetalStandaloneScenarioTest(BaremetalStandaloneManager):
@@ -284,6 +305,9 @@
# The deploy interface to use by the HW type
deploy_interface = None
+ # The rescue interface to use by the HW type
+ rescue_interface = None
+
# User image ref to boot node with.
image_ref = None
@@ -318,6 +342,13 @@
"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 not cls.wholedisk_image and CONF.baremetal.use_provision_network:
raise cls.skipException(
'Partitioned images are not supported with multitenancy.')
@@ -336,6 +367,8 @@
boot_kwargs = {'image_checksum': image_checksum}
if cls.deploy_interface:
boot_kwargs['deploy_interface'] = cls.deploy_interface
+ if cls.rescue_interface:
+ boot_kwargs['rescue_interface'] = cls.rescue_interface
cls.node = cls.boot_node(cls.driver, cls.image_ref, **boot_kwargs)
cls.node_ip = cls.add_floatingip_to_node(cls.node['uuid'])
diff --git a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py
index 62a0605..14dc69b 100644
--- a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py
+++ b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py
@@ -162,3 +162,51 @@
def test_ip_access_to_server(self):
self.assertTrue(self.ping_ip_address(self.node_ip,
should_succeed=True))
+
+
+class BaremetalIpmiRescueWholedisk(bsm.BaremetalStandaloneScenarioTest):
+
+ api_microversion = '1.38'
+ min_microversion = '1.38'
+ driver = 'ipmi'
+ rescue_interface = 'agent'
+ image_ref = CONF.baremetal.whole_disk_image_ref
+ wholedisk_image = True
+
+ # NOTE(tiendc) Using direct deploy interface and a whole disk
+ # image may lead to the bug:
+ # https://bugs.launchpad.net/ironic/+bug/1750958
+ # This is a workaround by using iscsi deploy interface.
+ deploy_interface = 'iscsi'
+
+ @decorators.idempotent_id('d6a1780f-c4bb-4136-8144-29e822e14d66')
+ @utils.services('image', 'network')
+ def test_rescue_mode(self):
+ self.rescue_node(self.node['uuid'], 'abc123')
+ self.assertTrue(self.ping_ip_address(self.node_ip,
+ should_succeed=True))
+
+ self.unrescue_node(self.node['uuid'])
+ self.assertTrue(self.ping_ip_address(self.node_ip,
+ should_succeed=True))
+
+
+class BaremetalIpmiRescuePartitioned(bsm.BaremetalStandaloneScenarioTest):
+
+ api_microversion = '1.38'
+ min_microversion = '1.38'
+ driver = 'ipmi'
+ rescue_interface = 'agent'
+ image_ref = CONF.baremetal.partition_image_ref
+ wholedisk_image = False
+
+ @decorators.idempotent_id('113acd0a-9872-4631-b3ee-54da7e3bb262')
+ @utils.services('image', 'network')
+ def test_rescue_mode(self):
+ self.rescue_node(self.node['uuid'], 'abc123')
+ self.assertTrue(self.ping_ip_address(self.node_ip,
+ should_succeed=True))
+
+ self.unrescue_node(self.node['uuid'])
+ self.assertTrue(self.ping_ip_address(self.node_ip,
+ should_succeed=True))