libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From anthonys...@apache.org
Subject libcloud git commit: Adding in ability to cancel a backup job Closes #739
Date Thu, 07 Apr 2016 01:50:04 GMT
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.dunham@gmail.com>
Authored: Wed Apr 6 17:41:29 2016 -0400
Committer: anthony-shaw <anthony.p.shaw@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])
 
 


Mime
View raw message