You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by to...@apache.org on 2015/08/30 14:32:55 UTC

[07/21] libcloud git commit: [LIBCLOUD-737] Started a load balancer driver for the Dimension Data VIP functionality.

[LIBCLOUD-737] Started a load balancer driver for the Dimension Data VIP functionality.


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

Branch: refs/heads/trunk
Commit: b90c56106930dedbe526ffb18fb34661c0fcb0de
Parents: fbd02da
Author: Anthony Shaw <an...@gmail.com>
Authored: Wed Aug 26 17:24:10 2015 +1000
Committer: Anthony Shaw <an...@gmail.com>
Committed: Wed Aug 26 17:24:10 2015 +1000

----------------------------------------------------------------------
 libcloud/common/dimensiondata.py                | 342 +++++++++++++++++++
 libcloud/compute/drivers/dimensiondata.py       | 308 +----------------
 libcloud/loadbalancer/drivers/dimensiondata.py  | 248 ++++++++++++++
 libcloud/loadbalancer/providers.py              |   3 +-
 libcloud/loadbalancer/types.py                  |   3 +-
 ..._9cbc_8dabe5a7d0e4_networkDomainVip_pool.xml |  37 ++
 ...8dabe5a7d0e4_networkDomainVip_poolMember.xml |  29 ++
 ...ber_3dd806a2_c2c8_4c0c_9a4f_5219ea9266c0.xml |  13 +
 ...ool_4d360b1f_bc2c_4ab7_9884_1f03ba2768f7.xml |  17 +
 ...5a7d0e4_networkDomainVip_virtualListener.xml |  51 +++
 ...ner_6115469d_a8bb_445b_bb23_d23b5283f2b9.xml |  44 +++
 .../dimensiondata/oec_0_9_myaccount.xml         |  26 ++
 .../test/loadbalancer/test_dimensiondata.py     | 160 +++++++++
 13 files changed, 981 insertions(+), 300 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/common/dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/common/dimensiondata.py b/libcloud/common/dimensiondata.py
