diff --git a/dracclient/resources/raid.py b/dracclient/resources/raid.py index 3522158..44cea69 100644 --- a/dracclient/resources/raid.py +++ b/dracclient/resources/raid.py @@ -911,6 +911,15 @@ class RAIDManagement(object): RAID to JBOD or vice versa. It does this by only converting the disks that are not already in the correct state. + When converting Dell EMC PERC H755 RAID controller physical disks + to non-RAID mode, RAID-0 virtual disks get created for each physical + disk and disks moved to 'Online' state. + + This is different from other controllers supporting non-RAID conversion + and takes up physical disks that cannot be later used for user intended + RAID configuration. H755 RAID controllers are excluded when converting + to non-RAID mode leaving disks in 'Ready' state. + :param mode: constants.RaidStatus enumeration that indicates the mode to change the disks to. :param controllers_to_physical_disk_ids: Dictionary of controllers and @@ -932,11 +941,12 @@ class RAIDManagement(object): physical_disks = self.list_physical_disks() raid = constants.RaidStatus.raid + jbod = constants.RaidStatus.jbod + all_controllers = self.list_raid_controllers() if not controllers_to_physical_disk_ids: controllers_to_physical_disk_ids = collections.defaultdict(list) - all_controllers = self.list_raid_controllers() for physical_d in physical_disks: # Weed out disks that are not attached to a RAID controller if self.is_raid_controller(physical_d.controller, @@ -946,6 +956,27 @@ class RAIDManagement(object): physical_disk_ids.append(physical_d.id) + controllers_to_results = {} + + # Filter out PERC H755 controller as it creates RAID-0 virtual disks + # when in non-RAID mode. Returns conversion result dictionary + # containing is_commit_required and is_reboot_required key with + # the value always set to False. + if mode == jbod: + for cntlr in all_controllers: + if cntlr.model.startswith("PERC H755") and \ + cntlr.id in controllers_to_physical_disk_ids: + LOG.debug("Excluding {} from converting to " + "non-RAID mode".format(cntlr.model)) + del controllers_to_physical_disk_ids[cntlr.id] + controllers_to_results[cntlr.id] = \ + utils.build_return_dict( + doc=None, + resource_uri=None, + is_commit_required_value=False, + is_reboot_required_value=constants. + RebootRequired.false) + '''Modify controllers_to_physical_disk_ids dict by inspecting desired status vs current status of each controller's disks. Raise exception if there are any failed drives or @@ -954,7 +985,6 @@ class RAIDManagement(object): final_ctls_to_phys_disk_ids = self._check_disks_status( mode, physical_disks, controllers_to_physical_disk_ids) - controllers_to_results = {} for controller, physical_disk_ids \ in final_ctls_to_phys_disk_ids.items(): if physical_disk_ids: diff --git a/dracclient/tests/test_raid.py b/dracclient/tests/test_raid.py index 0793ca5..7323511 100644 --- a/dracclient/tests/test_raid.py +++ b/dracclient/tests/test_raid.py @@ -40,12 +40,15 @@ class ClientRAIDManagementTestCase(base.BaseTest): **test_utils.FAKE_ENDPOINT) self.raid_controller_fqdd = "RAID.Integrated.1-1" self.boss_controller_fqdd = "AHCI.Slot.3-1" + self.h755_controller_fqdd = "RAID.SL.8-1" cntl_dict = {'RAID.Integrated.1-1': ['Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1', 'Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1'], 'AHCI.Integrated.1-1': ['Disk.Bay.0:Enclosure.Internal.0-1:AHCI.Integrated.1-1', - 'Disk.Bay.1:Enclosure.Internal.0-1:AHCI.Integrated.1-1']} + 'Disk.Bay.1:Enclosure.Internal.0-1:AHCI.Integrated.1-1'], + 'RAID.SL.8-1': + ['Disk.Bay.0:Enclosure.Internal.0-1:RAID.SL.8-1']} self.controllers_to_physical_disk_ids = cntl_dict self.disk_1 = raid.PhysicalDisk( id='Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1', @@ -119,6 +122,24 @@ class ClientRAIDManagementTestCase(base.BaseTest): device_protocol=None, bus=None) + self.disk_5 = raid.PhysicalDisk( + id='Disk.Bay.0:Enclosure.Internal.0-1:RAID.SL.8-1', + description='Disk 0 in Backplane 1 of RAID Controller in SL 8', + controller='RAID.SL.8-1', + manufacturer='ATA', + model='ST91000640NS', + media_type='hdd', + interface_type='sata', + size_mb=953344, + free_size_mb=953344, + serial_number='9XG4SLGZ', + firmware_version='AA09', + status='ok', + raid_status='ready', + sas_address='500056B37789ABE3', + device_protocol=None, + bus=None) + @mock.patch.object(dracclient.client.WSManClient, 'wait_until_idrac_is_ready', spec_set=True, autospec=True) @@ -1226,7 +1247,8 @@ class ClientRAIDManagementTestCase(base.BaseTest): def test_check_disks_status_bad(self, mock_requests): mode = constants.RaidStatus.raid disk_2 = self.disk_2._replace(raid_status='FAKE_STATUS') - physical_disks = [self.disk_1, disk_2, self.disk_3, self.disk_4] + physical_disks = [self.disk_1, disk_2, self.disk_3, self.disk_4, + self.disk_5] raid_mgt = self.drac_client._raid_mgmt self.assertRaises(ValueError, @@ -1244,7 +1266,8 @@ class ClientRAIDManagementTestCase(base.BaseTest): def test_check_disks_status_fail(self, mock_requests): mode = constants.RaidStatus.raid disk_2_failed = self.disk_2._replace(raid_status='failed') - physical_disks = [self.disk_1, disk_2_failed, self.disk_3, self.disk_4] + physical_disks = [self.disk_1, disk_2_failed, self.disk_3, self.disk_4, + self.disk_5] raid_mgt = self.drac_client._raid_mgmt self.assertRaises(ValueError, @@ -1263,7 +1286,7 @@ class ClientRAIDManagementTestCase(base.BaseTest): raid_mgt = self.drac_client._raid_mgmt mode = constants.RaidStatus.raid physical_disks = [self.disk_1, self.disk_2, - self.disk_3, self.disk_4] + self.disk_3, self.disk_4, self.disk_5] raid_cntl_to_phys_disk_ids = raid_mgt._check_disks_status( mode, physical_disks, self.controllers_to_physical_disk_ids) @@ -1274,7 +1297,7 @@ class ClientRAIDManagementTestCase(base.BaseTest): disk_1_non_raid = self.disk_1._replace(raid_status='non-RAID') disk_2_non_raid = self.disk_2._replace(raid_status='non-RAID') physical_disks = [disk_1_non_raid, disk_2_non_raid, - self.disk_3, self.disk_4] + self.disk_3, self.disk_4, self.disk_5] jbod_cntl_to_phys_disk_ids = raid_mgt._check_disks_status( mode, physical_disks, self.controllers_to_physical_disk_ids) @@ -1284,7 +1307,8 @@ class ClientRAIDManagementTestCase(base.BaseTest): def test_check_disks_status_change_state(self, mock_requests): raid_mgt = self.drac_client._raid_mgmt mode = constants.RaidStatus.jbod - physical_disks = [self.disk_1, self.disk_2, self.disk_3, self.disk_4] + physical_disks = [self.disk_1, self.disk_2, self.disk_3, + self.disk_4, self.disk_5] jbod_cntl_to_phys_disk_ids = raid_mgt._check_disks_status( mode, physical_disks, self.controllers_to_physical_disk_ids) @@ -1295,7 +1319,7 @@ class ClientRAIDManagementTestCase(base.BaseTest): disk_1_non_raid = self.disk_1._replace(raid_status='non-RAID') disk_2_non_raid = self.disk_2._replace(raid_status='non-RAID') physical_disks = [disk_1_non_raid, disk_2_non_raid, - self.disk_3, self.disk_4] + self.disk_3, self.disk_4, self.disk_5] raid_cntl_to_phys_disk_ids = raid_mgt._check_disks_status( mode, physical_disks, self.controllers_to_physical_disk_ids) raid_len = len(raid_cntl_to_phys_disk_ids['RAID.Integrated.1-1']) @@ -1305,7 +1329,8 @@ class ClientRAIDManagementTestCase(base.BaseTest): mode = constants.RaidStatus.raid disk_1_bad = self.disk_1._replace(raid_status='FAKE_STATUS') disk_2_failed = self.disk_2._replace(raid_status='failed') - physical_disks = [disk_1_bad, disk_2_failed, self.disk_3, self.disk_4] + physical_disks = [disk_1_bad, disk_2_failed, self.disk_3, + self.disk_4, self.disk_5] raid_mgt = self.drac_client._raid_mgmt self.assertRaises(ValueError, @@ -1331,23 +1356,33 @@ class ClientRAIDManagementTestCase(base.BaseTest): mock_convert_physical_disks, wait_until_idrac_is_ready): mode = constants.RaidStatus.jbod - mock_requests.post( - 'https://1.2.3.4:443/wsman', - text=test_utils.RAIDEnumerations[uris.DCIM_PhysicalDiskView]['ok']) + mock_requests.post('https://1.2.3.4:443/wsman', + [{'text': test_utils.RAIDEnumerations[ + uris.DCIM_PhysicalDiskView]['ok']}, + {'text': test_utils.RAIDEnumerations[ + uris.DCIM_ControllerView]['ok']}]) cvt_phys_disks_return_value = {'is_commit_required': True, 'is_reboot_required': constants. RebootRequired.true} + h755_return_value = {'is_commit_required': False, + 'is_reboot_required': constants. + RebootRequired.false} mock_convert_physical_disks.return_value = cvt_phys_disks_return_value expected_return_value = {'RAID.Integrated.1-1': cvt_phys_disks_return_value, 'AHCI.Integrated.1-1': - cvt_phys_disks_return_value} + cvt_phys_disks_return_value, + 'RAID.SL.8-1': + h755_return_value} results = self.drac_client.change_physical_disk_state( mode, self.controllers_to_physical_disk_ids) self.assertEqual(results['conversion_results'], expected_return_value) + @mock.patch.object(dracclient.client.WSManClient, + 'wait_until_idrac_is_ready', spec_set=True, + autospec=True) @mock.patch.object(dracclient.resources.raid.RAIDManagement, 'list_physical_disks', spec_set=True, autospec=True) @@ -1357,38 +1392,55 @@ class ClientRAIDManagementTestCase(base.BaseTest): def test_change_physical_disk_state_raid( self, mock_requests, mock_convert_physical_disks, - mock_list_physical_disks): + mock_list_physical_disks, + mock_wait_until_idrac_is_ready): mode = constants.RaidStatus.raid disk_1_non_raid = self.disk_1._replace(raid_status='non-RAID') disk_2_non_raid = self.disk_2._replace(raid_status='non-RAID') physical_disks = [disk_1_non_raid, disk_2_non_raid, - self.disk_3, self.disk_4] + self.disk_3, self.disk_4, self.disk_5] mock_list_physical_disks.return_value = physical_disks + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.RAIDEnumerations[uris.DCIM_ControllerView]['ok']) boss_return_value = {'is_commit_required': False, 'is_reboot_required': constants.RebootRequired.false} raid_return_value = {'is_commit_required': True, 'is_reboot_required': constants.RebootRequired.true} + h755_return_value = {'is_commit_required': False, + 'is_reboot_required': + constants.RebootRequired.false} mock_convert_physical_disks.return_value = raid_return_value results = self.drac_client.change_physical_disk_state( mode, self.controllers_to_physical_disk_ids) - self.assertEqual(len(results['conversion_results']), 2) + self.assertEqual(len(results['conversion_results']), 3) self.assertEqual(results['conversion_results']['AHCI.Integrated.1-1'], boss_return_value) self.assertEqual(results['conversion_results']['RAID.Integrated.1-1'], raid_return_value) + self.assertEqual(results['conversion_results']['RAID.SL.8-1'], + h755_return_value) + @mock.patch.object(dracclient.client.WSManClient, + 'wait_until_idrac_is_ready', spec_set=True, + autospec=True) @mock.patch.object(dracclient.resources.raid.RAIDManagement, 'list_physical_disks', spec_set=True, autospec=True) def test_change_physical_disk_state_none( self, mock_requests, - mock_list_physical_disks): + mock_list_physical_disks, + mock_wait_until_idrac_is_ready): mode = constants.RaidStatus.raid - physical_disks = [self.disk_1, self.disk_2, self.disk_3, self.disk_4] + physical_disks = [self.disk_1, self.disk_2, self.disk_3, self.disk_4, + self.disk_5] mock_list_physical_disks.return_value = physical_disks + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.RAIDEnumerations[uris.DCIM_ControllerView]['ok']) expected_return_value = {'is_commit_required': False, 'is_reboot_required': constants.RebootRequired.false} @@ -1398,7 +1450,12 @@ class ClientRAIDManagementTestCase(base.BaseTest): expected_return_value) self.assertEqual(results['conversion_results']['AHCI.Integrated.1-1'], expected_return_value) + self.assertEqual(results['conversion_results']['RAID.SL.8-1'], + expected_return_value) + @mock.patch.object(dracclient.client.WSManClient, + 'wait_until_idrac_is_ready', spec_set=True, + autospec=True) @mock.patch.object(dracclient.resources.raid.RAIDManagement, 'list_physical_disks', spec_set=True, autospec=True) @@ -1410,13 +1467,17 @@ class ClientRAIDManagementTestCase(base.BaseTest): def test_change_physical_disk_state_not_supported( self, mock_requests, mock_convert_physical_disks, - mock_list_physical_disks): + mock_list_physical_disks, + mock_wait_until_idrac_is_ready): mode = constants.RaidStatus.raid disk_1_non_raid = self.disk_1._replace(raid_status='non-RAID') disk_2_non_raid = self.disk_2._replace(raid_status='non-RAID') physical_disks = [disk_1_non_raid, disk_2_non_raid, - self.disk_3, self.disk_4] + self.disk_3, self.disk_4, self.disk_5] mock_list_physical_disks.return_value = physical_disks + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.RAIDEnumerations[uris.DCIM_ControllerView]['ok']) expected_return_value = {'is_commit_required': False, 'is_reboot_required': constants.RebootRequired.false} @@ -1426,7 +1487,12 @@ class ClientRAIDManagementTestCase(base.BaseTest): expected_return_value) self.assertEqual(results['conversion_results']['AHCI.Integrated.1-1'], expected_return_value) + self.assertEqual(results['conversion_results']['RAID.SL.8-1'], + expected_return_value) + @mock.patch.object(dracclient.client.WSManClient, + 'wait_until_idrac_is_ready', spec_set=True, + autospec=True) @mock.patch.object(dracclient.resources.raid.RAIDManagement, 'list_physical_disks', spec_set=True, autospec=True) @@ -1438,13 +1504,17 @@ class ClientRAIDManagementTestCase(base.BaseTest): def test_change_physical_disk_state_raise_drac_operation_other( self, mock_requests, mock_convert_physical_disks, - mock_list_physical_disks): + mock_list_physical_disks, + mock_wait_until_idrac_is_ready): mode = constants.RaidStatus.raid disk_1_non_raid = self.disk_1._replace(raid_status='non-RAID') disk_2_non_raid = self.disk_2._replace(raid_status='non-RAID') physical_disks = [disk_1_non_raid, disk_2_non_raid, - self.disk_3, self.disk_4] + self.disk_3, self.disk_4, self.disk_5] mock_list_physical_disks.return_value = physical_disks + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.RAIDEnumerations[uris.DCIM_ControllerView]['ok']) self.assertRaisesRegexp( exceptions.DRACOperationFailed, "OTHER_MESSAGE", @@ -1452,6 +1522,9 @@ class ClientRAIDManagementTestCase(base.BaseTest): mode, self.controllers_to_physical_disk_ids) + @mock.patch.object(dracclient.client.WSManClient, + 'wait_until_idrac_is_ready', spec_set=True, + autospec=True) @mock.patch.object(dracclient.resources.raid.RAIDManagement, 'list_physical_disks', spec_set=True, autospec=True) @@ -1462,13 +1535,17 @@ class ClientRAIDManagementTestCase(base.BaseTest): def test_change_physical_disk_state_raise_other( self, mock_requests, mock_convert_physical_disks, - mock_list_physical_disks): + mock_list_physical_disks, + mock_wait_until_idrac_is_ready): mode = constants.RaidStatus.raid disk_1_non_raid = self.disk_1._replace(raid_status='non-RAID') disk_2_non_raid = self.disk_2._replace(raid_status='non-RAID') physical_disks = [disk_1_non_raid, disk_2_non_raid, - self.disk_3, self.disk_4] + self.disk_3, self.disk_4, self.disk_5] mock_list_physical_disks.return_value = physical_disks + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.RAIDEnumerations[uris.DCIM_ControllerView]['ok']) self.assertRaisesRegexp( Exception, "SOMETHING_BAD_HAPPENED", @@ -1494,16 +1571,23 @@ class ClientRAIDManagementTestCase(base.BaseTest): 'https://1.2.3.4:443/wsman', text=test_utils.RAIDEnumerations[uris.DCIM_ControllerView]['ok']) mode = constants.RaidStatus.jbod - physical_disks = [self.disk_1, self.disk_2, self.disk_3, self.disk_4] + physical_disks = [self.disk_1, self.disk_2, self.disk_3, self.disk_4, + self.disk_5] mock_list_physical_disks.return_value = physical_disks cvt_phys_disks_return_value = {'is_commit_required': True, 'is_reboot_required': constants. RebootRequired.true} + h755_return_value = {'is_commit_required': False, + 'is_reboot_required': + constants.RebootRequired.false} + mock_convert_physical_disks.return_value = cvt_phys_disks_return_value expected_return_value = {'RAID.Integrated.1-1': cvt_phys_disks_return_value, 'AHCI.Integrated.1-1': - cvt_phys_disks_return_value} + cvt_phys_disks_return_value, + 'RAID.SL.8-1': + h755_return_value} results = self.drac_client.change_physical_disk_state(mode) self.assertDictEqual(results['conversion_results'], expected_return_value) diff --git a/dracclient/tests/wsman_mocks/controller_view-enum-ok.xml b/dracclient/tests/wsman_mocks/controller_view-enum-ok.xml index 069a0d8..0fa4097 100644 --- a/dracclient/tests/wsman_mocks/controller_view-enum-ok.xml +++ b/dracclient/tests/wsman_mocks/controller_view-enum-ok.xml @@ -93,6 +93,55 @@ 0 0 + + 1 + + 1 + 8192 + 0 + 0 + 0 + 4 + 52.13.2-3661 + 0 + Unknown + DELL + 2 + Unknown + RAID Controller in SL 8 + + 1 + 0 + RAID.SL.8-1 + 0 + RAID.SL.8-1 + + 20210831081620.000000+000 + 20210831081620.000000+000 + + + 10E2 + + 1AE1 + 1028 + 1000 + 1 + 0 + 0 + PERC H755 Front + 0 + 0 + 52CEA7F06A603500 + 1 + 0 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + diff --git a/dracclient/tests/wsman_mocks/physical_disk_view-enum-ok.xml b/dracclient/tests/wsman_mocks/physical_disk_view-enum-ok.xml index 791fa59..f960083 100644 --- a/dracclient/tests/wsman_mocks/physical_disk_view-enum-ok.xml +++ b/dracclient/tests/wsman_mocks/physical_disk_view-enum-ok.xml @@ -199,6 +199,43 @@ 0 0 + + 512 + 6 + 0 + Disk 0 in Backplane 1 of Int RAID Controller 1 + 2 + Disk.Bay.0:Enclosure.Internal.0-1:RAID.SL.8-1 + 599550590976 + 0 + Disk.Bay.0:Enclosure.Internal.0-1:RAID.SL.8-1 + 20150226180025.000000+000 + 20150226180025.000000+000 + SEAGATE + 2 + 33 + 2014 + 3 + 0 + ST600MM0006 + None + 0 + CN07YX587262248G01MHA02 + 0 + 1 + 1 + 255 + LS0A + 1 + 5000C5007764FF6D + 0 + S0M3EVL6 + 599550590976 + 0 + None + 0 + 0 + 7 3E