From da9a0e4042acd76d7c9a3c867a2ae5e104e4929f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aija=20Jaunt=C4=93va?= Date: Tue, 2 Aug 2022 10:49:11 -0400 Subject: [PATCH] Add OEM storage controller extension Can determine controller's mode and convert to RAID mode for eligible controllers (PERC 9 and PERC 10). Depends-On: https://review.opendev.org/c/openstack/sushy/+/850899 Change-Id: I492c476c6d69b458449eed69644b051b66dfdc65 --- setup.cfg | 2 + sushy_oem_idrac/__init__.py | 3 +- .../resources/system/storage/constants.py | 28 +++++++ .../resources/system/storage/controller.py | 56 ++++++++++++++ .../unit/json_samples/storage_controller.json | 58 ++++++++++++++ .../unit/resources/system/storage/__init__.py | 0 .../system/storage/test_controller.py | 76 +++++++++++++++++++ 7 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 sushy_oem_idrac/resources/system/storage/constants.py create mode 100644 sushy_oem_idrac/resources/system/storage/controller.py create mode 100644 sushy_oem_idrac/tests/unit/json_samples/storage_controller.json create mode 100644 sushy_oem_idrac/tests/unit/resources/system/storage/__init__.py create mode 100644 sushy_oem_idrac/tests/unit/resources/system/storage/test_controller.py diff --git a/setup.cfg b/setup.cfg index 96b2827..3b6f08a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,6 +28,8 @@ packages = [entry_points] sushy.resources.manager.oems = dell = sushy_oem_idrac.resources.manager.manager:get_extension +sushy.resources.storage_controller.oems = + dell = sushy_oem_idrac.resources.system.storage.controller:get_extension sushy.resources.system.oems = dell = sushy_oem_idrac.resources.system.system:get_extension sushy.resources.task.oems = diff --git a/sushy_oem_idrac/__init__.py b/sushy_oem_idrac/__init__.py index e6f761a..ee1bf50 100644 --- a/sushy_oem_idrac/__init__.py +++ b/sushy_oem_idrac/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Dell Inc. or its subsidiaries. +# Copyright (c) 2021-2022 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 @@ -14,4 +14,5 @@ from sushy_oem_idrac.resources.manager.constants import * # noqa from sushy_oem_idrac.resources.system.constants import * # noqa +from sushy_oem_idrac.resources.system.storage.constants import * # noqa from sushy_oem_idrac.resources.taskservice.constants import * # noqa diff --git a/sushy_oem_idrac/resources/system/storage/constants.py b/sushy_oem_idrac/resources/system/storage/constants.py new file mode 100644 index 0000000..fd9570f --- /dev/null +++ b/sushy_oem_idrac/resources/system/storage/constants.py @@ -0,0 +1,28 @@ +# Copyright (c) 2022 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import enum + + +class ControllerMode(enum.Enum): + """RAID controller modes.""" + + RAID = "RAID" + """RAID mode.""" + + HBA = "HBA" + """HBA/Passthru mode. Does not support RAID. For PERC 9 controllers.""" + + EHBA = "EnhancedHBA" + """Enhanced HBA mode. Limited RAID support. For PERC 10 controllers.""" diff --git a/sushy_oem_idrac/resources/system/storage/controller.py b/sushy_oem_idrac/resources/system/storage/controller.py new file mode 100644 index 0000000..6cc13ba --- /dev/null +++ b/sushy_oem_idrac/resources/system/storage/controller.py @@ -0,0 +1,56 @@ +# Copyright (c) 2022 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sushy +from sushy.resources import base +from sushy.resources.oem import base as oem_base + +from sushy_oem_idrac.resources.system.storage import constants as s_cons + + +class DellStorageController(base.CompositeField): + + controller_mode = base.MappedField('ControllerMode', s_cons.ControllerMode) + """Mode of RAID controller""" + + +class DellStorageControllerExtension(oem_base.OEMResourceBase): + dell_storage_controller = DellStorageController('DellStorageController') + + def convert_to_raid(self): + """Converts to RAID mode if applicable + + If PERC 9 or PERC 10 controller is in non-RAID mode, then convert + to RAID mode. No changes made for PERC 11 and above as they support + only RAID mode, and BOSS controller as it does not have controller + mode. + :returns: TaskMonitor if controller mode changes applied and need to + reboot, otherwise None + """ + controller_mode = self.dell_storage_controller.controller_mode + + # BOSS will have this empty, PERC will have something assigned + if controller_mode and controller_mode != s_cons.ControllerMode.RAID: + patch = { + "Oem": { + "Dell": { + "DellStorageController": { + "ControllerMode": + s_cons.ControllerMode.RAID.value}}}} + return self._parent_resource.update( + patch, apply_time=sushy.ApplyTime.ON_RESET) + + +def get_extension(*args, **kwargs): + return DellStorageControllerExtension diff --git a/sushy_oem_idrac/tests/unit/json_samples/storage_controller.json b/sushy_oem_idrac/tests/unit/json_samples/storage_controller.json new file mode 100644 index 0000000..43df746 --- /dev/null +++ b/sushy_oem_idrac/tests/unit/json_samples/storage_controller.json @@ -0,0 +1,58 @@ +{ + "@Redfish.Settings": { + "@odata.context": "/redfish/v1/$metadata#Settings.Settings", + "@odata.type": "#Settings.v1_3_3.Settings", + "SettingsObject": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage/1/Controllers/1/Settings" + }, + "SupportedApplyTimes": [ + "Immediate", + "OnReset" + ] + }, + "@odata.context": "/redfish/v1/$metadata#StorageController.StorageController", + "@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage/1/Controllers/1", + "@odata.type": "#StorageController.v1_2_0.StorageController", + "Description": "Integrated RAID Controller", + "FirmwareVersion": "1.0.0.7", + "Id": "1", + "Identifiers": [ + { + "@odata.type": "#Resource.v1_1_0.Identifier", + "DurableNameFormat": "NAA", + "DurableName": "345C59DBD970859C" + } + ], + "Identifiers@odata.count": 1, + "Manufacturer": "Contoso", + "Model": "12Gbs Integrated RAID", + "Name": "Contoso Integrated RAID", + "Oem": { + "Dell": { + "@odata.type": "#DellOemStorageController.v1_0_0.DellOemStorageController", + "DellStorageController": { + "ControllerMode": "EnhancedHBA" + } + } + }, + "SpeedGbps": 12, + "Status": { + "Health": "OK", + "HealthRollup": "OK", + "State": "Enabled" + }, + "SupportedControllerProtocols": [ + "PCIe" + ], + "SupportedControllerProtocols@odata.count": 1, + "SupportedDeviceProtocols": [ + "SAS", + "SATA" + ], + "SupportedDeviceProtocols@odata.count": 2, + "SupportedRAIDTypes": [ + "RAID0", + "RAID1" + ], + "SupportedRAIDTypes@odata.count": 2 +} diff --git a/sushy_oem_idrac/tests/unit/resources/system/storage/__init__.py b/sushy_oem_idrac/tests/unit/resources/system/storage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sushy_oem_idrac/tests/unit/resources/system/storage/test_controller.py b/sushy_oem_idrac/tests/unit/resources/system/storage/test_controller.py new file mode 100644 index 0000000..9809090 --- /dev/null +++ b/sushy_oem_idrac/tests/unit/resources/system/storage/test_controller.py @@ -0,0 +1,76 @@ +# Copyright (c) 2022 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +from unittest import mock + +from oslotest.base import BaseTestCase +import sushy +from sushy.resources.system.storage import controller as sushy_constroller + +from sushy_oem_idrac.resources.system.storage import constants as ctrl_cons +from sushy_oem_idrac.resources.system.storage import controller as oem_ctrl + + +class ControllerTestCase(BaseTestCase): + + def setUp(self): + super(ControllerTestCase, self).setUp() + self.conn = mock.Mock() + + with open('sushy_oem_idrac/tests/unit/json_samples/' + 'storage_controller.json') as f: + mock_response = self.conn.get.return_value + mock_response.json.return_value = json.load(f) + mock_response.status_code = 200 + + self.controller = sushy_constroller.StorageController( + self.conn, + '/redfish/v1/Systems/437XR1138R2/Storage/1/Controllers/1') + self.oem_controller = oem_ctrl.DellStorageControllerExtension( + self.conn, + '/redfish/v1/Systems/437XR1138R2/Storage/1/Controllers/1') + self.oem_controller = self.oem_controller.set_parent_resource( + self.controller, 'Dell') + + def test_parse_attributes(self): + self.assertEqual( + ctrl_cons.ControllerMode.EHBA, + self.oem_controller.dell_storage_controller.controller_mode) + + def test_convert_to_raid(self): + mock_controller = mock.Mock() + mock_task_monitor = mock.Mock() + mock_controller.update.return_value = mock_task_monitor + self.oem_controller._parent_resource = mock_controller + + res = self.oem_controller.convert_to_raid() + + self.assertEqual(mock_task_monitor, res) + patch = {"Oem": {"Dell": {"DellStorageController": { + "ControllerMode": "RAID"}}}} + mock_controller.update.assert_called_once_with( + patch, apply_time=sushy.ApplyTime.ON_RESET) + + def test_convert_to_raid_already_raid(self): + mock_controller = mock.Mock() + self.oem_controller._parent_resource = mock_controller + json = self.oem_controller.json + json['Oem']['Dell']['DellStorageController']['ControllerMode'] = 'RAID' + self.oem_controller.refresh() + + res = self.oem_controller.convert_to_raid() + + self.assertIsNone(res) + mock_controller.update.assert_not_called()