diff --git a/dracclient/client.py b/dracclient/client.py index ef32f74..580a529 100644 --- a/dracclient/client.py +++ b/dracclient/client.py @@ -326,6 +326,25 @@ class DRACClient(object): """ return self._raid_mgmt.list_physical_disks() + def convert_physical_disks(self, raid_controller, physical_disks, + raid_enable=True): + """Changes the operational mode of a physical disk. + + Disks can be enabled or disabled for RAID mode. + + :param raid_controller: the FQDD ID of the RAID controller + :param physical_disks: list of FQDD ID strings of the physical disks + to update + :param raid_enable: boolean flag, set to True if the disk is to + become part of the RAID. The same flag is applied to all + listed disks + :returns: a dictionary containing the commit_required key with a + boolean value indicating whether a config job must be + created for the values to be applied. + """ + return self._raid_mgmt.convert_physical_disks( + physical_disks, raid_enable) + def create_virtual_disk(self, raid_controller, physical_disks, raid_level, size_mb, disk_name=None, span_length=None, span_depth=None): diff --git a/dracclient/resources/raid.py b/dracclient/resources/raid.py index 16ce455..f30bff3 100644 --- a/dracclient/resources/raid.py +++ b/dracclient/resources/raid.py @@ -234,6 +234,36 @@ class RAIDManagement(object): return utils.get_wsman_resource_attr( drac_disk, uris.DCIM_PhysicalDiskView, attr_name) + def convert_physical_disks(self, physical_disks, raid_enable): + """Converts a list of physical disks into or out of RAID mode. + + Disks can be enabled or disabled for RAID mode. + + :param physical_disks: list of FQDD ID strings of the physical disks + to update + :param raid_enable: boolean flag, set to True if the disk is to + become part of the RAID. The same flag is applied to all + listed disks + :returns: a dictionary containing the commit_needed key with a boolean + value indicating whether a config job must be created for the + values to be applied. + """ + invocation = 'ConvertToRAID' if raid_enable else 'ConvertToNonRAID' + + selectors = {'SystemCreationClassName': 'DCIM_ComputerSystem', + 'CreationClassName': 'DCIM_RAIDService', + 'SystemName': 'DCIM:ComputerSystem', + 'Name': 'DCIM:RAIDService'} + + properties = {'PDArray': physical_disks} + + doc = self.client.invoke(uris.DCIM_RAIDService, invocation, + selectors, properties, + expected_return_value=utils.RET_SUCCESS) + + return {'commit_required': + utils.is_reboot_required(doc, uris.DCIM_RAIDService)} + def create_virtual_disk(self, raid_controller, physical_disks, raid_level, size_mb, disk_name=None, span_length=None, span_depth=None): diff --git a/dracclient/tests/test_client.py b/dracclient/tests/test_client.py index 347be2f..05e204e 100644 --- a/dracclient/tests/test_client.py +++ b/dracclient/tests/test_client.py @@ -15,6 +15,7 @@ import re import lxml.etree import mock +import random import requests_mock import dracclient.client @@ -702,6 +703,162 @@ class ClientRAIDManagementTestCase(base.BaseTest): self.assertIn(expected_physical_disk, self.drac_client.list_physical_disks()) + # Verify that various client convert_physical_disks calls to dracclient + # result in a WSMan.invoke with appropriate parameters + def _random_term(self): + return "".join(random.sample('ABCDEFGHabcdefgh0123456', + random.randint(4, 12))) + + def _random_fqdd(self): + result = self._random_term() + for i in range(0, random.randint(6, 10)): + result += random.sample('.:-', 1)[0] + self._random_term() + return result + + @mock.patch.object(dracclient.client.WSManClient, 'invoke', + spec_set=True, autospec=True) + def test_convert_physical_disks_1(self, mock_requests, mock_invoke): + '''Convert a single disk to RAID mode''' + device_fqdd = self._random_fqdd() + expected_invocation = 'ConvertToRAID' + expected_selectors = {'SystemCreationClassName': 'DCIM_ComputerSystem', + 'CreationClassName': 'DCIM_RAIDService', + 'SystemName': 'DCIM:ComputerSystem', + 'Name': 'DCIM:RAIDService'} + expected_properties = {'PDArray': [device_fqdd]} + + result = self.drac_client.convert_physical_disks( + raid_controller='controller', + physical_disks=[device_fqdd], + raid_enable=True) + + self.assertEqual({'commit_required': False}, result) + mock_invoke.assert_called_once_with( + mock.ANY, uris.DCIM_RAIDService, expected_invocation, + expected_selectors, expected_properties, + expected_return_value=utils.RET_SUCCESS) + + @mock.patch.object(dracclient.client.WSManClient, 'invoke', + spec_set=True, autospec=True) + def test_convert_physical_disks_n(self, mock_requests, mock_invoke): + '''Convert a number of disks to RAID mode''' + device_list = [] + for i in range(0, random.randint(2, 10)): + device_list += self._random_fqdd() + + expected_invocation = 'ConvertToRAID' + expected_selectors = {'SystemCreationClassName': 'DCIM_ComputerSystem', + 'CreationClassName': 'DCIM_RAIDService', + 'SystemName': 'DCIM:ComputerSystem', + 'Name': 'DCIM:RAIDService'} + expected_properties = {'PDArray': device_list} + + result = self.drac_client.convert_physical_disks( + raid_controller='controller', + physical_disks=device_list, + raid_enable=True) + + self.assertEqual({'commit_required': False}, result) + mock_invoke.assert_called_once_with( + mock.ANY, uris.DCIM_RAIDService, expected_invocation, + expected_selectors, expected_properties, + expected_return_value=utils.RET_SUCCESS) + + @mock.patch.object(dracclient.client.WSManClient, 'invoke', + spec_set=True, autospec=True) + def test_convert_physical_disks_nonraid_1(self, mock_requests, + mock_invoke): + '''Convert a single disk to non-RAID mode''' + device_fqdd = self._random_fqdd() + expected_invocation = 'ConvertToNonRAID' + expected_selectors = {'SystemCreationClassName': 'DCIM_ComputerSystem', + 'CreationClassName': 'DCIM_RAIDService', + 'SystemName': 'DCIM:ComputerSystem', + 'Name': 'DCIM:RAIDService'} + expected_properties = {'PDArray': [device_fqdd]} + + result = self.drac_client.convert_physical_disks( + raid_controller='controller', + physical_disks=[device_fqdd], + raid_enable=False) + + self.assertEqual({'commit_required': False}, result) + mock_invoke.assert_called_once_with( + mock.ANY, uris.DCIM_RAIDService, expected_invocation, + expected_selectors, expected_properties, + expected_return_value=utils.RET_SUCCESS) + + @mock.patch.object(dracclient.client.WSManClient, 'invoke', + spec_set=True, autospec=True) + def test_convert_physical_disks_nonraid_n(self, mock_requests, + mock_invoke): + '''Convert a number of disks to non-RAID mode''' + device_list = [] + for i in range(0, random.randint(2, 10)): + device_list += self._random_fqdd() + + expected_invocation = 'ConvertToNonRAID' + expected_selectors = {'SystemCreationClassName': 'DCIM_ComputerSystem', + 'CreationClassName': 'DCIM_RAIDService', + 'SystemName': 'DCIM:ComputerSystem', + 'Name': 'DCIM:RAIDService'} + expected_properties = {'PDArray': device_list} + + result = self.drac_client.convert_physical_disks( + raid_controller='controller', + physical_disks=device_list, + raid_enable=False) + + self.assertEqual({'commit_required': False}, result) + mock_invoke.assert_called_once_with( + mock.ANY, uris.DCIM_RAIDService, expected_invocation, + expected_selectors, expected_properties, + expected_return_value=utils.RET_SUCCESS) + + @mock.patch.object(dracclient.client.WSManClient, 'invoke', + spec_set=True, autospec=True) + def test_convert_physical_disks_ok(self, mock_requests, mock_invoke): + '''Convert a number of disks to RAID mode and check the return value''' + device_list = [] + for i in range(0, random.randint(2, 10)): + device_list += self._random_fqdd() + + expected_invocation = 'ConvertToRAID' + expected_selectors = {'SystemCreationClassName': 'DCIM_ComputerSystem', + 'CreationClassName': 'DCIM_RAIDService', + 'SystemName': 'DCIM:ComputerSystem', + 'Name': 'DCIM:RAIDService'} + expected_properties = {'PDArray': device_list} + + mock_invoke.return_value = lxml.etree.fromstring( + test_utils.RAIDInvocations[uris.DCIM_RAIDService][ + expected_invocation]['ok']) + + result = self.drac_client.convert_physical_disks( + raid_controller='controller', + physical_disks=device_list, + raid_enable=True) + + self.assertEqual({'commit_required': True}, result) + mock_invoke.assert_called_once_with( + mock.ANY, uris.DCIM_RAIDService, expected_invocation, + expected_selectors, expected_properties, + expected_return_value=utils.RET_SUCCESS) + + def test_convert_physical_disks_fail(self, mock_requests): + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.RAIDInvocations[ + uris.DCIM_RAIDService]['ConvertToRAID']['error']) + + self.assertRaises( + exceptions.DRACOperationFailed, + self.drac_client.convert_physical_disks, + raid_controller='controller', + physical_disks=['Disk0:Enclosure-1:RAID-1', + 'Disk1:Enclosure-1:RAID-1'], + raid_enable=True) + @mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True, autospec=True) def test_create_virtual_disk(self, mock_requests, mock_invoke): diff --git a/dracclient/tests/utils.py b/dracclient/tests/utils.py index 6b14081..47d590a 100644 --- a/dracclient/tests/utils.py +++ b/dracclient/tests/utils.py @@ -164,6 +164,12 @@ RAIDInvocations = { 'raid_service-invoke-delete_virtual_disk-ok'), 'error': load_wsman_xml( 'raid_service-invoke-delete_virtual_disk-error'), + }, + 'ConvertToRAID': { + 'ok': load_wsman_xml( + 'raid_service-invoke-convert_physical_disks-ok'), + 'error': load_wsman_xml( + 'raid_service-invoke-convert_physical_disks-error'), } } } diff --git a/dracclient/tests/wsman_mocks/raid_service-invoke-convert_physical_disks-error.xml b/dracclient/tests/wsman_mocks/raid_service-invoke-convert_physical_disks-error.xml new file mode 100644 index 0000000..d5fa89c --- /dev/null +++ b/dracclient/tests/wsman_mocks/raid_service-invoke-convert_physical_disks-error.xml @@ -0,0 +1,16 @@ + + + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_RAIDService/ConvertToRAIDResponse + uuid:6364e38d-4e3c-4769-8c67-ff4332ea603a + uuid:ad08919a-34b2-14b2-a4b0-ea19c46a0064 + + + + Invalid parameter value + NULL + STOR004 + 2 + + + diff --git a/dracclient/tests/wsman_mocks/raid_service-invoke-convert_physical_disks-ok.xml b/dracclient/tests/wsman_mocks/raid_service-invoke-convert_physical_disks-ok.xml new file mode 100644 index 0000000..5ad5c1e --- /dev/null +++ b/dracclient/tests/wsman_mocks/raid_service-invoke-convert_physical_disks-ok.xml @@ -0,0 +1,14 @@ + + + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_RAIDService/ConvertToRAIDResponse + uuid:c6c4bcf6-c340-4f0b-9aef-3c7be94f0d61 + uuid:3223ac5b-3439-1439-9855-ea19c46a0064 + + + + YES + 0 + + +