Fix idrac-redfish RAID controller mode conversion

PERC 9 and PERC 10 might not be in RAID mode with no or limited RAID
support. This fixes to convert any eligible controllers to RAID mode
during delete_configuration clean step or deploy step.

Story: 2010272
Task: 46199

Change-Id: I5e85df95a66aed9772ae0660b2c85ca3a39b96c7
This commit is contained in:
Aija Jauntēva 2022-09-14 05:15:51 -04:00
parent b9af8e4ef3
commit 397e49a5e6
4 changed files with 281 additions and 4 deletions

View File

@ -17,4 +17,4 @@ ansible>=2.7
python-ibmcclient>=0.2.2,<0.3.0 python-ibmcclient>=0.2.2,<0.3.0
# Dell EMC iDRAC sushy OEM extension # Dell EMC iDRAC sushy OEM extension
sushy-oem-idrac>=4.0.0,<6.0.0 sushy-oem-idrac>=5.0.0,<6.0.0

View File

@ -1327,6 +1327,8 @@ class DracRedfishRAID(redfish_raid.RedfishRAID):
"""Perform post delete_configuration action to commit the config. """Perform post delete_configuration action to commit the config.
Clears foreign configuration for all RAID controllers. Clears foreign configuration for all RAID controllers.
If no foreign configuration to clear, then checks if any controllers
can be converted to RAID mode.
:param task: a TaskManager instance containing the node to act on. :param task: a TaskManager instance containing the node to act on.
:param raid_configs: a list of dictionaries containing the RAID :param raid_configs: a list of dictionaries containing the RAID
@ -1338,7 +1340,15 @@ class DracRedfishRAID(redfish_raid.RedfishRAID):
async_proc = DracRedfishRAID._clear_foreign_config(system, task) async_proc = DracRedfishRAID._clear_foreign_config(system, task)
if async_proc: if async_proc:
# Async processing with system rebooting in progress # Async processing with system rebooting in progress
task.node.set_driver_internal_info(
'raid_config_substep', 'clear_foreign_config')
task.node.save()
return deploy_utils.get_async_step_return_state(task.node) return deploy_utils.get_async_step_return_state(task.node)
else:
conv_state = DracRedfishRAID._convert_controller_to_raid_mode(
task)
if conv_state:
return conv_state
return return_state return return_state
@ -1486,6 +1496,69 @@ class DracRedfishRAID(redfish_raid.RedfishRAID):
task_mon.wait(CONF.drac.raid_job_timeout) task_mon.wait(CONF.drac.raid_job_timeout)
return False return False
@staticmethod
def _convert_controller_to_raid_mode(task):
"""Convert eligible controllers to RAID mode if not already.
:param task: a TaskManager instance containing the node to act on
:returns: Return state if there are controllers to convert and
and rebooting, otherwise None.
"""
system = redfish_utils.get_system(task.node)
task_mons = []
warning_msg_templ = (
'Possibly because `%(pkg)s` is too old. Without newer `%(pkg)s` '
'PERC 9 and PERC 10 controllers that are not in RAID mode will '
'not be used or have limited RAID support. To avoid that update '
'`%(pkg)s`')
for storage in system.storage.get_members():
storage_controllers = None
try:
storage_controllers = storage.controllers
except sushy.exceptions.MissingAttributeError:
# Check if there storage_controllers to separate old iDRAC and
# storage without controller
if storage.storage_controllers:
LOG.warning('%(storage)s does not have controllers for '
'node %(node)s' + warning_msg_templ,
{'storage': storage.identity,
'node': task.node.uuid,
'pkg': 'iDRAC'})
continue
except AttributeError:
LOG.warning('%(storage)s does not have controllers attribute. '
+ warning_msg_templ, {'storage': storage.identity,
'pkg': 'sushy'})
return None
if storage_controllers:
controller = storage.controllers.get_members()[0]
try:
oem_controller = controller.get_oem_extension('Dell')
except sushy.exceptions.ExtensionError as ee:
LOG.warning('Failed to find extension to convert '
'controller to RAID mode. '
+ warning_msg_templ + '. Error: %(err)s',
{'err': ee, 'pkg': 'sushy-oem-idrac'})
return None
task_mon = oem_controller.convert_to_raid()
if task_mon:
task_mons.append(task_mon)
if task_mons:
deploy_utils.set_async_step_flags(
task.node,
reboot=True,
skip_current_step=True,
polling=True)
task.upgrade_lock()
task.node.set_driver_internal_info(
'raid_task_monitor_uris',
[tm.task_monitor_uri for tm in task_mons])
task.node.save()
return deploy_utils.reboot_to_finish_step(task)
@METRICS.timer('DracRedfishRAID._query_raid_tasks_status') @METRICS.timer('DracRedfishRAID._query_raid_tasks_status')
@periodics.node_periodic( @periodics.node_periodic(
purpose='checking async RAID tasks', purpose='checking async RAID tasks',
@ -1545,6 +1618,15 @@ class DracRedfishRAID(redfish_raid.RedfishRAID):
else: else:
# all tasks completed and none of them failed # all tasks completed and none of them failed
node.del_driver_internal_info('raid_task_monitor_uris') node.del_driver_internal_info('raid_task_monitor_uris')
substep = node.driver_internal_info.get(
'raid_config_substep')
if substep == 'clear_foreign_config':
node.del_driver_internal_info('raid_config_substep')
node.save()
res = DracRedfishRAID._convert_controller_to_raid_mode(
task)
if res: # New tasks submitted
return
self._set_success(task) self._set_success(task)
node.save() node.save()

View File

@ -2457,13 +2457,145 @@ class DracRedfishRAIDTestCase(test_utils.BaseDracTest):
self.assertEqual(False, result) self.assertEqual(False, result)
mock_log.assert_called_once() mock_log.assert_called_once()
@mock.patch.object(deploy_utils, 'reboot_to_finish_step',
autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__convert_controller_to_raid_mode(
self, mock_get_system, mock_reboot):
mock_task_mon = mock.Mock(task_monitor_uri='/TaskService/1')
mock_oem_controller = mock.Mock()
mock_oem_controller.convert_to_raid.return_value = mock_task_mon
mock_controller = mock.Mock()
mock_controller.get_oem_extension.return_value = mock_oem_controller
mock_controllers_col = mock.Mock()
mock_controllers_col.get_members.return_value = [mock_controller]
mock_storage = mock.Mock(controllers=mock_controllers_col)
mock_storage_col = mock.Mock()
mock_storage_col.get_members.return_value = [mock_storage]
mock_system = mock.Mock(storage=mock_storage_col)
mock_get_system.return_value = mock_system
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
result = self.raid._convert_controller_to_raid_mode(task)
self.assertEqual(
['/TaskService/1'],
task.node.driver_internal_info.get('raid_task_monitor_uris'))
self.assertEqual(mock_reboot.return_value, result)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__convert_controller_to_raid_mode_no_conversion(
self, mock_get_system):
mock_oem_controller = mock.Mock()
mock_oem_controller.convert_to_raid.return_value = None
mock_controller = mock.Mock()
mock_controller.get_oem_extension.return_value = mock_oem_controller
mock_controllers_col = mock.Mock()
mock_controllers_col.get_members.return_value = [mock_controller]
mock_storage = mock.Mock(controllers=mock_controllers_col)
mock_storage_col = mock.Mock()
mock_storage_col.get_members.return_value = [mock_storage]
mock_system = mock.Mock(storage=mock_storage_col)
mock_get_system.return_value = mock_system
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
result = self.raid._convert_controller_to_raid_mode(task)
self.assertIsNone(
task.node.driver_internal_info.get('raid_task_monitor_uris'))
self.assertIsNone(result)
@mock.patch.object(drac_raid.LOG, 'warning', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__convert_controller_to_raid_mode_not_raid(
self, mock_get_system, mock_log):
mock_storage = mock.Mock(storage_controllers=None)
mock_controllers = mock.PropertyMock(
side_effect=sushy.exceptions.MissingAttributeError)
type(mock_storage).controllers = mock_controllers
mock_storage_col = mock.Mock()
mock_storage_col.get_members.return_value = [mock_storage]
mock_system = mock.Mock(storage=mock_storage_col)
mock_get_system.return_value = mock_system
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
result = self.raid._convert_controller_to_raid_mode(task)
self.assertIsNone(
task.node.driver_internal_info.get('raid_task_monitor_uris'))
self.assertIsNone(result)
mock_log.assert_not_called()
@mock.patch.object(drac_raid.LOG, 'warning', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__convert_controller_to_raid_mode_old_idrac(
self, mock_get_system, mock_log):
mock_storage = mock.Mock(storage_controllers=mock.Mock())
mock_controllers = mock.PropertyMock(
side_effect=sushy.exceptions.MissingAttributeError)
type(mock_storage).controllers = mock_controllers
mock_storage_col = mock.Mock()
mock_storage_col.get_members.return_value = [mock_storage]
mock_system = mock.Mock(storage=mock_storage_col)
mock_get_system.return_value = mock_system
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
result = self.raid._convert_controller_to_raid_mode(task)
self.assertIsNone(
task.node.driver_internal_info.get('raid_task_monitor_uris'))
self.assertIsNone(result)
mock_log.assert_called_once()
@mock.patch.object(drac_raid.LOG, 'warning', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__convert_controller_to_raid_mode_old_sushy(
self, mock_get_system, mock_log):
mock_storage = mock.Mock(spec=[])
mock_storage.identity = "Storage 1"
mock_storage_col = mock.Mock()
mock_storage_col.get_members.return_value = [mock_storage]
mock_system = mock.Mock(storage=mock_storage_col)
mock_get_system.return_value = mock_system
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
result = self.raid._convert_controller_to_raid_mode(task)
self.assertIsNone(
task.node.driver_internal_info.get('raid_task_monitor_uris'))
self.assertIsNone(result)
mock_log.assert_called_once()
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test__convert_controller_to_raid_mode_old_sushy_oem(
self, mock_get_system):
mock_controller = mock.Mock()
mock_controller.get_oem_extension.side_effect =\
sushy.exceptions.ExtensionError
mock_controllers_col = mock.Mock()
mock_controllers_col.get_members.return_value = [mock_controller]
mock_storage = mock.Mock(controllers=mock_controllers_col)
mock_storage_col = mock.Mock()
mock_storage_col.get_members.return_value = [mock_storage]
mock_system = mock.Mock(storage=mock_storage_col)
mock_get_system.return_value = mock_system
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
result = self.raid._convert_controller_to_raid_mode(task)
self.assertIsNone(
task.node.driver_internal_info.get('raid_task_monitor_uris'))
self.assertIsNone(result)
@mock.patch.object(drac_raid.DracRedfishRAID,
'_convert_controller_to_raid_mode', autospec=True)
@mock.patch.object(deploy_utils, 'get_async_step_return_state', @mock.patch.object(deploy_utils, 'get_async_step_return_state',
autospec=True) autospec=True)
@mock.patch.object(deploy_utils, 'build_agent_options', autospec=True) @mock.patch.object(deploy_utils, 'build_agent_options', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True) @mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test_post_delete_configuration_foreign_async( def test_post_delete_configuration_foreign_async(
self, mock_get_system, mock_build_agent_options, self, mock_get_system, mock_build_agent_options,
mock_get_async_step_return_state): mock_get_async_step_return_state, mock_convert):
fake_oem_system = mock.Mock() fake_oem_system = mock.Mock()
fake_system = mock.Mock() fake_system = mock.Mock()
fake_system.get_oem_extension.return_value = fake_oem_system fake_system.get_oem_extension.return_value = fake_oem_system
@ -2497,9 +2629,13 @@ class DracRedfishRAIDTestCase(test_utils.BaseDracTest):
mock_get_async_step_return_state.assert_called_once_with(task.node) mock_get_async_step_return_state.assert_called_once_with(task.node)
mock_task_mon1.wait.assert_not_called() mock_task_mon1.wait.assert_not_called()
mock_task_mon2.wait.assert_not_called() mock_task_mon2.wait.assert_not_called()
mock_convert.assert_not_called()
@mock.patch.object(drac_raid.DracRedfishRAID,
'_convert_controller_to_raid_mode', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True) @mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test_post_delete_configuration_foreign_sync(self, mock_get_system): def test_post_delete_configuration_foreign_sync(
self, mock_get_system, mock_convert):
fake_oem_system = mock.Mock() fake_oem_system = mock.Mock()
fake_system = mock.Mock() fake_system = mock.Mock()
fake_system.get_oem_extension.return_value = fake_oem_system fake_system.get_oem_extension.return_value = fake_oem_system
@ -2520,15 +2656,34 @@ class DracRedfishRAIDTestCase(test_utils.BaseDracTest):
mock_task_mon2.get_task.return_value = mock_task2 mock_task_mon2.get_task.return_value = mock_task2
fake_oem_system.clear_foreign_config.return_value = [ fake_oem_system.clear_foreign_config.return_value = [
mock_task_mon1, mock_task_mon2] mock_task_mon1, mock_task_mon2]
mock_convert_state = mock.Mock()
mock_convert.return_value = mock_convert_state
result = self.raid.post_delete_configuration( result = self.raid.post_delete_configuration(
task, None, return_state=mock_return_state1) task, None, return_state=mock_return_state1)
self.assertEqual(result, mock_return_state1) self.assertEqual(result, mock_convert_state)
fake_oem_system.clear_foreign_config.assert_called_once() fake_oem_system.clear_foreign_config.assert_called_once()
mock_task_mon1.wait.assert_called_once_with(CONF.drac.raid_job_timeout) mock_task_mon1.wait.assert_called_once_with(CONF.drac.raid_job_timeout)
mock_task_mon2.wait.assert_not_called() mock_task_mon2.wait.assert_not_called()
@mock.patch.object(drac_raid.DracRedfishRAID,
'_convert_controller_to_raid_mode', autospec=True)
@mock.patch.object(drac_raid.DracRedfishRAID,
'_clear_foreign_config', autospec=True)
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
def test_post_delete_configuration_no_subtasks(
self, mock_get_system, mock_foreign, mock_convert):
mock_foreign.return_value = False
mock_convert.return_value = None
task = mock.Mock(node=self.node, context=self.context)
mock_return_state1 = mock.Mock()
result = self.raid.post_delete_configuration(
task, None, return_state=mock_return_state1)
self.assertEqual(mock_return_state1, result)
@mock.patch.object(drac_raid.LOG, 'warning', autospec=True) @mock.patch.object(drac_raid.LOG, 'warning', autospec=True)
def test__clear_foreign_config_attribute_error(self, mock_log): def test__clear_foreign_config_attribute_error(self, mock_log):
fake_oem_system = mock.Mock(spec=[]) fake_oem_system = mock.Mock(spec=[])
@ -2682,6 +2837,41 @@ class DracRedfishRAIDTestCase(test_utils.BaseDracTest):
task.node.driver_internal_info.get('raid_task_monitor_uris')) task.node.driver_internal_info.get('raid_task_monitor_uris'))
self.raid._set_failed.assert_called_once() self.raid._set_failed.assert_called_once()
@mock.patch.object(drac_raid.DracRedfishRAID,
'_convert_controller_to_raid_mode', autospec=True)
@mock.patch.object(redfish_utils, 'get_task_monitor', autospec=True)
def test__check_raid_tasks_status_convert_controller(
self, mock_get_task_monitor, mock_convert):
driver_internal_info = {
'raid_task_monitor_uris': '/TaskService/1',
'raid_config_substep': 'clear_foreign_config'}
self.node.driver_internal_info = driver_internal_info
self.node.save()
mock_config_task = mock.Mock()
mock_config_task.task_state = sushy.TASK_STATE_COMPLETED
mock_config_task.task_status = sushy.HEALTH_OK
mock_task_monitor = mock.Mock()
mock_task_monitor.is_processing = False
mock_task_monitor.get_task.return_value = mock_config_task
mock_get_task_monitor.return_value = mock_task_monitor
self.raid._set_success = mock.Mock()
self.raid._set_failed = mock.Mock()
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.raid._check_raid_tasks_status(
task, ['/TaskService/1'])
mock_convert.assert_called_once_with(task)
self.raid._set_success.assert_not_called()
self.raid._set_failed.assert_not_called()
self.assertIsNone(
task.node.driver_internal_info.get('raid_task_monitor_uris'))
self.assertIsNone(
task.node.driver_internal_info.get('raid_config_substep'))
@mock.patch.object(manager_utils, 'notify_conductor_resume_deploy', @mock.patch.object(manager_utils, 'notify_conductor_resume_deploy',
autospec=True) autospec=True)
@mock.patch.object(manager_utils, 'notify_conductor_resume_clean', @mock.patch.object(manager_utils, 'notify_conductor_resume_clean',

View File

@ -0,0 +1,5 @@
---
fixes:
- |
Fixes ``idrac-redfish`` RAID ``delete_configuration`` step to convert PERC
9 and PERC 10 controllers to RAID mode if it is not already set.