new file mode 100644
index 0000000..f5cdf05
--- /dev/null
+++ b/libcloud/common/dimensiondata.py
@@ -0,0 +1,342 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Dimension Data Common Components
+"""
+from base64 import b64encode
+from libcloud.utils.py3 import httplib
+from libcloud.utils.py3 import b
+
+from libcloud.common.base import ConnectionUserAndKey, XmlResponse
+from libcloud.common.types import LibcloudError, InvalidCredsError
+from libcloud.utils.xml import findtext
+
+# Roadmap / TODO:
+#
+# 1.0 - Copied from OpSource API, named provider details.
+
+# setup a few variables to represent all of the DimensionData cloud namespaces
+NAMESPACE_BASE = "http://oec.api.opsource.net/schemas"
+ORGANIZATION_NS = NAMESPACE_BASE + "/organization"
+SERVER_NS = NAMESPACE_BASE + "/server"
+NETWORK_NS = NAMESPACE_BASE + "/network"
+DIRECTORY_NS = NAMESPACE_BASE + "/directory"
+
+# API 2.0 Namespaces and URNs
+TYPES_URN = "urn:didata.com:api:cloud:types"
+
+# API end-points
+API_ENDPOINTS = {
+    'dd-na': {
+        'name': 'North America (NA)',
+        'host': 'api-na.dimensiondata.com',
+        'vendor': 'DimensionData'
+    },
+    'dd-eu': {
+        'name': 'Europe (EU)',
+        'host': 'api-eu.dimensiondata.com',
+        'vendor': 'DimensionData'
+    },
+    'dd-au': {
+        'name': 'Australia (AU)',
+        'host': 'api-au.dimensiondata.com',
+        'vendor': 'DimensionData'
+    },
+    'dd-af': {
+        'name': 'Africa (AF)',
+        'host': 'api-af.dimensiondata.com',
+        'vendor': 'DimensionData'
+    },
+    'dd-ap': {
+        'name': 'Asia Pacific (AP)',
+        'host': 'api-na.dimensiondata.com',
+        'vendor': 'DimensionData'
+    },
+    'dd-latam': {
+        'name': 'South America (LATAM)',
+        'host': 'api-latam.dimensiondata.com',
+        'vendor': 'DimensionData'
+    },
+    'dd-canada': {
+        'name': 'Canada (CA)',
+        'host': 'api-canada.dimensiondata.com',
+        'vendor': 'DimensionData'
+    }
+}
+
+# Default API end-point for the base connection class.
+DEFAULT_REGION = 'dd-na'
+
+
+class DimensionDataResponse(XmlResponse):
+    def parse_error(self):
+        if self.status == httplib.UNAUTHORIZED:
+            raise InvalidCredsError(self.body)
+        elif self.status == httplib.FORBIDDEN:
+            raise InvalidCredsError(self.body)
+
+        body = self.parse_body()
+
+        # TODO: The path is not fixed as server.
+        if self.status == httplib.BAD_REQUEST:
+            code = findtext(body, 'responseCode', SERVER_NS)
+            message = findtext(body, 'message', SERVER_NS)
+            raise DimensionDataAPIException(code,
+                                            message,
+                                            driver=self.connection.driver)
+
+        return self.body
+
+
+class DimensionDataAPIException(LibcloudError):
+    def __init__(self, code, msg, driver):
+        self.code = code
+        self.msg = msg
+        self.driver = driver
+
+    def __str__(self):
+        return "%s: %s" % (self.code, self.msg)
+
+    def __repr__(self):
+        return ("<DimensionDataAPIException: code='%s', msg='%s'>" %
+                (self.code, self.msg))
+
+
+class DimensionDataConnection(ConnectionUserAndKey):
+    """
+    Connection class for the DimensionData driver
+    """
+
+    api_path_version_1 = '/oec'
+    api_path_version_2 = '/caas'
+    api_version_1 = '0.9'
+    api_version_2 = '2.0'
+
+    _orgId = None
+    responseCls = DimensionDataResponse
+
+    allow_insecure = False
+
+    def __init__(self, user_id, key, secure=True, host=None, port=None,
+                 url=None, timeout=None, proxy_url=None, **conn_kwargs):
+        super(DimensionDataConnection, self).__init__(
+            user_id=user_id,
+            key=key,
+            secure=secure,
+            host=host, port=port,
+            url=url, timeout=timeout,
+            proxy_url=proxy_url)
+
+        if conn_kwargs['region']:
+            self.host = conn_kwargs['region']['host']
+
+    def add_default_headers(self, headers):
+        headers['Authorization'] = \
+            ('Basic %s' % b64encode(b('%s:%s' % (self.user_id,
+                                                 self.key))).decode('utf-8'))
+        return headers
+
+    def request_api_1(self, action, params=None, data='',
+                      headers=None, method='GET'):
+        action = "%s/%s/%s" % (self.api_path_version_1,
+                               self.api_version_1, action)
+
+        return super(DimensionDataConnection, self).request(
+            action=action,
+            params=params, data=data,
+            method=method, headers=headers)
+
+    def request_api_2(self, path, action, params=None, data='',
+                      headers=None, method='GET'):
+        action = "%s/%s/%s/%s" % (self.api_path_version_2,
+                                  self.api_version_2, path, action)
+
+        return super(DimensionDataConnection, self).request(
+            action=action,
+            params=params, data=data,
+            method=method, headers=headers)
+
+    def request_with_orgId_api_1(self, action, params=None, data='',
+                                 headers=None, method='GET'):
+        action = "%s/%s" % (self.get_resource_path_api_1(), action)
+
+        return super(DimensionDataConnection, self).request(
+            action=action,
+            params=params, data=data,
+            method=method, headers=headers)
+
+    def request_with_orgId_api_2(self, action, params=None, data='',
+                                 headers=None, method='GET'):
+        action = "%s/%s" % (self.get_resource_path_api_2(), action)
+
+        return super(DimensionDataConnection, self).request(
+            action=action,
+            params=params, data=data,
+            method=method, headers=headers)
+
+    def get_resource_path_api_1(self):
+        """
+        This method returns a resource path which is necessary for referencing
+        resources that require a full path instead of just an ID, such as
+        networks, and customer snapshots.
+        """
+        return ("%s/%s/%s" % (self.api_path_version_1, self.api_version_1,
+                              self._get_orgId()))
+
+    def get_resource_path_api_2(self):
+        """
+        This method returns a resource path which is necessary for referencing
+        resources that require a full path instead of just an ID, such as
+        networks, and customer snapshots.
+        """
+        return ("%s/%s/%s" % (self.api_path_version_2, self.api_version_2,
+                              self._get_orgId()))
+
+    def _get_orgId(self):
+        """
+        Send the /myaccount API request to DimensionData cloud and parse the
+        'orgId' from the XML response object. We need the orgId to use most
+        of the other API functions
+        """
+        if self._orgId is None:
+            body = self.request_api_1('myaccount').object
+            self._orgId = findtext(body, 'orgId', DIRECTORY_NS)
+        return self._orgId
+
+
+class DimensionDataStatus(object):
+    """
+    DimensionData API pending operation status class
+        action, request_time, user_name, number_of_steps, update_time,
+        step.name, step.number, step.percent_complete, failure_reason,
+    """
+    def __init__(self, action=None, request_time=None, user_name=None,
+                 number_of_steps=None, update_time=None, step_name=None,
+                 step_number=None, step_percent_complete=None,
+                 failure_reason=None):
+        self.action = action
+        self.request_time = request_time
+        self.user_name = user_name
+        self.number_of_steps = number_of_steps
+        self.update_time = update_time
+        self.step_name = step_name
+        self.step_number = step_number
+        self.step_percent_complete = step_percent_complete
+        self.failure_reason = failure_reason
+
+    def __repr__(self):
+        return (('<DimensionDataStatus: action=%s, request_time=%s, '
+                 'user_name=%s, number_of_steps=%s, update_time=%s, '
+                 'step_name=%s, step_number=%s, '
+                 'step_percent_complete=%s, failure_reason=%s')
+                % (self.action, self.request_time, self.user_name,
+                   self.number_of_steps, self.update_time, self.step_name,
+                   self.step_number, self.step_percent_complete,
+                   self.failure_reason))
+
+
+class DimensionDataNetwork(object):
+    """
+    DimensionData network with location.
+    """
+
+    def __init__(self, id, name, description, location, private_net,
+                 multicast, status):
+        self.id = str(id)
+        self.name = name
+        self.description = description
+        self.location = location
+        self.private_net = private_net
+        self.multicast = multicast
+        self.status = status
+
+    def __repr__(self):
+        return (('<DimensionDataNetwork: id=%s, name=%s, description=%s, '
+                 'location=%s, private_net=%s, multicast=%s>')
+                % (self.id, self.name, self.description, self.location,
+                   self.private_net, self.multicast))
+
+
+class DimensionDataNetworkDomain(object):
+    """
+    DimensionData network domain with location.
+    """
+
+    def __init__(self, id, name, description, location, status):
+        self.id = str(id)
+        self.name = name
+        self.description = description
+        self.location = location
+        self.status = status
+
+    def __repr__(self):
+        return (('<DimensionDataNetworkDomain: id=%s, name=%s,'
+                 'description=%s, location=%s, status=%s>')
+                % (self.id, self.name, self.description, self.location,
+                   self.status))
+
+
+class DimensionDataVlan(object):
+    """
+    DimensionData VLAN.
+    """
+
+    def __init__(self, id, name, description, location, status):
+        self.id = str(id)
+        self.name = name
+        self.location = location
+        self.description = description
+        self.status = status
+
+    def __repr__(self):
+        return (('<DimensionDataNetworkDomain: id=%s, name=%s, '
+                 'description=%s, location=%s, status=%s>')
+                % (self.id, self.name, self.description,
+                   self.location, self.status))
+
+class DimensionDataPool(object):
+    """
+    DimensionData VIP Pool.
+    """
+
+    def __init__(self, id, name, description, status):
+        self.id = str(id)
+        self.name = name
+        self.description = description
+        self.status = status
+
+    def __repr__(self):
+        return (('<DimensionDataPool: id=%s, name=%s, '
+                 'description=%s, status=%s>')
+                % (self.id, self.name, self.description,
+                   self.status))
+    
+class DimensionDataPoolMember(object):
+    """
+    DimensionData VIP Pool Member.
+    """
+
+    def __init__(self, id, name, status, ip_address, port):
+        self.id = str(id)
+        self.name = name
+        self.status = status
+        self.ip_address = ip_address
+        self.port = port
+
+    def __repr__(self):
+        return (('<DimensionDataPool: id=%s, name=%s, '
+                 'ip_address=%s, status=%s, port=%s>')
+                % (self.id, self.name,
+                   self.ip_address, self.status, self.port))
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/compute/drivers/dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/dimensiondata.py b/libcloud/compute/drivers/dimensiondata.py
index 8f0f71d..dcfe4b5 100644
--- a/libcloud/compute/drivers/dimensiondata.py
+++ b/libcloud/compute/drivers/dimensiondata.py
@@ -21,309 +21,21 @@ try:
 except ImportError:
     from xml.etree import ElementTree as ET
 
-from base64 import b64encode
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-
 from libcloud.compute.base import NodeDriver, Node
 from libcloud.compute.base import NodeSize, NodeImage, NodeLocation
-from libcloud.common.types import LibcloudError, InvalidCredsError
-from libcloud.common.base import ConnectionUserAndKey, XmlResponse
+from libcloud.common.dimensiondata import (DimensionDataConnection,
+                                           DimensionDataStatus)
+from libcloud.common.dimensiondata import DimensionDataNetwork
+from libcloud.common.dimensiondata import DimensionDataNetworkDomain
+from libcloud.common.dimensiondata import DimensionDataVlan
+from libcloud.common.dimensiondata import API_ENDPOINTS
+from libcloud.common.dimensiondata import DEFAULT_REGION
+from libcloud.common.dimensiondata import TYPES_URN
+from libcloud.common.dimensiondata import SERVER_NS
+from libcloud.common.dimensiondata import NETWORK_NS
 from libcloud.utils.xml import fixxpath, findtext, findall
 from libcloud.compute.types import NodeState, Provider
 
-# Roadmap / TODO:
-#
-# 1.0 - Copied from OpSource API, named provider details.
-
-# setup a few variables to represent all of the DimensionData cloud namespaces
-NAMESPACE_BASE = "http://oec.api.opsource.net/schemas"
-ORGANIZATION_NS = NAMESPACE_BASE + "/organization"
-SERVER_NS = NAMESPACE_BASE + "/server"
-NETWORK_NS = NAMESPACE_BASE + "/network"
-DIRECTORY_NS = NAMESPACE_BASE + "/directory"
-RESET_NS = NAMESPACE_BASE + "/reset"
-VIP_NS = NAMESPACE_BASE + "/vip"
-IMAGEIMPORTEXPORT_NS = NAMESPACE_BASE + "/imageimportexport"
-DATACENTER_NS = NAMESPACE_BASE + "/datacenter"
-SUPPORT_NS = NAMESPACE_BASE + "/support"
-GENERAL_NS = NAMESPACE_BASE + "/general"
-IPPLAN_NS = NAMESPACE_BASE + "/ipplan"
-WHITELABEL_NS = NAMESPACE_BASE + "/whitelabel"
-
-# API 2.0 Namespaces and URNs
-TYPES_URN = "urn:didata.com:api:cloud:types"
-
-# API end-points
-API_ENDPOINTS = {
-    'dd-na': {
-        'name': 'North America (NA)',
-        'host': 'api-na.dimensiondata.com',
-        'vendor': 'DimensionData'
-    },
-    'dd-eu': {
-        'name': 'Europe (EU)',
-        'host': 'api-eu.dimensiondata.com',
-        'vendor': 'DimensionData'
-    },
-    'dd-au': {
-        'name': 'Australia (AU)',
-        'host': 'api-au.dimensiondata.com',
-        'vendor': 'DimensionData'
-    },
-    'dd-af': {
-        'name': 'Africa (AF)',
-        'host': 'api-af.dimensiondata.com',
-        'vendor': 'DimensionData'
-    },
-    'dd-ap': {
-        'name': 'Asia Pacific (AP)',
-        'host': 'api-na.dimensiondata.com',
-        'vendor': 'DimensionData'
-    },
-    'dd-latam': {
-        'name': 'South America (LATAM)',
-        'host': 'api-latam.dimensiondata.com',
-        'vendor': 'DimensionData'
-    },
-    'dd-canada': {
-        'name': 'Canada (CA)',
-        'host': 'api-canada.dimensiondata.com',
-        'vendor': 'DimensionData'
-    }
-}
-
-# Default API end-point for the base connection class.
-DEFAULT_REGION = 'dd-na'
-
-
-class DimensionDataResponse(XmlResponse):
-    def parse_error(self):
-        if self.status == httplib.UNAUTHORIZED:
-            raise InvalidCredsError(self.body)
-        elif self.status == httplib.FORBIDDEN:
-            raise InvalidCredsError(self.body)
-
-        body = self.parse_body()
-
-        # TODO: The path is not fixed as server.
-        if self.status == httplib.BAD_REQUEST:
-            code = findtext(body, 'responseCode', SERVER_NS)
-            message = findtext(body, 'message', SERVER_NS)
-            raise DimensionDataAPIException(code,
-                                            message,
-                                            driver=DimensionDataNodeDriver)
-
-        return self.body
-
-
-class DimensionDataAPIException(LibcloudError):
-    def __init__(self, code, msg, driver):
-        self.code = code
-        self.msg = msg
-        self.driver = driver
-
-    def __str__(self):
-        return "%s: %s" % (self.code, self.msg)
-
-    def __repr__(self):
-        return ("<DimensionDataAPIException: code='%s', msg='%s'>" %
-                (self.code, self.msg))
-
-
-class DimensionDataConnection(ConnectionUserAndKey):
-    """
-    Connection class for the DimensionData driver
-    """
-
-    api_path_version_1 = '/oec'
-    api_path_version_2 = '/caas'
-    api_version_1 = '0.9'
-    api_version_2 = '2.0'
-
-    _orgId = None
-    responseCls = DimensionDataResponse
-
-    allow_insecure = False
-
-    def __init__(self, user_id, key, secure=True, host=None, port=None,
-                 url=None, timeout=None, proxy_url=None, **conn_kwargs):
-        super(DimensionDataConnection, self).__init__(
-            user_id=user_id,
-            key=key,
-            secure=secure,
-            host=host, port=port,
-            url=url, timeout=timeout,
-            proxy_url=proxy_url)
-
-        if conn_kwargs['region']:
-            self.host = conn_kwargs['region']['host']
-
-    def add_default_headers(self, headers):
-        headers['Authorization'] = \
-            ('Basic %s' % b64encode(b('%s:%s' % (self.user_id,
-                                                 self.key))).decode('utf-8'))
-        return headers
-
-    def request_api_1(self, action, params=None, data='',
-                      headers=None, method='GET'):
-        action = "%s/%s/%s" % (self.api_path_version_1,
-                               self.api_version_1, action)
-
-        return super(DimensionDataConnection, self).request(
-            action=action,
-            params=params, data=data,
-            method=method, headers=headers)
-
-    def request_api_2(self, path, action, params=None, data='',
-                      headers=None, method='GET'):
-        action = "%s/%s/%s/%s" % (self.api_path_version_2,
-                                  self.api_version_2, path, action)
-
-        return super(DimensionDataConnection, self).request(
-            action=action,
-            params=params, data=data,
-            method=method, headers=headers)
-
-    def request_with_orgId_api_1(self, action, params=None, data='',
-                                 headers=None, method='GET'):
-        action = "%s/%s" % (self.get_resource_path_api_1(), action)
-
-        return super(DimensionDataConnection, self).request(
-            action=action,
-            params=params, data=data,
-            method=method, headers=headers)
-
-    def request_with_orgId_api_2(self, action, params=None, data='',
-                                 headers=None, method='GET'):
-        action = "%s/%s" % (self.get_resource_path_api_2(), action)
-
-        return super(DimensionDataConnection, self).request(
-            action=action,
-            params=params, data=data,
-            method=method, headers=headers)
-
-    def get_resource_path_api_1(self):
-        """
-        This method returns a resource path which is necessary for referencing
-        resources that require a full path instead of just an ID, such as
-        networks, and customer snapshots.
-        """
-        return ("%s/%s/%s" % (self.api_path_version_1, self.api_version_1,
-                              self._get_orgId()))
-
-    def get_resource_path_api_2(self):
-        """
-        This method returns a resource path which is necessary for referencing
-        resources that require a full path instead of just an ID, such as
-        networks, and customer snapshots.
-        """
-        return ("%s/%s/%s" % (self.api_path_version_2, self.api_version_2,
-                              self._get_orgId()))
-
-    def _get_orgId(self):
-        """
-        Send the /myaccount API request to DimensionData cloud and parse the
-        'orgId' from the XML response object. We need the orgId to use most
-        of the other API functions
-        """
-        if self._orgId is None:
-            body = self.request_api_1('myaccount').object
-            self._orgId = findtext(body, 'orgId', DIRECTORY_NS)
-        return self._orgId
-
-
-class DimensionDataStatus(object):
-    """
-    DimensionData API pending operation status class
-        action, request_time, user_name, number_of_steps, update_time,
-        step.name, step.number, step.percent_complete, failure_reason,
-    """
-    def __init__(self, action=None, request_time=None, user_name=None,
-                 number_of_steps=None, update_time=None, step_name=None,
-                 step_number=None, step_percent_complete=None,
-                 failure_reason=None):
-        self.action = action
-        self.request_time = request_time
-        self.user_name = user_name
-        self.number_of_steps = number_of_steps
-        self.update_time = update_time
-        self.step_name = step_name
-        self.step_number = step_number
-        self.step_percent_complete = step_percent_complete
-        self.failure_reason = failure_reason
-
-    def __repr__(self):
-        return (('<DimensionDataStatus: action=%s, request_time=%s, '
-                 'user_name=%s, number_of_steps=%s, update_time=%s, '
-                 'step_name=%s, step_number=%s, '
-                 'step_percent_complete=%s, failure_reason=%s')
-                % (self.action, self.request_time, self.user_name,
-                   self.number_of_steps, self.update_time, self.step_name,
-                   self.step_number, self.step_percent_complete,
-                   self.failure_reason))
-
-
-class DimensionDataNetwork(object):
-    """
-    DimensionData network with location.
-    """
-
-    def __init__(self, id, name, description, location, private_net,
-                 multicast, status):
-        self.id = str(id)
-        self.name = name
-        self.description = description
-        self.location = location
-        self.private_net = private_net
-        self.multicast = multicast
-        self.status = status
-
-    def __repr__(self):
-        return (('<DimensionDataNetwork: id=%s, name=%s, description=%s, '
-                 'location=%s, private_net=%s, multicast=%s>')
-                % (self.id, self.name, self.description, self.location,
-                   self.private_net, self.multicast))
-
-
-class DimensionDataNetworkDomain(object):
-    """
-    DimensionData network domain with location.
-    """
-
-    def __init__(self, id, name, description, location, status):
-        self.id = str(id)
-        self.name = name
-        self.description = description
-        self.location = location
-        self.status = status
-
-    def __repr__(self):
-        return (('<DimensionDataNetworkDomain: id=%s, name=%s,'
-                 'description=%s, location=%s, status=%s>')
-                % (self.id, self.name, self.description, self.location,
-                   self.status))
-
-
-class DimensionDataVlan(object):
-    """
-    DimensionData VLAN.
-    """
-
-    def __init__(self, id, name, description, location, status):
-        self.id = str(id)
-        self.name = name
-        self.location = location
-        self.description = description
-        self.status = status
-
-    def __repr__(self):
-        return (('<DimensionDataNetworkDomain: id=%s, name=%s, '
-                 'description=%s, location=%s, status=%s>')
-                % (self.id, self.name, self.description,
-                   self.location, self.status))
-
 
 class DimensionDataNodeDriver(NodeDriver):
     """

