DimensionData: Backups adding more tests, moving classes to common

Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/67f7e06f
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/67f7e06f
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/67f7e06f

Branch: refs/heads/trunk
Commit: 67f7e06fad55660be8d67c377f18167cce516803
Parents: bb8ab66
Author: Jeffrey Dunham <jeffrey.a.dun...@gmail.com>
Authored: Tue Feb 9 18:05:34 2016 -0500
Committer: anthony-shaw <anthony.p.s...@gmail.com>
Committed: Wed Feb 10 12:08:06 2016 +1100

----------------------------------------------------------------------
 libcloud/backup/drivers/dimensiondata.py        | 118 ++++-------
 libcloud/common/dimensiondata.py                | 209 +++++++++++++++++++
 ..._692f_4314_8725_c8a4f4d13a87_backup_INFO.xml |  27 ++-
 ...4_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml |   7 +
 ...4_8725_c8a4f4d13a87_backup_INFO_NOCLIENT.xml |   2 +
 ...4314_8725_c8a4f4d13a87_backup_INFO_NOJOB.xml |  11 +
 libcloud/test/backup/test_dimensiondata.py      |  75 ++++++-
 7 files changed, 361 insertions(+), 88 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/67f7e06f/libcloud/backup/drivers/dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/backup/drivers/dimensiondata.py 
b/libcloud/backup/drivers/dimensiondata.py
index 45fb1d7..41cc9f7 100644
--- a/libcloud/backup/drivers/dimensiondata.py
+++ b/libcloud/backup/drivers/dimensiondata.py
@@ -22,69 +22,18 @@ from libcloud.backup.base import BackupDriver, BackupTarget
 from libcloud.backup.types import BackupTargetType
 from libcloud.backup.types import Provider
 from libcloud.common.dimensiondata import DimensionDataConnection
-from libcloud.common.dimensiondata import API_ENDPOINTS
-from libcloud.common.dimensiondata import DEFAULT_REGION
+from libcloud.common.dimensiondata import DimensionDataBackupClientType
+from libcloud.common.dimensiondata import DimensionDataBackupDetails
+from libcloud.common.dimensiondata import DimensionDataBackupClient
+from libcloud.common.dimensiondata import DimensionDataBackupClientAlert
+from libcloud.common.dimensiondata import DimensionDataBackupClientRunningJob
+from libcloud.common.dimensiondata import DimensionDataBackupStoragePolicy
+from libcloud.common.dimensiondata import DimensionDataBackupSchedulePolicy
+from libcloud.common.dimensiondata import API_ENDPOINTS, DEFAULT_REGION
 from libcloud.common.dimensiondata import TYPES_URN
-from libcloud.common.dimensiondata import GENERAL_NS
+from libcloud.common.dimensiondata import GENERAL_NS, BACKUP_NS
 from libcloud.utils.xml import fixxpath, findtext, findall
 
