From 8bda34245178f3db40651d94c7c1e95b8ddddf51 Mon Sep 17 00:00:00 2001 From: Anshul Jain Date: Sun, 17 Dec 2017 22:01:54 -0600 Subject: [PATCH] Redfish: Adds `create_raid_configuration` API to create raid. This commit adds functionality to create logical drives in a SmartStorageConfig redfish systems. Co-Authored-By: Paresh Sao Change-Id: I679deac7e6b15d7a0de980fb16c9e283780fca80 Closes-Bug: 1716329 --- proliantutils/ilo/client.py | 17 +++ proliantutils/ilo/operations.py | 15 +++ proliantutils/ilo/ribcl.py | 16 ++- proliantutils/ilo/ris.py | 16 ++- proliantutils/redfish/redfish.py | 16 +++ .../resources/system/smart_storage_config.py | 49 +++++++ .../system/storage/array_controller.py | 38 ++++++ .../redfish/resources/system/system.py | 76 +++++++++++ proliantutils/tests/ilo/test_client.py | 32 +++++ proliantutils/tests/ilo/test_ribcl.py | 9 ++ proliantutils/tests/ilo/test_ris.py | 10 ++ .../system/storage/test_array_controller.py | 33 +++++ .../system/test_smart_storage_config.py | 41 ++++++ .../redfish/resources/system/test_system.py | 124 ++++++++++++++++++ proliantutils/tests/redfish/test_redfish.py | 8 ++ 15 files changed, 498 insertions(+), 2 deletions(-) diff --git a/proliantutils/ilo/client.py b/proliantutils/ilo/client.py index 9baee66a..6ac6a44c 100644 --- a/proliantutils/ilo/client.py +++ b/proliantutils/ilo/client.py @@ -25,6 +25,7 @@ from proliantutils.redfish import redfish SUPPORTED_RIS_METHODS = [ 'activate_license', 'clear_secure_boot_keys', + 'create_raid_configuration', 'delete_raid_configuration', 'eject_virtual_media', 'get_current_bios_settings', @@ -69,6 +70,7 @@ SUPPORTED_RIS_METHODS = [ ] SUPPORTED_REDFISH_METHODS = [ + 'create_raid_configuration', 'delete_raid_configuration', 'get_product_name', 'get_host_post_state', @@ -652,6 +654,21 @@ class IloClient(operations.IloOperations): """ return self._call_method('delete_raid_configuration') + def create_raid_configuration(self, raid_config): + """Create the raid configuration on the hardware. + + :param raid_config: A dictionary containing target raid configuration + data. This data stucture should be as follows: + raid_config = {'logical_disks': [{'raid_level': 1, + 'size_gb': 100, 'physical_disks': ['6I:1:5'], + 'controller': 'HPE Smart Array P408i-a SR Gen10'}, + ]} + :raises: IloError, on an error from iLO. + :raises: IloCommandNotSupportedError, if the command is not supported + on the server + """ + return self._call_method('create_raid_configuration', raid_config) + def update_firmware(self, firmware_url, component_type): """Updates the given firmware on the server diff --git a/proliantutils/ilo/operations.py b/proliantutils/ilo/operations.py index 2b9de2cc..96f0f5cc 100644 --- a/proliantutils/ilo/operations.py +++ b/proliantutils/ilo/operations.py @@ -454,3 +454,18 @@ class IloOperations(object): not supported on the server. """ raise exception.IloCommandNotSupportedError(ERRMSG) + + def create_raid_configuration(self, raid_config): + """Create the raid configuration on the hardware. + + :param raid_config: A dictionary containing target raid configuration + data. This data stucture should be as follows: + raid_config = {'logical_disks': [{'raid_level': 1, + 'size_gb': 100, 'controller': + 'HPE Smart Array P408i-p SR Gen10'}, + ]} + :raises: IloError, on an error from iLO. + :raises: IloCommandNotSupportedError, if the command is + not supported on the server. + """ + raise exception.IloCommandNotSupportedError(ERRMSG) diff --git a/proliantutils/ilo/ribcl.py b/proliantutils/ilo/ribcl.py index 67150c57..f2eb1ed7 100644 --- a/proliantutils/ilo/ribcl.py +++ b/proliantutils/ilo/ribcl.py @@ -1215,11 +1215,25 @@ class RIBCLOperations(operations.IloOperations): Loops through each SmartStorageConfig controller and clears the raid configuration. - :raises: IloError, on an error from iLO :raises: IloCommandNotSupportedError """ self._raise_command_not_supported("delete_raid_configuration") + def create_raid_configuration(self, raid_config): + """Create the raid configuration on the hardware. + + Based on user raid_config input, it will create raid + + :param raid_config: A dictionary containing target raid configuration + data. This data stucture should be as follows: + raid_config = {'logical_disks': [{'raid_level': 1, + 'size_gb': 100, 'physical_disks': ['6I:1:5'], + 'controller': 'HPE Smart Array P408i-a SR Gen10'}, + ]} + :raises: IloCommandNotSupportedError + """ + self._raise_command_not_supported("create_raid_configuration") + # The below block of code is there only for backward-compatibility # reasons (before commit 47608b6 for ris-support). diff --git a/proliantutils/ilo/ris.py b/proliantutils/ilo/ris.py index 1cd0ca8a..33f2ca24 100755 --- a/proliantutils/ilo/ris.py +++ b/proliantutils/ilo/ris.py @@ -1987,7 +1987,21 @@ class RISOperations(rest.RestConnectorBase, operations.IloOperations): Loops through each SmartStorageConfig controller and clears the raid configuration. - :raises: IloError, on an error from iLO :raises: IloCommandNotSupportedError """ self._raise_command_not_supported("delete_raid_configuration") + + def create_raid_configuration(self, raid_config): + """Create the raid configuration on the hardware. + + Based on user raid_config input, it will create raid + + :param raid_config: A dictionary containing target raid configuration + data. This data stucture should be as follows: + raid_config = {'logical_disks': [{'raid_level': 1, + 'size_gb': 100, 'physical_disks': ['6I:1:5'], + 'controller': 'HPE Smart Array P408i-a SR Gen10'}, + ]} + :raises: IloCommandNotSupportedError + """ + self._raise_command_not_supported("create_raid_configuration") diff --git a/proliantutils/redfish/redfish.py b/proliantutils/redfish/redfish.py index 9f2dfd85..5bfc3c6b 100644 --- a/proliantutils/redfish/redfish.py +++ b/proliantutils/redfish/redfish.py @@ -1168,3 +1168,19 @@ class RedfishOperations(operations.IloOperations): return common_utils.apply_bios_properties_filter( settings, ilo_cons.SUPPORTED_REDFISH_BIOS_PROPERTIES) return settings + + def create_raid_configuration(self, raid_config): + """Create the raid configuration on the hardware. + + Based on user raid_config input, it will create raid + + :param raid_config: A dictionary containing target raid configuration + data. This data stucture should be as follows: + raid_config = {'logical_disks': [{'raid_level': 1, + 'size_gb': 100, 'physical_disks': ['6I:1:5'], + 'controller': 'HPE Smart Array P408i-a SR Gen10'}, + ]} + :raises: IloError, on an error from iLO. + """ + sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID) + sushy_system.create_raid(raid_config) diff --git a/proliantutils/redfish/resources/system/smart_storage_config.py b/proliantutils/redfish/resources/system/smart_storage_config.py index 5f8be9e1..417d7633 100644 --- a/proliantutils/redfish/resources/system/smart_storage_config.py +++ b/proliantutils/redfish/resources/system/smart_storage_config.py @@ -16,6 +16,9 @@ from proliantutils import exception from proliantutils import log from sushy.resources import base +from proliantutils.hpssa import constants +from proliantutils.hpssa import manager + LOG = log.get_logger(__name__) @@ -32,6 +35,8 @@ class HPESmartStorageConfig(base.ResourceBase): logical_drives = LogicalDriveListField("LogicalDrives", default=[]) + location = base.Field("Location") + settings_uri = base.Field(["@Redfish.Settings", "SettingsObject", "@odata.id"]) @@ -53,3 +58,47 @@ class HPESmartStorageConfig(base.ResourceBase): data = {'LogicalDrives': lds, 'DataGuard': 'Permissive'} self._conn.put(self.settings_uri, data=data) + + def create_raid(self, raid_config): + """Create the raid configuration on the hardware. + + :param raid_config: A dictionary containing target raid configuration + data. This data stucture should be as follows: + raid_config = {'logical_disks': [{'raid_level': 1, + 'size_gb': 100, 'physical_disks': ['6I:1:5'], + 'controller': 'HPE Smart Array P408i-a SR Gen10'}, + ]} + """ + manager.validate(raid_config) + logical_drives = raid_config['logical_disks'] + redfish_logical_disk = [] + for ld in logical_drives: + ld_attr = {"Raid": "Raid" + ld["raid_level"]} + ld_attr[ + "CapacityGiB"] = -1 if ld[ + "size_gb"] == "MAX" else int(ld["size_gb"]) + if 'physical_disks' in ld: + ld_attr["DataDrives"] = ld["physical_disks"] + else: + datadrives = {} + if 'number_of_physical_disks' in ld: + datadrives["DataDriveCount"] = ( + ld["number_of_physical_disks"]) + else: + datadrives["DataDriveCount"] = (constants. + RAID_LEVEL_MIN_DISKS + [ld["raid_level"]]) + if 'disk_type' in ld: + datadrives["DataDriveMediaType"] = ld["disk_type"] + if 'interface_type' in ld: + datadrives["DataDriveInterfaceType"] = ld["interface_type"] + ld_attr["DataDrives"] = datadrives + if 'volume_name' in ld: + ld_attr["LogicalDriveName"] = ld["volume_name"] + redfish_logical_disk.append(ld_attr) + + data = { + "DataGuard": "Disabled", + "LogicalDrives": redfish_logical_disk + } + self._conn.put(self.settings_uri, data=data) diff --git a/proliantutils/redfish/resources/system/storage/array_controller.py b/proliantutils/redfish/resources/system/storage/array_controller.py index 408bc51f..78d52867 100644 --- a/proliantutils/redfish/resources/system/storage/array_controller.py +++ b/proliantutils/redfish/resources/system/storage/array_controller.py @@ -34,6 +34,12 @@ class HPEArrayController(base.ResourceBase): description = base.Field('Description') """Description""" + model = base.Field('Model') + """Controller model""" + + location = base.Field('Location') + """Controller slot location""" + _logical_drives = None _physical_drives = None @@ -89,6 +95,8 @@ class HPEArrayControllerCollection(base.ResourceCollectionBase): _has_rotational = None _logical_raid_levels = None _drive_rotational_speed_rpm = None + _get_models = None + _get_default_controller = None @property def _resource_type(self): @@ -166,6 +174,34 @@ class HPEArrayControllerCollection(base.ResourceCollectionBase): member.physical_drives.drive_rotational_speed_rpm) return self._drive_rotational_speed_rpm + @property + def get_default_controller(self): + """Gets default array controller + + :returns default array controller + """ + if self._get_default_controller is None: + self._get_default_controller = self.get_members()[0] + return self._get_default_controller + + def array_controller_by_location(self, location): + """Returns array controller instance by location + + :returns Instance of array controller + """ + for member in self.get_members(): + if member.location == location: + return member + + def array_controller_by_model(self, model): + """Returns array controller instance by model + + :returns Instance of array controller + """ + for member in self.get_members(): + if member.model == model: + return member + def _do_refresh(self, force): """Do custom resource specific refresh activities @@ -179,3 +215,5 @@ class HPEArrayControllerCollection(base.ResourceCollectionBase): self._has_rotational = None self._logical_raid_levels = None self._drive_rotational_speed_rpm = None + self._get_models = None + self._get_default_controller = None diff --git a/proliantutils/redfish/resources/system/system.py b/proliantutils/redfish/resources/system/system.py index a6b16483..238fb7d0 100644 --- a/proliantutils/redfish/resources/system/system.py +++ b/proliantutils/redfish/resources/system/system.py @@ -337,6 +337,18 @@ class HPESystem(system.System): HPESmartStorageConfig(self._conn, smart_storage_config_url, redfish_version=self.redfish_version)) + def _get_smart_storage_config_by_controller_model(self, controller_model): + """Returns a SmartStorageConfig Instance for controller by model. + + :returns: SmartStorageConfig Instance for controller + """ + ac = self.smart_storage.array_controllers.array_controller_by_model( + controller_model) + for ssc_id in self.smart_storage_config_identities: + ssc_obj = self.get_smart_storage_config(ssc_id) + if ac.location == ssc_obj.location: + return ssc_obj + def check_smart_storage_config_ids(self): """Check SmartStorageConfig controllers is there in hardware. @@ -378,3 +390,67 @@ class HPESystem(system.System): msg = ('No logical drives are found in any controllers. Nothing ' 'to delete.') raise exception.IloLogicalDriveNotFoundError(msg) + + def _parse_raid_config_data(self, raid_config): + """It will parse raid config data based on raid controllers + + :param raid_config: A dictionary containing target raid configuration + data. This data stucture should be as follows: + raid_config = {'logical_disks': [{'raid_level': 1, + 'size_gb': 100, 'controller': + 'HPE Smart Array P408i-a SR Gen10'}, + ]} + :returns: A dictionary of controllers, each containing list of + their respected logical drives. + """ + default = ( + self.smart_storage.array_controllers.get_default_controller.model) + controllers = {default: []} + for ld in raid_config['logical_disks']: + if 'controller' not in ld.keys(): + controllers[default].append(ld) + else: + ctrl = ld['controller'] + if ctrl not in controllers: + controllers[ctrl] = [] + controllers[ctrl].append(ld) + return controllers + + def create_raid(self, raid_config): + """Create the raid configuration on the hardware. + + :param raid_config: A dictionary containing target raid configuration + data. This data stucture should be as follows: + raid_config = {'logical_disks': [{'raid_level': 1, + 'size_gb': 100, 'physical_disks': ['6I:1:5'], + 'controller': 'HPE Smart Array P408i-a SR Gen10'}, + ]} + :raises: IloError, on an error from iLO. + """ + self.check_smart_storage_config_ids() + any_exceptions = [] + controllers = self._parse_raid_config_data(raid_config) + # Creating raid on rest of the controllers + for controller in controllers: + try: + config = {'logical_disks': controllers[controller]} + ssc_obj = ( + self._get_smart_storage_config_by_controller_model( + controller)) + if ssc_obj: + ssc_obj.create_raid(config) + else: + members = ( + self.smart_storage.array_controllers.get_members()) + models = [member.model for member in members] + msg = ('Controller not found. Available controllers are: ' + '%(models)s' % {'models': models}) + any_exceptions.append((controller, msg)) + except sushy.exceptions.SushyError as e: + any_exceptions.append((controller, str(e))) + + if any_exceptions: + msg = ('The Redfish controller failed to create the ' + 'raid configuration for one or more controllers with ' + 'Error: %(error)s' % {'error': str(any_exceptions)}) + raise exception.IloError(msg) diff --git a/proliantutils/tests/ilo/test_client.py b/proliantutils/tests/ilo/test_client.py index 399879c3..c08c5940 100644 --- a/proliantutils/tests/ilo/test_client.py +++ b/proliantutils/tests/ilo/test_client.py @@ -779,6 +779,38 @@ class IloClientTestCase(testtools.TestCase): 'on ProLiant DL380 G8', self.client.delete_raid_configuration) + @mock.patch.object(client.IloClient, '_call_method') + def test_create_raid_configuration(self, call_mock): + ld1 = {"size_gb": 150, "raid_level": '0', "is_root_volume": True} + raid_config = {"logical_disks": [ld1]} + self.client.create_raid_configuration(raid_config) + call_mock.assert_called_once_with('create_raid_configuration', + raid_config) + + @mock.patch.object(ris.RISOperations, 'get_product_name') + def test_create_raid_configuration_gen9(self, get_product_mock): + self.client.model = 'Gen9' + ld1 = {"size_gb": 150, "raid_level": '0', "is_root_volume": True} + raid_config = {"logical_disks": [ld1]} + get_product_mock.return_value = 'ProLiant BL460c Gen9' + self.assertRaisesRegexp(exception.IloCommandNotSupportedError, + '`create_raid_configuration` is not supported ' + 'on ProLiant BL460c Gen9', + self.client.create_raid_configuration, + raid_config) + + @mock.patch.object(ribcl.RIBCLOperations, 'get_product_name') + def test_create_raid_configuration_gen8(self, get_product_mock): + self.client.model = 'Gen8' + ld1 = {"size_gb": 150, "raid_level": '0', "is_root_volume": True} + raid_config = {"logical_disks": [ld1]} + get_product_mock.return_value = 'ProLiant DL380 G8' + self.assertRaisesRegexp(exception.IloCommandNotSupportedError, + '`create_raid_configuration` is not supported ' + 'on ProLiant DL380 G8', + self.client.create_raid_configuration, + raid_config) + @mock.patch.object(ris.RISOperations, 'eject_virtual_media') def test_eject_virtual_media_gen9(self, eject_virtual_media_mock): self.client.model = 'Gen9' diff --git a/proliantutils/tests/ilo/test_ribcl.py b/proliantutils/tests/ilo/test_ribcl.py index 4206245d..d82a1c2e 100644 --- a/proliantutils/tests/ilo/test_ribcl.py +++ b/proliantutils/tests/ilo/test_ribcl.py @@ -1062,6 +1062,15 @@ class IloRibclTestCaseBeforeRisSupport(unittest.TestCase): 'ProLiant DL380 G7', self.ilo.delete_raid_configuration) + @mock.patch.object(ribcl.RIBCLOperations, 'get_product_name') + def test_create_raid_configuration(self, product_name_mock): + ld1 = {"size_gb": 150, "raid_level": '0', "is_root_volume": True} + raid_config = {"logical_disks": [ld1]} + product_name_mock.return_value = constants.GET_PRODUCT_NAME + self.assertRaisesRegexp(exception.IloCommandNotSupportedError, + 'ProLiant DL380 G7', + self.ilo.create_raid_configuration, + raid_config) if __name__ == '__main__': unittest.main() diff --git a/proliantutils/tests/ilo/test_ris.py b/proliantutils/tests/ilo/test_ris.py index 9566f87a..dfb08393 100755 --- a/proliantutils/tests/ilo/test_ris.py +++ b/proliantutils/tests/ilo/test_ris.py @@ -2558,3 +2558,13 @@ class TestRISOperationsPrivateMethods(testtools.TestCase): self.assertRaisesRegexp(exception.IloCommandNotSupportedError, 'ProLiant BL460c Gen9', self.client.delete_raid_configuration) + + @mock.patch.object(ris.RISOperations, 'get_product_name') + def test_create_raid_configuration(self, product_name_mock): + ld1 = {"size_gb": 150, "raid_level": '0', "is_root_volume": True} + raid_config = {"logical_disks": [ld1]} + product_name_mock.return_value = 'ProLiant BL460c Gen9' + self.assertRaisesRegexp(exception.IloCommandNotSupportedError, + 'ProLiant BL460c Gen9', + self.client.create_raid_configuration, + raid_config) diff --git a/proliantutils/tests/redfish/resources/system/storage/test_array_controller.py b/proliantutils/tests/redfish/resources/system/storage/test_array_controller.py index efb83610..c43f9e46 100644 --- a/proliantutils/tests/redfish/resources/system/storage/test_array_controller.py +++ b/proliantutils/tests/redfish/resources/system/storage/test_array_controller.py @@ -38,6 +38,9 @@ class HPEArrayControllerTestCase(testtools.TestCase): def test__parse_attributes(self): self.sys_stor._parse_attributes() self.assertEqual('1.0.2', self.sys_stor.redfish_version) + self.assertEqual('HPE Smart Array P408i-a SR Gen10', + self.sys_stor.model) + self.assertEqual('Slot 0', self.sys_stor.location) def test_logical_drives(self): log_coll = None @@ -252,3 +255,33 @@ class HPEArrayControllerCollectionTestCase(testtools.TestCase): expected = set([10000]) self.assertEqual(expected, self.sys_stor_col.drive_rotational_speed_rpm) + + def test_get_default_controller(self): + self.assertIsNone(self.sys_stor_col._get_default_controller) + self.conn.get.return_value.json.reset_mock() + with open('proliantutils/tests/redfish/' + 'json_samples/array_controller.json', 'r') as f: + ac_json = json.loads(f.read()) + self.conn.get.return_value.json.return_value = ac_json + result_location = self.sys_stor_col.get_default_controller.location + self.assertEqual(result_location, 'Slot 0') + + def test_array_controller_by_location(self): + self.conn.get.return_value.json.reset_mock() + with open('proliantutils/tests/redfish/' + 'json_samples/array_controller.json', 'r') as f: + ac_json = json.loads(f.read()) + self.conn.get.return_value.json.return_value = ac_json + model_result = ( + self.sys_stor_col.array_controller_by_location('Slot 0').model) + self.assertEqual(model_result, 'HPE Smart Array P408i-a SR Gen10') + + def test_array_controller_by_model(self): + self.conn.get.return_value.json.reset_mock() + with open('proliantutils/tests/redfish/' + 'json_samples/array_controller.json', 'r') as f: + ac_json = json.loads(f.read()) + self.conn.get.return_value.json.return_value = ac_json + model = 'HPE Smart Array P408i-a SR Gen10' + result_model = self.sys_stor_col.array_controller_by_model(model).model + self.assertEqual(result_model, model) diff --git a/proliantutils/tests/redfish/resources/system/test_smart_storage_config.py b/proliantutils/tests/redfish/resources/system/test_smart_storage_config.py index 9d58dc21..1bf6c987 100644 --- a/proliantutils/tests/redfish/resources/system/test_smart_storage_config.py +++ b/proliantutils/tests/redfish/resources/system/test_smart_storage_config.py @@ -19,6 +19,7 @@ import mock import testtools from proliantutils import exception +from proliantutils.hpssa import manager from proliantutils.redfish.resources.system import smart_storage_config @@ -38,6 +39,7 @@ class HPESmartStorageConfigTestCase(testtools.TestCase): def test_attributes(self): self.assertEqual('smartstorageconfig', self.ssc_inst.controller_id) + self.assertEqual('Slot 0', self.ssc_inst.location) self.assertEqual( '600508B1001C045A9BAAC9F4F49498AE', self.ssc_inst.logical_drives[0].volume_unique_identifier) @@ -61,3 +63,42 @@ class HPESmartStorageConfigTestCase(testtools.TestCase): return_value=[]) self.assertRaises(exception.IloLogicalDriveNotFoundError, self.ssc_inst.delete_raid) + + @mock.patch.object(manager, 'validate', autospec=True) + def test_create_raid(self, validate_mock): + settings_uri = "/redfish/v1/systems/1/smartstorageconfig/settings/" + ld1 = {'size_gb': 50, + 'raid_level': '1', + 'physical_disks': ['5I:1:1', '5I:1:2']} + raid_config = {'logical_disks': [ld1]} + validate_mock.return_value = True + self.ssc_inst.create_raid(raid_config) + data = {"DataGuard": "Disabled", + "LogicalDrives": [ + {"CapacityGiB": 50, "Raid": "Raid1", + "DataDrives": ['5I:1:1', '5I:1:2']}]} + validate_mock.assert_called_once_with(raid_config) + self.ssc_inst._conn.put.assert_called_once_with(settings_uri, + data=data) + + @mock.patch.object(manager, 'validate', autospec=True) + def test_create_raid_multiple_logical_drives(self, validate_mock): + settings_uri = "/redfish/v1/systems/1/smartstorageconfig/settings/" + ld1 = {'size_gb': 50, + 'raid_level': '0', + 'physical_disks': ['5I:1:1']} + ld2 = {'size_gb': 100, + 'raid_level': '1', + 'number_of_physical_disks': 2} + raid_config = {'logical_disks': [ld1, ld2]} + validate_mock.return_value = True + self.ssc_inst.create_raid(raid_config) + data = {"DataGuard": "Disabled", + "LogicalDrives": [ + {"CapacityGiB": 50, "Raid": "Raid0", + "DataDrives": ['5I:1:1']}, + {"CapacityGiB": 100, "Raid": "Raid1", + "DataDrives": {"DataDriveCount": 2}}]} + validate_mock.assert_called_once_with(raid_config) + self.ssc_inst._conn.put.assert_called_once_with(settings_uri, + data=data) diff --git a/proliantutils/tests/redfish/resources/system/test_system.py b/proliantutils/tests/redfish/resources/system/test_system.py index 2d40c3ea..f96b69d1 100644 --- a/proliantutils/tests/redfish/resources/system/test_system.py +++ b/proliantutils/tests/redfish/resources/system/test_system.py @@ -26,6 +26,7 @@ from proliantutils.redfish.resources.system import ethernet_interface from proliantutils.redfish.resources.system import memory from proliantutils.redfish.resources.system import secure_boot from proliantutils.redfish.resources.system import smart_storage_config +from proliantutils.redfish.resources.system.storage import array_controller from proliantutils.redfish.resources.system.storage import simple_storage from proliantutils.redfish.resources.system.storage import smart_storage from proliantutils.redfish.resources.system.storage import storage @@ -560,3 +561,126 @@ class HPESystemTestCase(testtools.TestCase): "The Redfish controller failed to get the SmartStorageConfig " "controller configurations", self.sys_inst.check_smart_storage_config_ids) + + @mock.patch.object(system.HPESystem, 'check_smart_storage_config_ids') + @mock.patch.object(system.HPESystem, '_parse_raid_config_data') + @mock.patch.object(system.HPESystem, + '_get_smart_storage_config_by_controller_model') + def test_create_raid(self, get_smart_storage_config_model_mock, + parse_raid_config_mock, + check_smart_storage_config_ids_mock): + ld1 = {'raid_level': '0', 'is_root_volume': True, + 'size_gb': 150, + 'controller': 'HPE Smart Array P408i-p SR Gen10'} + ld2 = {'raid_level': '1', 'size_gb': 200, + 'controller': 'HPE Smart Array P408i-p SR Gen10'} + raid_config = {'logical_disks': [ld1, ld2]} + parse_data = {'HPE Smart Array P408i-p SR Gen10': [ld1, ld2]} + parse_raid_config_mock.return_value = parse_data + check_smart_storage_config_ids_mock.return_value = None + self.sys_inst.create_raid(raid_config) + get_smart_storage_config_model_mock.assert_called_once_with( + 'HPE Smart Array P408i-p SR Gen10') + (get_smart_storage_config_model_mock.return_value. + create_raid.assert_called_once_with(raid_config)) + + @mock.patch.object(system.HPESystem, 'check_smart_storage_config_ids') + @mock.patch.object(system.HPESystem, '_parse_raid_config_data') + @mock.patch.object(system.HPESystem, + '_get_smart_storage_config_by_controller_model') + def test_create_raid_controller_not_found( + self, get_smart_storage_config_model_mock, parse_raid_config_mock, + check_smart_storage_config_ids_mock): + with open('proliantutils/tests/redfish/' + 'json_samples/smart_storage.json', 'r') as f: + ss_json = json.loads(f.read()) + with open('proliantutils/tests/redfish/' + 'json_samples/array_controller_collection.json', 'r') as f: + acc_json = json.loads(f.read()) + with open('proliantutils/tests/redfish/' + 'json_samples/array_controller.json', 'r') as f: + ac_json = json.loads(f.read()) + self.conn.get.return_value.json.reset_mock() + self.conn.get.return_value.json.side_effect = [ + ss_json, acc_json, ac_json] + ld1 = {'raid_level': '1', 'size_gb': 200, + 'controller': 'HPE Gen10 Controller'} + raid_config = {'logical_disks': [ld1]} + parse_data = {'HPE Gen10 Controller': [ld1]} + parse_raid_config_mock.return_value = parse_data + check_smart_storage_config_ids_mock.return_value = None + get_smart_storage_config_model_mock.return_value = None + self.assertRaisesRegexp( + exception.IloError, + "The Redfish controller failed to create the raid " + "configuration for one or more controllers with", + self.sys_inst.create_raid, raid_config) + + @mock.patch.object(system.HPESystem, 'check_smart_storage_config_ids') + @mock.patch.object(system.HPESystem, '_parse_raid_config_data') + @mock.patch.object(system.HPESystem, + '_get_smart_storage_config_by_controller_model') + def test_create_raid_failed(self, get_smart_storage_config_model_mock, + parse_raid_config_mock, + check_smart_storage_config_ids_mock): + ld1 = {'raid_level': '0', 'is_root_volume': True, + 'size_gb': 150, + 'controller': 'HPE Smart Array P408i-p SR Gen10'} + ld2 = {'raid_level': '1', 'size_gb': 200, + 'controller': 'HPE Smart Array P408i-p SR Gen10'} + raid_config = {'logical_disks': [ld1, ld2]} + parse_data = {'HPE Smart Array P408i-p SR Gen10': [ld1, ld2]} + check_smart_storage_config_ids_mock.return_value = None + parse_raid_config_mock.return_value = parse_data + (get_smart_storage_config_model_mock. + return_value.create_raid.side_effect) = sushy.exceptions.SushyError + self.assertRaisesRegexp( + exception.IloError, + "The Redfish controller failed to create the " + "raid configuration for one or more controllers with Error:", + self.sys_inst.create_raid, raid_config) + + def test__parse_raid_config_data(self): + ld1 = {'raid_level': '0', 'is_root_volume': True, + 'size_gb': 150, + 'controller': 'HPE Smart Array P408i-a SR Gen10'} + ld2 = {'raid_level': '1', 'size_gb': 200, + 'controller': 'HPE Smart Array P408i-a SR Gen10'} + raid_config = {'logical_disks': [ld1, ld2]} + with open('proliantutils/tests/redfish/' + 'json_samples/smart_storage.json', 'r') as f: + ss_json = json.loads(f.read()) + with open('proliantutils/tests/redfish/' + 'json_samples/array_controller_collection.json', 'r') as f: + acc_json = json.loads(f.read()) + with open('proliantutils/tests/redfish/' + 'json_samples/array_controller.json', 'r') as f: + ac_json = json.loads(f.read()) + self.conn.get.return_value.json.reset_mock() + self.conn.get.return_value.json.side_effect = [ + ss_json, acc_json, ac_json] + expected = {'HPE Smart Array P408i-a SR Gen10': [ld1, ld2]} + self.assertEqual(expected, + self.sys_inst._parse_raid_config_data(raid_config)) + + @mock.patch.object(array_controller.HPEArrayControllerCollection, + 'array_controller_by_model') + @mock.patch.object(system.HPESystem, 'get_smart_storage_config') + def test__get_smart_storage_config_by_controller_model( + self, get_smart_storage_config_mock, + array_controller_by_model_mock): + with open('proliantutils/tests/redfish/' + 'json_samples/smart_storage.json', 'r') as f: + ss_json = json.loads(f.read()) + with open('proliantutils/tests/redfish/' + 'json_samples/array_controller_collection.json', 'r') as f: + acc_json = json.loads(f.read()) + self.conn.get.return_value.json.reset_mock() + self.conn.get.return_value.json.side_effect = [ss_json, acc_json] + type(array_controller_by_model_mock.return_value).location = 'Slot 0' + ssc_obj_mock = mock.Mock(location='Slot 0') + get_smart_storage_config_mock.return_value = ssc_obj_mock + self.assertEqual( + ssc_obj_mock.location, + self.sys_inst._get_smart_storage_config_by_controller_model( + 'HPE Smart Array P408i-a SR Gen10').location) diff --git a/proliantutils/tests/redfish/test_redfish.py b/proliantutils/tests/redfish/test_redfish.py index f3c72e89..d6809fab 100644 --- a/proliantutils/tests/redfish/test_redfish.py +++ b/proliantutils/tests/redfish/test_redfish.py @@ -1675,3 +1675,11 @@ class RedfishOperationsTestCase(testtools.TestCase): def test_delete_raid_configuration(self, get_system_mock): self.rf_client.delete_raid_configuration() get_system_mock.return_value.delete_raid.assert_called_once_with() + + @mock.patch.object(redfish.RedfishOperations, '_get_sushy_system') + def test_create_raid_configuration(self, get_system_mock): + ld1 = {"size_gb": 150, "raid_level": '0', "is_root_volume": True} + raid_config = {"logical_disks": [ld1]} + self.rf_client.create_raid_configuration(raid_config) + get_system_mock.return_value.create_raid.assert_called_once_with( + raid_config)