Merge "Implements validate_rescue() for IRMCVirtualMediaBoot"

This commit is contained in:
Zuul 2018-03-13 14:20:18 +00:00 committed by Gerrit Code Review
commit d13713acf5
4 changed files with 280 additions and 95 deletions

View File

@ -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)
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)
_setup_vmedia_for_boot(task, 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.
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 "deploy-%s.iso" % node.uuid
def _get_boot_iso_name(node):
"""Returns the boot ISO file name for a given node.
:param node: the node for which ISO file name is to be provided.
"""
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."""

View File

@ -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):

View File

@ -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)

View File

@ -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``.