Add RAID clear foreign config method

Change-Id: If1669e7a666d0897557bc96554a2e403b2ccecf8
This commit is contained in:
Aija Jauntēva 2021-07-08 11:18:05 -04:00
parent 79d4ecb587
commit c63e28113a
5 changed files with 173 additions and 0 deletions

View File

@ -1,3 +1,5 @@
# Copyright (c) 2021 Dell Inc. or its subsidiaries.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@ -12,3 +14,4 @@
IDRAC_CONFIG_PENDING = 'LC068'
IDRAC_JOB_RUNNING = 'RAC0679'
NO_FOREIGN_CONFIG = 'STOR018'

View File

@ -19,6 +19,8 @@ from sushy.resources import base
from sushy.resources import common
from sushy import taskmonitor
from sushy_oem_idrac import constants
LOG = logging.getLogger(__name__)
@ -26,6 +28,8 @@ class ActionsField(base.CompositeField):
convert_to_raid = common.ActionField("#DellRaidService.ConvertToRAID")
convert_to_nonraid = common.ActionField(
"#DellRaidService.ConvertToNonRAID")
clear_foreign_config = common.ActionField(
"#DellRaidService.ClearForeignConfig")
class DellRaidService(base.ResourceBase):
@ -78,6 +82,41 @@ class DellRaidService(base.ResourceBase):
LOG.info('Converting to non-RAID mode: %s', physical_disk_fqdds)
return task_monitor
def clear_foreign_config(self, controller_fqdd):
"""Clears foreign configuration
Prepares any foreign physical disks for inclusion in the local
configuration
:param controller_fqdd: FQDD of controller to clear foreign
config
:returns: Sushy's TaskMonitor instance for TaskService task if
there are foreign drives to clear, otherwise None.
"""
target_uri = self._actions.clear_foreign_config.target_uri
payload = {'TargetFQDD': controller_fqdd}
try:
response = self._conn.post(target_uri, data=payload)
except exceptions.BadRequestError as ex:
# Check if failed for no foreign drives
errors = ex.body and ex.body.get('@Message.ExtendedInfo') or []
no_foreign_conf = [x for x in errors if constants.NO_FOREIGN_CONFIG
in x.get('MessageId')]
if len(no_foreign_conf) == 0:
raise ex
else:
LOG.debug('%s: %s', no_foreign_conf[0].get('Message'),
controller_fqdd)
return
task_mon = self._get_task_monitor_from_dell_job(response)
LOG.info('Clearing foreign config: %s', controller_fqdd)
return task_mon
def _get_task_monitor_from_dell_job(self, response):
"""From OEM job response returns generic Task monitor

View File

@ -108,6 +108,25 @@ class DellSystemExtension(oem_base.OEMResourceBase):
return task_monitors
def clear_foreign_config(self, storage_list=None):
"""Clears foreign config on given controllers
:param storage_list: List of storage objects, each of which
corresponds to a controller
:returns: List of task monitors, where each entry is for a
controller that has foreign config to clear
"""
if storage_list is None:
storage_list = self._get_storage_list()
task_monitors = []
for storage in storage_list:
task_mon = self.raid_service.clear_foreign_config(storage.identity)
if task_mon:
task_monitors.append(task_mon)
return task_monitors
def _get_controller_to_disks(self):
"""Gets all RAID controllers and their disks on system
@ -122,6 +141,20 @@ class DellSystemExtension(oem_base.OEMResourceBase):
controller_to_disks[controller] = storage.drives
return controller_to_disks
def _get_storage_list(self):
"""Gets all storage items corresponding to RAID controllers
:returns: list of storage items
"""
storage_list = []
for storage in self._parent_resource.storage.get_members():
controller = (storage.storage_controllers[0]
if storage.storage_controllers else None)
if not controller or controller and not controller.raid_types:
continue
storage_list.append(storage)
return storage_list
def get_extension(*args, **kwargs):
return DellSystemExtension

