diff --git a/dracclient/client.py b/dracclient/client.py index 340a625..e8ad33d 100644 --- a/dracclient/client.py +++ b/dracclient/client.py @@ -463,7 +463,8 @@ class DRACClient(object): cim_system_creation_class_name='DCIM_ComputerSystem', cim_system_name='DCIM:ComputerSystem', reboot=False, - start_time='TIME_NOW'): + start_time='TIME_NOW', + realtime=False): """Creates a configuration job. In CIM (Common Information Model), weak association is used to name an @@ -487,6 +488,8 @@ class DRACClient(object): means execute immediately or None which means the job will not execute until schedule_job_execution is called + :param realtime: Indicates if reatime mode should be used. + Valid values are True and False. :returns: id of the created job :raises: WSManRequestFailure on request failures :raises: WSManInvalidResponse when receiving invalid response @@ -503,7 +506,8 @@ class DRACClient(object): cim_system_creation_class_name=cim_system_creation_class_name, cim_system_name=cim_system_name, reboot=reboot, - start_time=start_time) + start_time=start_time, + realtime=realtime) def create_nic_config_job( self, @@ -765,7 +769,7 @@ class DRACClient(object): return self._raid_mgmt.delete_virtual_disk(virtual_disk) def commit_pending_raid_changes(self, raid_controller, reboot=False, - start_time='TIME_NOW'): + start_time='TIME_NOW', realtime=False): """Applies all pending changes on a RAID controller ...by creating a config job. @@ -778,6 +782,8 @@ class DRACClient(object): means execute immediately or None which means the job will not execute until schedule_job_execution is called + :param realtime: Indicates if reatime mode should be used. + Valid values are True and False. :returns: id of the created job :raises: WSManRequestFailure on request failures :raises: WSManInvalidResponse when receiving invalid response @@ -791,7 +797,8 @@ class DRACClient(object): cim_name='DCIM:RAIDService', target=raid_controller, reboot=reboot, - start_time=start_time) + start_time=start_time, + realtime=realtime) def abandon_pending_raid_changes(self, raid_controller): """Deletes all pending changes on a RAID controller @@ -810,6 +817,14 @@ class DRACClient(object): cim_creation_class_name='DCIM_RAIDService', cim_name='DCIM:RAIDService', target=raid_controller) + def is_realtime_supported(self, raid_controller): + """Find if controller supports realtime or not + + :param raid_controller: ID of RAID controller + :returns: True or False + """ + return self._raid_mgmt.is_realtime_supported(raid_controller) + def list_cpus(self): """Returns the list of CPUs diff --git a/dracclient/resources/job.py b/dracclient/resources/job.py index 5bddec7..32618db 100644 --- a/dracclient/resources/job.py +++ b/dracclient/resources/job.py @@ -101,7 +101,8 @@ class JobManagement(object): cim_system_creation_class_name='DCIM_ComputerSystem', cim_system_name='DCIM:ComputerSystem', reboot=False, - start_time='TIME_NOW'): + start_time='TIME_NOW', + realtime=False): """Creates a config job In CIM (Common Information Model), weak association is used to name an @@ -126,6 +127,8 @@ class JobManagement(object): but will not start execution until schedule_job_execution is called with the returned job id. + :param realtime: Indicates if reatime mode should be used. + Valid values are True and False. Default value is False. :returns: id of the created job :raises: WSManRequestFailure on request failures :raises: WSManInvalidResponse when receiving invalid response @@ -141,7 +144,10 @@ class JobManagement(object): properties = {'Target': target} - if reboot: + if realtime: + properties['RealTime'] = '1' + + if not realtime and reboot: properties['RebootJobType'] = '3' if start_time is not None: diff --git a/dracclient/resources/raid.py b/dracclient/resources/raid.py index 2ddf494..669e4b1 100644 --- a/dracclient/resources/raid.py +++ b/dracclient/resources/raid.py @@ -34,6 +34,11 @@ RAID_LEVELS = { REVERSE_RAID_LEVELS = dict((v, k) for (k, v) in RAID_LEVELS.items()) +RAID_CONTROLLER_IS_REALTIME = { + '1': True, + '0': False +} + DISK_RAID_STATUS = { '0': 'unknown', '1': 'ready', @@ -80,7 +85,8 @@ PhysicalDisk = collections.namedtuple( RAIDController = collections.namedtuple( 'RAIDController', ['id', 'description', 'manufacturer', 'model', - 'primary_status', 'firmware_version', 'bus']) + 'primary_status', 'firmware_version', 'bus', + 'supports_realtime']) VirtualDisk = collections.namedtuple( 'VirtualDisk', @@ -131,7 +137,10 @@ class RAIDManagement(object): 'PrimaryStatus')], firmware_version=self._get_raid_controller_attr( drac_controller, 'ControllerFirmwareVersion'), - bus=self._get_raid_controller_attr(drac_controller, 'Bus')) + bus=self._get_raid_controller_attr(drac_controller, 'Bus'), + supports_realtime=RAID_CONTROLLER_IS_REALTIME[ + self._get_raid_controller_attr( + drac_controller, 'RealtimeCapability')]) def _get_raid_controller_attr(self, drac_controller, attr_name): return utils.get_wsman_resource_attr( @@ -697,3 +706,18 @@ class RAIDManagement(object): return {'is_reboot_required': is_reboot_required, 'commit_required_ids': controllers} + + def is_realtime_supported(self, raid_controller_fqdd): + """Find if controller supports realtime or not + + :param raid_controller_fqdd: ID of RAID controller + :returns: True or False + """ + drac_raid_controllers = self.list_raid_controllers() + realtime_controller = [cnt.id for cnt in drac_raid_controllers + if cnt.supports_realtime] + + if raid_controller_fqdd in realtime_controller: + return True + + return False diff --git a/dracclient/tests/test_job.py b/dracclient/tests/test_job.py index 4dcbc56..051b847 100644 --- a/dracclient/tests/test_job.py +++ b/dracclient/tests/test_job.py @@ -342,7 +342,34 @@ class ClientJobManagementTestCase(base.BaseTest): job_id = self.drac_client.create_config_job( uris.DCIM_BIOSService, cim_creation_class_name, cim_name, target, - reboot=True) + reboot=True, realtime=False) + + mock_invoke.assert_called_once_with( + mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob', + expected_selectors, expected_properties, + expected_return_value=utils.RET_CREATED) + self.assertEqual('JID_442507917525', job_id) + + @mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True, + autospec=True) + def test_create_config_job_with_realtime(self, mock_invoke): + cim_creation_class_name = 'DCIM_BIOSService' + cim_name = 'DCIM:BIOSService' + target = 'BIOS.Setup.1-1' + expected_selectors = {'CreationClassName': cim_creation_class_name, + 'Name': cim_name, + 'SystemCreationClassName': 'DCIM_ComputerSystem', + 'SystemName': 'DCIM:ComputerSystem'} + expected_properties = {'Target': target, + 'ScheduledStartTime': 'TIME_NOW', + 'RealTime': '1'} + mock_invoke.return_value = lxml.etree.fromstring( + test_utils.JobInvocations[uris.DCIM_BIOSService][ + 'CreateTargetedConfigJob']['ok']) + + job_id = self.drac_client.create_config_job( + uris.DCIM_BIOSService, cim_creation_class_name, cim_name, target, + reboot=False, realtime=True) mock_invoke.assert_called_once_with( mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob', diff --git a/dracclient/tests/test_raid.py b/dracclient/tests/test_raid.py index 6d56516..c52851c 100644 --- a/dracclient/tests/test_raid.py +++ b/dracclient/tests/test_raid.py @@ -124,8 +124,8 @@ class ClientRAIDManagementTestCase(base.BaseTest): model='PERC H710 Mini', primary_status='ok', firmware_version='21.3.0-0009', - bus='1') - + bus='1', + supports_realtime=True) mock_requests.post( 'https://1.2.3.4:443/wsman', text=test_utils.RAIDEnumerations[uris.DCIM_ControllerView]['ok']) @@ -590,25 +590,28 @@ class ClientRAIDManagementTestCase(base.BaseTest): 'create_config_job', spec_set=True, autospec=True) def test_commit_pending_raid_changes(self, mock_requests, mock_create_config_job): - self.drac_client.commit_pending_raid_changes('controller') + self.drac_client.commit_pending_raid_changes('controller', + realtime=False) mock_create_config_job.assert_called_once_with( mock.ANY, resource_uri=uris.DCIM_RAIDService, cim_creation_class_name='DCIM_RAIDService', cim_name='DCIM:RAIDService', target='controller', reboot=False, - start_time='TIME_NOW') + start_time='TIME_NOW', realtime=False) @mock.patch.object(dracclient.resources.job.JobManagement, 'create_config_job', spec_set=True, autospec=True) def test_commit_pending_raid_changes_with_reboot(self, mock_requests, mock_create_config_job): - self.drac_client.commit_pending_raid_changes('controller', reboot=True) + self.drac_client.commit_pending_raid_changes('controller', + reboot=True, + realtime=False) mock_create_config_job.assert_called_once_with( mock.ANY, resource_uri=uris.DCIM_RAIDService, cim_creation_class_name='DCIM_RAIDService', cim_name='DCIM:RAIDService', target='controller', reboot=True, - start_time='TIME_NOW') + start_time='TIME_NOW', realtime=False) @mock.patch.object(dracclient.resources.job.JobManagement, 'create_config_job', spec_set=True, autospec=True) @@ -617,13 +620,14 @@ class ClientRAIDManagementTestCase(base.BaseTest): mock_create_config_job): timestamp = '20140924140201' self.drac_client.commit_pending_raid_changes('controller', - start_time=timestamp) + start_time=timestamp, + realtime=False) mock_create_config_job.assert_called_once_with( mock.ANY, resource_uri=uris.DCIM_RAIDService, cim_creation_class_name='DCIM_RAIDService', cim_name='DCIM:RAIDService', target='controller', reboot=False, - start_time=timestamp) + start_time=timestamp, realtime=False) @mock.patch.object(dracclient.resources.job.JobManagement, 'create_config_job', spec_set=True, autospec=True) @@ -633,13 +637,31 @@ class ClientRAIDManagementTestCase(base.BaseTest): timestamp = '20140924140201' self.drac_client.commit_pending_raid_changes('controller', reboot=True, - start_time=timestamp) + start_time=timestamp, + realtime=False) mock_create_config_job.assert_called_once_with( mock.ANY, resource_uri=uris.DCIM_RAIDService, cim_creation_class_name='DCIM_RAIDService', cim_name='DCIM:RAIDService', target='controller', reboot=True, - start_time=timestamp) + start_time=timestamp, realtime=False) + + @mock.patch.object(dracclient.resources.job.JobManagement, + 'create_config_job', spec_set=True, autospec=True) + def test_commit_pending_raid_changes_with_realtime( + self, mock_requests, + mock_create_config_job): + timestamp = '20140924140201' + self.drac_client.commit_pending_raid_changes('controller', + reboot=False, + start_time=timestamp, + realtime=True) + + mock_create_config_job.assert_called_once_with( + mock.ANY, resource_uri=uris.DCIM_RAIDService, + cim_creation_class_name='DCIM_RAIDService', + cim_name='DCIM:RAIDService', target='controller', reboot=False, + start_time=timestamp, realtime=True) @mock.patch.object(dracclient.resources.job.JobManagement, 'delete_pending_config', spec_set=True, autospec=True) @@ -652,6 +674,17 @@ class ClientRAIDManagementTestCase(base.BaseTest): cim_creation_class_name='DCIM_RAIDService', cim_name='DCIM:RAIDService', target='controller') + @mock.patch.object(dracclient.resources.job.JobManagement, + 'delete_pending_config', spec_set=True, autospec=True) + def test_abandon_pending_raid_changes_realtime(self, mock_requests, + mock_delete_pending_config): + self.drac_client.abandon_pending_raid_changes('controller') + + mock_delete_pending_config.assert_called_once_with( + 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) @@ -1170,3 +1203,37 @@ class ClientRAIDManagementTestCase(base.BaseTest): results = self.drac_client.change_physical_disk_state(mode) self.assertFalse(results["is_reboot_required"]) self.assertEqual(len(results["commit_required_ids"]), 0) + + @mock.patch.object(dracclient.client.WSManClient, + 'wait_until_idrac_is_ready', spec_set=True, + autospec=True) + def test_is_realtime_supported_with_realtime_controller( + self, + mock_requests, + mock_wait_until_idrac_is_ready): + expected_raid_controller = 'RAID.Integrated.1-1' + + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.RAIDEnumerations[uris.DCIM_ControllerView]['ok']) + + self.assertTrue( + self.drac_client.is_realtime_supported( + expected_raid_controller)) + + @mock.patch.object(dracclient.client.WSManClient, + 'wait_until_idrac_is_ready', spec_set=True, + autospec=True) + def test_is_realtime_supported_with_non_realtime_controller( + self, + mock_requests, + mock_wait_until_idrac_is_ready): + expected_raid_controller = 'AHCI.Integrated.1-1' + + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.RAIDEnumerations[uris.DCIM_ControllerView]['ok']) + + self.assertFalse( + self.drac_client.is_realtime_supported( + expected_raid_controller)) diff --git a/dracclient/tests/wsman_mocks/controller_view-enum-ok.xml b/dracclient/tests/wsman_mocks/controller_view-enum-ok.xml index 2188685..069a0d8 100644 --- a/dracclient/tests/wsman_mocks/controller_view-enum-ok.xml +++ b/dracclient/tests/wsman_mocks/controller_view-enum-ok.xml @@ -43,6 +43,7 @@ 0 1 PERC H710 Mini + 1 1 5B083FE0D2D0F200 1 @@ -82,6 +83,7 @@ 0 1 BOSS-S1 + 0 1 5B083FE0D2D0F201 1