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)