From 1d203354a28d7d924609cf9a4535b7d497a2f0f3 Mon Sep 17 00:00:00 2001 From: David Paterson Date: Wed, 27 Jun 2018 17:25:56 -0400 Subject: [PATCH] Added is_jbod_capable method to raid resource Some RAID controllers support JBOD (passthrough) mode, while others do not. This method determines if the passed RAID controller supports JBOD mode. Note: that the controller must have at least one disk in the non-RAID or ready state, or this method will be unable to determine if the RAID controller supports JBOD mode, and an exception will be thrown. Co-Authored-By: Christopher Dearborn Change-Id: I62d8f92197faf6a6b4fe8a72da5c1a6a79d2af82 --- dracclient/client.py | 14 +++++ dracclient/resources/raid.py | 60 ++++++++++++++++++ dracclient/tests/test_raid.py | 115 ++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) diff --git a/dracclient/client.py b/dracclient/client.py index 7081527..d693086 100644 --- a/dracclient/client.py +++ b/dracclient/client.py @@ -677,6 +677,20 @@ class DRACClient(object): return self.client.wait_until_idrac_is_ready(retries, retry_delay) + def is_jbod_capable(self, raid_controller_fqdd): + """Find out if raid controller supports jbod + + :param raid_controller_fqdd: The raid controller's fqdd + being being checked to see if it is jbod + capable. + :raises: DRACRequestFailed if unable to find any disks in the Ready + or non-RAID states + :raises: DRACOperationFailed on error reported back by the DRAC + and the exception message does not contain + NOT_SUPPORTED_MSG constant + """ + return self._raid_mgmt.is_jbod_capable(raid_controller_fqdd) + class WSManClient(wsman.Client): """Wrapper for wsman.Client that can wait until iDRAC is ready diff --git a/dracclient/resources/raid.py b/dracclient/resources/raid.py index f492e2b..c4f1973 100644 --- a/dracclient/resources/raid.py +++ b/dracclient/resources/raid.py @@ -151,6 +151,8 @@ class VirtualDisk(VirtualDiskTuple): class RAIDManagement(object): + NOT_SUPPORTED_MSG = " operation is not supported on th" + def __init__(self, client): """Creates RAIDManagement object @@ -517,3 +519,61 @@ class RAIDManagement(object): return utils.build_return_dict(doc, uris.DCIM_RAIDService, include_commit_required=True, is_commit_required_value=True) + + def is_jbod_capable(self, raid_controller_fqdd): + """Find out if raid controller supports jbod + + :param raid_controller_fqdd: The raid controller's fqdd + being being checked to see if it is jbod + capable. + :raises: DRACRequestFailed if unable to find any disks in the Ready + or non-RAID states + :raises: DRACOperationFailed on error reported back by the DRAC + and the exception message does not contain + NOT_SUPPORTED_MSG constant + """ + is_jbod_capable = False + + # Grab all the disks associated with the RAID controller + all_physical_disks = self.list_physical_disks() + physical_disks = [physical_disk for physical_disk in all_physical_disks + if physical_disk.controller == raid_controller_fqdd] + + # If there is a disk in the Non-RAID state, then the controller is JBOD + # capable + ready_disk = None + for physical_disk in physical_disks: + if physical_disk.raid_status == 'non-RAID': + is_jbod_capable = True + break + elif not ready_disk and physical_disk.raid_status == 'ready': + ready_disk = physical_disk + + if not is_jbod_capable: + if not ready_disk: + msg = "Unable to find a disk in the Ready state" + raise exceptions.DRACRequestFailed(msg) + + # Try moving a disk in the Ready state to JBOD mode + try: + self.convert_physical_disks( + [ready_disk.id], + False) + is_jbod_capable = True + + # Flip the disk back to the Ready state. This results in the + # pending value being reset to nothing, so it effectively + # undoes the last command and makes the check non-destructive + self.convert_physical_disks( + [ready_disk.id], + True) + except exceptions.DRACOperationFailed as ex: + # Fix for python 3, Exception.message no longer + # a valid attribute, str(ex) works for both 2.7 + # and 3.x + if self.NOT_SUPPORTED_MSG in str(ex): + pass + else: + raise + + return is_jbod_capable diff --git a/dracclient/tests/test_raid.py b/dracclient/tests/test_raid.py index 76fd36a..80a8c3b 100644 --- a/dracclient/tests/test_raid.py +++ b/dracclient/tests/test_raid.py @@ -34,6 +34,7 @@ class ClientRAIDManagementTestCase(base.BaseTest): super(ClientRAIDManagementTestCase, self).setUp() self.drac_client = dracclient.client.DRACClient( **test_utils.FAKE_ENDPOINT) + self.raid_controller_fqdd = "RAID.Integrated.1-1" @mock.patch.object(dracclient.client.WSManClient, 'wait_until_idrac_is_ready', spec_set=True, @@ -548,3 +549,117 @@ class ClientRAIDManagementTestCase(base.BaseTest): mock.ANY, resource_uri=uris.DCIM_RAIDService, cim_creation_class_name='DCIM_RAIDService', cim_name='DCIM:RAIDService', target='controller') + + @mock.patch.object(dracclient.client.WSManClient, + 'wait_until_idrac_is_ready', spec_set=True, + autospec=True) + @mock.patch.object(dracclient.resources.raid.RAIDManagement, + 'convert_physical_disks', + return_value={}, spec_set=True, + autospec=True) + def test_raid_controller_jbod_capable(self, mock_requests, + mock_wait_until_idrac_is_ready, + mock_convert_physical_disks): + + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.RAIDEnumerations[uris.DCIM_PhysicalDiskView]['ok']) + + is_jbod = self.drac_client.is_jbod_capable(self.raid_controller_fqdd) + + self.assertTrue(is_jbod, msg="is_jbod is true") + + @mock.patch.object(dracclient.client.WSManClient, + 'wait_until_idrac_is_ready', spec_set=True, + autospec=True) + @mock.patch.object(dracclient.resources.raid.RAIDManagement, + 'convert_physical_disks', + return_value={}, spec_set=True, + autospec=True) + def test_raid_controller_jbod_non_raid(self, mock_requests, + mock_wait_until_idrac_is_ready, + mock_convert_physical_disks): + + pdv = test_utils.RAIDEnumerations[uris.DCIM_PhysicalDiskView]['ok'] + # change to non-RAID value + pdv = pdv.replace("1", + "8") + + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=pdv) + + is_jbod = self.drac_client.is_jbod_capable(self.raid_controller_fqdd) + + self.assertTrue(is_jbod, msg="is_jbod is true") + + @mock.patch.object(dracclient.client.WSManClient, + 'wait_until_idrac_is_ready', spec_set=True, + autospec=True) + @mock.patch.object(dracclient.resources.raid.RAIDManagement, + 'convert_physical_disks', + return_value={}, spec_set=True, + autospec=True) + def test_raid_controller_jbod_unknown(self, mock_requests, + mock_wait_until_idrac_is_ready, + mock_convert_physical_disks): + + is_jbod = False + pdv = test_utils.RAIDEnumerations[uris.DCIM_PhysicalDiskView]['ok'] + # change to non-RAID value + pdv = pdv.replace("1", + "0") + + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=pdv) + self.assertRaises(exceptions.DRACRequestFailed, + self.drac_client.is_jbod_capable, + self.raid_controller_fqdd) + self.assertFalse(is_jbod, msg="is_jbod is false") + + @mock.patch.object(dracclient.client.WSManClient, + 'wait_until_idrac_is_ready', spec_set=True, + autospec=True) + @mock.patch.object(dracclient.resources.raid.RAIDManagement, + 'convert_physical_disks', + spec_set=True, + autospec=True) + def test_raid_controller_jbod_not_supported(self, + mock_requests, + mock_convert_physical_disks, + mock_wait_idrac_is_ready): + + msg = " operation is not supported on th" + exc = exceptions.DRACOperationFailed(drac_messages=msg) + mock_convert_physical_disks.side_effect = exc + + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.RAIDEnumerations[uris.DCIM_PhysicalDiskView]['ok']) + + is_jbod = self.drac_client.is_jbod_capable(self.raid_controller_fqdd) + self.assertFalse(is_jbod, msg="is_jbod is false") + + @mock.patch.object(dracclient.client.WSManClient, + 'wait_until_idrac_is_ready', spec_set=True, + autospec=True) + @mock.patch.object(dracclient.resources.raid.RAIDManagement, + 'convert_physical_disks', + spec_set=True, + autospec=True) + def test_raid_controller_jbod_ex_no_match(self, + mock_requests, + mock_convert_physical_disks, + mock_wait_until_idrac_is_ready): + + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.RAIDEnumerations[uris.DCIM_PhysicalDiskView]['ok']) + msg = "NON_MATCHING_MESSAGE" + exc = exceptions.DRACOperationFailed(drac_messages=msg) + mock_convert_physical_disks.side_effect = exc + + self.assertRaises( + exceptions.DRACOperationFailed, + self.drac_client.is_jbod_capable, self.raid_controller_fqdd)