Allow unsupported redfish set_boot_mode

Currently if the baremetal boot mode is unknown and the driver doesn't
support setting the boot mode then the error is logged and deployment
continues.

However if the BMC doesn't support getting or setting the boot mode
then setting the boot mode raises an error which results in the deploy
failing. This is the case for HPE Gen9 baremetal, which doesn't have a
'BootSourceOverrideMode' attribute in its system Boot field, and
raises a 400 iLO.2.14.UnsupportedOperation in response to setting the
boot mode.

This is raised from set_boot_mode as a RedfishError. This change
raises UnsupportedDriverExtension exception when the 'mode' attribute
is missing from the 'boot' field, allowing the deployment to continue.

Change-Id: I360ff8180be252de21f5fcd2208947087e332a39
This commit is contained in:
Steve Baker 2021-03-09 15:50:24 +13:00 committed by Julia Kreger
parent f1641468bb
commit 9f221a7d42
4 changed files with 57 additions and 1 deletions

View File

@ -303,6 +303,19 @@ class RedfishManagement(base.ManagementInterface):
{'node': task.node.uuid, 'mode': mode, {'node': task.node.uuid, 'mode': mode,
'error': e}) 'error': e})
LOG.error(error_msg) LOG.error(error_msg)
# NOTE(sbaker): Some systems such as HPE Gen9 do not support
# getting or setting the boot mode. When setting failed and the
# mode attribute is missing from the boot field, raising
# UnsupportedDriverExtension will allow the deploy to continue.
if system.boot.get('mode') is None:
LOG.info(_('Attempt to set boot mode on node %(node)s '
'failed to set boot mode as the node does not '
'appear to support overriding the boot mode. '
'Possibly partial Redfish implementation?'),
{'node': task.node.uuid})
raise exception.UnsupportedDriverExtension(
driver=task.node.driver, extension='set_boot_mode')
raise exception.RedfishError(error=error_msg) raise exception.RedfishError(error=error_msg)
def get_boot_mode(self, task): def get_boot_mode(self, task):

View File

@ -350,6 +350,12 @@ class RedfishManagementTestCase(db_base.DbTestCase):
@mock.patch.object(redfish_utils, 'get_system', autospec=True) @mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test_set_boot_mode(self, mock_get_system): def test_set_boot_mode(self, mock_get_system):
boot_attribute = {
'target': sushy.BOOT_SOURCE_TARGET_PXE,
'enabled': sushy.BOOT_SOURCE_ENABLED_CONTINUOUS,
'mode': sushy.BOOT_SOURCE_MODE_BIOS,
}
fake_system = mock.Mock(boot=boot_attribute)
fake_system = mock.Mock() fake_system = mock.Mock()
mock_get_system.return_value = fake_system mock_get_system.return_value = fake_system
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
@ -374,7 +380,12 @@ class RedfishManagementTestCase(db_base.DbTestCase):
@mock.patch.object(sushy, 'Sushy', autospec=True) @mock.patch.object(sushy, 'Sushy', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True) @mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test_set_boot_mode_fail(self, mock_get_system, mock_sushy): def test_set_boot_mode_fail(self, mock_get_system, mock_sushy):
fake_system = mock.Mock() boot_attribute = {
'target': sushy.BOOT_SOURCE_TARGET_PXE,
'enabled': sushy.BOOT_SOURCE_ENABLED_CONTINUOUS,
'mode': sushy.BOOT_SOURCE_MODE_BIOS,
}
fake_system = mock.Mock(boot=boot_attribute)
fake_system.set_system_boot_options.side_effect = ( fake_system.set_system_boot_options.side_effect = (
sushy.exceptions.SushyError) sushy.exceptions.SushyError)
mock_get_system.return_value = fake_system mock_get_system.return_value = fake_system
@ -387,6 +398,27 @@ class RedfishManagementTestCase(db_base.DbTestCase):
mode=boot_modes.UEFI) mode=boot_modes.UEFI)
mock_get_system.assert_called_once_with(task.node) mock_get_system.assert_called_once_with(task.node)
@mock.patch.object(sushy, 'Sushy', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test_set_boot_mode_unsupported(self, mock_get_system, mock_sushy):
boot_attribute = {
'target': sushy.BOOT_SOURCE_TARGET_PXE,
'enabled': sushy.BOOT_SOURCE_ENABLED_CONTINUOUS,
}
fake_system = mock.Mock(boot=boot_attribute)
error = sushy.exceptions.BadRequestError('PATCH', '/', mock.Mock())
fake_system.set_system_boot_options.side_effect = error
mock_get_system.return_value = fake_system
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.assertRaisesRegex(
exception.UnsupportedDriverExtension,
'does not support set_boot_mode',
task.driver.management.set_boot_mode, task, boot_modes.UEFI)
fake_system.set_system_boot_options.assert_called_once_with(
mode=boot_modes.UEFI)
mock_get_system.assert_called_once_with(task.node)
@mock.patch.object(redfish_utils, 'get_system', autospec=True) @mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test_get_boot_mode(self, mock_get_system): def test_get_boot_mode(self, mock_get_system):
boot_attribute = { boot_attribute = {

View File

@ -240,6 +240,8 @@ if not sushy:
type('OEMExtensionNotFoundError', (sushy.exceptions.SushyError,), {})) type('OEMExtensionNotFoundError', (sushy.exceptions.SushyError,), {}))
sushy.exceptions.ServerSideError = ( sushy.exceptions.ServerSideError = (
type('ServerSideError', (sushy.exceptions.SushyError,), {})) type('ServerSideError', (sushy.exceptions.SushyError,), {}))
sushy.exceptions.BadRequestError = (
type('BadRequestError', (sushy.exceptions.SushyError,), {}))
sushy.auth = mock.MagicMock(spec_set=mock_specs.SUSHY_AUTH_SPEC) sushy.auth = mock.MagicMock(spec_set=mock_specs.SUSHY_AUTH_SPEC)
sys.modules['sushy.auth'] = sushy.auth sys.modules['sushy.auth'] = sushy.auth

View File

@ -0,0 +1,9 @@
---
fixes:
- |
Adds handling of Redfish BMC's which lack a ``BootSourceOverrideMode``
flag, such that it is no longer a fatal error for a deployment if the BMC
does not support this field. This most common on BMCs which feature only
a partial implementation of the ``ComputerSystem`` resource ``boot``,
but may also be observable on some older generations of BMCs which
recieved updates to have partial Redfish support.