http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/loadbalancer/drivers/dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/loadbalancer/drivers/dimensiondata.py b/libcloud/loadbalancer/drivers/dimensiondata.py
new file mode 100644
index 0000000..d294a9a
--- /dev/null
+++ b/libcloud/loadbalancer/drivers/dimensiondata.py
@@ -0,0 +1,248 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance withv
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from libcloud.common.dimensiondata import DimensionDataConnection
+from libcloud.common.dimensiondata import DimensionDataPool
+from libcloud.common.dimensiondata import DimensionDataPoolMember
+from libcloud.common.dimensiondata import API_ENDPOINTS
+from libcloud.common.dimensiondata import DEFAULT_REGION
+from libcloud.common.dimensiondata import TYPES_URN
+from libcloud.common.dimensiondata import SERVER_NS
+from libcloud.utils.misc import find, reverse_dict
+from libcloud.utils.xml import fixxpath, findtext, findall
+from libcloud.loadbalancer.types import State
+from libcloud.loadbalancer.base import Algorithm, Driver, LoadBalancer
+from libcloud.loadbalancer.base import DEFAULT_ALGORITHM, Member
+from libcloud.loadbalancer.types import Provider
+
+class DimensionDataLBDriver(Driver):
+    """
+    DimensionData node driver.
+    """
+
+    selected_region = None
+    connectionCls = DimensionDataConnection
+    name = 'Dimension Data Load Balancer'
+    website = 'https://cloud.dimensiondata.com/'
+    type = Provider.DIMENSIONDATA
+    api_version = 1.0
+
+    _VALUE_TO_ALGORITHM_MAP = {
+        'ROUND_ROBIN': Algorithm.ROUND_ROBIN,
+        'LEAST_CONNECTIONS': Algorithm.LEAST_CONNECTIONS,
+        'SHORTEST_RESPONSE': Algorithm.SHORTEST_RESPONSE,
+        'PERSISTENT_IP': Algorithm.PERSISTENT_IP
+    }
+    _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)
+
+    _VALUE_TO_STATE_MAP = {
+        'NORMAL': State.RUNNING,
+        'PENDING_ADD': State.PENDING,
+        'PENDING_CHANGE': State.PENDING,
+        'PENDING_DELETE': State.PENDING,
+        'FAILED_ADD': State.ERROR,
+        'FAILED_CHANGE': State.ERROR,
+        'FAILED_DELETE': State.ERROR,
+        'REQUIRES_SUPPORT': State.ERROR
+    }
+
+    def __init__(self, key, secret=None, secure=True, host=None, port=None,
+                 api_version=None, region=DEFAULT_REGION, **kwargs):
+
+        if region not in API_ENDPOINTS:
+            raise ValueError('Invalid region: %s' % (region))
+
+        self.selected_region = API_ENDPOINTS[region]
+
+        super(DimensionDataLBDriver, self).__init__(key=key, secret=secret,
+                                                      secure=secure, host=host,
+                                                      port=port,
+                                                      api_version=api_version,
+                                                      region=region,
+                                                      **kwargs)
+
+    def _ex_connection_class_kwargs(self):
+        """
+            Add the region to the kwargs before the connection is instantiated
+        """
+
+        kwargs = super(DimensionDataLBDriver,
+                       self)._ex_connection_class_kwargs()
+        kwargs['region'] = self.selected_region
+        return kwargs
+
+    def list_balancers(self):
+        """
+        List all loadbalancers inside a gepgraphy.
+        
+        In Dimension Data terminology these are known as virtual listeners
+
+        :rtype: ``list`` of :class:`LoadBalancer`
+        """
+        
+        return self._to_balancers(
+            self.connection
+            .request_with_orgId_api_2('networkDomainVip/virtualListener').object)
+
+    def get_balancer(self, balancer_id):
+        """
+        Return a :class:`LoadBalancer` object.
+
+        :param balancer_id: id of a load balancer you want to fetch
+        :type  balancer_id: ``str``
+
+        :rtype: :class:`LoadBalancer`
+        """
+        
+        bal = self.connection \
+            .request_with_orgId_api_2('networkDomainVip/virtualListener/%s'
+                                      % balancer_id).object
+        return self._to_balancer(bal)
+
+    def list_protocols(self):
+        """
+        Return a list of supported protocols.
+
+        Since all protocols are support by Dimension Data, this is a list
+        of common protocols. 
+
+        :rtype: ``list`` of ``str``
+        """
+        return ['dns', 'ftp', 'http', 'https', 'tcp', 'udp']
+
+    def balancer_list_members(self, balancer):
+        """
+        Return list of members attached to balancer.
+        
+        In Dimension Data terminology these are the members of the pools
+        within a virtual listener.
+
+        :param balancer: LoadBalancer which should be used
+        :type  balancer: :class:`LoadBalancer`
+
+        :rtype: ``list`` of :class:`Member`
+        """
+        
+        pool_members = self.ex_get_pool_members(balancer.extra['pool_id'])
+        members = []
+        for pool_member in pool_members:
+            members.append(Member(
+                id=pool_member.id,
+                ip=pool_member.ip_address,
+                port=pool_member.port,
+                balancer=balancer,
+                extra=None
+            ))
+        return members
+
+    def balancer_attach_member(self, balancer, member):
+        return True
+
+    def balancer_detach_member(self, balancer, member):
+        return True
+
+    def destroy_balancer(self, balancer):
+        return True
+
+    def ex_get_pools(self):
+        pools = self.connection \
+            .request_with_orgId_api_2('networkDomainVip/pool').object
+        return self._to_pools(pools)
+
+    def ex_get_pool(self, pool_id):
+        pool = self.connection \
+            .request_with_orgId_api_2('networkDomainVip/pool/%s'
+                                      % pool_id).object
+        return self._to_pool(pool)
+    
+    def ex_get_pool_members(self, pool_id):
+        members = self.connection \
+            .request_with_orgId_api_2('networkDomainVip/poolMember?poolId=%s'
+                                      % pool_id).object
+        return self._to_members(members)
+    
+    def ex_get_pool_member(self, pool_member_id):
+        member = self.connection \
+            .request_with_orgId_api_2('networkDomainVip/poolMember/%s'
+                                      % pool_member_id).object
+        return self._to_member(member)
+
+    def _to_balancers(self, object ):
+        loadbalancers = []
+        for element in object.findall(fixxpath("virtualListener", TYPES_URN)):
+            loadbalancers.append(self._to_balancer(element))
+
+        return loadbalancers
+
+    def _to_balancer(self, element):
+        ipaddress = findtext(element, 'listenerIpAddress', TYPES_URN)
+        name = findtext(element, 'name', TYPES_URN)
+        port = findtext(element, 'port', TYPES_URN)
+        extra = {}
+        
+        extra['pool_id'] = element.find(fixxpath(
+                'pool',
+                TYPES_URN)).get('id')
+        
+        balancer = LoadBalancer(
+            id=element.get('id'),
+            name=name,
+            state=self._VALUE_TO_STATE_MAP.get(
+                              findtext(element, 'state', TYPES_URN),
+                              State.UNKNOWN),
+            ip=ipaddress,
+            port=port,
+            driver=self.connection.driver,
+            extra=extra
+        )
+
+        return balancer
+    
+    def _to_members(self, object ):
+        members = []
+        for element in object.findall(fixxpath("poolMember", TYPES_URN)):
+            members.append(self._to_member(element))
+
+        return members
+    
+    def _to_member(self, element):
+        pool = DimensionDataPoolMember(
+            id=element.get('id'),
+            name=element.find(fixxpath(
+                'node',
+                TYPES_URN)).get('name'),
+            status=findtext(element, 'state', TYPES_URN),
+            ip_address=element.find(fixxpath(
+                'node',
+                TYPES_URN)).get('ipAddress'),
+            port=int(findtext(element, 'port', TYPES_URN))
+        )
+        return pool
+    
+    def _to_pools(self, object ):
+        pools = []
+        for element in object.findall(fixxpath("pool", TYPES_URN)):
+            pools.append(self._to_pool(element))
+
+        return pools
+    
+    def _to_pool(self, element):
+        pool = DimensionDataPool(
+            id=element.get('id'),
+            name=findtext(element, 'name', TYPES_URN),
+            status=findtext(element,'state', TYPES_URN),
+            description=findtext(element, 'description', TYPES_URN)
+        )
+        return pool

