redfish-virtual-media: allow USB devices instead of floppies

Support for floppy disks is increasingly harder to find, let us support
USB devices as an alternative.

Change-Id: Ib02b716cbcf1f7b4ed8e47cf3fcf40872f1dc9a1
This commit is contained in:
Dmitry Tantsur 2021-03-25 15:14:45 +01:00
parent ea6dff7d31
commit 1abff50dbb
3 changed files with 175 additions and 54 deletions

View File

@ -41,10 +41,10 @@ REQUIRED_PROPERTIES = {
}
OPTIONAL_PROPERTIES = {
'config_via_floppy': _("Boolean value to indicate whether or not the "
"driver should use virtual media Floppy device "
"for passing configuration information to the "
"ramdisk. Defaults to False. Optional."),
'config_via_removable': _("Boolean value to indicate whether or not the "
"driver should use virtual media USB or floppy "
"device for passing configuration information "
"to the ramdisk. Defaults to False. Optional."),
'kernel_append_params': _("Additional kernel parameters to pass down to "
"instance kernel. These parameters can be "
"consumed by the kernel or by the applications "
@ -126,6 +126,12 @@ def _parse_driver_info(node):
{option: d_info.get(option, getattr(CONF.conductor, option, None))
for option in OPTIONAL_PROPERTIES})
if (d_info.get('config_via_removable') is None
and d_info.get('config_via_floppy') is not None):
LOG.warning('The config_via_floppy driver_info option is deprecated, '
'use config_via_removable for node %s', node.uuid)
deploy_info['config_via_removable'] = d_info['config_via_floppy']
deploy_info.update(redfish_utils.parse_driver_info(node))
return deploy_info
@ -255,13 +261,19 @@ def _has_vmedia_device(managers, boot_device):
"""Indicate if device exists at any of the managers
:param managers: A list of System managers.
:param boot_device: sushy boot device e.g. `VIRTUAL_MEDIA_CD`,
`VIRTUAL_MEDIA_DVD` or `VIRTUAL_MEDIA_FLOPPY`.
:param boot_device: One or more sushy boot device e.g. `VIRTUAL_MEDIA_CD`,
`VIRTUAL_MEDIA_DVD` or `VIRTUAL_MEDIA_FLOPPY`. Several devices are
checked in the given order.
:return: The device that could be found or False.
"""
for manager in managers:
for v_media in manager.virtual_media.get_members():
if boot_device in v_media.media_types:
return True
if isinstance(boot_device, str):
boot_device = [boot_device]
for dev in boot_device:
for manager in managers:
for v_media in manager.virtual_media.get_members():
if dev in v_media.media_types:
return dev
return False
@ -465,7 +477,7 @@ class RedfishVirtualMediaBoot(base.BootInterface):
d_info = _parse_driver_info(node)
config_via_floppy = d_info.get('config_via_floppy')
config_via_removable = d_info.get('config_via_removable')
deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task)
if deploy_nic_mac is not None:
@ -475,27 +487,32 @@ class RedfishVirtualMediaBoot(base.BootInterface):
managers = redfish_utils.get_system(task.node).managers
if config_via_floppy:
if config_via_removable:
if _has_vmedia_device(managers, sushy.VIRTUAL_MEDIA_FLOPPY):
# NOTE (etingof): IPA will read the diskette only if
removable = _has_vmedia_device(
managers,
# Prefer USB devices since floppies are outdated
[sushy.VIRTUAL_MEDIA_USBSTICK, sushy.VIRTUAL_MEDIA_FLOPPY])
if removable:
# NOTE (etingof): IPA will read the device only if
# we tell it to
ramdisk_params['boot_method'] = 'vmedia'
floppy_ref = image_utils.prepare_floppy_image(
task, params=ramdisk_params)
_eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_FLOPPY)
_insert_vmedia(
task, managers, floppy_ref, sushy.VIRTUAL_MEDIA_FLOPPY)
_eject_vmedia(task, managers, removable)
_insert_vmedia(task, managers, floppy_ref, removable)
LOG.debug('Inserted virtual floppy with configuration for '
'node %(node)s', {'node': task.node.uuid})
LOG.info('Inserted virtual %(type)s device with configuration'
' for node %(node)s',
{'node': task.node.uuid, 'type': removable})
else:
LOG.warning('Config via floppy is requested, but '
'Floppy drive is not available on node '
'%(node)s', {'node': task.node.uuid})
LOG.warning('Config via a removable device is requested, but '
'virtual USB and floppy devices are not '
'available on node %(node)s',
{'node': task.node.uuid})
mode = deploy_utils.rescue_or_deploy_mode(node)
@ -524,23 +541,9 @@ class RedfishVirtualMediaBoot(base.BootInterface):
:param task: A task from TaskManager.
:returns: None
"""
d_info = _parse_driver_info(task.node)
config_via_floppy = d_info.get('config_via_floppy')
LOG.debug("Cleaning up deploy boot for "
"%(node)s", {'node': task.node.uuid})
managers = redfish_utils.get_system(task.node).managers
_eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_CD)
image_utils.cleanup_iso_image(task)
if (config_via_floppy
and _has_vmedia_device(managers, sushy.VIRTUAL_MEDIA_FLOPPY)):
_eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_FLOPPY)
image_utils.cleanup_floppy_image(task)
self._eject_all(task)
def prepare_instance(self, task):
"""Prepares the boot of instance over virtual media.
@ -625,11 +628,16 @@ class RedfishVirtualMediaBoot(base.BootInterface):
managers = redfish_utils.get_system(task.node).managers
_eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_CD)
d_info = task.node.driver_info
config_via_floppy = d_info.get('config_via_floppy')
if config_via_floppy:
config_via_removable = (
task.node.driver_info.get('config_via_removable')
or task.node.driver_info.get('config_via_floppy')
)
if config_via_removable:
_eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_USBSTICK)
_eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_FLOPPY)
image_utils.cleanup_floppy_image(task)
boot_option = deploy_utils.get_boot_option(task.node)
if (boot_option == 'ramdisk'
and task.node.instance_info.get('configdrive')):

