summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDao Cong Tien <tiendc@vn.fujitsu.com>2018-02-13 16:55:22 +0700
committerDao Cong Tien <tiendc@vn.fujitsu.com>2018-03-13 09:45:42 +0000
commitf7da3f6ec2b906c9e8a42308659778dbd8038171 (patch)
tree5a6aaf8b0c233833fc9f10b0d2d69d1b3f4ee37b
parent9ab04d0962b5d3ccf9b616efdb0c5113be84041c (diff)
downloadironic-f7da3f6ec2b906c9e8a42308659778dbd8038171.tar.gz
Implements validate_rescue() for IRMCVirtualMediaBoot
This commit implements validate_rescue() for 'irmc-virtual-media' boot interface of 'irmc' hardware type. With this it enables 'agent' rescue interface for 'irmc' hardware type when corresponding boot interface being used is 'irmc-virtual-media'. Support already exists for the 'agent' rescue interface with 'irmc-pxe' boot interface. Also fix a bug in _remove_share_file() causing files not to be removed correctly. Change-Id: Ib602b2705b8fa4f7161b9f97857ec6546f5e9b19 Closes-Bug: #1747842
-rw-r--r--ironic/drivers/modules/irmc/boot.py149
-rw-r--r--ironic/tests/unit/drivers/modules/irmc/test_boot.py188
-rw-r--r--ironic/tests/unit/drivers/test_irmc.py31
-rw-r--r--releasenotes/notes/rescue-interface-for-irmc-hardware-type-17e38197849748e0.yaml7
4 files changed, 280 insertions, 95 deletions
diff --git a/ironic/drivers/modules/irmc/boot.py b/ironic/drivers/modules/irmc/boot.py
index 3d4cd0659..2732702bb 100644
--- a/ironic/drivers/modules/irmc/boot.py
+++ b/ironic/drivers/modules/irmc/boot.py
@@ -58,6 +58,12 @@ REQUIRED_PROPERTIES = {
"Required."),
}
+RESCUE_PROPERTIES = {
+ 'irmc_rescue_iso': _("UUID (from Glance) of the rescue ISO. Only "
+ "required if rescue mode is being used and ironic "
+ "is managing booting the rescue ramdisk.")
+}
+
OPTIONAL_PROPERTIES = {
'irmc_pci_physical_ids':
_("Physical IDs of PCI cards. A dictionary of pairs of resource UUID "
@@ -96,7 +102,7 @@ def _parse_config_option():
raise exception.InvalidParameterValue(msg)
-def _parse_driver_info(node):
+def _parse_driver_info(node, mode='deploy'):
"""Gets the driver specific Node deployment info.
This method validates whether the 'driver_info' property of the
@@ -104,6 +110,9 @@ def _parse_driver_info(node):
for this driver to deploy images to the node.
:param node: a target node of the deployment
+ :param mode: Label indicating a deploy or rescue operation being
+ carried out on the node. Supported values are
+ 'deploy' and 'rescue'. Defaults to 'deploy'.
:returns: the driver_info values of the node.
:raises: MissingParameterValue, if any of the required parameters are
missing.
@@ -113,19 +122,26 @@ def _parse_driver_info(node):
d_info = node.driver_info
deploy_info = {}
- deploy_info['irmc_deploy_iso'] = d_info.get('irmc_deploy_iso')
- error_msg = _("Error validating iRMC virtual media deploy. Some parameters"
- " were missing in node's driver_info")
+ if mode == 'deploy':
+ image_iso = d_info.get('irmc_deploy_iso')
+ deploy_info['irmc_deploy_iso'] = image_iso
+ else:
+ image_iso = d_info.get('irmc_rescue_iso')
+ deploy_info['irmc_rescue_iso'] = image_iso
+
+ error_msg = (_("Error validating iRMC virtual media for %s. Some "
+ "parameters were missing in node's driver_info") % mode)
deploy_utils.check_for_missing_params(deploy_info, error_msg)
- if service_utils.is_image_href_ordinary_file_name(
- deploy_info['irmc_deploy_iso']):
- deploy_iso = os.path.join(CONF.irmc.remote_image_share_root,
- deploy_info['irmc_deploy_iso'])
- if not os.path.isfile(deploy_iso):
- msg = (_("Deploy ISO file, %(deploy_iso)s, "
+ if service_utils.is_image_href_ordinary_file_name(image_iso):
+ image_iso_file = os.path.join(CONF.irmc.remote_image_share_root,
+ image_iso)
+ if not os.path.isfile(image_iso_file):
+ msg = (_("%(mode)s ISO file, %(iso_file)s, "
"not found for node: %(node)s.") %
- {'deploy_iso': deploy_iso, 'node': node.uuid})
+ {'mode': mode.capitalize(),
+ 'iso_file': image_iso_file,
+ 'node': node.uuid})
raise exception.InvalidParameterValue(msg)
return deploy_info
@@ -185,13 +201,16 @@ def _parse_deploy_info(node):
return deploy_info
-def _setup_deploy_iso(task, ramdisk_options):
+def _setup_vmedia(task, mode, ramdisk_options):
"""Attaches virtual media and sets it as boot device.
- This method attaches the given deploy ISO as virtual media, prepares the
- arguments for ramdisk in virtual media floppy.
+ This method attaches the deploy or rescue ISO as virtual media, prepares
+ the arguments for ramdisk in virtual media floppy.
:param task: a TaskManager instance containing the node to act on.
+ :param mode: Label indicating a deploy or rescue operation being
+ carried out on the node. Supported values are
+ 'deploy' and 'rescue'.
:param ramdisk_options: the options to be passed to the ramdisk in virtual
media floppy.
:raises: ImageRefValidationFailed if no image service can handle specified
@@ -201,35 +220,31 @@ def _setup_deploy_iso(task, ramdisk_options):
:raises: InvalidParameterValue if the validation of the
PowerInterface or ManagementInterface fails.
"""
- d_info = task.node.driver_info
- deploy_iso_href = d_info['irmc_deploy_iso']
- if service_utils.is_image_href_ordinary_file_name(deploy_iso_href):
- deploy_iso_file = deploy_iso_href
+ if mode == 'rescue':
+ iso = task.node.driver_info['irmc_rescue_iso']
else:
- deploy_iso_file = _get_deploy_iso_name(task.node)
- deploy_iso_fullpathname = os.path.join(
- CONF.irmc.remote_image_share_root, deploy_iso_file)
- images.fetch(task.context, deploy_iso_href, deploy_iso_fullpathname)
+ iso = task.node.driver_info['irmc_deploy_iso']
- _setup_vmedia_for_boot(task, deploy_iso_file, ramdisk_options)
- manager_utils.node_set_boot_device(task, boot_devices.CDROM)
-
-
-def _get_deploy_iso_name(node):
- """Returns the deploy ISO file name for a given node.
+ if service_utils.is_image_href_ordinary_file_name(iso):
+ iso_file = iso
+ else:
+ iso_file = _get_iso_name(task.node, label=mode)
+ iso_fullpathname = os.path.join(
+ CONF.irmc.remote_image_share_root, iso_file)
+ images.fetch(task.context, iso, iso_fullpathname)
- :param node: the node for which ISO file name is to be provided.
- """
- return "deploy-%s.iso" % node.uuid
+ _setup_vmedia_for_boot(task, iso_file, ramdisk_options)
+ manager_utils.node_set_boot_device(task, boot_devices.CDROM)
-def _get_boot_iso_name(node):
- """Returns the boot ISO file name for a given node.
+def _get_iso_name(node, label):
+ """Returns the ISO file name for a given node.
:param node: the node for which ISO file name is to be provided.
+ :param label: a string used as a base name for the ISO file.
"""
- return "boot-%s.iso" % node.uuid
+ return "%s-%s.iso" % (label, node.uuid)
def _prepare_boot_iso(task, root_uuid):
@@ -253,7 +268,7 @@ def _prepare_boot_iso(task, root_uuid):
if service_utils.is_image_href_ordinary_file_name(boot_iso_href):
driver_internal_info['irmc_boot_iso'] = boot_iso_href
else:
- boot_iso_filename = _get_boot_iso_name(task.node)
+ boot_iso_filename = _get_iso_name(task.node, label='boot')
boot_iso_fullpathname = os.path.join(
CONF.irmc.remote_image_share_root, boot_iso_filename)
images.fetch(task.context, boot_iso_href, boot_iso_fullpathname)
@@ -271,13 +286,13 @@ def _prepare_boot_iso(task, root_uuid):
ramdisk_href = (task.node.instance_info.get('ramdisk') or
image_properties['ramdisk_id'])
- deploy_iso_filename = _get_deploy_iso_name(task.node)
+ deploy_iso_filename = _get_iso_name(task.node, label='deploy')
deploy_iso = ('file://' + os.path.join(
CONF.irmc.remote_image_share_root, deploy_iso_filename))
boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node)
kernel_params = CONF.pxe.pxe_append_params
- boot_iso_filename = _get_boot_iso_name(task.node)
+ boot_iso_filename = _get_iso_name(task.node, label='boot')
boot_iso_fullpathname = os.path.join(
CONF.irmc.remote_image_share_root, boot_iso_filename)
@@ -399,7 +414,8 @@ def _cleanup_vmedia_boot(task):
_detach_virtual_fd(node)
_remove_share_file(_get_floppy_image_name(node))
- _remove_share_file(_get_deploy_iso_name(node))
+ _remove_share_file(_get_iso_name(node, label='deploy'))
+ _remove_share_file(_get_iso_name(node, label='rescue'))
def _remove_share_file(share_filename):
@@ -408,7 +424,7 @@ def _remove_share_file(share_filename):
:param share_filename: a file name to be removed.
"""
share_fullpathname = os.path.join(
- CONF.irmc.remote_image_share_name, share_filename)
+ CONF.irmc.remote_image_share_root, share_filename)
ironic_utils.unlink_without_raise(share_fullpathname)
@@ -877,6 +893,9 @@ class IRMCVirtualMediaBoot(base.BootInterface, IRMCVolumeBootMixIn):
super(IRMCVirtualMediaBoot, self).__init__()
def get_properties(self):
+ # TODO(tiendc): COMMON_PROPERTIES should also include rescue
+ # related properties (RESCUE_PROPERTIES). We can add them in Rocky,
+ # when classic drivers get removed.
return COMMON_PROPERTIES
@METRICS.timer('IRMCVirtualMediaBoot.validate')
@@ -913,13 +932,13 @@ class IRMCVirtualMediaBoot(base.BootInterface, IRMCVolumeBootMixIn):
@METRICS.timer('IRMCVirtualMediaBoot.prepare_ramdisk')
def prepare_ramdisk(self, task, ramdisk_params):
- """Prepares the deploy ramdisk using virtual media.
+ """Prepares the deploy or rescue ramdisk using virtual media.
- Prepares the options for the deployment ramdisk, sets the node to boot
- from virtual media cdrom.
+ Prepares the options for the deploy or rescue ramdisk, sets the node
+ to boot from virtual media cdrom.
:param task: a TaskManager instance containing the node to act on.
- :param ramdisk_params: the options to be passed to the deploy ramdisk.
+ :param ramdisk_params: the options to be passed to the ramdisk.
:raises: ImageRefValidationFailed if no image service can handle
specified href.
:raises: ImageCreationFailed, if it failed while creating the floppy
@@ -930,11 +949,12 @@ class IRMCVirtualMediaBoot(base.BootInterface, IRMCVolumeBootMixIn):
"""
# NOTE(TheJulia): If this method is being called by something
- # aside from deployment and clean, such as conductor takeover, we
- # should treat this as a no-op and move on otherwise we would modify
- # the state of the node due to virtual media operations.
- if (task.node.provision_state != states.DEPLOYING and
- task.node.provision_state != states.CLEANING):
+ # aside from deployment, clean and rescue, such as conductor takeover,
+ # we should treat this as a no-op and move on otherwise we would
+ # modify the state of the node due to virtual media operations.
+ if task.node.provision_state not in (states.DEPLOYING,
+ states.CLEANING,
+ states.RESCUING):
return
# NOTE(tiendc): Before deploying, we need to backup BIOS config
@@ -951,14 +971,19 @@ class IRMCVirtualMediaBoot(base.BootInterface, IRMCVolumeBootMixIn):
deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task)
ramdisk_params['BOOTIF'] = deploy_nic_mac
- _setup_deploy_iso(task, ramdisk_params)
+ if task.node.provision_state == states.RESCUING:
+ mode = 'rescue'
+ else:
+ mode = 'deploy'
+
+ _setup_vmedia(task, mode, ramdisk_params)
@METRICS.timer('IRMCVirtualMediaBoot.clean_up_ramdisk')
def clean_up_ramdisk(self, task):
"""Cleans up the boot of ironic ramdisk.
This method cleans up the environment that was setup for booting the
- deploy ramdisk.
+ deploy or rescue ramdisk.
:param task: a task from TaskManager.
:returns: None
@@ -1018,10 +1043,18 @@ class IRMCVirtualMediaBoot(base.BootInterface, IRMCVolumeBootMixIn):
if deploy_utils.is_secure_boot_requested(task.node):
irmc_common.set_secure_boot_mode(task.node, enable=False)
- _remove_share_file(_get_boot_iso_name(task.node))
+ _remove_share_file(_get_iso_name(task.node, label='boot'))
driver_internal_info = task.node.driver_internal_info
driver_internal_info.pop('irmc_boot_iso', None)
- driver_internal_info.pop('root_uuid_or_disk_id', None)
+
+ # When rescue, this function is called. But we need to retain the
+ # root_uuid_or_disk_id to use on unrescue (see prepare_instance).
+ boot_local_or_iwdi = (
+ deploy_utils.get_boot_option(task.node) == "local" or
+ driver_internal_info.get('is_whole_disk_image'))
+ if task.node.provision_state != states.RESCUING or boot_local_or_iwdi:
+ driver_internal_info.pop('root_uuid_or_disk_id', None)
+
task.node.driver_internal_info = driver_internal_info
task.node.save()
_cleanup_vmedia_boot(task)
@@ -1035,6 +1068,18 @@ class IRMCVirtualMediaBoot(base.BootInterface, IRMCVolumeBootMixIn):
manager_utils.node_set_boot_device(task, boot_devices.CDROM,
persistent=True)
+ @METRICS.timer('IRMCVirtualMediaBoot.validate_rescue')
+ def validate_rescue(self, task):
+ """Validate that the node has required properties for rescue.
+
+ :param task: a TaskManager instance with the node being checked
+ :raises: MissingParameterValue if node is missing one or more required
+ parameters
+ :raises: InvalidParameterValue, if any of the parameters have invalid
+ value.
+ """
+ _parse_driver_info(task.node, mode='rescue')
+
class IRMCPXEBoot(pxe.PXEBoot):
"""iRMC PXE boot."""
diff --git a/ironic/tests/unit/drivers/modules/irmc/test_boot.py b/ironic/tests/unit/drivers/modules/irmc/test_boot.py
index af6fa2521..9e4159623 100644
--- a/ironic/tests/unit/drivers/modules/irmc/test_boot.py
+++ b/ironic/tests/unit/drivers/modules/irmc/test_boot.py
@@ -111,7 +111,8 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
self.node.driver_info['irmc_deploy_iso'] = 'deploy.iso'
driver_info_expected = {'irmc_deploy_iso': 'deploy.iso'}
- driver_info_actual = irmc_boot._parse_driver_info(self.node)
+ driver_info_actual = irmc_boot._parse_driver_info(self.node,
+ mode='deploy')
isfile_mock.assert_called_once_with(
'/remote_image_share_root/deploy.iso')
@@ -123,17 +124,18 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
self, is_image_href_ordinary_file_name_mock):
"""With required 'irmc_deploy_iso' not in share."""
self.node.driver_info[
- 'irmc_deploy_iso'] = 'bc784057-a140-4130-add3-ef890457e6b3'
- driver_info_expected = {'irmc_deploy_iso':
+ 'irmc_rescue_iso'] = 'bc784057-a140-4130-add3-ef890457e6b3'
+ driver_info_expected = {'irmc_rescue_iso':
'bc784057-a140-4130-add3-ef890457e6b3'}
is_image_href_ordinary_file_name_mock.return_value = False
- driver_info_actual = irmc_boot._parse_driver_info(self.node)
+ driver_info_actual = irmc_boot._parse_driver_info(self.node,
+ mode='rescue')
self.assertEqual(driver_info_expected, driver_info_actual)
@mock.patch.object(os.path, 'isfile', spec_set=True, autospec=True)
- def test__parse_driver_info_with_deploy_iso_invalid(self, isfile_mock):
+ def test__parse_driver_info_with_iso_invalid(self, isfile_mock):
"""With required 'irmc_deploy_iso' non existed."""
isfile_mock.return_value = False
@@ -146,19 +148,19 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
e = self.assertRaises(exception.InvalidParameterValue,
irmc_boot._parse_driver_info,
- task.node)
+ task.node, mode='deploy')
self.assertEqual(error_msg, str(e))
- def test__parse_driver_info_with_deploy_iso_missing(self):
- """With required 'irmc_deploy_iso' empty."""
- self.node.driver_info['irmc_deploy_iso'] = None
+ def test__parse_driver_info_with_iso_missing(self):
+ """With required 'irmc_rescue_iso' empty."""
+ self.node.driver_info['irmc_rescue_iso'] = None
- error_msg = ("Error validating iRMC virtual media deploy. Some"
+ error_msg = ("Error validating iRMC virtual media for rescue. Some"
" parameters were missing in node's driver_info."
- " Missing are: ['irmc_deploy_iso']")
+ " Missing are: ['irmc_rescue_iso']")
e = self.assertRaises(exception.MissingParameterValue,
irmc_boot._parse_driver_info,
- self.node)
+ self.node, mode='rescue')
self.assertEqual(error_msg, str(e))
def test__parse_instance_info_with_boot_iso_file_name_ok(self):
@@ -274,15 +276,16 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
autospec=True)
@mock.patch.object(images, 'fetch', spec_set=True,
autospec=True)
- def test__setup_deploy_iso_with_file(self,
- fetch_mock,
- setup_vmedia_mock,
- set_boot_device_mock):
+ def test__setup_vmedia_with_file_deploy(self,
+ fetch_mock,
+ setup_vmedia_mock,
+ set_boot_device_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.driver_info['irmc_deploy_iso'] = 'deploy_iso_filename'
ramdisk_opts = {'a': 'b'}
- irmc_boot._setup_deploy_iso(task, ramdisk_opts)
+ irmc_boot._setup_vmedia(task, mode='deploy',
+ ramdisk_options=ramdisk_opts)
self.assertFalse(fetch_mock.called)
@@ -299,7 +302,33 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
autospec=True)
@mock.patch.object(images, 'fetch', spec_set=True,
autospec=True)
- def test_setup_deploy_iso_with_image_service(
+ def test__setup_vmedia_with_file_rescue(self,
+ fetch_mock,
+ setup_vmedia_mock,
+ set_boot_device_mock):
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ task.node.driver_info['irmc_rescue_iso'] = 'rescue_iso_filename'
+ ramdisk_opts = {'a': 'b'}
+ irmc_boot._setup_vmedia(task, mode='rescue',
+ ramdisk_options=ramdisk_opts)
+
+ self.assertFalse(fetch_mock.called)
+
+ setup_vmedia_mock.assert_called_once_with(
+ task,
+ 'rescue_iso_filename',
+ ramdisk_opts)
+ set_boot_device_mock.assert_called_once_with(task,
+ boot_devices.CDROM)
+
+ @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True,
+ autospec=True)
+ @mock.patch.object(irmc_boot, '_setup_vmedia_for_boot', spec_set=True,
+ autospec=True)
+ @mock.patch.object(images, 'fetch', spec_set=True,
+ autospec=True)
+ def test_setup_vmedia_with_image_service_deploy(
self,
fetch_mock,
setup_vmedia_mock,
@@ -310,7 +339,8 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
shared=False) as task:
task.node.driver_info['irmc_deploy_iso'] = 'glance://deploy_iso'
ramdisk_opts = {'a': 'b'}
- irmc_boot._setup_deploy_iso(task, ramdisk_opts)
+ irmc_boot._setup_vmedia(task, mode='deploy',
+ ramdisk_options=ramdisk_opts)
fetch_mock.assert_called_once_with(
task.context,
@@ -324,14 +354,41 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
set_boot_device_mock.assert_called_once_with(
task, boot_devices.CDROM)
- def test__get_deploy_iso_name(self):
- actual = irmc_boot._get_deploy_iso_name(self.node)
- expected = "deploy-%s.iso" % self.node.uuid
- self.assertEqual(expected, actual)
+ @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True,
+ autospec=True)
+ @mock.patch.object(irmc_boot, '_setup_vmedia_for_boot', spec_set=True,
+ autospec=True)
+ @mock.patch.object(images, 'fetch', spec_set=True,
+ autospec=True)
+ def test_setup_vmedia_with_image_service_rescue(
+ self,
+ fetch_mock,
+ setup_vmedia_mock,
+ set_boot_device_mock):
+ CONF.irmc.remote_image_share_root = '/'
- def test__get_boot_iso_name(self):
- actual = irmc_boot._get_boot_iso_name(self.node)
- expected = "boot-%s.iso" % self.node.uuid
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ task.node.driver_info['irmc_rescue_iso'] = 'glance://rescue_iso'
+ ramdisk_opts = {'a': 'b'}
+ irmc_boot._setup_vmedia(task, mode='rescue',
+ ramdisk_options=ramdisk_opts)
+
+ fetch_mock.assert_called_once_with(
+ task.context,
+ 'glance://rescue_iso',
+ "/rescue-%s.iso" % self.node.uuid)
+
+ setup_vmedia_mock.assert_called_once_with(
+ task,
+ "rescue-%s.iso" % self.node.uuid,
+ ramdisk_opts)
+ set_boot_device_mock.assert_called_once_with(
+ task, boot_devices.CDROM)
+
+ def test__get_iso_name(self):
+ actual = irmc_boot._get_iso_name(self.node, label='deploy')
+ expected = "deploy-%s.iso" % self.node.uuid
self.assertEqual(expected, actual)
@mock.patch.object(images, 'create_boot_iso', spec_set=True, autospec=True)
@@ -599,7 +656,7 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
task.node,
'bootable_iso_filename')
- @mock.patch.object(irmc_boot, '_get_deploy_iso_name', spec_set=True,
+ @mock.patch.object(irmc_boot, '_get_iso_name', spec_set=True,
autospec=True)
@mock.patch.object(irmc_boot, '_get_floppy_image_name', spec_set=True,
autospec=True)
@@ -614,7 +671,7 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
_detach_virtual_fd_mock,
_remove_share_file_mock,
_get_floppy_image_name_mock,
- _get_deploy_iso_name_mock):
+ _get_iso_name_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
irmc_boot._cleanup_vmedia_boot(task)
@@ -622,20 +679,23 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
_detach_virtual_cd_mock.assert_called_once_with(task.node)
_detach_virtual_fd_mock.assert_called_once_with(task.node)
_get_floppy_image_name_mock.assert_called_once_with(task.node)
- _get_deploy_iso_name_mock.assert_called_once_with(task.node)
- self.assertTrue(_remove_share_file_mock.call_count, 2)
+ _get_iso_name_mock.assert_has_calls(
+ [mock.call(task.node, label='deploy'),
+ mock.call(task.node, label='rescue')])
+ self.assertTrue(_remove_share_file_mock.call_count, 3)
_remove_share_file_mock.assert_has_calls(
[mock.call(_get_floppy_image_name_mock(task.node)),
- mock.call(_get_deploy_iso_name_mock(task.node))])
+ mock.call(_get_iso_name_mock(task.node, label='deploy')),
+ mock.call(_get_iso_name_mock(task.node, label='rescue'))])
@mock.patch.object(ironic_utils, 'unlink_without_raise', spec_set=True,
autospec=True)
def test__remove_share_file(self, unlink_without_raise_mock):
- CONF.irmc.remote_image_share_name = '/'
+ CONF.irmc.remote_image_share_root = '/share'
irmc_boot._remove_share_file("boot.iso")
- unlink_without_raise_mock.assert_called_once_with('/boot.iso')
+ unlink_without_raise_mock.assert_called_once_with('/share/boot.iso')
@mock.patch.object(irmc_common, 'get_irmc_client', spec_set=True,
autospec=True)
@@ -833,9 +893,18 @@ class IRMCVirtualMediaBootTestCase(db_base.DbTestCase):
irmc_boot.check_share_fs_mounted_patcher.start()
self.addCleanup(irmc_boot.check_share_fs_mounted_patcher.stop)
super(IRMCVirtualMediaBootTestCase, self).setUp()
- mgr_utils.mock_the_extension_manager(driver="iscsi_irmc")
+ self.config(enabled_hardware_types=['irmc'],
+ enabled_boot_interfaces=['irmc-virtual-media'],
+ enabled_console_interfaces=['ipmitool-socat'],
+ enabled_deploy_interfaces=['iscsi'],
+ enabled_inspect_interfaces=['irmc'],
+ enabled_management_interfaces=['irmc'],
+ enabled_power_interfaces=['irmc'],
+ enabled_raid_interfaces=['no-raid'],
+ enabled_rescue_interfaces=['agent'],
+ enabled_vendor_interfaces=['no-vendor'])
self.node = obj_utils.create_test_node(
- self.context, driver='iscsi_irmc', driver_info=INFO_DICT)
+ self.context, driver='irmc', driver_info=INFO_DICT)
@mock.patch.object(deploy_utils, 'validate_image_properties',
spec_set=True, autospec=True)
@@ -915,14 +984,15 @@ class IRMCVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(irmc_management, 'backup_bios_config', spec_set=True,
autospec=True)
- @mock.patch.object(irmc_boot, '_setup_deploy_iso',
+ @mock.patch.object(irmc_boot, '_setup_vmedia',
spec_set=True, autospec=True)
@mock.patch.object(deploy_utils, 'get_single_nic_with_vif_port_id',
spec_set=True, autospec=True)
def _test_prepare_ramdisk(self,
get_single_nic_with_vif_port_id_mock,
- _setup_deploy_iso_mock,
- mock_backup_bios):
+ _setup_vmedia_mock,
+ mock_backup_bios,
+ mode='deploy'):
instance_info = self.node.instance_info
instance_info['irmc_boot_iso'] = 'glance://abcdef'
instance_info['image_source'] = '6b2f0c0c-79e8-4db6-842e-43c9764204af'
@@ -938,8 +1008,8 @@ class IRMCVirtualMediaBootTestCase(db_base.DbTestCase):
expected_ramdisk_opts = {'a': 'b', 'BOOTIF': '12:34:56:78:90:ab'}
get_single_nic_with_vif_port_id_mock.assert_called_once_with(
task)
- _setup_deploy_iso_mock.assert_called_once_with(
- task, expected_ramdisk_opts)
+ _setup_vmedia_mock.assert_called_once_with(
+ task, mode, expected_ramdisk_opts)
self.assertEqual('glance://abcdef',
self.node.instance_info['irmc_boot_iso'])
provision_state = task.node.provision_state
@@ -951,12 +1021,17 @@ class IRMCVirtualMediaBootTestCase(db_base.DbTestCase):
self.node.save()
self._test_prepare_ramdisk()
+ def test_prepare_ramdisk_glance_image_rescuing(self):
+ self.node.provision_state = states.RESCUING
+ self.node.save()
+ self._test_prepare_ramdisk(mode='rescue')
+
def test_prepare_ramdisk_glance_image_cleaning(self):
self.node.provision_state = states.CLEANING
self.node.save()
self._test_prepare_ramdisk()
- @mock.patch.object(irmc_boot, '_setup_deploy_iso', spec_set=True,
+ @mock.patch.object(irmc_boot, '_setup_vmedia', spec_set=True,
autospec=True)
def test_prepare_ramdisk_not_deploying_not_cleaning(self, mock_is_image):
"""Ensure deploy ops are blocked when not deploying and not cleaning"""
@@ -1037,7 +1112,7 @@ class IRMCVirtualMediaBootTestCase(db_base.DbTestCase):
task.driver.boot.clean_up_instance(task)
_remove_share_file_mock.assert_called_once_with(
- irmc_boot._get_boot_iso_name(task.node))
+ irmc_boot._get_iso_name(task.node, label='boot'))
self.assertNotIn('irmc_boot_iso',
task.node.driver_internal_info)
self.assertNotIn('root_uuid_or_disk_id',
@@ -1208,6 +1283,35 @@ class IRMCVirtualMediaBootTestCase(db_base.DbTestCase):
self.assertFalse(mock_set_secure_boot_mode.called)
mock_cleanup_vmedia_boot.assert_called_once_with(task)
+ @mock.patch.object(os.path, 'isfile', return_value=True,
+ autospec=True)
+ def test_validate_rescue(self, mock_isfile):
+ driver_info = self.node.driver_info
+ driver_info['irmc_rescue_iso'] = 'rescue.iso'
+ self.node.driver_info = driver_info
+ self.node.save()
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ task.driver.boot.validate_rescue(task)
+
+ def test_validate_rescue_no_rescue_ramdisk(self):
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ self.assertRaisesRegex(exception.MissingParameterValue,
+ 'Missing.*irmc_rescue_iso',
+ task.driver.boot.validate_rescue, task)
+
+ @mock.patch.object(os.path, 'isfile', return_value=False,
+ autospec=True)
+ def test_validate_rescue_ramdisk_not_exist(self, mock_isfile):
+ driver_info = self.node.driver_info
+ driver_info['irmc_rescue_iso'] = 'rescue.iso'
+ self.node.driver_info = driver_info
+ self.node.save()
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ self.assertRaisesRegex(exception.InvalidParameterValue,
+ 'Rescue ISO file, .*'
+ 'not found for node: .*',
+ task.driver.boot.validate_rescue, task)
+
class IRMCPXEBootTestCase(db_base.DbTestCase):
diff --git a/ironic/tests/unit/drivers/test_irmc.py b/ironic/tests/unit/drivers/test_irmc.py
index 6970b9f2a..47e67263e 100644
--- a/ironic/tests/unit/drivers/test_irmc.py
+++ b/ironic/tests/unit/drivers/test_irmc.py
@@ -128,7 +128,8 @@ class IRMCHardwareTestCase(db_base.DbTestCase):
enabled_inspect_interfaces=['irmc'],
enabled_management_interfaces=['irmc'],
enabled_power_interfaces=['irmc'],
- enabled_raid_interfaces=['no-raid', 'agent'])
+ enabled_raid_interfaces=['no-raid', 'agent'],
+ enabled_rescue_interfaces=['no-rescue', 'agent'])
def test_default_interfaces(self):
node = obj_utils.create_test_node(self.context, driver='irmc')
@@ -147,6 +148,8 @@ class IRMCHardwareTestCase(db_base.DbTestCase):
irmc.power.IRMCPower)
self.assertIsInstance(task.driver.raid,
noop.NoRAID)
+ self.assertIsInstance(task.driver.rescue,
+ noop.NoRescue)
def test_override_with_inspector(self):
self.config(enabled_inspect_interfaces=['inspector', 'irmc'])
@@ -170,3 +173,29 @@ class IRMCHardwareTestCase(db_base.DbTestCase):
irmc.power.IRMCPower)
self.assertIsInstance(task.driver.raid,
agent.AgentRAID)
+ self.assertIsInstance(task.driver.rescue,
+ noop.NoRescue)
+
+ def test_override_with_agent_rescue(self):
+ node = obj_utils.create_test_node(
+ self.context, driver='irmc',
+ deploy_interface='direct',
+ rescue_interface='agent',
+ raid_interface='agent')
+ with task_manager.acquire(self.context, node.id) as task:
+ self.assertIsInstance(task.driver.boot,
+ irmc.boot.IRMCVirtualMediaBoot)
+ self.assertIsInstance(task.driver.console,
+ ipmitool.IPMISocatConsole)
+ self.assertIsInstance(task.driver.deploy,
+ agent.AgentDeploy)
+ self.assertIsInstance(task.driver.inspect,
+ irmc.inspect.IRMCInspect)
+ self.assertIsInstance(task.driver.management,
+ irmc.management.IRMCManagement)
+ self.assertIsInstance(task.driver.power,
+ irmc.power.IRMCPower)
+ self.assertIsInstance(task.driver.raid,
+ agent.AgentRAID)
+ self.assertIsInstance(task.driver.rescue,
+ agent.AgentRescue)
diff --git a/releasenotes/notes/rescue-interface-for-irmc-hardware-type-17e38197849748e0.yaml b/releasenotes/notes/rescue-interface-for-irmc-hardware-type-17e38197849748e0.yaml
new file mode 100644
index 000000000..652262b27
--- /dev/null
+++ b/releasenotes/notes/rescue-interface-for-irmc-hardware-type-17e38197849748e0.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Adds support for rescue interface ``agent`` for ``irmc`` hardware type
+ when corresponding boot interface is ``irmc-virtual-media``.
+ The supported values of rescue interface for ``irmc`` hardware type
+ are ``agent`` and ``no-rescue``. The default value is ``no-rescue``. \ No newline at end of file