Allow usage of virtual media via System

DMTF now changes their Virtual Media URI to support Systems. Redfish driver now
support this resource to boot, so it is needed that Ironic have a way to use it.

Closes-Bug: #2039458
Change-Id: I66e8edb847e93f96374072525222f05e7561fb07
This commit is contained in:
Winicius Silva 2024-03-09 19:37:04 -03:00
parent 2f3448a421
commit 6d046ad7b4
4 changed files with 684 additions and 128 deletions

View File

@ -167,10 +167,36 @@ def _test_retry(exception):
return False return False
@tenacity.retry(retry=tenacity.retry_if_exception(_test_retry), def _has_vmedia_via_systems(system):
stop=tenacity.stop_after_attempt(3), """Indicates if virtual media is available trough Systems
wait=tenacity.wait_fixed(3),
reraise=True) :param system: A redfish System object
:return: True if the System has virtual media, else False
"""
try:
system.virtual_media
return True
except sushy.exceptions.MissingAttributeError:
return False
except AttributeError:
# NOTE(wncslln): In case of older versions of sushy are
# used.
return False
def _has_vmedia_via_manager(manager):
"""Indicates if virtual media is available in the Manager
:param manager: A redfish System object
:return: True if the System has virtual media, else False
"""
try:
manager.virtual_media
return True
except sushy.exceptions.MissingAttributeError:
return False
def _insert_vmedia(task, managers, boot_url, boot_device): def _insert_vmedia(task, managers, boot_url, boot_device):
"""Insert bootable ISO image into virtual CD or DVD """Insert bootable ISO image into virtual CD or DVD
@ -182,9 +208,46 @@ def _insert_vmedia(task, managers, boot_url, boot_device):
:raises: InvalidParameterValue, if no suitable virtual CD or DVD is :raises: InvalidParameterValue, if no suitable virtual CD or DVD is
found on the node. found on the node.
""" """
err_msg = None err_msgs = []
system = redfish_utils.get_system(task.node)
if _has_vmedia_via_systems(system):
inserted = _insert_vmedia_in_resource(task, system, boot_url,
boot_device, err_msgs)
if inserted:
return
else:
for manager in managers: for manager in managers:
for v_media in manager.virtual_media.get_members(): inserted = _insert_vmedia_in_resource(task, manager, boot_url,
boot_device, err_msgs)
if inserted:
return
if err_msgs:
exc_msg = ("All virtual media mount attempts failed. "
"Most recent error: ", err_msgs[-1])
else:
exc_msg = 'No suitable virtual media device found'
raise exception.InvalidParameterValue(exc_msg)
@tenacity.retry(retry=tenacity.retry_if_exception(_test_retry),
stop=tenacity.stop_after_attempt(3),
wait=tenacity.wait_fixed(3),
reraise=True)
def _insert_vmedia_in_resource(task, resource, boot_url, boot_device,
err_msgs):
"""Insert virtual media from a given redfish resource (System/Manager)
:param task: A task from TaskManager.
:param resource: A redfish resource either a System or Manager.
:param boot_url: URL to a bootable ISO image
:param boot_device: sushy boot device e.g. `VIRTUAL_MEDIA_CD`,
`VIRTUAL_MEDIA_DVD` or `VIRTUAL_MEDIA_FLOPPY`
:param err_msgs: A list that will contain all errors found
:raises: InvalidParameterValue, if no suitable virtual CD or DVD is
found on the node.
"""
for v_media in resource.virtual_media.get_members():
if boot_device not in v_media.media_types: if boot_device not in v_media.media_types:
# NOTE(janders): this conditional allows v_media that only # NOTE(janders): this conditional allows v_media that only
# support DVD MediaType and NOT CD to also be used. # support DVD MediaType and NOT CD to also be used.
@ -211,7 +274,7 @@ def _insert_vmedia(task, managers, boot_url, boot_device):
"%(node)s", {'node': task.node.uuid, "%(node)s", {'node': task.node.uuid,
'boot_url': boot_url, 'boot_url': boot_url,
'boot_device': boot_device}) 'boot_device': boot_device})
return return True
continue continue
@ -229,6 +292,7 @@ def _insert_vmedia(task, managers, boot_url, boot_device):
"media device, if available", "media device, if available",
{'node': task.node.uuid, {'node': task.node.uuid,
'boot_device': boot_device}) 'boot_device': boot_device})
err_msgs.append(err_msg)
LOG.warning(err_msg) LOG.warning(err_msg)
continue continue
except sushy.exceptions.ServerSideError as e: except sushy.exceptions.ServerSideError as e:
@ -240,13 +304,7 @@ def _insert_vmedia(task, managers, boot_url, boot_device):
"%(node)s", {'node': task.node.uuid, "%(node)s", {'node': task.node.uuid,
'boot_url': boot_url, 'boot_url': boot_url,
'boot_device': boot_device}) 'boot_device': boot_device})
return return True
if (err_msg is not None):
exc_msg = ("All virtual media mount attempts failed. "
"Most recent error: ", err_msg)
else:
exc_msg = 'No suitable virtual media device found'
raise exception.InvalidParameterValue(exc_msg)
def _eject_vmedia(task, managers, boot_device=None): def _eject_vmedia(task, managers, boot_device=None):
@ -262,8 +320,38 @@ def _eject_vmedia(task, managers, boot_device=None):
found on the node. found on the node.
""" """
found = False found = False
system = redfish_utils.get_system(task.node)
# NOTE(wncslln): we will attempt to eject virtual media in Systems
# and in Managers.
if _has_vmedia_via_systems(system):
ejected = _eject_vmedia_from_resource(task, resource=system,
boot_device=boot_device)
if ejected:
found = True
for manager in managers: for manager in managers:
for v_media in manager.virtual_media.get_members(): if _has_vmedia_via_manager(manager):
ejected = _eject_vmedia_from_resource(task, resource=manager,
boot_device=boot_device)
if ejected:
found = True
continue
return found
def _eject_vmedia_from_resource(task, resource, boot_device=None):
"""Eject virtual media from a given redfish resource (System/Manager)
:param task: A task from TaskManager.
:param resource: A redfish resource either a System or Manager.
:param boot_device: sushy boot device e.g. `VIRTUAL_MEDIA_CD`,
`VIRTUAL_MEDIA_DVD` or `VIRTUAL_MEDIA_FLOPPY` or `None` to
eject everything (default).
:return: True if any device was ejected, else False
"""
found = False
for v_media in resource.virtual_media.get_members():
if boot_device and boot_device not in v_media.media_types: if boot_device and boot_device not in v_media.media_types:
# NOTE(iurygregory): this conditional allows v_media that only # NOTE(iurygregory): this conditional allows v_media that only
# support DVD MediaType and NOT CD to also be used. # support DVD MediaType and NOT CD to also be used.
@ -283,7 +371,6 @@ def _eject_vmedia(task, managers, boot_device=None):
else: else:
continue continue
inserted = v_media.inserted inserted = v_media.inserted
if inserted: if inserted:
v_media.eject_media() v_media.eject_media()
found = True found = True
@ -318,8 +405,8 @@ def eject_vmedia(task, boot_device=None):
image_utils.cleanup_iso_image(task) image_utils.cleanup_iso_image(task)
def _has_vmedia_device(managers, boot_device, inserted=None): def _has_vmedia_device(managers, boot_device, inserted=None, system=None):
"""Indicate if device exists at any of the managers """Indicate if device exists at any of the managers or system
:param managers: A list of System managers. :param managers: A list of System managers.
:param boot_device: One or more sushy boot device e.g. `VIRTUAL_MEDIA_CD`, :param boot_device: One or more sushy boot device e.g. `VIRTUAL_MEDIA_CD`,
@ -333,6 +420,15 @@ def _has_vmedia_device(managers, boot_device, inserted=None):
boot_device = [boot_device] boot_device = [boot_device]
for dev in boot_device: for dev in boot_device:
if _has_vmedia_via_systems(system):
for v_media in system.virtual_media.get_members():
if dev not in v_media.media_types:
continue
if (inserted is not None
and bool(v_media.inserted) is not inserted):
continue
return dev
else:
for manager in managers: for manager in managers:
for v_media in manager.virtual_media.get_members(): for v_media in manager.virtual_media.get_members():
if dev not in v_media.media_types: if dev not in v_media.media_types:
@ -526,13 +622,14 @@ class RedfishVirtualMediaBoot(base.BootInterface):
return return
d_info = _parse_driver_info(node) d_info = _parse_driver_info(node)
managers = redfish_utils.get_system(task.node).managers system = redfish_utils.get_system(task.node)
managers = system.managers
self._validate_vendor(task, managers) self._validate_vendor(task, managers)
if manager_utils.is_fast_track(task): if manager_utils.is_fast_track(task):
if _has_vmedia_device(managers, sushy.VIRTUAL_MEDIA_CD, if _has_vmedia_device(managers, sushy.VIRTUAL_MEDIA_CD,
inserted=True): inserted=True, system=system):
LOG.debug('Fast track operation for node %s, not inserting ' LOG.debug('Fast track operation for node %s, not inserting '
'any devices', node.uuid) 'any devices', node.uuid)
return return
@ -570,7 +667,8 @@ class RedfishVirtualMediaBoot(base.BootInterface):
removable = _has_vmedia_device( removable = _has_vmedia_device(
managers, managers,
# Prefer USB devices since floppies are outdated # Prefer USB devices since floppies are outdated
[sushy.VIRTUAL_MEDIA_USBSTICK, sushy.VIRTUAL_MEDIA_FLOPPY]) [sushy.VIRTUAL_MEDIA_USBSTICK, sushy.VIRTUAL_MEDIA_FLOPPY],
system=system)
if removable: if removable:
floppy_ref = image_utils.prepare_floppy_image( floppy_ref = image_utils.prepare_floppy_image(
task, params=ramdisk_params) task, params=ramdisk_params)

