Repository: libcloud Updated Branches: refs/heads/trunk 850aa8f6c -> d8e19f6ac
Adding in ability to cancel a backup job Closes #739 Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/d8e19f6a Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/d8e19f6a Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/d8e19f6a Branch: refs/heads/trunk Commit: d8e19f6ac59a92be3d1dfefe9e31266503af7c58 Parents: 850aa8f Author: Jeffrey Dunham <jeffrey.a.dun...@gmail.com> Authored: Wed Apr 6 17:41:29 2016 -0400 Committer: anthony-shaw <anthony.p.s...@gmail.com> Committed: Thu Apr 7 11:49:38 2016 +1000 ---------------------------------------------------------------------- libcloud/backup/base.py | 12 +-- libcloud/backup/drivers/dimensiondata.py | 93 +++++++++++--------- libcloud/backup/drivers/ebs.py | 15 +--- libcloud/common/dimensiondata.py | 4 +- .../_remove_backup_client_BUSY.xml | 7 -- .../_remove_backup_client_FAIL.xml | 7 ++ ...76_c76d_4d7c_b39d_3b72be0384c8_cancelJob.xml | 7 ++ ...6d_4d7c_b39d_3b72be0384c8_cancelJob_FAIL.xml | 7 ++ libcloud/test/backup/test_dimensiondata.py | 81 ++++++++++++++--- 9 files changed, 155 insertions(+), 78 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/d8e19f6a/libcloud/backup/base.py ---------------------------------------------------------------------- diff --git a/libcloud/backup/base.py b/libcloud/backup/base.py index dcc7d60..8d1c5a7 100644 --- a/libcloud/backup/base.py +++ b/libcloud/backup/base.py @@ -113,13 +113,13 @@ class BackupTargetJob(object): self.extra = extra or {} def cancel(self): - return self.driver.cancel_target_job(target=self.target, job=self) + return self.driver.cancel_target_job(job=self) def suspend(self): - return self.driver.suspend_target_job(target=self.target, job=self) + return self.driver.suspend_target_job(job=self) def resume(self): - return self.driver.resume_target_job(target=self.target, job=self) + return self.driver.resume_target_job(job=self) def __repr__(self): return ('<Job: id=%s, status=%s, progress=%s' @@ -443,7 +443,7 @@ class BackupDriver(BaseDriver): raise NotImplementedError( 'create_target_job not implemented for this driver') - def resume_target_job(self, target, job): + def resume_target_job(self, job): """ Resume a suspended backup job on a target @@ -458,7 +458,7 @@ class BackupDriver(BaseDriver): raise NotImplementedError( 'resume_target_job not implemented for this driver') - def suspend_target_job(self, target, job): + def suspend_target_job(self, job): """ Suspend a running backup job on a target @@ -473,7 +473,7 @@ class BackupDriver(BaseDriver): raise NotImplementedError( 'suspend_target_job not implemented for this driver') - def cancel_target_job(self, target, job): + def cancel_target_job(self, job): """ Cancel a backup job on a target http://git-wip-us.apache.org/repos/asf/libcloud/blob/d8e19f6a/libcloud/backup/drivers/dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/backup/drivers/dimensiondata.py b/libcloud/backup/drivers/dimensiondata.py index cea656a..a16b2e5 100644 --- a/libcloud/backup/drivers/dimensiondata.py +++ b/libcloud/backup/drivers/dimensiondata.py @@ -18,13 +18,13 @@ try: except ImportError: from xml.etree import ElementTree as ET -from libcloud.backup.base import BackupDriver, BackupTarget +from libcloud.backup.base import BackupDriver, BackupTarget, BackupTargetJob from libcloud.backup.types import BackupTargetType from libcloud.backup.types import Provider +from libcloud.common.dimensiondata import dd_object_to_id from libcloud.common.dimensiondata import DimensionDataConnection from libcloud.common.dimensiondata import DimensionDataBackupClient from libcloud.common.dimensiondata import DimensionDataBackupClientAlert -from libcloud.common.dimensiondata import DimensionDataBackupClientRunningJob from libcloud.common.dimensiondata import DimensionDataBackupClientType from libcloud.common.dimensiondata import DimensionDataBackupDetails from libcloud.common.dimensiondata import DimensionDataBackupSchedulePolicy @@ -32,7 +32,6 @@ from libcloud.common.dimensiondata import DimensionDataBackupStoragePolicy from libcloud.common.dimensiondata import API_ENDPOINTS, DEFAULT_REGION from libcloud.common.dimensiondata import TYPES_URN from libcloud.common.dimensiondata import GENERAL_NS, BACKUP_NS -from libcloud.utils.py3 import basestring from libcloud.utils.xml import fixxpath, findtext, findall DEFAULT_BACKUP_PLAN = 'Advanced' @@ -119,6 +118,7 @@ class DimensionDataBackupDriver(BackupDriver): service_plan = extra.get('servicePlan', DEFAULT_BACKUP_PLAN) else: service_plan = DEFAULT_BACKUP_PLAN + extra = {'servicePlan': service_plan} create_node = ET.Element('NewBackup', {'xmlns': BACKUP_NS}) @@ -369,20 +369,42 @@ class DimensionDataBackupDriver(BackupDriver): raise NotImplementedError( 'suspend_target_job not implemented for this driver') - def cancel_target_job(self, target, job): + def cancel_target_job(self, job, ex_client=None, ex_target=None): """ Cancel a backup job on a target - :param target: Backup target with the backup data - :type target: Instance of :class:`BackupTarget` + :param job: Backup target job to cancel. If it is ``None`` + ex_client and ex_target must be set + :type job: Instance of :class:`BackupTargetJob` or ``None`` - :param job: Backup target job to cancel - :type job: Instance of :class:`BackupTargetJob` + :param ex_client: Client of the job to cancel. + Not necessary if job is specified. + DimensionData only has 1 job per client + :type ex_client: Instance of :class:`DimensionDataBackupClient` + or ``str`` + + :param ex_target: Target to cancel a job from. + Not necessary if job is specified. + :type ex_target: Instance of :class:`BackupTarget` or ``str`` :rtype: ``bool`` """ - raise NotImplementedError( - 'cancel_target_job not implemented for this driver') + if job is None: + if ex_client is None or ex_target is None: + raise ValueError("Either job or ex_client and " + "ex_target have to be set") + server_id = self._target_to_target_address(ex_target) + client_id = self._client_to_client_id(ex_client) + else: + server_id = job.target.address + client_id = job.extra['clientId'] + + response = self.connection.request_with_orgId_api_1( + 'server/%s/backup/client/%s?cancelJob' % (server_id, + client_id), + method='GET').object + response_code = findtext(response, 'result', GENERAL_NS) + return response_code in ['IN_PROGRESS', 'SUCCESS'] def ex_get_target_by_id(self, id): """ @@ -490,11 +512,12 @@ class DimensionDataBackupDriver(BackupDriver): :rtype: :class:`DimensionDataBackupDetails` """ - server_id = self._target_to_target_address(target) + if not isinstance(target, BackupTarget): + target = self.ex_get_target_by_id(target) response = self.connection.request_with_orgId_api_1( - 'server/%s/backup' % (server_id), + 'server/%s/backup' % (target.address), method='GET').object - return self._to_backup_details(response) + return self._to_backup_details(response, target) def ex_list_available_client_types(self, target): """ @@ -579,28 +602,29 @@ class DimensionDataBackupDriver(BackupDriver): is_file_system=bool(element.get('isFileSystem') == 'true') ) - def _to_backup_details(self, object): + def _to_backup_details(self, object, target): return DimensionDataBackupDetails( asset_id=object.get('asset_id'), service_plan=object.get('servicePlan'), status=object.get('state'), - clients=self._to_clients(object) + clients=self._to_clients(object, target) ) - def _to_clients(self, object): + def _to_clients(self, object, target): elements = object.findall(fixxpath('backupClient', BACKUP_NS)) - return [self._to_client(el) for el in elements] + return [self._to_client(el, target) for el in elements] - def _to_client(self, element): + def _to_client(self, element, target): + client_id = element.get('id') return DimensionDataBackupClient( - id=element.get('id'), - type=self._to_client_type(element, ), + id=client_id, + type=self._to_client_type(element), status=element.get('status'), schedule_policy=findtext(element, 'schedulePolicyName', BACKUP_NS), storage_policy=findtext(element, 'storagePolicyName', BACKUP_NS), download_url=findtext(element, 'downloadUrl', BACKUP_NS), - running_job=self._to_running_job(element), + running_job=self._to_backup_job(element, target, client_id), alert=self._to_alert(element) ) @@ -617,13 +641,16 @@ class DimensionDataBackupDriver(BackupDriver): ) return None - def _to_running_job(self, element): + def _to_backup_job(self, element, target, client_id): running_job = element.find(fixxpath('runningJob', BACKUP_NS)) if running_job is not None: - return DimensionDataBackupClientRunningJob( + return BackupTargetJob( id=running_job.get('id'), status=running_job.get('status'), - percentage=int(running_job.get('percentageComplete')) + progress=int(running_job.get('percentageComplete')), + driver=self.connection.driver, + target=target, + extra={'clientId': client_id} ) return None @@ -654,22 +681,8 @@ class DimensionDataBackupDriver(BackupDriver): @staticmethod def _client_to_client_id(backup_client): - if isinstance(backup_client, DimensionDataBackupClient): - return backup_client.id - elif isinstance(backup_client, basestring): - return backup_client - else: - raise TypeError( - "Invalid backup_client type for _client_to_client_id()" - ) + return dd_object_to_id(backup_client, DimensionDataBackupClient) @staticmethod def _target_to_target_address(target): - if isinstance(target, BackupTarget): - return target.address - elif isinstance(target, basestring): - return target - else: - raise TypeError( - "Invalid target type for _target_to_target_address()" - ) + return dd_object_to_id(target, BackupTarget, id_value='address') http://git-wip-us.apache.org/repos/asf/libcloud/blob/d8e19f6a/libcloud/backup/drivers/ebs.py ---------------------------------------------------------------------- diff --git a/libcloud/backup/drivers/ebs.py b/libcloud/backup/drivers/ebs.py index 4aba536..fbeddb1 100644 --- a/libcloud/backup/drivers/ebs.py +++ b/libcloud/backup/drivers/ebs.py @@ -293,13 +293,10 @@ class EBSBackupDriver(BackupDriver): return self._to_job(findall(element=data, xpath=xpath, namespace=NS)[0]) - def resume_target_job(self, target, job): + def resume_target_job(self, job): """ Resume a suspended backup job on a target - :param target: Backup target with the backup data - :type target: Instance of :class:`BackupTarget` - :param job: Backup target job to resume :type job: Instance of :class:`BackupTargetJob` @@ -308,13 +305,10 @@ class EBSBackupDriver(BackupDriver): raise NotImplementedError( 'resume_target_job not supported for this driver') - def suspend_target_job(self, target, job): + def suspend_target_job(self, job): """ Suspend a running backup job on a target - :param target: Backup target with the backup data - :type target: Instance of :class:`BackupTarget` - :param job: Backup target job to suspend :type job: Instance of :class:`BackupTargetJob` @@ -323,13 +317,10 @@ class EBSBackupDriver(BackupDriver): raise NotImplementedError( 'suspend_target_job not supported for this driver') - def cancel_target_job(self, target, job): + def cancel_target_job(self, job): """ Cancel a backup job on a target - :param target: Backup target with the backup data - :type target: Instance of :class:`BackupTarget` - :param job: Backup target job to cancel :type job: Instance of :class:`BackupTargetJob` http://git-wip-us.apache.org/repos/asf/libcloud/blob/d8e19f6a/libcloud/common/dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/common/dimensiondata.py b/libcloud/common/dimensiondata.py index 84c7c9d..b7ee2d0 100644 --- a/libcloud/common/dimensiondata.py +++ b/libcloud/common/dimensiondata.py @@ -286,7 +286,7 @@ BAD_MESSAGE_XML_ELEMENTS = ( ) -def dd_object_to_id(obj, obj_type): +def dd_object_to_id(obj, obj_type, id_value='id'): """ Takes in a DD object or string and prints out it's id This is a helper method, as many of our functions can take either an object @@ -303,7 +303,7 @@ def dd_object_to_id(obj, obj_type): :rtype: ``str`` """ if isinstance(obj, obj_type): - return obj.id + return getattr(obj, id_value) elif isinstance(obj, (basestring)): return obj else: http://git-wip-us.apache.org/repos/asf/libcloud/blob/d8e19f6a/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client_BUSY.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client_BUSY.xml b/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client_BUSY.xml deleted file mode 100644 index 6c2db63..0000000 --- a/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client_BUSY.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<ns6:Status xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.opsource.net/schemas/vip"> - <ns6:operation>Disable Backup Client</ns6:operation> - <ns6:result>ERROR</ns6:result> - <ns6:resultDetail>DISABLE_BACKUP_CLIENT 'didata-backup-test6[172-16-1-14]' - failed - Unexpected error occurred with NA9 Backup system at 2016-02-12 00:03:50.952, TransactionId: (9d483a7a-1cc9-441b-920c-e11fb0e94ba6), PCSOperation: DeprovisionBackupClient, Backup Client is currently performing another operation: Backup client is currently busy</ns6:resultDetail> - <ns6:resultCode>REASON_547</ns6:resultCode> -</ns6:Status> http://git-wip-us.apache.org/repos/asf/libcloud/blob/d8e19f6a/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client_FAIL.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client_FAIL.xml b/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client_FAIL.xml new file mode 100644 index 0000000..6c2db63 --- /dev/null +++ b/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client_FAIL.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<ns6:Status xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.opsource.net/schemas/vip"> + <ns6:operation>Disable Backup Client</ns6:operation> + <ns6:result>ERROR</ns6:result> + <ns6:resultDetail>DISABLE_BACKUP_CLIENT 'didata-backup-test6[172-16-1-14]' - failed - Unexpected error occurred with NA9 Backup system at 2016-02-12 00:03:50.952, TransactionId: (9d483a7a-1cc9-441b-920c-e11fb0e94ba6), PCSOperation: DeprovisionBackupClient, Backup Client is currently performing another operation: Backup client is currently busy</ns6:resultDetail> + <ns6:resultCode>REASON_547</ns6:resultCode> +</ns6:Status> http://git-wip-us.apache.org/repos/asf/libcloud/blob/d8e19f6a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob.xml b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob.xml new file mode 100644 index 0000000..02ececc --- /dev/null +++ b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<ns6:Status xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.opsource.net/schemas/vip"> + <ns6:operation>Cancel Backup Job</ns6:operation> + <ns6:result>SUCCESS</ns6:result> + <ns6:resultDetail>Backup Job Canceled</ns6:resultDetail> + <ns6:resultCode>REASON_0</ns6:resultCode> +</ns6:Status> http://git-wip-us.apache.org/repos/asf/libcloud/blob/d8e19f6a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob_FAIL.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob_FAIL.xml b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob_FAIL.xml new file mode 100644 index 0000000..6dc29c3 --- /dev/null +++ b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob_FAIL.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<ns6:Status xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.opsource.net/schemas/vip"> + <ns6:operation>Cancel Backup Job</ns6:operation> + <ns6:result>ERROR</ns6:result> + <ns6:resultDetail>No backup job currently running on client</ns6:resultDetail> + <ns6:resultCode>REASON_547</ns6:resultCode> +</ns6:Status> http://git-wip-us.apache.org/repos/asf/libcloud/blob/d8e19f6a/libcloud/test/backup/test_dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/test/backup/test_dimensiondata.py b/libcloud/test/backup/test_dimensiondata.py index 62fa4e9..6a80fd4 100644 --- a/libcloud/test/backup/test_dimensiondata.py +++ b/libcloud/test/backup/test_dimensiondata.py @@ -23,6 +23,7 @@ from libcloud.utils.py3 import httplib from libcloud.common.dimensiondata import DimensionDataAPIException from libcloud.common.types import InvalidCredsError +from libcloud.backup.base import BackupTargetJob from libcloud.backup.drivers.dimensiondata import DimensionDataBackupDriver as DimensionData from libcloud.backup.drivers.dimensiondata import DEFAULT_BACKUP_PLAN @@ -127,10 +128,37 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin): client = response.clients[0] self.assertEqual(client.id, '30b1ff76-c76d-4d7c-b39d-3b72be0384c8') self.assertEqual(client.type.type, 'FA.Linux') - self.assertEqual(client.running_job.percentage, 5) + self.assertEqual(client.running_job.progress, 5) + self.assertTrue(isinstance(client.running_job, BackupTargetJob)) self.assertEqual(len(client.alert.notify_list), 2) self.assertTrue(isinstance(client.alert.notify_list, list)) + def test_ex_cancel_target_job(self): + target = self.driver.list_targets()[0] + response = self.driver.ex_get_backup_details_for_target(target) + client = response.clients[0] + self.assertTrue(isinstance(client.running_job, BackupTargetJob)) + success = client.running_job.cancel() + self.assertTrue(success) + + def test_ex_cancel_target_job_with_extras(self): + success = self.driver.cancel_target_job( + None, + ex_client='30b1ff76_c76d_4d7c_b39d_3b72be0384c8', + ex_target='e75ead52_692f_4314_8725_c8a4f4d13a87' + ) + self.assertTrue(success) + + def test_ex_cancel_target_job_FAIL(self): + DimensionDataMockHttp.type = 'FAIL' + with self.assertRaises(DimensionDataAPIException) as context: + self.driver.cancel_target_job( + None, + ex_client='30b1ff76_c76d_4d7c_b39d_3b72be0384c8', + ex_target='e75ead52_692f_4314_8725_c8a4f4d13a87' + ) + self.assertEqual(context.exception.code, 'ERROR') + """Test a backup info for a target that does not have a client""" def test_ex_get_backup_details_for_target_NO_CLIENT(self): DimensionDataMockHttp.type = 'NOCLIENT' @@ -196,8 +224,8 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin): ) ) - def test_ex_remove_client_from_target_BUSY(self): - DimensionDataMockHttp.type = 'BUSY' + def test_ex_remove_client_from_target_FAIL(self): + DimensionDataMockHttp.type = 'FAIL' with self.assertRaises(DimensionDataAPIException) as context: self.driver.ex_remove_client_from_target( 'e75ead52-692f-4314-8725-c8a4f4d13a87', @@ -269,7 +297,7 @@ class DimensionDataMockHttp(MockHttp): body = self.fixtures.load('oec_0_9_myaccount.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) - def _oec_0_9_myaccount_BUSY(self, method, url, body, headers): + def _oec_0_9_myaccount_FAIL(self, method, url, body, headers): body = self.fixtures.load('oec_0_9_myaccount.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) @@ -295,6 +323,21 @@ class DimensionDataMockHttp(MockHttp): 'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOCLIENT(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOJOB(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DISABLED(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server(self, method, url, body, headers): body = self.fixtures.load( 'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server.xml') @@ -414,16 +457,32 @@ class DimensionDataMockHttp(MockHttp): def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8( self, method, url, body, headers): - body = self.fixtures.load( - ('_remove_backup_client.xml') - ) + if url.endswith('disable'): + body = self.fixtures.load( + ('_remove_backup_client.xml') + ) + elif url.endswith('cancelJob'): + body = self.fixtures.load( + ('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87' + '_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob.xml') + ) + else: + raise ValueError("Unknown URL: %s" % url) return (httplib.OK, body, {}, httplib.responses[httplib.OK]) - def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_BUSY( + def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_FAIL( self, method, url, body, headers): - body = self.fixtures.load( - ('_remove_backup_client_BUSY.xml') - ) + if url.endswith('disable'): + body = self.fixtures.load( + ('_remove_backup_client_FAIL.xml') + ) + elif url.endswith('cancelJob'): + body = self.fixtures.load( + ('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87' + '_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob_FAIL.xml') + ) + else: + raise ValueError("Unknown URL: %s" % url) return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])