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 2013/02/03 20:24:20 UTC

svn commit: r1441956 - in /libcloud/trunk: ./ libcloud/common/ libcloud/compute/drivers/ libcloud/dns/drivers/ libcloud/test/common/ libcloud/test/compute/ libcloud/test/dns/ libcloud/test/dns/fixtures/gandi/

Author: tomaz
Date: Sun Feb  3 19:24:19 2013
New Revision: 1441956

URL: http://svn.apache.org/viewvc?rev=1441956&view=rev
Log:
Modify Gandi.net driver to use Libcloud http(s) layer when performing
http requests using xmlrpclib.

This change allows driver to support LIBCLOUD_DEBUG and SSL vertificate
validation functionality. Previously it used xmlrpclib directly which means
it bypassed Libcloud's http(s) layer and didn't support previously
mentioned features.

Contributed by John Carr, part of LIBCLOUD-288.

Added:
    libcloud/trunk/libcloud/test/dns/fixtures/gandi/zone_doesnt_exist.xml
Modified:
    libcloud/trunk/CHANGES
    libcloud/trunk/libcloud/common/gandi.py
    libcloud/trunk/libcloud/compute/drivers/gandi.py
    libcloud/trunk/libcloud/dns/drivers/gandi.py
    libcloud/trunk/libcloud/test/common/test_gandi.py
    libcloud/trunk/libcloud/test/compute/test_gandi.py
    libcloud/trunk/libcloud/test/dns/test_gandi.py

Modified: libcloud/trunk/CHANGES
URL: http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1441956&r1=1441955&r2=1441956&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Sun Feb  3 19:24:19 2013
@@ -133,6 +133,15 @@ Changes with Apache Libcloud in developm
      Also update pricing to reflect new (decreased) prices.
      [Tomaz Muraus]
 
+   - Modify Gandi.net driver to use Libcloud http(s) layer when performing
+     http requests using xmlrpclib.
+
+     This change allows driver to support LIBCLOUD_DEBUG and SSL vertificate
+     validation functionality. Previously it used xmlrpclib directly which means
+     it bypassed Libcloud's http(s) layer and didn't support previously
+     mentioned features. (LIBCLOUD-288)
+     [John Carr]
+
   *) Storage
 
     - Add a new local storage driver.

Modified: libcloud/trunk/libcloud/common/gandi.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/common/gandi.py?rev=1441956&r1=1441955&r2=1441956&view=diff
==============================================================================
--- libcloud/trunk/libcloud/common/gandi.py (original)
+++ libcloud/trunk/libcloud/common/gandi.py Sun Feb  3 19:24:19 2013
@@ -23,11 +23,10 @@ import sys
 from libcloud.utils.py3 import xmlrpclib
 from libcloud.utils.py3 import b
 
-from libcloud.common.base import ConnectionKey
+from libcloud.common.base import Response, ConnectionKey
 
 # Global constants
 
-API_URL = "https://rpc.gandi.net/xmlrpc/"
 
 DEFAULT_TIMEOUT = 600   # operation pooling max seconds
 DEFAULT_INTERVAL = 20   # seconds between 2 operation.info
@@ -38,65 +37,31 @@ class GandiException(Exception):
     Exception class for Gandi driver
     """
     def __str__(self):
-        return "(%u) %s" % (self.args[0], self.args[1])
+        return '(%u) %s' % (self.args[0], self.args[1])
 
     def __repr__(self):
-        return "<GandiException code %u '%s'>" % (self.args[0], self.args[1])
+        return '<GandiException code %u "%s">' % (self.args[0], self.args[1])
 
 
-class GandiSafeTransport(xmlrpclib.SafeTransport):
-    pass
-
-
-class GandiTransport(xmlrpclib.Transport):
-    pass
-
-
-class GandiProxy(xmlrpclib.ServerProxy):
-    transportCls = (GandiTransport, GandiSafeTransport)
-
-    def __init__(self, user_agent, verbose=0):
-        cls = self.transportCls[0]
-        if API_URL.startswith("https://"):
-            cls = self.transportCls[1]
-        t = cls(use_datetime=0)
-        t.user_agent = user_agent
-        xmlrpclib.ServerProxy.__init__(
-            self,
-            uri=API_URL,
-            transport=t,
-            verbose=verbose,
-            allow_none=True
-        )
-
-
-class GandiConnection(ConnectionKey):
+class GandiResponse(Response):
     """
