Prevent redfish-virtual-media from being used with Dell nodes

Indicate that idrac-redfish-virtual-media must be used instead,
otherwise a confusing failure will happen.

Change-Id: I3b6ced6dcf03580903f5ea7237fc057f372999f9
(cherry picked from commit cf22604c58)
This commit is contained in:
Dmitry Tantsur 2021-01-20 11:58:44 +01:00
parent 7d74ea0eee
commit 46b34a73bf
6 changed files with 103 additions and 1 deletions

View File

@ -166,6 +166,12 @@ BIOS boot mode, it suffice to set ironic boot interface to
openstack baremetal node set --boot-interface redfish-virtual-media node-0 openstack baremetal node set --boot-interface redfish-virtual-media node-0
.. warning::
Dell hardware requires a non-standard Redfish call to boot from virtual
media, thus you **must** use the ``idrac`` hardware type and the
``idrac-redfish-virtual-media`` boot interface with it instead. See
:doc:`/admin/drivers/idrac` for more details on this hardware type.
If UEFI boot mode is desired, the user should additionally supply EFI If UEFI boot mode is desired, the user should additionally supply EFI
System Partition image (ESP_) via ``[driver-info]/bootloader`` ironic node System Partition image (ESP_) via ``[driver-info]/bootloader`` ironic node
property or ironic configuration file in form of Glance image UUID or a URL. property or ironic configuration file in form of Glance image UUID or a URL.

View File

@ -70,6 +70,9 @@ class DracRedfishVirtualMediaBoot(redfish_boot.RedfishVirtualMediaBoot):
boot_devices.CDROM: sushy.VIRTUAL_MEDIA_CD boot_devices.CDROM: sushy.VIRTUAL_MEDIA_CD
} }
def _validate_vendor(self, task):
pass # assume people are doing the right thing
@classmethod @classmethod
def _set_boot_device(cls, task, device, persistent=False): def _set_boot_device(cls, task, device, persistent=False):
"""Set boot device for a node. """Set boot device for a node.

View File

@ -360,6 +360,19 @@ class RedfishVirtualMediaBoot(base.BootInterface):
deploy_utils.validate_image_properties(task.context, d_info, props) deploy_utils.validate_image_properties(task.context, d_info, props)
def _validate_vendor(self, task):
vendor = task.node.properties.get('vendor')
if not vendor:
return
if 'Dell' in vendor.split():
raise exception.InvalidParameterValue(
_("The %(iface)s boot interface is not suitable for node "
"%(node)s with vendor %(vendor)s, use "
"idrac-redfish-virtual-media instead")
% {'iface': task.node.boot_interface,
'node': task.node.uuid, 'vendor': vendor})
def validate(self, task): def validate(self, task):
"""Validate the deployment information for the task's node. """Validate the deployment information for the task's node.
@ -371,6 +384,7 @@ class RedfishVirtualMediaBoot(base.BootInterface):
:raises: InvalidParameterValue on malformed parameter(s) :raises: InvalidParameterValue on malformed parameter(s)
:raises: MissingParameterValue on missing parameter(s) :raises: MissingParameterValue on missing parameter(s)
""" """
self._validate_vendor(task)
self._validate_driver_info(task) self._validate_driver_info(task)
if task.driver.storage.should_write_image(task): if task.driver.storage.should_write_image(task):

View File