-BACKUP_NS = 'http://oec.api.opsource.net/schemas/backup'
-
-
-class DimensionDataBackupClientType(object):
-    def __init__(self, type, is_file_system, description):
-        self.type = type
-        self.is_file_system = is_file_system
-        self.description = description
-
-
-class DimensionDataBackupDetails(object):
-    def __init__(self, asset_id, service_plan, state, clients=[]):
-        self.asset_id = asset_id
-        self.service_plan = service_plan
-        self.state = state
-        self.clients = clients
-
-
-class DimensionDataBackupClient(object):
-    def __init__(self, type, is_file_system, status, description,
-                 schedule_pol, storage_pol, trigger=None, email=None,
-                 last_backup_time=None, next_backup=None, download_url=None,
-                 total_backup_size=None, running_job=None):
-        self.type = type
-        self.is_file_system = is_file_system
-        self.status = status
-        self.description = description
-        self.schedule_policy = schedule_pol
-        self.storage_policy = storage_pol
-        self.trigger = trigger
-        self.email = email
-        self.last_backup_time = last_backup_time
-        self.next_backup = next_backup
-        self.total_backup_size = total_backup_size
-        self.download_url = download_url
-        self.running_job = running_job
-
-
-class DimensionDataBackupRunningJob(object):
-    def __init__(self, id, status, percentage=0):
-        self.id = id
-        self.percentage = percentage
-        self.status = status
-
-
-class DimensionDataBackupStoragePolicy(object):
-    def __init__(self, name, retention_period, secondary_location):
-        self.name = name
-        self.retention_period = retention_period
-        self.secondary_location = secondary_location
-
-
-class DimensionDataBackupSchedulePolicy(object):
-    def __init__(self, name, description):
-        self.name = name
-        self.description = description
-
 
 class DimensionDataBackupDriver(BackupDriver):
     """
@@ -579,9 +528,12 @@ class DimensionDataBackupDriver(BackupDriver):
         return [self._to_client_type(el) for el in elements]
 
     def _to_client_type(self, element):
+        description = element.get('description')
+        if description is None:
+            description = findtext(element, 'description', BACKUP_NS)
         return DimensionDataBackupClientType(
             type=element.get('type'),
-            description=element.get('description'),
+            description=description,
             is_file_system=bool(element.get('isFileSystem') == 'true')
         )
 
@@ -599,26 +551,40 @@ class DimensionDataBackupDriver(BackupDriver):
         return [self._to_client(el) for el in elements]
 
     def _to_client(self, element):
-        job = element.find(fixxpath('runningJob', BACKUP_NS))
-        running_job = None
-        if job is not None:
-            running_job = DimensionDataBackupRunningJob(
-                id=job.get('id'),
-                status=job.get('status'),
-                percentage=int(job.get('percentageComplete'))
-            )
-
         return DimensionDataBackupClient(
-            type=element.get('type'),
-            is_file_system=bool(element.get('isFileSystem') == 'true'),
+            id=element.get('id'),
+            type=self._to_client_type(element, ),
             status=element.get('status'),
-            description=findtext(element, 'description', BACKUP_NS),
-            schedule_pol=findtext(element, 'schedulePolicyName', BACKUP_NS),
-            storage_pol=findtext(element, 'storagePolicyName', BACKUP_NS),
+            schedule_policy=findtext(element, 'schedulePolicyName', BACKUP_NS),
+            storage_policy=findtext(element, 'storagePolicyName', BACKUP_NS),
             download_url=findtext(element, 'downloadUrl', BACKUP_NS),
-            running_job=running_job
+            running_job=self._to_running_job(element),
+            alert=self._to_alert(element)
         )
 
+    def _to_alert(self, element):
+        alert = element.find(fixxpath('alerting', BACKUP_NS))
+        if alert is not None:
+            notify_list = [
+                email_addr.text for email_addr
+                in alert.findall(fixxpath('emailAddress', BACKUP_NS))
+            ]
+            return DimensionDataBackupClientAlert(
+                trigger=element.get('trigger'),
+                notify_list=notify_list
+            )
+        return None
+
+    def _to_running_job(self, element):
+        running_job = element.find(fixxpath('runningJob', BACKUP_NS))
+        if running_job is not None:
+            return DimensionDataBackupClientRunningJob(
+                id=running_job.get('id'),
+                status=running_job.get('status'),
+                percentage=int(running_job.get('percentageComplete'))
+            )
+        return None
+
     def _to_targets(self, object):
         node_elements = object.findall(fixxpath('server', TYPES_URN))
 

http://git-wip-us.apache.org/repos/asf/libcloud/blob/67f7e06f/libcloud/common/dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/common/dimensiondata.py b/libcloud/common/dimensiondata.py
index 841f790..5ecd89e 100644
--- a/libcloud/common/dimensiondata.py
+++ b/libcloud/common/dimensiondata.py
@@ -35,6 +35,7 @@ SERVER_NS = NAMESPACE_BASE + "/server"
 NETWORK_NS = NAMESPACE_BASE + "/network"
 DIRECTORY_NS = NAMESPACE_BASE + "/directory"
 GENERAL_NS = NAMESPACE_BASE + "/general"
+BACKUP_NS = NAMESPACE_BASE + "/backup"
 
 # API 2.0 Namespaces and URNs
 TYPES_URN = "urn:didata.com:api:cloud:types"
@@ -812,3 +813,211 @@ class DimensionDataVirtualListenerCompatibility(object):
         return (('<DimensionDataVirtualListenerCompatibility: '
                  'type=%s, protocol=%s>')
                 % (self.type, self.protocol))
+
+
+class DimensionDataBackupClientType(object):
+    """
+    A client type object for backups
+    """
+    def __init__(self, type, is_file_system, description):
+        """
+        Initialize an instance of :class:`DimensionDataBackupClientType`
+
+        :param type: The type of client i.e. (FA.Linux, MySQL, ect.)
+        :type  type: ``str``
+
+        :param is_file_system: The name of the iRule
+        :type  is_file_system: ``bool``
+
+        :param description: Description of the client
+        :type  description: ``str``
+        """
+        self.type = type
+        self.is_file_system = is_file_system
+        self.description = description
+
+    def __repr__(self):
+        return (('<DimensionDataBackupClientType: type=%s>')
+                % (self.type))
+
+
+class DimensionDataBackupDetails(object):
+    """
+    Dimension Data Backup Details represents information about
+    a targets backups configuration
+    """
+
+    def __init__(self, asset_id, service_plan, state, clients=None):
+        """
+        Initialize an instance of :class:`DimensionDataBackupDetails`
+
+        :param asset_id: Asset identification for backups
+        :type  asset_id: ``str``
+
+        :param service_plan: The service plan for backups. i.e (Essentials)
+        :type  service_plan: ``str``
+
+        :param state: The overall state this backup target. i.e. (unregistered)
+        :type  state: ``str``
+
+        :param clients: Backup clients attached to this target
+        :type  clients: ``list`` of :class:`DimensionDataBackupClient`
+        """
+        self.asset_id = asset_id
+        self.service_plan = service_plan
+        self.state = state
+        self.clients = clients
+
+    def __repr__(self):
+        return (('<DimensionDataBackupDetails: id=%s>')
+                % (self.asset_id))
+
+
+class DimensionDataBackupClient(object):
+    """
+    An object that represents a backup client
+    """
+    def __init__(self, id, type, status,
+                 schedule_policy, storage_policy, download_url,
+                 alert=None, running_job=None):
+        """
+        Initialize an instance of :class:`DimensionDataBackupClient`
+
+        :param id: Unique ID for the client
+        :type  id: ``str``
+
+        :param type: The type of client that this client is
+        :type  type: :class:`DimensionDataBackupClientType`
+
+        :param status: The states of this particular backup client.
+                       i.e. (Unregistered)
+        :type  status: ``str``
+
+        :param schedule_policy: The schedule policy for this client
+                                NOTE: Dimension Data only sends back the name
+                                of the schedule policy, no further details
+        :type  schedule_policy: ``str``
+
+        :param storage_policy: The storage policy for this client
+                               NOTE: Dimension Data only sends back the name
+                               of the storage policy, no further details
+        :type  storage_policy: ``str``
+
+        :param download_url: The download url for this client
+        :type  download_url: ``str``
+
+        :param alert: The alert configured for this backup client (optional)
+        :type  alert: :class:`DimensionDataBackupClientAlert`
+
+        :param alert: The running job for the client (optional)
+        :type  alert: :class:`DimensionDataBackupClientRunningJob`
+        """
+        self.id = id
+        self.type = type
+        self.status = status
+        self.schedule_policy = schedule_policy
+        self.storage_policy = storage_policy
+        self.download_url = download_url
+        self.alert = alert
+        self.running_job = running_job
+
+    def __repr__(self):
+        return (('<DimensionDataBackupClient: id=%s>')
+                % (self.asset_id))
+
+
+class DimensionDataBackupClientAlert(object):
+    """
+    An alert for a backup client
+    """
+    def __init__(self, trigger, notify_list=[]):
+        """
+        Initialize an instance of :class:`DimensionDataBackupClientAlert`
+
+        :param trigger: Trigger type for the client i.e. ON_FAILURE
+        :type  trigger: ``str``
+
+        :param notify_list: List of email addresses that are notified
+                            when the alert is fired
+        :type  notify_list: ``list`` of ``str``
+        """
+        self.trigger = trigger
+        self.notify_list = notify_list
+
+    def __repr__(self):
+        return (('<DimensionDataBackupClient: id=%s>')
+                % (self.asset_id))
+
+
+class DimensionDataBackupClientRunningJob(object):
+    """
+    A running job for a given backup client
+    """
+    def __init__(self, id, status, percentage=0):
+        """
+        Initialize an instance of :class:`DimensionDataBackupClientRunningJob`
+
+        :param id: The unqiue ID of the job
+        :type  id: ``str``
+
+        :param status: The status of the job i.e. Waiting
+        :type  status: ``str``
+
+        :param percentage: The percentage completion of the job
+        :type  percentage: ``int``
+        """
+        self.id = id
+        self.percentage = percentage
+        self.status = status
+
+    def __repr__(self):
+        return (('<DimensionDataBackupClientRunningJob: id=%s>')
+                % (self.id))
+
+
+class DimensionDataBackupStoragePolicy(object):
+    """
+    A representation of a storage policy
+    """
+    def __init__(self, name, retention_period, secondary_location):
+        """
+        Initialize an instance of :class:`DimensionDataBackupStoragePolicy`
+
+        :param name: The name of the storage policy i.e. 14 Day Storage Policy
+        :type  name: ``str``
+
+        :param retention_period: How long to keep the backup in days
+        :type  retention_period: ``int``
+
+        :param secondary_location: The secondary location i.e. Primary
+        :type  secondary_location: ``str``
+        """
+        self.name = name
+        self.retention_period = retention_period
+        self.secondary_location = secondary_location
+
+    def __repr__(self):
+        return (('<DimensionDataBackupClientStoragePolicy: name=%s>')
+                % (self.name))
+
+
+class DimensionDataBackupSchedulePolicy(object):
+    """
+    A representation of a schedule policy
+    """
+    def __init__(self, name, description):
+        """
+        Initialize an instance of :class:`DimensionDataBackupSchedulePolicy`
+
+        :param name: The name of the policy i.e 12AM - 6AM
+        :type  name: ``str``
+
+        :param description: Short summary of the details of the policy
+        :type  description: ``str``
+        """
+        self.name = name
+        self.description = description
+
+    def __repr__(self):
+        return (('<DimensionDataBackupClientSchedulePolicy: name=%s>')
+                % (self.name))

http://git-wip-us.apache.org/repos/asf/libcloud/blob/67f7e06f/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO.xml
----------------------------------------------------------------------
diff --git 
a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO.xml
 
b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO.xml
index 8b5cf55..fc61cce 100644
--- 
a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO.xml
+++ 
b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO.xml
@@ -1,11 +1,16 @@
-<ns5:BackupDetails assetId="fbf90c74-a6de-459e-942c-b75b5176ed72" 
servicePlan="Enterprise" state="NORMAL" 
xmlns:ns16="http://oec.api.opsource.net/schemas/serverbootstrap"; 
xmlns="http://oec.api.opsource.net/schemas/server"; 
xmlns:ns14="http://oec.api.opsource.net/schemas/datacenter"; 
xmlns:ns15="http://oec.api.opsource.net/schemas/reset"; 
xmlns:ns9="http://oec.api.opsource.net/schemas/storage"; 
xmlns:ns5="http://oec.api.opsource.net/schemas/backup"; 
xmlns:ns12="http://oec.api.opsource.net/schemas/whitelabel"; 
xmlns:ns13="http://oec.api.opsource.net/schemas/general"; 
xmlns:ns6="http://oec.api.opsource.net/schemas/support"; 
xmlns:ns7="http://oec.api.opsource.net/schemas/organization"; 
xmlns:ns10="http://oec.api.opsource.net/schemas/manualimport"; 
xmlns:ns8="http://oec.api.opsource.net/schemas/multigeo"; 
xmlns:ns11="http://oec.api.opsource.net/schemas/vip"; 
xmlns:ns2="http://oec.api.opsource.net/schemas/network"; 
xmlns:ns4="http://oec.api.opsource.net/schemas/directory"; 
xmlns:ns3="http://oec.api.ops
 ource.net/schemas/admin">
-    <ns5:backupClient id="5fe7502e-f8d7-4547-93b5-f54ac6e58281" 
type="FA.Linux" isFileSystem="true" status="Offline">
-        <ns5:description>Linux File Agent</ns5:description>
-        <ns5:schedulePolicyName>12AM - 6AM</ns5:schedulePolicyName>
-        <ns5:storagePolicyName>30 Day Storage Policy + Secondary 
Copy</ns5:storagePolicyName>
-        <ns5:times nextBackup="2016-02-09T00:00:00" 
lastOnline="2016-02-08T06:10:25"/>
-        <ns5:totalBackupSizeGb>0</ns5:totalBackupSizeGb>
-        
<ns5:downloadUrl>https://backups-na.cloud-vpn.net/PCS/BackupClientInstallerDownload/27645708a713cf406b47ccf73895cc8d27d4172c</ns5:downloadUrl>
-        <ns5:runningJob id="106130" name="Backup" percentageComplete="5" 
status="Waiting"/>
-    </ns5:backupClient>
-</ns5:BackupDetails>
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns9:BackupDetails assetId="12769311-938c-4669-9460-7723eb194269" 
servicePlan="Enterprise" state="NORMAL" 
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.o
 psource.net/schemas/vip">
+    <ns9:backupClient id="30b1ff76-c76d-4d7c-b39d-3b72be0384c8" 
type="FA.Linux" isFileSystem="true" status="Unregistered">
+        <ns9:description>Linux File Agent</ns9:description>
+        <ns9:schedulePolicyName>12AM - 6AM</ns9:schedulePolicyName>
+        <ns9:storagePolicyName>14 Day Storage Policy</ns9:storagePolicyName>
+        <ns9:alerting trigger="ON_FAILURE">
+            <ns9:emailAddress>fake_em...@example.com</ns9:emailAddress>
+            <ns9:emailAddress>fake_ema...@example.com</ns9:emailAddress>
+        </ns9:alerting>
+        <ns9:times nextBackup="2016-02-09T00:00:00" 
lastOnline="2016-02-08T06:10:25"/>
+        <ns9:totalBackupSizeGb>0</ns9:totalBackupSizeGb>
+        
<ns9:downloadUrl>https://backups-na.cloud-vpn.net/PCS/BackupClientInstallerDownload/cbb8a8c607ca4144e8828814edfc1634c8dd8782</ns9:downloadUrl>
+        <ns9:runningJob id="106130" name="Backup" percentageComplete="5" 
status="Waiting"/>
+    </ns9:backupClient>
+</ns9:BackupDetails>

http://git-wip-us.apache.org/repos/asf/libcloud/blob/67f7e06f/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml
----------------------------------------------------------------------
diff --git 
a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml
 
b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml
new file mode 100644
index 0000000..c3a9c37
--- /dev/null
+++ 
b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.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>Get Backup Details</ns6:operation>
+    <ns6:result>ERROR</ns6:result>
+    <ns6:resultDetail>Server e75ead52-692f-4314_8725-c8a4f4d13a87 has not been 
provisioned for backup</ns6:resultDetail>
+    <ns6:resultCode>REASON_543</ns6:resultCode>
+</ns6:Status>

http://git-wip-us.apache.org/repos/asf/libcloud/blob/67f7e06f/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOCLIENT.xml
----------------------------------------------------------------------
diff --git 
a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOCLIENT.xml
 
b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOCLIENT.xml
new file mode 100644
index 0000000..0f62576
--- /dev/null
+++ 
b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOCLIENT.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns9:BackupDetails assetId="71febbbc-337a-40c4-8c29-0afe85ccb3ea" 
servicePlan="Essentials" state="NORMAL" 
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.o
 psource.net/schemas/vip"/>

http://git-wip-us.apache.org/repos/asf/libcloud/blob/67f7e06f/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOJOB.xml
----------------------------------------------------------------------
diff --git 
a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOJOB.xml
 
b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOJOB.xml
new file mode 100644
index 0000000..45c97ce
--- /dev/null
+++ 
b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOJOB.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns9:BackupDetails assetId="12769311-938c-4669-9460-7723eb194269" 
servicePlan="Enterprise" state="NORMAL" 
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.o
 psource.net/schemas/vip">
+    <ns9:backupClient id="30b1ff76-c76d-4d7c-b39d-3b72be0384c8" 
type="FA.Linux" isFileSystem="true" status="Unregistered">
+        <ns9:description>Linux File Agent</ns9:description>
+        <ns9:schedulePolicyName>12AM - 6AM</ns9:schedulePolicyName>
+        <ns9:storagePolicyName>14 Day Storage Policy</ns9:storagePolicyName>
+        <ns9:times nextBackup="2016-02-09T00:00:00" 
lastOnline="2016-02-08T06:10:25"/>
+        <ns9:totalBackupSizeGb>0</ns9:totalBackupSizeGb>
+        
<ns9:downloadUrl>https://backups-na.cloud-vpn.net/PCS/BackupClientInstallerDownload/cbb8a8c607ca4144e8828814edfc1634c8dd8782</ns9:downloadUrl>
+    </ns9:backupClient>
+</ns9:BackupDetails>

http://git-wip-us.apache.org/repos/asf/libcloud/blob/67f7e06f/libcloud/test/backup/test_dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/test/backup/test_dimensiondata.py 
b/libcloud/test/backup/test_dimensiondata.py
index fe5a964..5ce91e3 100644
--- a/libcloud/test/backup/test_dimensiondata.py
+++ b/libcloud/test/backup/test_dimensiondata.py
@@ -100,8 +100,39 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin):
         response = self.driver.ex_get_backup_details_for_target(target)
         self.assertEqual(response.service_plan, 'Enterprise')
         client = response.clients[0]
-        self.assertEqual(client.type, 'FA.Linux')
+        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(len(client.alert.notify_list), 2)
+        self.assertTrue(isinstance(client.alert.notify_list, list))
+
+    """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'
+        response = 
self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314_8725-c8a4f4d13a87')
+        self.assertEqual(response.service_plan, 'Essentials')
+        self.assertEqual(len(response.clients), 0)
+
+    """Test a backup details that has a client, but no alerting or running 
jobs"""
+    def test_ex_get_backup_details_for_target_NO_JOB_OR_ALERT(self):
+        DimensionDataMockHttp.type = 'NOJOB'
+        response = 
self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314_8725-c8a4f4d13a87')
+        self.assertEqual(response.service_plan, 'Enterprise')
+        self.assertTrue(isinstance(response.clients, list))
+        self.assertEqual(len(response.clients), 1)
+        client = response.clients[0]
+        self.assertEqual(client.id, '30b1ff76-c76d-4d7c-b39d-3b72be0384c8')
+        self.assertEqual(client.type.type, 'FA.Linux')
+        self.assertIsNone(client.running_job)
+        self.assertIsNone(client.alert)
+
+    """Test getting backup info for a server that doesn't exist"""
+    def test_ex_get_backup_details_for_target_DISABLED(self):
+        DimensionDataMockHttp.type = 'DISABLED'
+        with self.assertRaises(DimensionDataAPIException) as context:
+            
self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314_8725-c8a4f4d13a87')
+        self.assertEqual(context.exception.code, 'ERROR')
+        self.assertEqual(context.exception.msg, 'Server 
e75ead52-692f-4314_8725-c8a4f4d13a87 has not been provisioned for backup')
 
     def test_ex_list_available_client_types(self):
         target = self.driver.list_targets()[0]
@@ -152,6 +183,18 @@ 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_NOCLIENT(self, method, url, body, headers):
+        body = self.fixtures.load('oec_0_9_myaccount.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _oec_0_9_myaccount_DISABLED(self, method, url, body, headers):
+        body = self.fixtures.load('oec_0_9_myaccount.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _oec_0_9_myaccount_NOJOB(self, method, url, body, headers):
+        body = self.fixtures.load('oec_0_9_myaccount.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')
@@ -183,6 +226,33 @@ class DimensionDataMockHttp(MockHttp):
         else:
             raise ValueError("Unknown Method {0}".format(method))
 
+    def 
_oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_NOCLIENT(
+            self, method, url, body, headers):
+        # only gets here are implemented
+        # If we get any other method something has gone wrong
+        assert(method == 'GET')
+        body = self.fixtures.load(
+            
'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOCLIENT.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def 
_oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_DISABLED(
+            self, method, url, body, headers):
+        # only gets here are implemented
+        # If we get any other method something has gone wrong
+        assert(method == 'GET')
+        body = self.fixtures.load(
+            
'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml')
+        return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
+
+    def 
_oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_NOJOB(
+            self, method, url, body, headers):
+        # only gets here are implemented
+        # If we get any other method something has gone wrong
+        assert(method == 'GET')
+        body = self.fixtures.load(
+            
'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOJOB.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
     def 
_oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup(
             self, method, url, body, headers):
         if method == 'POST':
@@ -203,6 +273,9 @@ class DimensionDataMockHttp(MockHttp):
 
     def 
_oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_EXISTS(
             self, method, url, body, headers):
+        # only POSTs are implemented
+        # If we get any other method something has gone wrong
+        assert(method == 'POST')
         body = self.fixtures.load(
             
'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_EXISTS.xml')
         return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])

Reply via email to