-    Connection class for the Gandi driver
+    A Base Gandi Response class to derive from.
     """
 
-    proxyCls = GandiProxy
-
-    def __init__(self, key, password=None):
-        super(GandiConnection, self).__init__(key)
-        self.driver = BaseGandiDriver
-
+    def parse_body(self):
         try:
-            self._proxy = self.proxyCls(self._user_agent())
-        except xmlrpclib.Fault:
-            e = sys.exc_info()[1]
-            raise GandiException(1000, e)
+            params, methodname = xmlrpclib.loads(self.body)
 
-    def request(self, method, *args):
-        """ Request xmlrpc method with given args"""
-        try:
-            return getattr(self._proxy, method)(self.key, *args)
+            if len(params) == 1:
+                return params[0]
+
+            return params
         except xmlrpclib.Fault:
             e = sys.exc_info()[1]
             self.parse_error(e.faultCode, e.faultString)
-            raise GandiException(1001, e.faultString)
+            raise GandiException(1000, e)
 
-    def parse_error(self, code, message):
+    def parse_error(self, code=None, message=None):
         """
         This hook allows you to inspect any xmlrpclib errors and
         potentially raise a more useful and specific exception.
@@ -104,6 +69,27 @@ class GandiConnection(ConnectionKey):
         pass
 
 
+class GandiConnection(ConnectionKey):
+    """
+    Connection class for the Gandi driver
+    """
+
+    responseCls = GandiResponse
+    host = 'rpc.gandi.net'
+
+    def request(self, method, *args):
+        """ Request xmlrpc method with given args"""
+        args = (self.key, ) + args
+        data = xmlrpclib.dumps(args, methodname=method, allow_none=True)
+        headers = {
+            'Content-Type': 'text/xml',
+        }
+        return super(GandiConnection, self).request('/xmlrpc/',
+                                                    data=data,
+                                                    headers=headers,
+                                                    method='POST')
+
+
 class BaseGandiDriver(object):
     """
     Gandi base driver
@@ -112,22 +98,6 @@ class BaseGandiDriver(object):
     connectionCls = GandiConnection
     name = 'Gandi'
 
-    def __init__(self, key, secret=None, secure=False):
-        """
-        @param    key:    API key or username to used (required)
-        @type     key:    C{str}
-
-        @param    secret: Secret password to be used (required)
-        @type     secret: C{str}
-
-        @param    secure: Weither to use HTTPS or HTTP.
-        @type     secure: C{bool}
-        """
-        self.key = key
-        self.secret = secret
-        self.connection = self.connectionCls(key, secret)
-        self.connection.driver = self
-
     # Specific methods for gandi
     def _wait_operation(self, id, timeout=DEFAULT_TIMEOUT,
                         check_interval=DEFAULT_INTERVAL):
@@ -135,7 +105,7 @@ class BaseGandiDriver(object):
 
         for i in range(0, timeout, check_interval):
             try:
-                op = self.connection.request('operation.info', int(id))
+                op = self.connection.request('operation.info', int(id)).object
 
                 if op['step'] == 'DONE':
                     return True
@@ -180,7 +150,7 @@ class BaseObject(object):
         Note, for example, that this example will always produce the
         same UUID!
         """
-        hashstring = "%s:%s:%s" % \
+        hashstring = '%s:%s:%s' % \
             (self.uuid_prefix, self.id, self.driver.type)
         return hashlib.sha1(b(hashstring)).hexdigest()
 