@ -25,13 +25,15 @@ from oslo_utils import importutils
from ironic.common import boot_devices from ironic.common import boot_devices
from ironic.common import exception from ironic.common import exception
from ironic.conductor import task_manager from ironic.conductor import task_manager
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.drac import boot as drac_boot from ironic.drivers.modules.drac import boot as drac_boot
from ironic.tests.unit.db import utils as db_utils
from ironic.tests.unit.drivers.modules.drac import utils as test_utils from ironic.tests.unit.drivers.modules.drac import utils as test_utils
from ironic.tests.unit.objects import utils as obj_utils from ironic.tests.unit.objects import utils as obj_utils
sushy = importutils.try_import('sushy') sushy = importutils.try_import('sushy')
INFO_DICT = test_utils.INFO_DICT INFO_DICT = dict(db_utils.get_test_redfish_info(), **test_utils.INFO_DICT)
@mock.patch.object(drac_boot, 'redfish_utils', autospec=True) @mock.patch.object(drac_boot, 'redfish_utils', autospec=True)
@ -42,6 +44,28 @@ class DracBootTestCase(test_utils.BaseDracTest):
self.node = obj_utils.create_test_node( self.node = obj_utils.create_test_node(
self.context, driver='idrac', driver_info=INFO_DICT) self.context, driver='idrac', driver_info=INFO_DICT)
@mock.patch.object(deploy_utils, 'validate_image_properties',
autospec=True)
def test_validate_correct_vendor(self, mock_redfish_utils,
mock_validate_image_properties):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.instance_info.update(
{'kernel': 'kernel',
'ramdisk': 'ramdisk',
'image_source': 'http://image/source'}
)
task.node.driver_info.update(
{'deploy_kernel': 'kernel',
'deploy_ramdisk': 'ramdisk',
'bootloader': 'bootloader'}
)
task.node.properties['vendor'] = "Dell Inc."
task.driver.boot.validate(task)
def test__set_boot_device_persistent(self, mock_redfish_utils): def test__set_boot_device_persistent(self, mock_redfish_utils):
mock_system = mock_redfish_utils.get_system.return_value mock_system = mock_redfish_utils.get_system.return_value

View File

@ -292,6 +292,55 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock_validate_image_properties.assert_called_once_with( mock_validate_image_properties.assert_called_once_with(
mock.ANY, mock.ANY, mock.ANY) mock.ANY, mock.ANY, mock.ANY)
@mock.patch.object(redfish_utils, 'parse_driver_info', autospec=True)
@mock.patch.object(deploy_utils, 'validate_image_properties',
autospec=True)
def test_validate_correct_vendor(self, mock_validate_image_properties,
mock_parse_driver_info):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.instance_info.update(
{'kernel': 'kernel',
'ramdisk': 'ramdisk',
'image_source': 'http://image/source'}
)
task.node.driver_info.update(
{'deploy_kernel': 'kernel',
'deploy_ramdisk': 'ramdisk',
'bootloader': 'bootloader'}
)
task.node.properties['vendor'] = "Ironic Co."
task.driver.boot.validate(task)
@mock.patch.object(redfish_utils, 'parse_driver_info', autospec=True)
@mock.patch.object(deploy_utils, 'validate_image_properties',
autospec=True)
def test_validate_incompatible_with_idrac(self,
mock_validate_image_properties,
mock_parse_driver_info):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.instance_info.update(
{'kernel': 'kernel',
'ramdisk': 'ramdisk',
'image_source': 'http://image/source'}
)
task.node.driver_info.update(
{'deploy_kernel': 'kernel',
'deploy_ramdisk': 'ramdisk',
'bootloader': 'bootloader'}
)
task.node.properties['vendor'] = "Dell Inc."
self.assertRaisesRegex(exception.InvalidParameterValue,
"with vendor Dell Inc.",
task.driver.boot.validate, task)
@mock.patch.object(redfish_utils, 'parse_driver_info', autospec=True) @mock.patch.object(redfish_utils, 'parse_driver_info', autospec=True)
@mock.patch.object(deploy_utils, 'validate_image_properties', @mock.patch.object(deploy_utils, 'validate_image_properties',
autospec=True) autospec=True)

View File

@ -0,0 +1,6 @@
---
fixes:
- |
The ``redfish-virtual-media`` boot interface no longer passes validation
for Dell nodes. The ``idrac-redfish-virtual-media`` boot interface must
be used for these nodes instead.