http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/loadbalancer/providers.py
----------------------------------------------------------------------
diff --git a/libcloud/loadbalancer/providers.py b/libcloud/loadbalancer/providers.py
index 5a65402..a4ff090 100644
--- a/libcloud/loadbalancer/providers.py
+++ b/libcloud/loadbalancer/providers.py
@@ -40,7 +40,8 @@ DRIVERS = {
     ('libcloud.loadbalancer.drivers.gce', 'GCELBDriver'),
     Provider.SOFTLAYER:
     ('libcloud.loadbalancer.drivers.softlayer', 'SoftlayerLBDriver'),
-
+    Provider.DIMENSIONDATA:
+    ('libcloud.loadbalancer.drivers.dimensiondata', 'DimensionDataLBDriver'),
     # Deprecated
     Provider.RACKSPACE_US:
     ('libcloud.loadbalancer.drivers.rackspace', 'RackspaceLBDriver'),

http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/loadbalancer/types.py
----------------------------------------------------------------------
diff --git a/libcloud/loadbalancer/types.py b/libcloud/loadbalancer/types.py
index 8ae82e9..9a34293 100644
--- a/libcloud/loadbalancer/types.py
+++ b/libcloud/loadbalancer/types.py
@@ -40,7 +40,8 @@ class Provider(object):
     CLOUDSTACK = 'cloudstack'
     GCE = 'gce'
     SOFTLAYER = 'softlayer'
-
+    DIMENSIONDATA = 'dimensiondata'
+    
     # Deprecated
     RACKSPACE_US = 'rackspace_us'
     RACKSPACE_UK = 'rackspace_uk'

http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool.xml b/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool.xml
new file mode 100644
index 0000000..239fe52
--- /dev/null
+++ b/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<pools
+xmlns="urn:didata.com:api:cloud:types" pageNumber="1" pageCount="2"
+totalCount="2" pageSize="250">
+<pool id="4d360b1f-bc2c-4ab7-9884-1f03ba2768f7" datacenterId="NA9">
+<networkDomainId>553f26b6-2a73-42c3-a78b-
+6116f11291d0</networkDomainId>
+<name>myDevelopmentPool.1</name>
+<description>Pool for load balancing development application
+servers.</description>
+<loadBalanceMethod>ROUND_ROBIN</loadBalanceMethod>
+<healthMonitor id="01683574-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Http"/>
+<healthMonitor id="0168546c-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Https"/>
+<serviceDownAction>RESELECT</serviceDownAction>
+<slowRampTime>10</slowRampTime>
+<state>NORMAL</state>
+<createTime>2015-06-04T09:15:07.000Z</createTime>
+</pool>
+<pool id="afb1fb1a-eab9-43f4-95c2-36a4cdda6cb8" datacenterId="NA9">
+<networkDomainId>553f26b6-2a73-42c3-a78b-
+6116f11291d0</networkDomainId>
+<name>myProductionPool.1</name>
+<description>Pool for load balancing production application
+servers.</description>
+<loadBalanceMethod>ROUND_ROBIN</loadBalanceMethod>
+<healthMonitor id="01683574-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Http"/>
+<healthMonitor id="0168546c-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Https"/>
+<serviceDownAction>NONE</serviceDownAction>
+<slowRampTime>10</slowRampTime>
+<state>NORMAL</state>
+<createTime>2015-06-03T14:11:17.000Z</createTime>
+</pool>
+</pools>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember.xml b/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember.xml
new file mode 100644
index 0000000..b36f75e
--- /dev/null
+++ b/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<poolMembers
+xmlns="urn:didata.com:api:cloud:types" pageNumber="1" pageCount="2"
+totalCount="2" pageSize="250">
+<poolMember id="3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0" datacenterId="NA9">
+<networkDomainId>553f26b6-2a73-42c3-a78b-
+6116f11291d0</networkDomainId>
+<pool id="4d360b1f-bc2c-4ab7-9884-1f03ba2768f7"
+name="myDevelopmentPool.1"/>
+<node id="3c207269-e75e-11e4-811f-005056806999" name="10.0.3.13"
+ipAddress="10.0.3.13" status="ENABLED"/>
+<port>9889</port>
+<status>ENABLED</status>
+<state>NORMAL</state>
+<createTime>2015-06-09T11:02:50.000Z</createTime>
+</poolMember>
+<poolMember id="b977578b-a827-4172-b285-030c3ba15daa" datacenterId="NA9">
+<networkDomainId>553f26b6-2a73-42c3-a78b-
+6116f11291d0</networkDomainId>
+<pool id="4d360b1f-bc2c-4ab7-9884-1f03ba2768f7"
+name="myDevelopmentPool.1"/>
+<node id="3c207269-e75e-11e4-811f-005056806999" name="10.0.3.13"
+ipAddress="10.0.3.13" status="ENABLED"/>
+<port>9888</port>
+<status>ENABLED</status>
+<state>NORMAL</state>
+<createTime>2015-06-09T10:43:29.000Z</createTime>
+</poolMember>
+</poolMembers>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember_3dd806a2_c2c8_4c0c_9a4f_5219ea9266c0.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember_3dd806a2_c2c8_4c0c_9a4f_5219ea9266c0.xml b/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember_3dd806a2_c2c8_4c0c_9a4f_5219ea9266c0.xml
new file mode 100644
index 0000000..7c86d4a
--- /dev/null
+++ b/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember_3dd806a2_c2c8_4c0c_9a4f_5219ea9266c0.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<poolMember
+xmlns="urn:didata.com:api:cloud:types" id="3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0" datacenterId="NA9">
+<networkDomainId>553f26b6-2a73-42c3-a78b-6116f11291d0</networkDomainId>
+<pool id="6f2f5d7b-cdd9-4d84-8ad7-999b64a87978"
+name="myDevelopmentPool.1"/>
+<node id="3c207269-e75e-11e4-811f-005056806999" name="10.0.3.13"
+ipAddress="10.0.3.13" status="ENABLED"/>
+<port>9889</port>
+<status>ENABLED</status>
+<state>NORMAL</state>
+<createTime>2015-06-09T11:02:50.000Z</createTime>
+</poolMember>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool_4d360b1f_bc2c_4ab7_9884_1f03ba2768f7.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool_4d360b1f_bc2c_4ab7_9884_1f03ba2768f7.xml b/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool_4d360b1f_bc2c_4ab7_9884_1f03ba2768f7.xml
new file mode 100644
index 0000000..89d3400
--- /dev/null
+++ b/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool_4d360b1f_bc2c_4ab7_9884_1f03ba2768f7.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<pool
+xmlns="urn:didata.com:api:cloud:types" id="4d360b1f-bc2c-4ab7-9884-1f03ba2768f7" datacenterId="NA9">
+<networkDomainId>553f26b6-2a73-42c3-a78b-6116f11291d0</networkDomainId>
+<name>myDevelopmentPool.1</name>
+<description>Pool for load balancing development application
+servers.</description>
+<loadBalanceMethod>ROUND_ROBIN</loadBalanceMethod>
+<healthMonitor id="01683574-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Http"/>
+<healthMonitor id="0168546c-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Https"/>
+<serviceDownAction>RESELECT</serviceDownAction>
+<slowRampTime>10</slowRampTime>
+<state>NORMAL</state>
+<createTime>2015-06-04T09:15:07.000Z</createTime>
+</pool>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener.xml b/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener.xml
new file mode 100644
index 0000000..88a94fa
--- /dev/null
+++ b/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<virtualListeners
+xmlns="urn:didata.com:api:cloud:types" pageNumber="1" pageCount="5"
+totalCount="5" pageSize="250">
+<virtualListener id="6115469d-a8bb-445b-bb23-d23b5283f2b9"
+datacenterId="NA9">
+<networkDomainId>553f26b6-2a73-42c3-a78b-
+6116f11291d0</networkDomainId>
+<name>myProduction.Virtual.Listener</name>
+<state>NORMAL</state>
+<description>Virtual Listener for load balancing our test
+systems.</description>
+<createTime>2015-05-28T15:59:49.000Z</createTime>
+<type>PERFORMANCE_LAYER_4</type>
+<protocol>HTTP</protocol>
+<listenerIpAddress>165.180.12.22</listenerIpAddress>
+<port>8899</port>
+<enabled>true</enabled>
+<connectionLimit>10000</connectionLimit>
+<connectionRateLimit>400</connectionRateLimit>
+<sourcePortPreservation>PRESERVE</sourcePortPreservation>
+<pool id="afb1fb1a-eab9-43f4-95c2-36a4cdda6cb8"
+name="myProductionPool.1">
+<loadBalanceMethod>ROUND_ROBIN</loadBalanceMethod>
+<serviceDownAction>NONE</serviceDownAction>
+<slowRampTime>10</slowRampTime>
+<healthMonitor id="01683574-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Http"/>
+<healthMonitor id="0168546c-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Https"/>
+</pool>
+<clientClonePool id="6f2f5d7b-cdd9-4d84-8ad7-999b64a87978"
+name="myDevelopmentPool.1">
+<loadBalanceMethod>ROUND_ROBIN</loadBalanceMethod>
+<serviceDownAction>RESELECT</serviceDownAction>
+<slowRampTime>10</slowRampTime>
+<healthMonitor id="01683574-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Http"/>
+<healthMonitor id="0168546c-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Https"/>
+</clientClonePool>
+<persistenceProfile id="a34ca25c-f3db-11e4-b010-005056806999"
+name="CCDEFAULT.DestinationAddress"/>
+<fallbackPersistenceProfile id="a34ca3f6-f3db-11e4-b010-005056806999"
+name="CCDEFAULT.SourceAddress"/>
+<irule id="2b20abd9-ffdc-11e4-b010-005056806999"
+name="CCDEFAULT.IpProtocolTimers"/>
+<irule id="2b20e790-ffdc-11e4-b010-005056806999"
+name="CCDEFAULT.Ips"/>
+</virtualListener>
+</virtualListeners>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener_6115469d_a8bb_445b_bb23_d23b5283f2b9.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener_6115469d_a8bb_445b_bb23_d23b5283f2b9.xml b/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener_6115469d_a8bb_445b_bb23_d23b5283f2b9.xml
new file mode 100644
index 0000000..aea2f6f
--- /dev/null
+++ b/libcloud/test/loadbalancer/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener_6115469d_a8bb_445b_bb23_d23b5283f2b9.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<virtualListener
+xmlns="urn:didata.com:api:cloud:types" id="6115469d-a8bb-445b-bb23-d23b5283f2b9" datacenterId="NA9">
+<networkDomainId>553f26b6-2a73-42c3-a78b-6116f11291d0</networkDomainId>
+<name>myProduction.Virtual.Listener</name>
+<state>NORMAL</state>
+<description>Virtual Listener for load balancing our test
+systems.</description>
+<createTime>2015-05-28T15:59:49.000Z</createTime>
+<type>PERFORMANCE_LAYER_4</type>
+<protocol>HTTP</protocol>
+<listenerIpAddress>165.180.12.22</listenerIpAddress>
+<port>8899</port>
+<enabled>true</enabled>
+<connectionLimit>10000</connectionLimit>
+<connectionRateLimit>400</connectionRateLimit>
+<sourcePortPreservation>PRESERVE</sourcePortPreservation>
+<pool id="afb1fb1a-eab9-43f4-95c2-36a4cdda6cb8" name="myProductionPool.1">
+<loadBalanceMethod>ROUND_ROBIN</loadBalanceMethod>
+<serviceDownAction>NONE</serviceDownAction>
+<slowRampTime>10</slowRampTime>
+<healthMonitor id="01683574-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Http"/>
+<healthMonitor id="0168546c-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Https"/>
+</pool>
+<clientClonePool id="6f2f5d7b-cdd9-4d84-8ad7-999b64a87978"
+name="myDevelopmentPool.1">
+<loadBalanceMethod>ROUND_ROBIN</loadBalanceMethod>
+<serviceDownAction>RESELECT</serviceDownAction>
+<slowRampTime>10</slowRampTime>
+<healthMonitor id="01683574-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Http"/>
+<healthMonitor id="0168546c-d487-11e4-811f-005056806999"
+name="CCDEFAULT.Https"/>
+</clientClonePool>
+<persistenceProfile id="a34ca25c-f3db-11e4-b010-005056806999"
+name="CCDEFAULT.DestinationAddress"/>
+<fallbackPersistenceProfile id="a34ca3f6-f3db-11e4-b010-005056806999"
+name="CCDEFAULT.SourceAddress"/>
+<irule id="2b20abd9-ffdc-11e4-b010-005056806999"
+name="CCDEFAULT.IpProtocolTimers"/>
+<irule id="2b20e790-ffdc-11e4-b010-005056806999" name="CCDEFAULT.Ips"/>
+</virtualListener>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/test/loadbalancer/fixtures/dimensiondata/oec_0_9_myaccount.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/loadbalancer/fixtures/dimensiondata/oec_0_9_myaccount.xml b/libcloud/test/loadbalancer/fixtures/dimensiondata/oec_0_9_myaccount.xml
new file mode 100644
index 0000000..4f3b132
--- /dev/null
+++ b/libcloud/test/loadbalancer/fixtures/dimensiondata/oec_0_9_myaccount.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns3:Account xmlns="http://oec.api.opsource.net/schemas/server" xmlns:ns9="http://oec.api.opsource.net/schemas/reset" xmlns:ns5="http://oec.api.opsource.net/schemas/vip" xmlns:ns12="http://oec.api.opsource.net/schemas/general" xmlns:ns6="http://oec.api.opsource.net/schemas/imageimportexport" xmlns:ns13="http://oec.api.opsource.net/schemas/support" xmlns:ns7="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns10="http://oec.api.opsource.net/schemas/ipplan" xmlns:ns8="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns11="http://oec.api.opsource.net/schemas/storage" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/network" xmlns:ns3="http://oec.api.opsource.net/schemas/directory">
+    <ns3:userName>testuser</ns3:userName>
+    <ns3:fullName>Test User</ns3:fullName>
+    <ns3:firstName>Test</ns3:firstName>
+    <ns3:lastName>User</ns3:lastName>
+    <ns3:emailAddress>test@example.com</ns3:emailAddress>
+    <ns3:orgId>8a8f6abc-2745-4d8a-9cbc-8dabe5a7d0e4</ns3:orgId>
+    <ns3:roles>
+        <ns3:role>
+            <ns3:name>create image</ns3:name>
+        </ns3:role>
+        <ns3:role>
+            <ns3:name>reports</ns3:name>
+        </ns3:role>
+        <ns3:role>
+            <ns3:name>server</ns3:name>
+        </ns3:role>
+        <ns3:role>
+            <ns3:name>primary administrator</ns3:name>
+        </ns3:role>
+        <ns3:role>
+            <ns3:name>network</ns3:name>
+        </ns3:role>
+    </ns3:roles>
+</ns3:Account>

http://git-wip-us.apache.org/repos/asf/libcloud/blob/b90c5610/libcloud/test/loadbalancer/test_dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/test/loadbalancer/test_dimensiondata.py b/libcloud/test/loadbalancer/test_dimensiondata.py
new file mode 100644
index 0000000..0c62087
--- /dev/null
+++ b/libcloud/test/loadbalancer/test_dimensiondata.py
@@ -0,0 +1,160 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import sys
+import unittest
+from libcloud.utils.py3 import httplib
+
+from libcloud.common.types import InvalidCredsError
+from libcloud.loadbalancer.base import LoadBalancer
+from libcloud.loadbalancer.drivers.dimensiondata import DimensionDataLBDriver as DimensionData
+from libcloud.loadbalancer.types import State
+from libcloud.common.dimensiondata import DimensionDataAPIException
+
+from libcloud.test import MockHttp
+from libcloud.test.file_fixtures import LoadBalancerFileFixtures
+
+from libcloud.test.secrets import DIMENSIONDATA_PARAMS
+
+
+class DimensionDataTests(unittest.TestCase):
+
+    def setUp(self):
+        DimensionData.connectionCls.conn_classes = (None, DimensionDataMockHttp)
+        DimensionDataMockHttp.type = None
+        self.driver = DimensionData(*DIMENSIONDATA_PARAMS)
+
+    def test_invalid_creds(self):
+        DimensionDataMockHttp.type = 'UNAUTHORIZED'
+        try:
+            self.driver.list_balancers()
+            self.assertTrue(
+                False)  # Above command should have thrown an InvalidCredsException
+        except InvalidCredsError:
+            pass
+
+    def test_list_balancers(self):
+        bal = self.driver.list_balancers()
+        self.assertEqual(bal[0].name, 'myProduction.Virtual.Listener')
+        self.assertEqual(bal[0].id, '6115469d-a8bb-445b-bb23-d23b5283f2b9')
+        self.assertEqual(bal[0].port, '8899')
+        self.assertEqual(bal[0].ip, '165.180.12.22')
+        self.assertEqual(bal[0].state, State.RUNNING)
+
+    def test_balancer_list_members(self):
+        extra={}
+        extra['pool_id']='4d360b1f-bc2c-4ab7-9884-1f03ba2768f7'
+        balancer = LoadBalancer(
+            id='234',
+            name='test',
+            state=State.RUNNING,
+            ip='1.2.3.4',
+            port=1234,
+            driver=self.driver,
+            extra=extra
+        )
+        members = self.driver.balancer_list_members(balancer)
+        self.assertEqual(2, len(members))
+        self.assertEqual(members[0].ip, '10.0.3.13')
+        self.assertEqual(members[0].id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0')
+        self.assertEqual(members[0].port, 9889)
+
+    def test_get_balancer(self):
+        bal = self.driver.get_balancer('6115469d-a8bb-445b-bb23-d23b5283f2b9')
+        self.assertEqual(bal.name, 'myProduction.Virtual.Listener')
+        self.assertEqual(bal.id, '6115469d-a8bb-445b-bb23-d23b5283f2b9')
+        self.assertEqual(bal.port, '8899')
+        self.assertEqual(bal.ip, '165.180.12.22')
+        self.assertEqual(bal.state, State.RUNNING)
+
+    def test_list_protocols(self):
+        protocols = self.driver.list_protocols()
+        self.assertNotEqual(0, len(protocols))
+    
+    def test_get_pools(self):
+        pools = self.driver.ex_get_pools()
+        self.assertNotEqual(0, len(pools))
+        self.assertEqual(pools[0].name, 'myDevelopmentPool.1')
+        self.assertEqual(pools[0].id, '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7')
+        
+    def test_get_pool(self):
+        pool = self.driver.ex_get_pool('4d360b1f-bc2c-4ab7-9884-1f03ba2768f7')
+        self.assertEqual(pool.name, 'myDevelopmentPool.1')
+        self.assertEqual(pool.id, '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7')
+
+    def test_get_pool_members(self):
+        members = self.driver.ex_get_pool_members('4d360b1f-bc2c-4ab7-9884-1f03ba2768f7')
+        self.assertEqual(2, len(members))
+        self.assertEqual(members[0].id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0')
+        self.assertEqual(members[0].name, '10.0.3.13')
+        self.assertEqual(members[0].status, 'NORMAL')
+        self.assertEqual(members[0].ip_address, '10.0.3.13')
+        self.assertEqual(members[0].port, 9889)
+    
+    def test_get_pool_member(self):
+        member = self.driver.ex_get_pool_member('3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0')
+        self.assertEqual(member.id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0')
+        self.assertEqual(member.name, '10.0.3.13')
+        self.assertEqual(member.status, 'NORMAL')
+        self.assertEqual(member.ip_address, '10.0.3.13')
+        self.assertEqual(member.port, 9889)
+
+class DimensionDataMockHttp(MockHttp):
+
+    fixtures = LoadBalancerFileFixtures('dimensiondata')
+
+    def _oec_0_9_myaccount_UNAUTHORIZED(self, method, url, body, headers):
+        return (httplib.UNAUTHORIZED, "", {}, httplib.responses[httplib.UNAUTHORIZED])
+
+    def _oec_0_9_myaccount(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_INPROGRESS(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_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener_6115469d_a8bb_445b_bb23_d23b5283f2b9(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener_6115469d_a8bb_445b_bb23_d23b5283f2b9.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool_4d360b1f_bc2c_4ab7_9884_1f03ba2768f7(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool_4d360b1f_bc2c_4ab7_9884_1f03ba2768f7.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+    
+    def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember_3dd806a2_c2c8_4c0c_9a4f_5219ea9266c0(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember_3dd806a2_c2c8_4c0c_9a4f_5219ea9266c0.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+
+if __name__ == '__main__':
+    sys.exit(unittest.main())