View File

@ -80,6 +80,93 @@ class DellRaidService(BaseTestCase):
data={'PDArray': fqdds})
self.assertEqual(mock_task_mon, task_mon)
@mock.patch.object(raid_service.DellRaidService,
'_get_task_monitor_from_dell_job', autospec=True)
def test_clear_foreign_config(self, mock_get_task_mon):
mock_task_mon = mock.Mock()
mock_get_task_mon.return_value = mock_task_mon
result = self.raid_service.clear_foreign_config('RAID.Integrated.1-1')
self.conn.post.assert_called_once_with(
'/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/'
'Actions/DellRaidService.ClearForeignConfig',
data={'TargetFQDD': 'RAID.Integrated.1-1'})
self.assertEqual(mock_task_mon, result)
def test_clear_foreign_config_no_config(self):
mock_response = mock.Mock()
mock_response.status_code = 400
mock_response.json.return_value = {
"error": {
"@Message.ExtendedInfo": [
{
"Message": "No foreign configurations detected.",
"MessageArgs": [],
"MessageArgs@odata.count": 0,
"MessageId": "IDRAC.2.5.STOR018",
"RelatedProperties": [],
"RelatedProperties@odata.count": 0,
"Resolution": "If the only foreign drives present are "
"in a secured locked state, run a "
"secure erase operation on the drives "
"to securely erase data or unlock these "
"drives and retry the operation. "
"Otherwise the operation was not "
"successful because there are no "
"foreign drives.",
"Severity": "Warning"
}
],
"code": "Base.1.8.GeneralError",
"message": "A general error has occurred. See ExtendedInfo "
"for more information"
}
}
no_config_error = exceptions.BadRequestError(
'POST', '/redfish/v1/Dell/Systems/System.Embedded.1/'
'DellRaidService/Actions/DellRaidService.ClearForeignConfig',
mock_response)
self.conn.post.side_effect = no_config_error
result = self.raid_service.clear_foreign_config('RAID.Integrated.1-1')
self.assertIsNone(result)
def test_clear_foreign_config_bad_request(self):
mock_response = mock.Mock()
mock_response.status_code = 400
mock_response.json.return_value = {
"error": {
"@Message.ExtendedInfo": [
{
"Message": "Controller not found.",
"MessageArgs": [],
"MessageArgs@odata.count": 0,
"MessageId": "IDRAC.2.4.STOR030",
"RelatedProperties": [],
"RelatedProperties@odata.count": 0,
"Resolution": "Provide a valid controller FQDD (Fully "
"Qualified Device Descriptor) and retry "
"the operation.",
"Severity": "Warning"
}
],
"code": "Base.1.7.GeneralError",
"message": "A general error has occurred. See ExtendedInfo "
"for more information"
}
}
no_config_error = exceptions.BadRequestError(
'POST', '/redfish/v1/Dell/Systems/System.Embedded.1/'
'DellRaidService/Actions/DellRaidService.ClearForeignConfig',
mock_response)
self.conn.post.side_effect = no_config_error
self.assertRaises(exceptions.BadRequestError,
self.raid_service.clear_foreign_config,
'RAID.Integrated.999')
def test__get_task_monitor_from_dell_job(self):
mock_task1 = mock.Mock(identity='JID_111222333444',
path='/TaskService/Task/JID_111222333444')

View File

@ -110,3 +110,14 @@ class SystemTestCase(BaseTestCase):
mock_raid.assert_not_called()
mock_nonraid.assert_called_once_with(
['Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1'])
def test_clear_foreign_config(self):
mock_taskmon = mock.Mock()
mock_clear_foreign_config = mock.Mock()
mock_clear_foreign_config.side_effect = [None, mock_taskmon]
self.oem_system.raid_service.clear_foreign_config =\
mock_clear_foreign_config
task_mons = self.oem_system.clear_foreign_config()
self.assertEqual([mock_taskmon], task_mons)