diff --git a/ironic/drivers/modules/drac/management.py b/ironic/drivers/modules/drac/management.py index 3bacde9623..f20493934d 100644 --- a/ironic/drivers/modules/drac/management.py +++ b/ironic/drivers/modules/drac/management.py @@ -538,9 +538,21 @@ class DracRedfishManagement(redfish_management.RedfishManagement): info.pop('import_task_monitor_url', None) node.driver_internal_info = info + succeeded = False if (import_task.task_state == sushy.TASK_STATE_COMPLETED and import_task.task_status in [sushy.HEALTH_OK, sushy.HEALTH_WARNING]): + + # Task could complete with errors (partial success) + # iDRAC 5.00.00.00 has stopped reporting Critical messages + # so checking also by message_id + succeeded = not any(m.message for m in import_task.messages + if (m.severity + and m.severity != sushy.SEVERITY_OK) + or (m.message_id and 'SYS055' + in m.message_id)) + + if succeeded: LOG.info('Configuration import %(task_monitor_url)s ' 'successful for node %(node)s', {'node': node.uuid, @@ -570,7 +582,11 @@ class DracRedfishManagement(redfish_management.RedfishManagement): # Select all messages, skipping OEM messages that don't have # `message` field populated. messages = [m.message for m in import_task.messages - if m.message is not None] + if m.message is not None + and ((m.severity + and m.severity != sushy.SEVERITY_OK) + or (m.message_id + and 'SYS055' in m.message_id))] error_msg = (_("Failed import configuration task: " "%(task_monitor_url)s. Message: '%(message)s'.") % {'task_monitor_url': task_monitor_url, diff --git a/ironic/tests/unit/drivers/modules/drac/test_management.py b/ironic/tests/unit/drivers/modules/drac/test_management.py index ebc958424c..92c0101841 100644 --- a/ironic/tests/unit/drivers/modules/drac/test_management.py +++ b/ironic/tests/unit/drivers/modules/drac/test_management.py @@ -1149,6 +1149,83 @@ class DracRedfishManagementTestCase(test_utils.BaseDracTest): self.assertIsNone( task.node.driver_internal_info.get('import_task_monitor_url')) + @mock.patch.object(redfish_utils, 'get_task_monitor', autospec=True) + def test__check_import_configuration_task_partial_failed( + self, mock_get_task_monitor): + driver_internal_info = {'import_task_monitor_url': '/TaskService/123'} + self.node.driver_internal_info = driver_internal_info + self.node.save() + + mock_message1 = mock.Mock() + mock_message1.message_id = 'SYS413' + mock_message1.message = 'The operation successfully completed' + mock_message1.severity = sushy.SEVERITY_OK + mock_message2 = mock.Mock() + mock_message2.message_id = 'SYS055' + mock_message2.message = 'Firmware upgrade failed' + mock_message2.severity = sushy.SEVERITY_CRITICAL + mock_import_task = mock.Mock() + mock_import_task.task_state = sushy.TASK_STATE_COMPLETED + mock_import_task.task_status = sushy.HEALTH_OK + mock_import_task.messages = [mock_message1, mock_message2] + mock_task_monitor = mock.Mock() + mock_task_monitor.is_processing = False + mock_task_monitor.get_task.return_value = mock_import_task + mock_get_task_monitor.return_value = mock_task_monitor + + self.management._set_success = mock.Mock() + self.management._set_failed = mock.Mock() + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + self.management._check_import_configuration_task( + task, '/TaskService/123') + + self.management._set_failed.assert_called_once_with( + task, mock.ANY, + "Failed import configuration task: /TaskService/123. Message: " + "'Firmware upgrade failed'.") + self.management._set_success.assert_not_called() + self.assertIsNone( + task.node.driver_internal_info.get('import_task_monitor_url')) + + @mock.patch.object(redfish_utils, 'get_task_monitor', autospec=True) + def test__check_import_configuration_task_partial_failed_idrac5( + self, mock_get_task_monitor): + driver_internal_info = {'import_task_monitor_url': '/TaskService/123'} + self.node.driver_internal_info = driver_internal_info + self.node.save() + + mock_message1 = mock.Mock() + mock_message1.message = ('Import of Server Configuration Profile ' + 'operation completed with errors') + mock_message1.message_id = 'IDRAC.2.4.SYS055' + mock_import_task = mock.Mock() + mock_import_task.task_state = sushy.TASK_STATE_COMPLETED + mock_import_task.task_status = sushy.HEALTH_OK + mock_import_task.messages = [mock_message1] + mock_task_monitor = mock.Mock() + mock_task_monitor.is_processing = False + mock_task_monitor.get_task.return_value = mock_import_task + mock_get_task_monitor.return_value = mock_task_monitor + + self.management._set_success = mock.Mock() + self.management._set_failed = mock.Mock() + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + self.management._check_import_configuration_task( + task, '/TaskService/123') + + self.management._set_failed.assert_called_once_with( + task, mock.ANY, + "Failed import configuration task: /TaskService/123. Message: " + "'Import of Server Configuration Profile " + "operation completed with errors'.") + self.management._set_success.assert_not_called() + self.assertIsNone( + task.node.driver_internal_info.get('import_task_monitor_url')) + @mock.patch.object(redfish_utils, 'get_task_monitor', autospec=True) def test__check_import_configuration_task(self, mock_get_task_monitor): driver_internal_info = {'import_task_monitor_url': '/TaskService/123'} @@ -1156,7 +1233,9 @@ class DracRedfishManagementTestCase(test_utils.BaseDracTest): self.node.save() mock_message = mock.Mock() + mock_message.message_id = 'SYS413' mock_message.message = 'Configuration import done' + mock_message.severity = sushy.SEVERITY_OK mock_import_task = mock.Mock() mock_import_task.task_state = sushy.TASK_STATE_COMPLETED mock_import_task.task_status = sushy.HEALTH_OK @@ -1189,7 +1268,9 @@ class DracRedfishManagementTestCase(test_utils.BaseDracTest): self.node.save() mock_message = mock.Mock() + mock_message.message_id = 'SYS413' mock_message.message = 'Configuration import done' + mock_message.severity = sushy.SEVERITY_OK mock_import_task = mock.Mock() mock_import_task.task_state = sushy.TASK_STATE_COMPLETED mock_import_task.task_status = sushy.HEALTH_OK @@ -1232,7 +1313,9 @@ class DracRedfishManagementTestCase(test_utils.BaseDracTest): self.node.save() mock_message = mock.Mock() + mock_message.message_id = 'SYS413' mock_message.message = 'Configuration import done' + mock_message.severity = sushy.SEVERITY_OK mock_import_task = mock.Mock() mock_import_task.task_state = sushy.TASK_STATE_COMPLETED mock_import_task.task_status = sushy.HEALTH_OK diff --git a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py index 43c89f206b..87fb792dfc 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py +++ b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py @@ -166,7 +166,10 @@ SUSHY_SPEC = ( 'HEALTH_WARNING', 'SECURE_BOOT_RESET_KEYS_TO_DEFAULT', 'SECURE_BOOT_RESET_KEYS_DELETE_ALL', - 'VOLUME_TYPE_RAW_DEVICE' + 'VOLUME_TYPE_RAW_DEVICE', + 'SEVERITY_OK', + 'SEVERITY_WARNING', + 'SEVERITY_CRITICAL', ) SUSHY_AUTH_SPEC = ( diff --git a/ironic/tests/unit/drivers/third_party_driver_mocks.py b/ironic/tests/unit/drivers/third_party_driver_mocks.py index 3c5e2cd7ab..cd1b6bb3e5 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mocks.py +++ b/ironic/tests/unit/drivers/third_party_driver_mocks.py @@ -228,7 +228,10 @@ if not sushy: HEALTH_WARNING='warning', SECURE_BOOT_RESET_KEYS_TO_DEFAULT="ResetAllKeysToDefault", SECURE_BOOT_RESET_KEYS_DELETE_ALL="DeleteAllKeys", - VOLUME_TYPE_RAW_DEVICE='rawdevice' + VOLUME_TYPE_RAW_DEVICE='rawdevice', + SEVERITY_OK='ok', + SEVERITY_WARNING='warning', + SEVERITY_CRITICAL='critical' ) sys.modules['sushy'] = sushy diff --git a/releasenotes/notes/idrac-import-conf-partial-success-86b8bd1983d227f6.yaml b/releasenotes/notes/idrac-import-conf-partial-success-86b8bd1983d227f6.yaml new file mode 100644 index 0000000000..01171b9494 --- /dev/null +++ b/releasenotes/notes/idrac-import-conf-partial-success-86b8bd1983d227f6.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixes issue in ``idrac-redfish`` clean/deploy step ``import_configuration`` + where partially successful jobs were treated as fully successful. Such + jobs, completed with errors, are now treated as failures.