diff --git a/ironic/drivers/modules/redfish/management.py b/ironic/drivers/modules/redfish/management.py index 04e3d96b0c..ba05179e99 100644 --- a/ironic/drivers/modules/redfish/management.py +++ b/ironic/drivers/modules/redfish/management.py @@ -303,6 +303,19 @@ class RedfishManagement(base.ManagementInterface): {'node': task.node.uuid, 'mode': mode, 'error': e}) 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) def get_boot_mode(self, task): diff --git a/ironic/tests/unit/drivers/modules/redfish/test_management.py b/ironic/tests/unit/drivers/modules/redfish/test_management.py index 5bc5aaf418..3a48ce76d2 100644 --- a/ironic/tests/unit/drivers/modules/redfish/test_management.py +++ b/ironic/tests/unit/drivers/modules/redfish/test_management.py @@ -336,6 +336,12 @@ class RedfishManagementTestCase(db_base.DbTestCase): @mock.patch.object(redfish_utils, 'get_system', autospec=True) 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() mock_get_system.return_value = fake_system with task_manager.acquire(self.context, self.node.uuid, @@ -383,7 +389,12 @@ class RedfishManagementTestCase(db_base.DbTestCase): @mock.patch.object(sushy, 'Sushy', autospec=True) @mock.patch.object(redfish_utils, 'get_system', autospec=True) 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 = ( sushy.exceptions.SushyError) mock_get_system.return_value = fake_system @@ -396,6 +407,27 @@ class RedfishManagementTestCase(db_base.DbTestCase): mode=boot_modes.UEFI) 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) def test_get_boot_mode(self, mock_get_system): boot_attribute = { diff --git a/ironic/tests/unit/drivers/third_party_driver_mocks.py b/ironic/tests/unit/drivers/third_party_driver_mocks.py index ab1f493bb7..ca5199a79c 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mocks.py +++ b/ironic/tests/unit/drivers/third_party_driver_mocks.py @@ -228,6 +228,8 @@ if not sushy: type('ResourceNotFoundError', (sushy.exceptions.SushyError,), {})) sushy.exceptions.MissingAttributeError = ( type('MissingAttributeError', (sushy.exceptions.SushyError,), {})) + sushy.exceptions.BadRequestError = ( + type('BadRequestError', (sushy.exceptions.SushyError,), {})) sushy.auth = mock.MagicMock(spec_set=mock_specs.SUSHY_AUTH_SPEC) sys.modules['sushy.auth'] = sushy.auth diff --git a/releasenotes/notes/redfish-boot-mode-override-not-present-handling-92e7263617e467c4.yaml b/releasenotes/notes/redfish-boot-mode-override-not-present-handling-92e7263617e467c4.yaml new file mode 100644 index 0000000000..79f20e4390 --- /dev/null +++ b/releasenotes/notes/redfish-boot-mode-override-not-present-handling-92e7263617e467c4.yaml @@ -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.