diff --git a/dracclient/client.py b/dracclient/client.py index 74decc3..df18ea1 100644 --- a/dracclient/client.py +++ b/dracclient/client.py @@ -468,7 +468,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 @@ -492,6 +493,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 @@ -508,7 +511,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, @@ -785,7 +789,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. @@ -798,6 +802,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 @@ -811,7 +817,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 @@ -830,6 +837,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 26bf85c..0ad67ae 100644 --- a/dracclient/resources/job.py +++ b/dracclient/resources/job.py @@ -117,7 +117,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 @@ -142,6 +143,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 @@ -157,7 +160,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 b5701af..732b6b5 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', @@ -110,7 +115,8 @@ class PhysicalDisk(PhysicalDiskTuple): RAIDController = collections.namedtuple( 'RAIDController', ['id', 'description', 'manufacturer', 'model', - 'primary_status', 'firmware_version', 'bus']) + 'primary_status', 'firmware_version', 'bus', + 'supports_realtime']) VirtualDiskTuple = collections.namedtuple( 'VirtualDisk', @@ -191,7 +197,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( @@ -763,3 +772,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 5fbf60c..ced71cc 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']) @@ -597,25 +597,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) @@ -624,13 +627,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) @@ -640,13 +644,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) @@ -659,6 +681,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) @@ -1153,3 +1186,35 @@ 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