View File

@ -83,6 +83,34 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
self.assertEqual('http://boot.iso',
actual_driver_info['redfish_deploy_iso'])
def test_parse_driver_info_removable(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.driver_info.update(
{'deploy_kernel': 'kernel',
'deploy_ramdisk': 'ramdisk',
'bootloader': 'bootloader',
'config_via_removable': True}
)
actual_driver_info = redfish_boot._parse_driver_info(task.node)
self.assertTrue(actual_driver_info['config_via_removable'])
@mock.patch.object(redfish_boot.LOG, 'warning', autospec=True)
def test_parse_driver_info_removable_deprecated(self, mock_log):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.driver_info.update(
{'deploy_kernel': 'kernel',
'deploy_ramdisk': 'ramdisk',
'bootloader': 'bootloader',
'config_via_floppy': True}
)
actual_driver_info = redfish_boot._parse_driver_info(task.node)
self.assertTrue(actual_driver_info['config_via_removable'])
self.assertTrue(mock_log.called)
def test_parse_driver_info_rescue(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
@ -500,12 +528,12 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
task.node.provision_state = states.DEPLOYING
d_info = {
'config_via_floppy': True
'config_via_removable': True
}
mock__parse_driver_info.return_value = d_info
mock__has_vmedia_device.return_value = True
mock__has_vmedia_device.return_value = sushy.VIRTUAL_MEDIA_FLOPPY
mock_prepare_floppy_image.return_value = 'floppy-image-url'
mock_prepare_deploy_iso.return_value = 'cd-image-url'
@ -515,7 +543,8 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
task, states.POWER_OFF)
mock__has_vmedia_device.assert_called_once_with(
managers, sushy.VIRTUAL_MEDIA_FLOPPY)
managers,
[sushy.VIRTUAL_MEDIA_USBSTICK, sushy.VIRTUAL_MEDIA_FLOPPY])
eject_calls = [
mock.call(task, managers, dev)
@ -548,7 +577,79 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock_boot_mode_utils.sync_boot_mode.assert_called_once_with(task)
@mock.patch.object(redfish_boot.manager_utils, 'node_set_boot_device',
autospec=True)
@mock.patch.object(image_utils, 'prepare_floppy_image', autospec=True)
@mock.patch.object(image_utils, 'prepare_deploy_iso', autospec=True)
@mock.patch.object(redfish_boot, '_has_vmedia_device', autospec=True)
@mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_insert_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_parse_driver_info', autospec=True)
@mock.patch.object(redfish_boot.manager_utils, 'node_power_action',
autospec=True)
@mock.patch.object(redfish_boot, 'boot_mode_utils', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test_prepare_ramdisk_with_usb(
self, mock_system, mock_boot_mode_utils, mock_node_power_action,
mock__parse_driver_info, mock__insert_vmedia, mock__eject_vmedia,
mock__has_vmedia_device, mock_prepare_deploy_iso,
mock_prepare_floppy_image, mock_node_set_boot_device):
managers = mock_system.return_value.managers
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.provision_state = states.DEPLOYING
d_info = {
'config_via_removable': True
}
mock__parse_driver_info.return_value = d_info
mock__has_vmedia_device.return_value = sushy.VIRTUAL_MEDIA_USBSTICK
mock_prepare_floppy_image.return_value = 'floppy-image-url'
mock_prepare_deploy_iso.return_value = 'cd-image-url'
task.driver.boot.prepare_ramdisk(task, {})
mock_node_power_action.assert_called_once_with(
task, states.POWER_OFF)
mock__has_vmedia_device.assert_called_once_with(
managers,
[sushy.VIRTUAL_MEDIA_USBSTICK, sushy.VIRTUAL_MEDIA_FLOPPY])
eject_calls = [
mock.call(task, managers, dev)
for dev in (sushy.VIRTUAL_MEDIA_USBSTICK,
sushy.VIRTUAL_MEDIA_CD)
]
mock__eject_vmedia.assert_has_calls(eject_calls)
insert_calls = [
mock.call(task, managers, 'floppy-image-url',
sushy.VIRTUAL_MEDIA_USBSTICK),
mock.call(task, managers, 'cd-image-url',
sushy.VIRTUAL_MEDIA_CD),
]
mock__insert_vmedia.assert_has_calls(insert_calls)
expected_params = {
'boot_method': 'vmedia',
'ipa-debug': '1',
'ipa-agent-token': mock.ANY,
}
mock_prepare_deploy_iso.assert_called_once_with(
task, expected_params, 'deploy', d_info)
mock_node_set_boot_device.assert_called_once_with(
task, boot_devices.CDROM, False)
mock_boot_mode_utils.sync_boot_mode.assert_called_once_with(task)
@mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
@mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True)
@mock.patch.object(image_utils, 'cleanup_floppy_image', autospec=True)
@ -557,15 +658,13 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
def test_clean_up_ramdisk(
self, mock_system, mock__parse_driver_info,
mock_cleanup_floppy_image, mock_cleanup_iso_image,
mock__eject_vmedia, mock__has_vmedia_device):
mock__eject_vmedia):
managers = mock_system.return_value.managers
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.provision_state = states.DEPLOYING
mock__parse_driver_info.return_value = {'config_via_floppy': True}
mock__has_vmedia_device.return_value = True
task.node.driver_info['config_via_removable'] = True
task.driver.boot.clean_up_ramdisk(task)
@ -573,11 +672,9 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock_cleanup_floppy_image.assert_called_once_with(task)
mock__has_vmedia_device.assert_called_once_with(
managers, sushy.VIRTUAL_MEDIA_FLOPPY)
eject_calls = [
mock.call(task, managers, sushy.VIRTUAL_MEDIA_CD),
mock.call(task, managers, sushy.VIRTUAL_MEDIA_USBSTICK),
mock.call(task, managers, sushy.VIRTUAL_MEDIA_FLOPPY)
]
@ -837,9 +934,11 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(boot_mode_utils, 'deconfigure_secure_boot_if_needed',
autospec=True)
@mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
@mock.patch.object(image_utils, 'cleanup_floppy_image', autospec=True)
@mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def _test_clean_up_instance(self, mock_system, mock_cleanup_iso_image,
mock_cleanup_floppy_image,
mock__eject_vmedia, mock_secure_boot):
managers = mock_system.return_value.managers
with task_manager.acquire(self.context, self.node.uuid,
@ -849,9 +948,12 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock_cleanup_iso_image.assert_called_once_with(task)
eject_calls = [mock.call(task, managers, sushy.VIRTUAL_MEDIA_CD)]
if task.node.driver_info.get('config_via_floppy'):
eject_calls.append(mock.call(task, managers,
sushy.VIRTUAL_MEDIA_FLOPPY))
if task.node.driver_info.get('config_via_removable'):
eject_calls.extend([
mock.call(task, managers, sushy.VIRTUAL_MEDIA_USBSTICK),
mock.call(task, managers, sushy.VIRTUAL_MEDIA_FLOPPY),
])
mock_cleanup_floppy_image.assert_called_once_with(task)
mock__eject_vmedia.assert_has_calls(eject_calls)
mock_secure_boot.assert_called_once_with(task)
@ -861,7 +963,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
def test_clean_up_instance_cdrom_and_floppy(self):
driver_info = self.node.driver_info
driver_info['config_via_floppy'] = True
driver_info['config_via_removable'] = True
self.node.driver_info = driver_info
self.node.save()
self._test_clean_up_instance()

View File

@ -0,0 +1,11 @@
---
deprecations:
- |
The node's ``driver_info`` parameter ``config_via_floppy`` of the
``redfish-virtual-media`` boot interface has been renamed to
``config_via_removable``. The old alias is deprecated.
features:
- |
Supplying configuration to the agent using the ``redfish-virtual-media``
boot interface now works through USB instead of floppy by default. Modern
hardware (and even virtual machines) has limited support for floppies.