Modified: libcloud/trunk/libcloud/compute/drivers/gandi.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/gandi.py?rev=1441956&r1=1441955&r2=1441956&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/gandi.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/gandi.py Sun Feb  3 19:24:19 2013
@@ -62,7 +62,7 @@ class GandiNodeDriver(BaseGandiDriver, N
     def _resource_info(self, type, id):
         try:
             obj = self.connection.request('%s.info' % type, int(id))
-            return obj
+            return obj.object
         except Exception:
             e = sys.exc_info()[1]
             raise GandiException(1003, e)
@@ -109,8 +109,8 @@ class GandiNodeDriver(BaseGandiDriver, N
         return [self._to_volume(d) for d in disks]
 
     def list_nodes(self):
-        vms = self.connection.request('vm.list')
-        ips = self.connection.request('ip.list')
+        vms = self.connection.request('vm.list').object
+        ips = self.connection.request('ip.list').object
         for vm in vms:
             vm['ips'] = []
             for ip in ips:
@@ -124,7 +124,7 @@ class GandiNodeDriver(BaseGandiDriver, N
 
     def reboot_node(self, node):
         op = self.connection.request('vm.reboot', int(node.id))
-        self._wait_operation(op['id'])
+        self._wait_operation(op.object['id'])
         vm = self._node_info(int(node.id))
         if vm['state'] == 'running':
             return True
@@ -135,11 +135,11 @@ class GandiNodeDriver(BaseGandiDriver, N
         if vm['state'] == 'running':
             # Send vm_stop and wait for accomplish
             op_stop = self.connection.request('vm.stop', int(node.id))
-            if not self._wait_operation(op_stop['id']):
+            if not self._wait_operation(op_stop.object['id']):
                 raise GandiException(1010, 'vm.stop failed')
             # Delete
         op = self.connection.request('vm.delete', int(node.id))
-        if self._wait_operation(op['id']):
+        if self._wait_operation(op.object['id']):
             return True
         return False
 
@@ -221,7 +221,7 @@ class GandiNodeDriver(BaseGandiDriver, N
         (op_disk, op_iface, op_vm) = self.connection.request(
             'vm.create_from',
             vm_spec, disk_spec, src_disk_id
-        )
+        ).object
 
         # We wait for vm_create to finish
         if self._wait_operation(op_vm['id']):
@@ -251,7 +251,7 @@ class GandiNodeDriver(BaseGandiDriver, N
             else:
                 filtering = {}
             images = self.connection.request('image.list', filtering)
-            return [self._to_image(i) for i in images]
+            return [self._to_image(i) for i in images.object]
         except Exception:
             e = sys.exc_info()[1]
             raise GandiException(1011, e)
@@ -268,7 +268,7 @@ class GandiNodeDriver(BaseGandiDriver, N
         )
 
     def list_sizes(self, location=None):
-        account = self.connection.request('account.info')
+        account = self.connection.request('account.info').object
         # Look for available shares, and return a list of share_definition
         available_res = account['resources']['available']
 
@@ -306,8 +306,8 @@ class GandiNodeDriver(BaseGandiDriver, N
         )
 
     def list_locations(self):
-        res = self.connection.request("datacenter.list")
-        return [self._to_loc(l) for l in res]
+        res = self.connection.request('datacenter.list')
+        return [self._to_loc(l) for l in res.object]
 
     def list_volumes(self):
         """
@@ -315,7 +315,7 @@ class GandiNodeDriver(BaseGandiDriver, N
         @rtype: C{list} of L{StorageVolume}
         """
         res = self.connection.request('disk.list', {})
-        return self._to_volumes(res)
+        return self._to_volumes(res.object)
 
     def create_volume(self, size, name, location=None, snapshot=None):
         disk_param = {
@@ -328,15 +328,15 @@ class GandiNodeDriver(BaseGandiDriver, N
                                          disk_param, int(snapshot.id))
         else:
             op = self.connection.request('disk.create', disk_param)
-        if self._wait_operation(op['id']):
-            disk = self._volume_info(op['disk_id'])
+        if self._wait_operation(op.object['id']):
+            disk = self._volume_info(op.object['disk_id'])
             return self._to_volume(disk)
         return None
 
     def attach_volume(self, node, volume, device=None):
         op = self.connection.request('vm.disk_attach',
                                      int(node.id), int(volume.id))
-        if self._wait_operation(op['id']):
+        if self._wait_operation(op.object['id']):
             return True
         return False
 
@@ -354,13 +354,13 @@ class GandiNodeDriver(BaseGandiDriver, N
         """
         op = self.connection.request('vm.disk_detach',
                                      int(node.id), int(volume.id))
-        if self._wait_operation(op['id']):
+        if self._wait_operation(op.object['id']):
             return True
         return False
 
     def destroy_volume(self, volume):
         op = self.connection.request('disk.delete', int(volume.id))
-        if self._wait_operation(op['id']):
+        if self._wait_operation(op.object['id']):
             return True
         return False
 
@@ -401,8 +401,8 @@ class GandiNodeDriver(BaseGandiDriver, N
 
         @rtype: C{list} of L{GandiNetworkInterface}
         """
-        ifaces = self.connection.request('iface.list')
-        ips = self.connection.request('ip.list')
+        ifaces = self.connection.request('iface.list').object
+        ips = self.connection.request('ip.list').object
         for iface in ifaces:
             iface['ips'] = list(
                 filter(lambda i: i['iface_id'] == iface['id'], ips))
@@ -432,7 +432,7 @@ class GandiNodeDriver(BaseGandiDriver, N
         @rtype: C{list} of L{GandiDisk}
         """
         res = self.connection.request('disk.list', {})
-        return self._to_disks(res)
+        return self._to_disks(res.object)
 
     def ex_node_attach_disk(self, node, disk):
         """
@@ -448,7 +448,7 @@ class GandiNodeDriver(BaseGandiDriver, N
         """
         op = self.connection.request('vm.disk_attach',
                                      int(node.id), int(disk.id))
-        if self._wait_operation(op['id']):
+        if self._wait_operation(op.object['id']):
             return True
         return False
 
@@ -466,7 +466,7 @@ class GandiNodeDriver(BaseGandiDriver, N
         """
         op = self.connection.request('vm.disk_detach',
                                      int(node.id), int(disk.id))
-        if self._wait_operation(op['id']):
+        if self._wait_operation(op.object['id']):
             return True
         return False
 
@@ -485,7 +485,7 @@ class GandiNodeDriver(BaseGandiDriver, N
         """
         op = self.connection.request('vm.iface_attach',
                                      int(node.id), int(iface.id))
-        if self._wait_operation(op['id']):
+        if self._wait_operation(op.object['id']):
             return True
         return False
 
@@ -504,7 +504,7 @@ class GandiNodeDriver(BaseGandiDriver, N
         """
         op = self.connection.request('vm.iface_detach',
                                      int(node.id), int(iface.id))
-        if self._wait_operation(op['id']):
+        if self._wait_operation(op.object['id']):
             return True
         return False
 
@@ -521,16 +521,16 @@ class GandiNodeDriver(BaseGandiDriver, N
         @rtype: C{bool}
         """
         if not disk.extra.get('can_snapshot'):
-            raise GandiException(1021, "Disk %s can't snapshot" % disk.id)
+            raise GandiException(1021, 'Disk %s can\'t snapshot' % disk.id)
         if not name:
-            suffix = datetime.today().strftime("%Y%m%d")
-            name = "snap_%s" % (suffix)
+            suffix = datetime.today().strftime('%Y%m%d')
+            name = 'snap_%s' % (suffix)
         op = self.connection.request(
             'disk.create_from',
             {'name': name, 'type': 'snapshot', },
             int(disk.id),
         )
-        if self._wait_operation(op['id']):
+        if self._wait_operation(op.object['id']):
             return True
         return False
 
@@ -557,6 +557,6 @@ class GandiNodeDriver(BaseGandiDriver, N
         op = self.connection.request('disk.update',
                                      int(disk.id),
                                      params)
-        if self._wait_operation(op['id']):
+        if self._wait_operation(op.object['id']):
             return True
         return False

Modified: libcloud/trunk/libcloud/dns/drivers/gandi.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/dns/drivers/gandi.py?rev=1441956&r1=1441955&r2=1441956&view=diff
==============================================================================
--- libcloud/trunk/libcloud/dns/drivers/gandi.py (original)
+++ libcloud/trunk/libcloud/dns/drivers/gandi.py Sun Feb  3 19:24:19 2013
@@ -20,6 +20,7 @@ __all__ = [
 ]
 
 from libcloud.common.gandi import BaseGandiDriver, GandiConnection
+from libcloud.common.gandi import GandiResponse
 from libcloud.dns.types import Provider, RecordType
 from libcloud.dns.types import RecordError
 from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
@@ -51,7 +52,7 @@ class NewZoneVersion(object):
     def __enter__(self):
         zid = int(self.zone.id)
         self.connection.set_context({'zone_id': self.zone.id})
-        vid = self.connection.request('domain.zone.version.new', zid)
+        vid = self.connection.request('domain.zone.version.new', zid).object
         self.vid = vid
         return vid
 
@@ -60,18 +61,24 @@ class NewZoneVersion(object):
             zid = int(self.zone.id)
             con = self.connection
             con.set_context({'zone_id': self.zone.id})
-            con.request('domain.zone.version.set', zid, self.vid)
+            con.request('domain.zone.version.set', zid, self.vid).object
 
 
-class GandiDNSConnection(GandiConnection):
+class GandiDNSResponse(GandiResponse):
 
     def parse_error(self, code, message):
+        context = self.connection.context
+        driver = self.connection.driver
         if code == 581042:
-            zone_id = str(self.context.get('zone_id', None))
-            raise ZoneDoesNotExistError(value='', driver=self.driver,
+            zone_id = str(context.get('zone_id', None))
+            raise ZoneDoesNotExistError(value='', driver=driver,
                                         zone_id=zone_id)
 
 
+class GandiDNSConnection(GandiConnection):
+    responseCls = GandiDNSResponse
+
+
 class GandiDNSDriver(BaseGandiDriver, DNSDriver):
     """
     API reference can be found at:
@@ -116,31 +123,33 @@ class GandiDNSDriver(BaseGandiDriver, DN
 
     def list_zones(self):
         zones = self.connection.request('domain.zone.list')
-        return self._to_zones(zones)
+        return self._to_zones(zones.object)
 
     def get_zone(self, zone_id):
         zid = int(zone_id)
         self.connection.set_context({'zone_id': zid})
         zone = self.connection.request('domain.zone.info', zid)
-        return self._to_zone(zone)
+        return self._to_zone(zone.object)
 
     def create_zone(self, domain, type='master', ttl=None, extra=None):
-        params = {'name': domain}
+        params = {
+            'name': domain,
+        }
         info = self.connection.request('domain.zone.create', params)
-        return self._to_zone(info)
+        return self._to_zone(info.object)
 
     def update_zone(self, zone, domain=None, type=None, ttl=None, extra=None):
         zid = int(zone.id)
         params = {'name': domain}
         self.connection.set_context({'zone_id': zid})
         zone = self.connection.request('domain.zone.update', zid, params)
-        return self._to_zone(zone)
+        return self._to_zone(zone.object)
 
     def delete_zone(self, zone):
         zid = int(zone.id)
         self.connection.set_context({'zone_id': zid})
         res = self.connection.request('domain.zone.delete', zid)
-        return res
+        return res.object
 
     def _to_record(self, record, zone):
         return Record(
@@ -163,7 +172,7 @@ class GandiDNSDriver(BaseGandiDriver, DN
         zid = int(zone.id)
         self.connection.set_context({'zone_id': zid})
         records = self.connection.request('domain.zone.record.list', zid, 0)
-        return self._to_records(records, zone)
+        return self._to_records(records.object, zone)
 
     def get_record(self, zone_id, record_id):
         zid = int(zone_id)
@@ -174,7 +183,7 @@ class GandiDNSDriver(BaseGandiDriver, DN
         }
         self.connection.set_context({'zone_id': zid})
         records = self.connection.request('domain.zone.record.list',
-                                          zid, 0, filter_opts)
+                                          zid, 0, filter_opts).object
 
         if len(records) == 0:
             raise RecordDoesNotExistError(value='', driver=self,
@@ -212,7 +221,7 @@ class GandiDNSDriver(BaseGandiDriver, DN
             con = self.connection
             con.set_context({'zone_id': zid})
             rec = con.request('domain.zone.record.add',
-                              zid, vid, create)
+                              zid, vid, create).object
 
         return self._to_record(rec, zone)
 
@@ -241,7 +250,7 @@ class GandiDNSDriver(BaseGandiDriver, DN
             con.request('domain.zone.record.delete',
                         zid, vid, filter_opts)
             res = con.request('domain.zone.record.add',
-                              zid, vid, update)
+                              zid, vid, update).object
 
         return self._to_record(res, record.zone)
 
@@ -257,7 +266,7 @@ class GandiDNSDriver(BaseGandiDriver, DN
             con = self.connection
             con.set_context({'zone_id': zid})
             count = con.request('domain.zone.record.delete',
-                                zid, vid, filter_opts)
+                                zid, vid, filter_opts).object
 
         if count == 1:
             return True

Modified: libcloud/trunk/libcloud/test/common/test_gandi.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/common/test_gandi.py?rev=1441956&r1=1441955&r2=1441956&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/common/test_gandi.py (original)
+++ libcloud/trunk/libcloud/test/common/test_gandi.py Sun Feb  3 19:24:19 2013
@@ -1,33 +1,15 @@
-import sys
-import unittest
-
-from xml.etree import ElementTree as ET
-
 from libcloud.utils.py3 import xmlrpclib
+from libcloud.test import MockHttp
 
 
-class MockGandiTransport(xmlrpclib.Transport):
-
-    def request(self, host, handler, request_body, verbose=0):
-        self.verbose = 0
-        method = ET.XML(request_body).find('methodName').text
-        mock = self.mockCls(host, 80)
-        mock.request('POST', '%s/%s' % (handler, method))
-        resp = mock.getresponse()
-
-        if sys.version[0] == '2' and sys.version[2] == '7':
-            response = self.parse_response(resp)
-        else:
-            response = self.parse_response(resp.body)
-        return response
-
+class BaseGandiMockHttp(MockHttp):
 
-class BaseGandiTests(unittest.TestCase):
+    def _get_method_name(self, type, use_param, qs, path):
+        return "_xmlrpc"
 
-    def setUp(self):
-        d = self.driverCls
-        t = self.transportCls
-        t.mockCls.type = None
-        d.connectionCls.proxyCls.transportCls = \
-            [t, t]
-        self.driver = d(*self.params)
+    def _xmlrpc(self, method, url, body, headers):
+        params, methodName = xmlrpclib.loads(body)
+        meth_name = '_xmlrpc__' + methodName.replace('.', '_')
+        if self.type:
+            meth_name = '%s_%s' % (meth_name, self.type)
+        return getattr(self, meth_name)(method, url, body, headers)

Modified: libcloud/trunk/libcloud/test/compute/test_gandi.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/test_gandi.py?rev=1441956&r1=1441955&r2=1441956&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/compute/test_gandi.py (original)
+++ libcloud/trunk/libcloud/test/compute/test_gandi.py Sun Feb  3 19:24:19 2013
@@ -19,125 +19,26 @@ import random
 import string
 
 from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import xmlrpclib
 
 from libcloud.compute.drivers.gandi import GandiNodeDriver
-from libcloud.compute.base import StorageVolume
 from libcloud.common.gandi import GandiException
 from libcloud.compute.types import NodeState
 
-from xml.etree import ElementTree as ET
-from libcloud.test import MockHttp
 from libcloud.test.file_fixtures import ComputeFileFixtures
 from libcloud.test.secrets import GANDI_PARAMS
-from libcloud.test.common.test_gandi import MockGandiTransport, BaseGandiTests
+from libcloud.test.common.test_gandi import BaseGandiMockHttp
 
 
-class GandiMockHttp(MockHttp):
-
-    fixtures = ComputeFileFixtures('gandi')
-
-    def _xmlrpc__datacenter_list(self, method, url, body, headers):
-        body = self.fixtures.load('datacenter_list.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__image_list(self, method, url, body, headers):
-        body = self.fixtures.load('image_list_dc0.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__vm_list(self, method, url, body, headers):
-        body = self.fixtures.load('vm_list.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__ip_list(self, method, url, body, headers):
-        body = self.fixtures.load('ip_list.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__account_info(self, method, url, body, headers):
-        body = self.fixtures.load('account_info.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__vm_info(self, method, url, body, headers):
-        body = self.fixtures.load('vm_info.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__vm_delete(self, method, url, body, headers):
-        body = self.fixtures.load('vm_delete.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__operation_info(self, method, url, body, headers):
-        body = self.fixtures.load('operation_info.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__vm_create_from(self, method, url, body, headers):
-        body = self.fixtures.load('vm_create_from.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__vm_reboot(self, method, url, body, headers):
-        body = self.fixtures.load('vm_reboot.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__vm_stop(self, method, url, body, headers):
-        body = self.fixtures.load('vm_stop.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__iface_list(self, method, url, body, headers):
-        body = self.fixtures.load('iface_list.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__disk_list(self, method, url, body, headers):
-        body = self.fixtures.load('disk_list.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__vm_iface_attach(self, method, url, body, headers):
-        body = self.fixtures.load('iface_attach.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__vm_iface_detach(self, method, url, body, headers):
-        body = self.fixtures.load('iface_detach.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__vm_disk_attach(self, method, url, body, headers):
-        body = self.fixtures.load('disk_attach.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__vm_disk_detach(self, method, url, body, headers):
-        body = self.fixtures.load('disk_detach.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__disk_create(self, method, url, body, headers):
-        body = self.fixtures.load('disk_create.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__disk_create_from(self, method, url, body, headers):
-        body = self.fixtures.load('disk_create_from.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__disk_info(self, method, url, body, headers):
-        body = self.fixtures.load('disk_info.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__disk_update(self, method, url, body, headers):
-        body = self.fixtures.load('disk_update.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__disk_delete(self, method, url, body, headers):
-        body = self.fixtures.load('disk_delete.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-
-class DummyTransport(MockGandiTransport):
-    mockCls = GandiMockHttp
-
-
-class GandiTests(BaseGandiTests):
-
-    driverCls = GandiNodeDriver
-    transportCls = DummyTransport
-    params = GANDI_PARAMS
+class GandiTests(unittest.TestCase):
 
     node_name = 'test2'
 
+    def setUp(self):
+        GandiNodeDriver.connectionCls.conn_classes = (
+            GandiMockHttp, GandiMockHttp)
+        GandiMockHttp.type = None
+        self.driver = GandiNodeDriver(*GANDI_PARAMS)
+
     def test_list_nodes(self):
         nodes = self.driver.list_nodes()
         self.assertTrue(len(nodes) > 0)
@@ -254,5 +155,98 @@ class GandiTests(BaseGandiTests):
         self.assertTrue(self.driver.ex_update_disk(disks[0], new_size=4096))
 
 
+class GandiMockHttp(BaseGandiMockHttp):
+
+    fixtures = ComputeFileFixtures('gandi')
+
+    def _xmlrpc__datacenter_list(self, method, url, body, headers):
+        body = self.fixtures.load('datacenter_list.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__image_list(self, method, url, body, headers):
+        body = self.fixtures.load('image_list_dc0.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__vm_list(self, method, url, body, headers):
+        body = self.fixtures.load('vm_list.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__ip_list(self, method, url, body, headers):
+        body = self.fixtures.load('ip_list.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__account_info(self, method, url, body, headers):
+        body = self.fixtures.load('account_info.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__vm_info(self, method, url, body, headers):
+        body = self.fixtures.load('vm_info.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__vm_delete(self, method, url, body, headers):
+        body = self.fixtures.load('vm_delete.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__operation_info(self, method, url, body, headers):
+        body = self.fixtures.load('operation_info.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__vm_create_from(self, method, url, body, headers):
+        body = self.fixtures.load('vm_create_from.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__vm_reboot(self, method, url, body, headers):
+        body = self.fixtures.load('vm_reboot.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__vm_stop(self, method, url, body, headers):
+        body = self.fixtures.load('vm_stop.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__iface_list(self, method, url, body, headers):
+        body = self.fixtures.load('iface_list.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__disk_list(self, method, url, body, headers):
+        body = self.fixtures.load('disk_list.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__vm_iface_attach(self, method, url, body, headers):
+        body = self.fixtures.load('iface_attach.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__vm_iface_detach(self, method, url, body, headers):
+        body = self.fixtures.load('iface_detach.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__vm_disk_attach(self, method, url, body, headers):
+        body = self.fixtures.load('disk_attach.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__vm_disk_detach(self, method, url, body, headers):
+        body = self.fixtures.load('disk_detach.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__disk_create(self, method, url, body, headers):
+        body = self.fixtures.load('disk_create.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__disk_create_from(self, method, url, body, headers):
+        body = self.fixtures.load('disk_create_from.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__disk_info(self, method, url, body, headers):
+        body = self.fixtures.load('disk_info.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__disk_update(self, method, url, body, headers):
+        body = self.fixtures.load('disk_update.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__disk_delete(self, method, url, body, headers):
+        body = self.fixtures.load('disk_delete.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+
 if __name__ == '__main__':
     sys.exit(unittest.main())

Added: libcloud/trunk/libcloud/test/dns/fixtures/gandi/zone_doesnt_exist.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/dns/fixtures/gandi/zone_doesnt_exist.xml?rev=1441956&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/dns/fixtures/gandi/zone_doesnt_exist.xml (added)
+++ libcloud/trunk/libcloud/test/dns/fixtures/gandi/zone_doesnt_exist.xml Sun Feb  3 19:24:19 2013
@@ -0,0 +1,17 @@
+<?xml version='1.0'?>
+<methodResponse>
+  <fault>
+    <value>
+      <struct>
+        <member>
+          <name>faultCode</name>
+          <value><int>581042</int></value>
+        </member>
+        <member>
+          <name>faultString</name>
+          <value><string>Zone does not exist</string></value>
+        </member>
+      </struct>
+    </value>
+  </fault>
+</methodResponse>

Modified: libcloud/trunk/libcloud/test/dns/test_gandi.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/dns/test_gandi.py?rev=1441956&r1=1441955&r2=1441956&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/dns/test_gandi.py (original)
+++ libcloud/trunk/libcloud/test/dns/test_gandi.py Sun Feb  3 19:24:19 2013
@@ -17,106 +17,21 @@ import sys
 import unittest
 
 from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import xmlrpclib
 from libcloud.dns.types import RecordType, ZoneDoesNotExistError
 from libcloud.dns.types import RecordDoesNotExistError
 from libcloud.dns.drivers.gandi import GandiDNSDriver
-from libcloud.test import MockHttp
 from libcloud.test.file_fixtures import DNSFileFixtures
 from libcloud.test.secrets import DNS_GANDI
-from libcloud.test.common.test_gandi import MockGandiTransport, BaseGandiTests
+from libcloud.test.common.test_gandi import BaseGandiMockHttp
 
-Fault = xmlrpclib.Fault
 
-class GandiMockHttp(MockHttp):
-    fixtures = DNSFileFixtures('gandi')
-
-    def _xmlrpc__domain_zone_create(self, method, url, body, headers):
-        body = self.fixtures.load('create_zone.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_update(self, method, url, body, headers):
-        body = self.fixtures.load('get_zone.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_list(self, method, url, body, headers):
-        body = self.fixtures.load('list_zones.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_record_list(self, method, url, body, headers):
-        body = self.fixtures.load('list_records.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_record_add(self, method, url, body, headers):
-        body = self.fixtures.load('create_record.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_delete(self, method, url, body, headers):
-        body = self.fixtures.load('delete_zone.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_info(self, method, url, body, headers):
-        body = self.fixtures.load('get_zone.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_record_delete(self, method, url, body, headers):
-        body = self.fixtures.load('delete_record.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_record_update(self, method, url, body, headers):
-        body = self.fixtures.load('create_record.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_version_new(self, method, url, body, headers):
-        body = self.fixtures.load('new_version.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_version_set(self, method, url, body, headers):
-        body = self.fixtures.load('new_version.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_record_list_ZONE_DOES_NOT_EXIST(self, method, url, body, headers):
-        raise Fault(581042, "Zone does not exist")
-
-    def _xmlrpc__domain_zone_info_ZONE_DOES_NOT_EXIST(self, method, url, body, headers):
-        raise Fault(581042, "Zone does not exist")
+class GandiTests(unittest.TestCase):
 
-    def _xmlrpc__domain_zone_list_ZONE_DOES_NOT_EXIST(self, method, url, body, headers):
-        raise Fault(581042, "Zone does not exist")
-
-    def _xmlrpc__domain_zone_delete_ZONE_DOES_NOT_EXIST(self, method, url, body, headers):
-        raise Fault(581042, "Zone does not exist")
-
-    def _xmlrpc__domain_zone_record_list_RECORD_DOES_NOT_EXIST(self, method, url, body, headers):
-        body = self.fixtures.load('list_records_empty.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_info_RECORD_DOES_NOT_EXIST(self, method, url, body, headers):
-        body = self.fixtures.load('list_zones.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_record_delete_RECORD_DOES_NOT_EXIST(self, method, url, body, headers):
-        body = self.fixtures.load('delete_record_doesnotexist.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_version_new_RECORD_DOES_NOT_EXIST(self, method, url, body, headers):
-        body = self.fixtures.load('new_version.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-    def _xmlrpc__domain_zone_version_set_RECORD_DOES_NOT_EXIST(self, method, url, body, headers):
-        body = self.fixtures.load('new_version.xml')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-
-class DummyTransport(MockGandiTransport):
-    mockCls = GandiMockHttp
-
-
-class GandiTests(BaseGandiTests):
-
-    driverCls = GandiDNSDriver
-    transportCls = DummyTransport
-    params = DNS_GANDI
+    def setUp(self):
+        GandiDNSDriver.connectionCls.conn_classes = (
+            GandiMockHttp, GandiMockHttp)
+        GandiMockHttp.type = None
+        self.driver = GandiDNSDriver(*DNS_GANDI)
 
     def test_list_record_types(self):
         record_types = self.driver.list_record_types()
@@ -285,5 +200,88 @@ class GandiTests(BaseGandiTests):
             self.fail('Exception was not thrown')
 
 
+class GandiMockHttp(BaseGandiMockHttp):
+    fixtures = DNSFileFixtures('gandi')
+
+    def _xmlrpc__domain_zone_create(self, method, url, body, headers):
+        body = self.fixtures.load('create_zone.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_update(self, method, url, body, headers):
+        body = self.fixtures.load('get_zone.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_list(self, method, url, body, headers):
+        body = self.fixtures.load('list_zones.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_record_list(self, method, url, body, headers):
+        body = self.fixtures.load('list_records.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_record_add(self, method, url, body, headers):
+        body = self.fixtures.load('create_record.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_delete(self, method, url, body, headers):
+        body = self.fixtures.load('delete_zone.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_info(self, method, url, body, headers):
+        body = self.fixtures.load('get_zone.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_record_delete(self, method, url, body, headers):
+        body = self.fixtures.load('delete_record.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_record_update(self, method, url, body, headers):
+        body = self.fixtures.load('create_record.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_version_new(self, method, url, body, headers):
+        body = self.fixtures.load('new_version.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_version_set(self, method, url, body, headers):
+        body = self.fixtures.load('new_version.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_record_list_ZONE_DOES_NOT_EXIST(self, method, url, body, headers):
+        body = self.fixtures.load('zone_doesnt_exist.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_info_ZONE_DOES_NOT_EXIST(self, method, url, body, headers):
+        body = self.fixtures.load('zone_doesnt_exist.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_list_ZONE_DOES_NOT_EXIST(self, method, url, body, headers):
+        body = self.fixtures.load('zone_doesnt_exist.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_delete_ZONE_DOES_NOT_EXIST(self, method, url, body, headers):
+        body = self.fixtures.load('zone_doesnt_exist.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_record_list_RECORD_DOES_NOT_EXIST(self, method, url, body, headers):
+        body = self.fixtures.load('list_records_empty.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_info_RECORD_DOES_NOT_EXIST(self, method, url, body, headers):
+        body = self.fixtures.load('list_zones.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_record_delete_RECORD_DOES_NOT_EXIST(self, method, url, body, headers):
+        body = self.fixtures.load('delete_record_doesnotexist.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_version_new_RECORD_DOES_NOT_EXIST(self, method, url, body, headers):
+        body = self.fixtures.load('new_version.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _xmlrpc__domain_zone_version_set_RECORD_DOES_NOT_EXIST(self, method, url, body, headers):
+        body = self.fixtures.load('new_version.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
 if __name__ == '__main__':
     sys.exit(unittest.main())