View File

@ -635,7 +635,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock__parse_driver_info, mock__insert_vmedia, mock__eject_vmedia, mock__parse_driver_info, mock__insert_vmedia, mock__eject_vmedia,
mock__has_vmedia_device, mock_prepare_deploy_iso, mock__has_vmedia_device, mock_prepare_deploy_iso,
mock_prepare_floppy_image, mock_node_set_boot_device): mock_prepare_floppy_image, mock_node_set_boot_device):
system = mock_system.return_value
managers = mock_system.return_value.managers managers = mock_system.return_value.managers
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
@ -658,7 +658,8 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock__has_vmedia_device.assert_called_once_with( mock__has_vmedia_device.assert_called_once_with(
managers, managers,
[sushy.VIRTUAL_MEDIA_USBSTICK, sushy.VIRTUAL_MEDIA_FLOPPY]) [sushy.VIRTUAL_MEDIA_USBSTICK, sushy.VIRTUAL_MEDIA_FLOPPY],
system=system)
eject_calls = [ eject_calls = [
mock.call(task, managers, dev) mock.call(task, managers, dev)
@ -708,7 +709,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock__parse_driver_info, mock__insert_vmedia, mock__eject_vmedia, mock__parse_driver_info, mock__insert_vmedia, mock__eject_vmedia,
mock__has_vmedia_device, mock_prepare_deploy_iso, mock__has_vmedia_device, mock_prepare_deploy_iso,
mock_prepare_floppy_image, mock_node_set_boot_device): mock_prepare_floppy_image, mock_node_set_boot_device):
system = mock_system.return_value
managers = mock_system.return_value.managers managers = mock_system.return_value.managers
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
@ -731,7 +732,8 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock__has_vmedia_device.assert_called_once_with( mock__has_vmedia_device.assert_called_once_with(
managers, managers,
[sushy.VIRTUAL_MEDIA_USBSTICK, sushy.VIRTUAL_MEDIA_FLOPPY]) [sushy.VIRTUAL_MEDIA_USBSTICK, sushy.VIRTUAL_MEDIA_FLOPPY],
system=system)
eject_calls = [ eject_calls = [
mock.call(task, managers, dev) mock.call(task, managers, dev)
@ -834,7 +836,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock__parse_driver_info, mock__insert_vmedia, mock__eject_vmedia, mock__parse_driver_info, mock__insert_vmedia, mock__eject_vmedia,
mock__has_vmedia_device, mock__has_vmedia_device,
mock_prepare_deploy_iso, mock_node_set_boot_device): mock_prepare_deploy_iso, mock_node_set_boot_device):
system = mock_system.return_value
managers = mock_system.return_value.managers managers = mock_system.return_value.managers
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
@ -844,7 +846,8 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
task.driver.boot.prepare_ramdisk(task, {}) task.driver.boot.prepare_ramdisk(task, {})
mock__has_vmedia_device.assert_called_once_with( mock__has_vmedia_device.assert_called_once_with(
managers, sushy.VIRTUAL_MEDIA_CD, inserted=True) managers, sushy.VIRTUAL_MEDIA_CD, inserted=True,
system=system)
mock_node_power_action.assert_not_called() mock_node_power_action.assert_not_called()
mock__eject_vmedia.assert_not_called() mock__eject_vmedia.assert_not_called()
@ -870,7 +873,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock__parse_driver_info, mock__insert_vmedia, mock__eject_vmedia, mock__parse_driver_info, mock__insert_vmedia, mock__eject_vmedia,
mock__has_vmedia_device, mock__has_vmedia_device,
mock_prepare_deploy_iso, mock_node_set_boot_device): mock_prepare_deploy_iso, mock_node_set_boot_device):
system = mock_system.return_value
managers = mock_system.return_value.managers managers = mock_system.return_value.managers
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
@ -883,7 +886,8 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
task.driver.boot.prepare_ramdisk(task, {}) task.driver.boot.prepare_ramdisk(task, {})
mock__has_vmedia_device.assert_called_once_with( mock__has_vmedia_device.assert_called_once_with(
managers, sushy.VIRTUAL_MEDIA_CD, inserted=True) managers, sushy.VIRTUAL_MEDIA_CD, inserted=True,
system=system)
mock_node_power_action.assert_called_once_with( mock_node_power_action.assert_called_once_with(
task, states.POWER_OFF) task, states.POWER_OFF)
@ -1338,10 +1342,13 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock__eject_vmedia.assert_has_calls(eject_calls) mock__eject_vmedia.assert_has_calls(eject_calls)
mock_secure_boot.assert_called_once_with(task) mock_secure_boot.assert_called_once_with(task)
def test__insert_vmedia_anew(self): @mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__insert_vmedia_anew(self, mock_sys, mock_vmd_sys):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mock_vmd_sys.return_value = False
mock_vmedia_cd = mock.MagicMock( mock_vmedia_cd = mock.MagicMock(
inserted=False, inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_CD]) media_types=[sushy.VIRTUAL_MEDIA_CD])
@ -1362,8 +1369,10 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
self.assertFalse(mock_vmedia_floppy.insert_media.call_count) self.assertFalse(mock_vmedia_floppy.insert_media.call_count)
def test__insert_vmedia_anew_dvd(self): @mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__insert_vmedia_anew_dvd(self, mock_sys, mock_vmd_sys):
mock_vmd_sys.return_value = False
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mock_vmedia_dvd = mock.MagicMock( mock_vmedia_dvd = mock.MagicMock(
@ -1382,8 +1391,10 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
'img-url', inserted=True, write_protected=True) 'img-url', inserted=True, write_protected=True)
@mock.patch('time.sleep', lambda *args, **kwargs: None) @mock.patch('time.sleep', lambda *args, **kwargs: None)
def test__insert_vmedia_anew_dvd_retry(self): @mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__insert_vmedia_anew_dvd_retry(self, mock_sys, mock_vmd_sys):
mock_vmd_sys.return_value = False
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mock_vmedia_dvd_1 = mock.MagicMock( mock_vmedia_dvd_1 = mock.MagicMock(
@ -1409,10 +1420,12 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
self.assertEqual(mock_vmedia_dvd_2.insert_media.call_count, 1) self.assertEqual(mock_vmedia_dvd_2.insert_media.call_count, 1)
def test__insert_vmedia_already_inserted(self): @mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__insert_vmedia_already_inserted(self, mock_sys, mock_vmd_sys):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mock_vmd_sys.return_value = False
mock_vmedia_cd = mock.MagicMock( mock_vmedia_cd = mock.MagicMock(
inserted=True, inserted=True,
image='img-url', image='img-url',
@ -1428,10 +1441,12 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
self.assertFalse(mock_vmedia_cd.insert_media.call_count) self.assertFalse(mock_vmedia_cd.insert_media.call_count)
@mock.patch('time.sleep', lambda *args, **kwargs: None) @mock.patch('time.sleep', lambda *args, **kwargs: None)
def test__insert_vmedia_while_ejecting(self): @mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__insert_vmedia_while_ejecting(self, mock_sys, mock_vmd_sys):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mock_vmd_sys.return_value = False
mock_vmedia_cd = mock.MagicMock( mock_vmedia_cd = mock.MagicMock(
inserted=False, inserted=False,
image='img-url', image='img-url',
@ -1453,8 +1468,10 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
self.assertEqual(mock_vmedia_cd.insert_media.call_count, 2) self.assertEqual(mock_vmedia_cd.insert_media.call_count, 2)
@mock.patch('time.sleep', lambda *args, **kwargs: None) @mock.patch('time.sleep', lambda *args, **kwargs: None)
def test__insert_vmedia_bad_device(self): @mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__insert_vmedia_bad_device(self, mock_sys, mock_vmd_sys):
mock_vmd_sys.return_value = False
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mock_vmedia_floppy = mock.MagicMock( mock_vmedia_floppy = mock.MagicMock(
@ -1472,10 +1489,11 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(image_utils, 'cleanup_disk_image', autospec=True) @mock.patch.object(image_utils, 'cleanup_disk_image', autospec=True)
@mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True) @mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True) @mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
def test_eject_vmedia_everything(self, mock_redfish_utils, def test_eject_vmedia_everything(self, mock_redfish_utils, mock_vmd_sys,
mock_cleanup_iso, mock_cleanup_disk): mock_cleanup_iso, mock_cleanup_disk):
mock_vmd_sys.return_value = False
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mock_vmedia_cd = mock.MagicMock( mock_vmedia_cd = mock.MagicMock(
@ -1507,10 +1525,11 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(image_utils, 'cleanup_disk_image', autospec=True) @mock.patch.object(image_utils, 'cleanup_disk_image', autospec=True)
@mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True) @mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True) @mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
def test_eject_vmedia_specific(self, mock_redfish_utils, def test_eject_vmedia_specific(self, mock_redfish_utils, mock_vmd_sys,
mock_cleanup_iso, mock_cleanup_disk): mock_cleanup_iso, mock_cleanup_disk):
mock_vmd_sys.return_value = False
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mock_vmedia_cd = mock.MagicMock( mock_vmedia_cd = mock.MagicMock(
@ -1538,14 +1557,16 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(image_utils, 'cleanup_disk_image', autospec=True) @mock.patch.object(image_utils, 'cleanup_disk_image', autospec=True)
@mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True) @mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True) @mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_boot.LOG, 'debug', autospec=True) @mock.patch.object(redfish_boot.LOG, 'debug', autospec=True)
@mock.patch.object(redfish_boot.LOG, 'info', autospec=True) @mock.patch.object(redfish_boot.LOG, 'info', autospec=True)
def test_eject_vmedia_with_dvd_cisco_ucs(self, mock_log_info, def test_eject_vmedia_with_dvd_cisco_ucs(self, mock_log_info,
mock_log_debug, mock_log_debug,
mock_vmd_sys,
mock_redfish_utils, mock_redfish_utils,
mock_cleanup_iso, mock_cleanup_iso,
mock_cleanup_disk): mock_cleanup_disk):
mock_vmd_sys.return_value = False
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mock_vmedia_dvd_1 = mock.MagicMock( mock_vmedia_dvd_1 = mock.MagicMock(
@ -1573,11 +1594,13 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock_cleanup_iso.assert_called_once_with(task) mock_cleanup_iso.assert_called_once_with(task)
mock_cleanup_disk.assert_not_called() mock_cleanup_disk.assert_not_called()
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True) @mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
def test_eject_vmedia_not_inserted(self, mock_redfish_utils): def test_eject_vmedia_not_inserted(self, mock_redfish_utils, mock_vmd_sys):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mock_vmd_sys.return_value = False
mock_vmedia_cd = mock.MagicMock( mock_vmedia_cd = mock.MagicMock(
inserted=False, inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_CD]) media_types=[sushy.VIRTUAL_MEDIA_CD])
@ -1598,11 +1621,13 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
self.assertFalse(mock_vmedia_cd.eject_media.call_count) self.assertFalse(mock_vmedia_cd.eject_media.call_count)
self.assertFalse(mock_vmedia_floppy.eject_media.call_count) self.assertFalse(mock_vmedia_floppy.eject_media.call_count)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True) @mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
def test_eject_vmedia_unknown(self, mock_redfish_utils): def test_eject_vmedia_unknown(self, mock_redfish_utils, mock_vmd_sys):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mock_vmd_sys.return_value = False
mock_vmedia_cd = mock.MagicMock( mock_vmedia_cd = mock.MagicMock(
inserted=False, inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_CD]) media_types=[sushy.VIRTUAL_MEDIA_CD])
@ -1619,7 +1644,9 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
self.assertFalse(mock_vmedia_cd.eject_media.call_count) self.assertFalse(mock_vmedia_cd.eject_media.call_count)
def test__has_vmedia_device(self): @mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
def test__has_vmedia_device(self, mock_vmd_sys):
mock_vmd_sys.return_value = False
mock_vmedia_cd = mock.MagicMock( mock_vmedia_cd = mock.MagicMock(
inserted=False, inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_CD]) media_types=[sushy.VIRTUAL_MEDIA_CD])
@ -1645,7 +1672,9 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
redfish_boot._has_vmedia_device( redfish_boot._has_vmedia_device(
[mock_manager], sushy.VIRTUAL_MEDIA_USBSTICK)) [mock_manager], sushy.VIRTUAL_MEDIA_USBSTICK))
def test__has_vmedia_device_inserted(self): @mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
def test__has_vmedia_device_inserted(self, mock_vmd_sys):
mock_vmd_sys.return_value = False
mock_vmedia_cd = mock.MagicMock( mock_vmedia_cd = mock.MagicMock(
inserted=False, inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_CD]) media_types=[sushy.VIRTUAL_MEDIA_CD])
@ -2512,3 +2541,426 @@ class RedfishHTTPBootTestCase(db_base.DbTestCase):
mock_secure_boot.assert_called_once_with(task) mock_secure_boot.assert_called_once_with(task)
mock_cleanup_disk_image.assert_not_called() mock_cleanup_disk_image.assert_not_called()
@mock.patch('oslo_utils.eventletutils.EventletEvent.wait',
lambda *args, **kwargs: None)
class RedfishVirtualMediaBootViaSystemTestCase(db_base.DbTestCase):
def setUp(self):
super(RedfishVirtualMediaBootViaSystemTestCase, self).setUp()
self.config(enabled_hardware_types=['redfish'],
enabled_power_interfaces=['redfish'],
enabled_boot_interfaces=['redfish-virtual-media'],
enabled_management_interfaces=['redfish'],
enabled_inspect_interfaces=['redfish'],
enabled_bios_interfaces=['redfish'])
self.node = obj_utils.create_test_node(
self.context, driver='redfish', driver_info=INFO_DICT)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__insert_vmedia_anew(self, mock_system, mock_vmd_sys):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_vmd_sys.return_value = True
mock_vmedia_cd = mock.MagicMock(
inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_CD])
mock_vmedia_floppy = mock.MagicMock(
inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_FLOPPY])
mock_manager = mock.MagicMock()
mock_system.return_value.virtual_media.get_members.return_value = [
mock_vmedia_cd, mock_vmedia_floppy]
redfish_boot._insert_vmedia(
task, [mock_manager], 'img-url', sushy.VIRTUAL_MEDIA_CD)
mock_vmedia_cd.insert_media.assert_called_once_with(
'img-url', inserted=True, write_protected=True)
self.assertFalse(mock_vmedia_floppy.insert_media.call_count)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__insert_vmedia_anew_dvd(self, mock_system, mock_vmd_sys):
mock_vmd_sys.return_value = True
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_vmedia_dvd = mock.MagicMock(
inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_DVD])
mock_manager = mock.MagicMock()
mock_system.return_value.virtual_media.get_members.return_value = [
mock_vmedia_dvd]
redfish_boot._insert_vmedia(
task, [mock_manager], 'img-url', sushy.VIRTUAL_MEDIA_CD)
mock_vmedia_dvd.insert_media.assert_called_once_with(
'img-url', inserted=True, write_protected=True)
@mock.patch('time.sleep', lambda *args, **kwargs: None)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__insert_vmedia_anew_dvd_retry(self, mock_system, mock_vmd_sys):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_vmd_sys.return_value = True
mock_vmedia_dvd_1 = mock.MagicMock(
inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_DVD])
mock_vmedia_dvd_2 = mock.MagicMock(
inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_DVD])
mock_manager = mock.MagicMock()
def clear_and_raise(*args, **kwargs):
mock_vmedia_dvd_1.insert_media.side_effect = None
raise sushy.exceptions.BadRequestError(
"POST", 'img-url', mock.MagicMock())
mock_vmedia_dvd_1.insert_media.side_effect = clear_and_raise
mock_system.return_value.virtual_media.get_members.return_value = [
mock_vmedia_dvd_1, mock_vmedia_dvd_2]
redfish_boot._insert_vmedia(
task, [mock_manager], 'img-url', sushy.VIRTUAL_MEDIA_CD)
self.assertEqual(mock_vmedia_dvd_2.insert_media.call_count, 1)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__insert_vmedia_already_inserted(self, mock_sys, mock_vmd_sys):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_vmd_sys.return_value = False
mock_vmedia_cd = mock.MagicMock(
inserted=True,
image='img-url',
media_types=[sushy.VIRTUAL_MEDIA_CD])
mock_manager = mock.MagicMock()
mock_manager.virtual_media.get_members.return_value = [
mock_vmedia_cd]
redfish_boot._insert_vmedia(
task, [mock_manager], 'img-url', sushy.VIRTUAL_MEDIA_CD)
self.assertFalse(mock_vmedia_cd.insert_media.call_count)
@mock.patch('time.sleep', lambda *args, **kwargs: None)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__insert_vmedia_while_ejecting(self, mock_system, mock_vmd_sys):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_vmd_sys.return_value = True
mock_vmedia_cd = mock.MagicMock(
inserted=False,
image='img-url',
media_types=[sushy.VIRTUAL_MEDIA_CD],
)
mock_manager = mock.MagicMock()
def clear_and_raise(*args, **kwargs):
mock_vmedia_cd.insert_media.side_effect = None
raise sushy.exceptions.ServerSideError(
"POST", 'img-url', mock.MagicMock())
mock_vmedia_cd.insert_media.side_effect = clear_and_raise
mock_system.return_value.virtual_media.get_members.return_value = [
mock_vmedia_cd]
redfish_boot._insert_vmedia(
task, [mock_manager], 'img-url', sushy.VIRTUAL_MEDIA_CD)
self.assertEqual(mock_vmedia_cd.insert_media.call_count, 2)
@mock.patch('time.sleep', lambda *args, **kwargs: None)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__insert_vmedia_bad_device(self, mock_sys, mock_vmd_sys):
mock_vmd_sys.return_value = True
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_vmedia_floppy = mock.MagicMock(
inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_FLOPPY])
mock_system = mock.MagicMock()
mock_manager = mock.MagicMock()
mock_system.virtual_media.get_members.return_value = [
mock_vmedia_floppy]
self.assertRaises(
exception.InvalidParameterValue,
redfish_boot._insert_vmedia,
task, [mock_manager], 'img-url', sushy.VIRTUAL_MEDIA_CD)
@mock.patch.object(image_utils, 'cleanup_disk_image', autospec=True)
@mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
def test_eject_vmedia_everything(self, mock_redfish_utils, mock_vmd_sys,
mock_cleanup_iso, mock_cleanup_disk):
mock_vmd_sys.return_value = True
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_vmedia_cd = mock.MagicMock(
inserted=True,
media_types=[sushy.VIRTUAL_MEDIA_CD])
mock_vmedia_floppy = mock.MagicMock(
inserted=True,
media_types=[sushy.VIRTUAL_MEDIA_FLOPPY])
mock_vmedia_dvd = mock.MagicMock(
inserted=True,
media_types=[sushy.VIRTUAL_MEDIA_DVD])
mock_manager = mock.MagicMock()
mock_system = mock.MagicMock()
mock_redfish_utils.get_system.return_value = mock_system
mock_system.managers = [
mock_manager]
mock_system.virtual_media.get_members.return_value = [
mock_vmedia_cd, mock_vmedia_floppy, mock_vmedia_dvd]
redfish_boot.eject_vmedia(task)
mock_vmedia_cd.eject_media.assert_called_once_with()
mock_vmedia_floppy.eject_media.assert_called_once_with()
mock_vmedia_dvd.eject_media.assert_called_once_with()
mock_cleanup_iso.assert_called_once_with(task)
mock_cleanup_disk.assert_called_once_with(task,
prefix='configdrive')
@mock.patch.object(image_utils, 'cleanup_disk_image', autospec=True)
@mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True)
@mock.patch.object(redfish_boot, '_has_vmedia_via_manager', autospec=True)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
def test_eject_vmedia_from_all_resources(self, mock_redfish_utils,
mock_vmd_sys, mock_vmd_mg,
mock_cleanup_iso,
mock_cleanup_disk):
mock_vmd_sys.return_value = True
mock_vmd_mg.return_value = True
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_vmedia_cd = mock.MagicMock(
inserted=True,
media_types=[sushy.VIRTUAL_MEDIA_CD])
mock_vmedia_floppy = mock.MagicMock(
inserted=True,
media_types=[sushy.VIRTUAL_MEDIA_FLOPPY])
mock_vmedia_dvd = mock.MagicMock(
inserted=True,
media_types=[sushy.VIRTUAL_MEDIA_DVD])
mock_manager = mock.MagicMock()
mock_system = mock.MagicMock()
mock_redfish_utils.get_system.return_value = mock_system
mock_system.managers = [
mock_manager]
mock_system.virtual_media.get_members.return_value = [
mock_vmedia_floppy, mock_vmedia_dvd]
mock_manager.virtual_media.get_members.return_value = [
mock_vmedia_cd]
redfish_boot.eject_vmedia(task)
mock_vmedia_cd.eject_media.assert_called_once_with()
mock_vmedia_floppy.eject_media.assert_called_once_with()
mock_vmedia_dvd.eject_media.assert_called_once_with()
mock_cleanup_iso.assert_called_once_with(task)
mock_cleanup_disk.assert_called_once_with(task,
prefix='configdrive')
@mock.patch.object(image_utils, 'cleanup_disk_image', autospec=True)
@mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
def test_eject_vmedia_specific(self, mock_redfish_utils, mock_vmd_sys,
mock_cleanup_iso, mock_cleanup_disk):
mock_vmd_sys.return_value = True
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_vmedia_cd = mock.MagicMock(
inserted=True,
media_types=[sushy.VIRTUAL_MEDIA_CD])
mock_vmedia_floppy = mock.MagicMock(
inserted=True,
media_types=[sushy.VIRTUAL_MEDIA_FLOPPY])
mock_system = mock.MagicMock()
mock_manager = mock.MagicMock()
mock_redfish_utils.get_system.return_value = mock_system
mock_system.managers = [
mock_manager]
mock_system.virtual_media.get_members.return_value = [
mock_vmedia_cd, mock_vmedia_floppy]
redfish_boot.eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
mock_vmedia_cd.eject_media.assert_called_once_with()
self.assertFalse(mock_vmedia_floppy.eject_media.call_count)
mock_cleanup_iso.assert_called_once_with(task)
mock_cleanup_disk.assert_not_called()
@mock.patch.object(image_utils, 'cleanup_disk_image', autospec=True)
@mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_boot.LOG, 'debug', autospec=True)
@mock.patch.object(redfish_boot.LOG, 'info', autospec=True)
def test_eject_vmedia_with_dvd_cisco_ucs(self, mock_log_info,
mock_log_debug,
mock_vmd_sys,
mock_redfish_utils,
mock_cleanup_iso,
mock_cleanup_disk):
mock_vmd_sys.return_value = True
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_vmedia_dvd_1 = mock.MagicMock(
inserted=True,
media_types=[sushy.VIRTUAL_MEDIA_DVD])
mock_vmedia_dvd_2 = mock.MagicMock(
inserted=True,
media_types=[sushy.VIRTUAL_MEDIA_DVD])
mock_system = mock.MagicMock()
mock_manager = mock.MagicMock()
mock_redfish_utils.get_system.return_value = mock_system
mock_system.managers = [
mock_manager]
mock_system.virtual_media.get_members.return_value = [
mock_vmedia_dvd_1, mock_vmedia_dvd_2]
redfish_boot.eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
mock_vmedia_dvd_1.eject_media.assert_called_once_with()
mock_vmedia_dvd_2.eject_media.assert_called_once_with()
self.assertEqual(mock_log_info.call_count, 2)
self.assertEqual(mock_log_debug.call_count, 3)
mock_cleanup_iso.assert_called_once_with(task)
mock_cleanup_disk.assert_not_called()
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
def test_eject_vmedia_not_inserted(self, mock_redfish_utils, mock_vmd_sys):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_vmd_sys.return_value = True
mock_vmedia_cd = mock.MagicMock(
inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_CD])
mock_vmedia_floppy = mock.MagicMock(
inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_FLOPPY])
mock_manager = mock.MagicMock()
mock_system = mock.MagicMock()
mock_system.virtual_media.get_members.return_value = [
mock_vmedia_cd, mock_vmedia_floppy]
mock_redfish_utils.get_system.return_value.managers = [
mock_manager]
redfish_boot.eject_vmedia(task)
self.assertFalse(mock_vmedia_cd.eject_media.call_count)
self.assertFalse(mock_vmedia_floppy.eject_media.call_count)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
def test_eject_vmedia_unknown(self, mock_redfish_utils, mock_vmd_sys):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_vmd_sys.return_value = True
mock_vmedia_cd = mock.MagicMock(
inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_CD])
mock_manager = mock.MagicMock()
mock_system = mock.MagicMock()
mock_system.virtual_media.get_members.return_value = [
mock_vmedia_cd]
mock_redfish_utils.get_system.return_value.managers = [
mock_manager]
redfish_boot.eject_vmedia(task)
self.assertFalse(mock_vmedia_cd.eject_media.call_count)
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
def test__has_vmedia_device(self, mock_vmd_sys):
mock_vmd_sys.return_value = True
mock_vmedia_cd = mock.MagicMock(
inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_CD])
mock_vmedia_floppy = mock.MagicMock(
inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_FLOPPY])
mock_manager = mock.MagicMock()
mock_system = mock.MagicMock()
mock_system.virtual_media.get_members.return_value = [
mock_vmedia_cd, mock_vmedia_floppy]
self.assertEqual(
sushy.VIRTUAL_MEDIA_CD,
redfish_boot._has_vmedia_device(
[mock_manager], sushy.VIRTUAL_MEDIA_CD,
system=mock_system))
self.assertFalse(
redfish_boot._has_vmedia_device(
[mock_manager], sushy.VIRTUAL_MEDIA_CD,
inserted=True, system=mock_system))
self.assertFalse(
redfish_boot._has_vmedia_device(
[mock_manager], sushy.VIRTUAL_MEDIA_USBSTICK,
system=mock_system))
@mock.patch.object(redfish_boot, '_has_vmedia_via_systems', autospec=True)
def test__has_vmedia_device_inserted(self, mock_vmd_sys):
mock_vmd_sys.return_value = True
mock_vmedia_cd = mock.MagicMock(
inserted=False,
media_types=[sushy.VIRTUAL_MEDIA_CD])
mock_vmedia_floppy = mock.MagicMock(
inserted=True,
media_types=[sushy.VIRTUAL_MEDIA_FLOPPY])
mock_manager = mock.MagicMock()
mock_system = mock.MagicMock()
mock_system.virtual_media.get_members.return_value = [
mock_vmedia_cd, mock_vmedia_floppy]
self.assertEqual(
sushy.VIRTUAL_MEDIA_FLOPPY,
redfish_boot._has_vmedia_device(
[mock_manager], sushy.VIRTUAL_MEDIA_FLOPPY,
inserted=True, system=mock_system))

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Fixes Redfish virtual media boot on BMCs that only expose the VirtualMedia
resource on Systems instead of Managers. For more informations, you can see
`bug 2039458 <https://bugs.launchpad.net/sushy/+bug/2039458>`_.

View File

@ -46,6 +46,6 @@ psutil>=3.2.2 # BSD
futurist>=1.2.0 # Apache-2.0 futurist>=1.2.0 # Apache-2.0
tooz>=2.7.0 # Apache-2.0 tooz>=2.7.0 # Apache-2.0
openstacksdk>=0.48.0 # Apache-2.0 openstacksdk>=0.48.0 # Apache-2.0
sushy>=4.7.0 sushy>=4.8.0
construct>=2.9.39 # MIT construct>=2.9.39 # MIT
netaddr>=0.9.0 # BSD netaddr>=0.9.0 # BSD