Author: tomaz
Date: Tue Feb 5 10:59:52 2013
New Revision: 1442533
URL: http://svn.apache.org/viewvc?rev=1442533&view=rev
Log:
Add a common module (libcloud.common.xmlrpc) for handling XML-RPC
requests using Libcloud http layer.
Also refactor existing drivers which use xmlrpclib directly (VCL, Gandi,
Softlayer) to use this module.
This change allows drivers to support LIBCLOUD_DEBUG and SSL certificate
validation functionality. Previously they have bypassed Libcloud http
layer so this functionality was not available.
Contributed by John Carr, part of LIBCLOUD-288.
Added:
libcloud/trunk/libcloud/common/xmlrpc.py
libcloud/trunk/libcloud/test/compute/fixtures/softlayer/SoftLayer_Account.xml
libcloud/trunk/libcloud/test/compute/fixtures/softlayer/fail.xml
Modified:
libcloud/trunk/CHANGES
libcloud/trunk/libcloud/common/gandi.py
libcloud/trunk/libcloud/compute/drivers/softlayer.py
libcloud/trunk/libcloud/compute/drivers/vcl.py
libcloud/trunk/libcloud/dns/drivers/gandi.py
libcloud/trunk/libcloud/test/compute/test_softlayer.py
libcloud/trunk/libcloud/test/compute/test_vcl.py
libcloud/trunk/libcloud/test/dns/test_gandi.py
Modified: libcloud/trunk/CHANGES
URL:
http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1442533&r1=1442532&r2=1442533&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Tue Feb 5 10:59:52 2013
@@ -27,6 +27,17 @@ Changes with Apache Libcloud in developm
CA file which is used to verifying the server certificate. (LIBCLOUD-283)
[Tomaz Muraus, Erinn Looney-Triggs]
+ - Add a common module (libcloud.common.xmlrpc) for handling XML-RPC
+ requests using Libcloud http layer.
+
+ Also refactor existing drivers which use xmlrpclib directly (VCL, Gandi,
+ Softlayer) to use this module.
+
+ This change allows drivers to support LIBCLOUD_DEBUG and SSL certificate
+ validation functionality. Previously they have bypassed Libcloud http
+ layer so this functionality was not available. (LIBCLOUD-288)
+ [John Carr]
+
*) Compute
- Fix string interpolation bug in __repr__ methods in the IBM SCE driver.
@@ -133,15 +144,6 @@ 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=1442533&r1=1442532&r2=1442533&view=diff
==============================================================================
--- libcloud/trunk/libcloud/common/gandi.py (original)
+++ libcloud/trunk/libcloud/common/gandi.py Tue Feb 5 10:59:52 2013
@@ -20,14 +20,13 @@ import time
import hashlib
import sys
-from libcloud.utils.py3 import xmlrpclib
from libcloud.utils.py3 import b
-from libcloud.common.base import Response, ConnectionKey
+from libcloud.common.base import ConnectionKey
+from libcloud.common.xmlrpc import XMLRPCResponse, XMLRPCConnection
# Global constants
-
DEFAULT_TIMEOUT = 600 # operation pooling max seconds
DEFAULT_INTERVAL = 20 # seconds between 2 operation.info
@@ -43,51 +42,24 @@ class GandiException(Exception):
return '<GandiException code %u "%s">' % (self.args[0], self.args[1])
-class GandiResponse(Response):
+class GandiResponse(XMLRPCResponse):
"""
A Base Gandi Response class to derive from.
"""
- def parse_body(self):
- try:
- params, methodname = xmlrpclib.loads(self.body)
-
- 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(1000, e)
-
- 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.
- """
- pass
-
-class GandiConnection(ConnectionKey):
+class GandiConnection(XMLRPCConnection, ConnectionKey):
"""
Connection class for the Gandi driver
"""
responseCls = GandiResponse
host = 'rpc.gandi.net'
+ endpoint = '/xmlrpc/'
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')
+ return super(GandiConnection, self).request(method, *args)
class BaseGandiDriver(object):
Added: libcloud/trunk/libcloud/common/xmlrpc.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/common/xmlrpc.py?rev=1442533&view=auto
==============================================================================
--- libcloud/trunk/libcloud/common/xmlrpc.py (added)
+++ libcloud/trunk/libcloud/common/xmlrpc.py Tue Feb 5 10:59:52 2013
@@ -0,0 +1,108 @@
+# 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.
+"""
+Base classes for working with xmlrpc APIs
+"""
+
+import sys
+
+from libcloud.utils.py3 import xmlrpclib
+from libcloud.utils.py3 import httplib
+from libcloud.common.base import Response, Connection
+
+
+class ProtocolError(Exception):
+ pass
+
+
+class ErrorCodeMixin(object):
+ """
+ This is a helper for API's that have a well defined collection of error
+ codes that are easily parsed out of error messages. It acts as a factory:
+ it finds the right exception for the error code, fetches any parameters it
+ needs from the context and raises it.
+ """
+
+ exceptions = {}
+
+ def raise_exception_for_error(self, error_code, message):
+ exceptionCls = self.exceptions.get(error_code, None)
+ if exceptionCls is None:
+ return
+ context = self.connection.context
+ driver = self.connection.driver
+ params = {}
+ if hasattr(exceptionCls, 'kwargs'):
+ for key in exceptionCls.kwargs:
+ if key in context:
+ params[key] = context[key]
+ raise exceptionCls(value=message, driver=driver, **params)
+
+
+class XMLRPCResponse(ErrorCodeMixin, Response):
+
+ defaultExceptionCls = Exception
+
+ def success(self):
+ return self.status == httplib.OK
+
+ def parse_body(self):
+ try:
+ params, methodname = xmlrpclib.loads(self.body)
+ if len(params) == 1:
+ params = params[0]
+ return params
+ except xmlrpclib.Fault:
+ e = sys.exc_info()[1]
+ self.raise_exception_for_error(e.faultCode, e.faultString)
+ error_string = '%s: %s' % (e.faultCode, e.faultString)
+ raise self.defaultExceptionCls(error_string)
+
+ def parse_error(self):
+ msg = 'Server returned an invalid xmlrpc response (%d)' % (self.status)
+ raise ProtocolError(msg)
+
+
+class XMLRPCConnection(Connection):
+ """
+ Connection class which can call XMLRPC based API's.
+
+ This class uses the xmlrpclib marshalling and demarshalling code but uses
+ the http transports provided by libcloud giving it better certificate
+ validation and debugging helpers than the core client library.
+ """
+
+ responseCls = XMLRPCResponse
+
+ def add_default_headers(self, headers):
+ headers['Content-Type'] = 'text/xml'
+ return headers
+
+ def request(self, method_name, *args, **kwargs):
+ """
+ Call a given `method_name`.
+
+ @type method_name: C{str}
+ @param method_name: A method exposed by the xmlrpc endpoint that you
+ are connecting to.
+
+ @type args: C{tuple}
+ @param args: Arguments to invoke with method with.
+ """
+ endpoint = kwargs.get('endpoint', self.endpoint)
+ data = xmlrpclib.dumps(args, methodname=method_name, allow_none=True)
+ return super(XMLRPCConnection, self).request(endpoint,
+ data=data,
+ method='POST')
Modified: libcloud/trunk/libcloud/compute/drivers/softlayer.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/softlayer.py?rev=1442533&r1=1442532&r2=1442533&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/softlayer.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/softlayer.py Tue Feb 5 10:59:52
2013
@@ -23,6 +23,8 @@ import libcloud
from libcloud.utils.py3 import xmlrpclib
+from libcloud.common.base import ConnectionUserAndKey
+from libcloud.common.xmlrpc import XMLRPCResponse, XMLRPCConnection
from libcloud.common.types import InvalidCredsError, LibcloudError
from libcloud.compute.types import Provider, NodeState
from libcloud.compute.base import NodeDriver, Node, NodeLocation, NodeSize, \
@@ -97,78 +99,36 @@ class SoftLayerException(LibcloudError):
pass
-class SoftLayerSafeTransport(xmlrpclib.SafeTransport):
- pass
-
-
-class SoftLayerTransport(xmlrpclib.Transport):
- pass
-
-
-class SoftLayerProxy(xmlrpclib.ServerProxy):
- transportCls = (SoftLayerTransport, SoftLayerSafeTransport)
- API_PREFIX = 'https://api.softlayer.com/xmlrpc/v3/'
-
- def __init__(self, service, user_agent, verbose=False):
- cls = self.transportCls[0]
- if SoftLayerProxy.API_PREFIX[:8] == 'https://':
- cls = self.transportCls[1]
- t = cls(use_datetime=0)
- t.user_agent = user_agent
- xmlrpclib.ServerProxy.__init__(
- self,
- uri='%s/%s' % (SoftLayerProxy.API_PREFIX, service),
- transport=t,
- verbose=verbose,
- )
-
-
-class SoftLayerConnection(object):
- """
- Connection class for the SoftLayer driver
- """
+class SoftLayerResponse(XMLRPCResponse):
+ defaultExceptionCls = SoftLayerException
+ exceptions = {
+ 'SoftLayer_Account': InvalidCredsError,
+ }
- proxyCls = SoftLayerProxy
- driver = None
- def __init__(self, user, key):
- self.user = user
- self.key = key
- self.ua = []
+class SoftLayerConnection(XMLRPCConnection, ConnectionUserAndKey):
+ responseCls = SoftLayerResponse
+ endpoint = '/xmlrpc/v3/'
def request(self, service, method, *args, **kwargs):
- sl = self.proxyCls(service, self._user_agent())
-
headers = {}
headers.update(self._get_auth_headers())
headers.update(self._get_init_params(service, kwargs.get('id')))
headers.update(
self._get_object_mask(service, kwargs.get('object_mask')))
-
headers.update(
self._get_object_mask(service, kwargs.get('object_mask')))
- params = [{'headers': headers}] + list(args)
- try:
- return getattr(sl, method)(*params)
- except xmlrpclib.Fault:
- e = sys.exc_info()[1]
- if e.faultCode == 'SoftLayer_Account':
- raise InvalidCredsError(e.faultString)
- raise SoftLayerException(e)
-
- def _user_agent(self):
- return 'libcloud/%s (%s)%s' % (libcloud.__version__,
- self.driver.name,
- ''.join([' (%s)' % x for x in self.ua]))
+ args = ({'headers': headers}, ) + args
+ endpoint = '%s/%s' % (self.endpoint, service)
- def user_agent_append(self, s):
- self.ua.append(s)
+ return super(SoftLayerConnection, self).request(method, *args,
+ endpoint=endpoint)
def _get_auth_headers(self):
return {
'authenticate': {
- 'username': self.user,
+ 'username': self.user_id,
'apiKey': self.key
}
}
@@ -208,24 +168,6 @@ class SoftLayerNodeDriver(NodeDriver):
features = {'create_node': ['generates_password']}
- 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}
-
- @rtype: C{None}
- """
- self.key = key
- self.secret = secret
- self.connection = self.connectionCls(key, secret)
- self.connection.driver = self
-
def _to_node(self, host):
try:
password = \
@@ -283,7 +225,7 @@ class SoftLayerNodeDriver(NodeDriver):
'getObject',
id=node_id,
object_mask=mask
- )
+ ).object
if res.get('provisionDate', None):
return res
@@ -385,7 +327,7 @@ class SoftLayerNodeDriver(NodeDriver):
res = self.connection.request(
'SoftLayer_Virtual_Guest', 'createObject', newCCI
- )
+ ).object
node_id = res['id']
raw_node = self._get_order_information(node_id)
@@ -402,7 +344,7 @@ class SoftLayerNodeDriver(NodeDriver):
def list_images(self, location=None):
result = self.connection.request(
'SoftLayer_Virtual_Guest', 'getCreateObjectOptions'
- )
+ ).object
return [self._to_image(i) for i in result['operatingSystems']]
def _to_size(self, id, size):
@@ -429,7 +371,7 @@ class SoftLayerNodeDriver(NodeDriver):
def list_locations(self):
res = self.connection.request(
'SoftLayer_Location_Datacenter', 'getDatacenters'
- )
+ ).object
return [self._to_loc(l) for l in res]
def list_nodes(self):
@@ -444,5 +386,5 @@ class SoftLayerNodeDriver(NodeDriver):
"SoftLayer_Account",
"getVirtualGuests",
object_mask=mask
- )
+ ).object
return [self._to_node(h) for h in res]
Modified: libcloud/trunk/libcloud/compute/drivers/vcl.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/vcl.py?rev=1442533&r1=1442532&r2=1442533&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/vcl.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/vcl.py Tue Feb 5 10:59:52 2013
@@ -16,89 +16,30 @@
VCL driver
"""
-import sys
import time
-from libcloud.utils.py3 import xmlrpclib
-
+from libcloud.common.base import ConnectionUserAndKey
+from libcloud.common.xmlrpc import XMLRPCResponse, XMLRPCConnection
from libcloud.common.types import InvalidCredsError, LibcloudError
from libcloud.compute.types import Provider, NodeState
from libcloud.compute.base import NodeDriver, Node
from libcloud.compute.base import NodeSize, NodeImage
-class VCLSafeTransport(xmlrpclib.SafeTransport):
- def __init__(self, datetime, user, passwd, host):
-
- self._pass = passwd
- self._use_datetime = datetime
- self._connection = (None, None)
- self._extra_headers = []
-
- def send_content(self, connection, request_body):
- connection.putheader('Content-Type', 'text/xml')
- connection.putheader('X-APIVERSION', '2')
- connection.putheader('X-User', self._user)
- connection.putheader('X-Pass', self._pass)
- connection.putheader('Content-Length', str(len(request_body)))
- connection.endheaders(request_body)
-
-
-class VCLProxy(xmlrpclib.ServerProxy):
- API_POSTFIX = '/index.php?mode=xmlrpccall'
- transportCls = VCLSafeTransport
-
- def __init__(self, user, key, secure, host, port, driver, verbose=False):
- url = ''
- cls = self.transportCls
-
- if secure:
- url = 'https://'
- port = port or 443
- else:
- url = 'http://'
- port = port or 80
-
- url += host + ':' + str(port)
- url += VCLProxy.API_POSTFIX
-
- self.API = url
- t = cls(0, user, key, self.API)
-
- xmlrpclib.ServerProxy.__init__(
- self,
- uri=self.API,
- transport=t,
- verbose=verbose
- )
-
-
-class VCLConnection(object):
- """
- Connection class for the VCL driver
- """
+class VCLResponse(XMLRPCResponse):
+ exceptions = {
+ 'VCL_Account': InvalidCredsError,
+ }
- proxyCls = VCLProxy
- driver = None
- def __init__(self, user, key, secure, host, port):
- self.user = user
- self.key = key
- self.secure = secure
- self.host = host
- self.port = port
-
- def request(self, method, *args, **kwargs):
- sl = self.proxyCls(user=self.user, key=self.key, secure=self.secure,
- host=self.host, port=self.port, driver=self.driver)
+class VCLConnection(XMLRPCConnection, ConnectionUserAndKey):
+ endpoint = '/index.php?mode=xmlrpccall'
- try:
- return getattr(sl, method)(*args)
- except xmlrpclib.Fault:
- e = sys.exc_info()[1]
- if e.faultCode == 'VCL_Account':
- raise InvalidCredsError(e.faultString)
- raise LibcloudError(e, driver=self.driver)
+ def add_default_headers(self, headers):
+ headers['X-APIVERSION'] = '2'
+ headers['X-User'] = self.user_id
+ headers['X-Pass'] = self.key
+ return headers
class VCLNodeDriver(NodeDriver):
@@ -151,17 +92,15 @@ class VCLNodeDriver(NodeDriver):
raise Exception('When instantiating VCL driver directly ' +
'you also need to provide host')
- self.key = key
- self.host = host
- self.secret = secret
- self.connection = self.connectionCls(key, secret, secure, host, port)
- self.connection.driver = self
+ super(VCLNodeDriver, self).__init__(key, secret, secure=True,
+ host=None, port=None, *args,
+ **kwargs)
def _vcl_request(self, method, *args):
res = self.connection.request(
method,
*args
- )
+ ).object
if(res['status'] == 'error'):
raise LibcloudError(res['errormsg'], driver=self)
return res
@@ -237,7 +176,7 @@ class VCLNodeDriver(NodeDriver):
"""
res = self.connection.request(
"XMLRPCgetImages"
- )
+ ).object
return [self._to_image(i) for i in res]
def list_sizes(self, location=None):
Modified: libcloud/trunk/libcloud/dns/drivers/gandi.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/dns/drivers/gandi.py?rev=1442533&r1=1442532&r2=1442533&view=diff
==============================================================================
--- libcloud/trunk/libcloud/dns/drivers/gandi.py (original)
+++ libcloud/trunk/libcloud/dns/drivers/gandi.py Tue Feb 5 10:59:52 2013
@@ -65,14 +65,9 @@ class NewZoneVersion(object):
class GandiDNSResponse(GandiResponse):
-
- def parse_error(self, code, message):
- context = self.connection.context
- driver = self.connection.driver
- if code == 581042:
- zone_id = str(context.get('zone_id', None))
- raise ZoneDoesNotExistError(value='', driver=driver,
- zone_id=zone_id)
+ exceptions = {
+ 581042: ZoneDoesNotExistError,
+ }
class GandiDNSConnection(GandiConnection):
@@ -107,7 +102,7 @@ class GandiDNSDriver(BaseGandiDriver, DN
def _to_zone(self, zone):
return Zone(
- id=zone['id'],
+ id=str(zone['id']),
domain=zone['name'],
type='master',
ttl=0,
@@ -127,7 +122,7 @@ class GandiDNSDriver(BaseGandiDriver, DN
def get_zone(self, zone_id):
zid = int(zone_id)
- self.connection.set_context({'zone_id': zid})
+ self.connection.set_context({'zone_id': zone_id})
zone = self.connection.request('domain.zone.info', zid)
return self._to_zone(zone.object)
@@ -141,13 +136,13 @@ class GandiDNSDriver(BaseGandiDriver, DN
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})
+ self.connection.set_context({'zone_id': zone.id})
zone = self.connection.request('domain.zone.update', zid, params)
return self._to_zone(zone.object)
def delete_zone(self, zone):
zid = int(zone.id)
- self.connection.set_context({'zone_id': zid})
+ self.connection.set_context({'zone_id': zone.id})
res = self.connection.request('domain.zone.delete', zid)
return res.object
@@ -170,7 +165,7 @@ class GandiDNSDriver(BaseGandiDriver, DN
def list_records(self, zone):
zid = int(zone.id)
- self.connection.set_context({'zone_id': zid})
+ self.connection.set_context({'zone_id': zone.id})
records = self.connection.request('domain.zone.record.list', zid, 0)
return self._to_records(records.object, zone)
@@ -181,7 +176,7 @@ class GandiDNSDriver(BaseGandiDriver, DN
'name': name,
'type': record_type
}
- self.connection.set_context({'zone_id': zid})
+ self.connection.set_context({'zone_id': zone_id})
records = self.connection.request('domain.zone.record.list',
zid, 0, filter_opts).object
@@ -219,7 +214,7 @@ class GandiDNSDriver(BaseGandiDriver, DN
with NewZoneVersion(self, zone) as vid:
con = self.connection
- con.set_context({'zone_id': zid})
+ con.set_context({'zone_id': zone.id})
rec = con.request('domain.zone.record.add',
zid, vid, create).object
@@ -246,7 +241,7 @@ class GandiDNSDriver(BaseGandiDriver, DN
with NewZoneVersion(self, record.zone) as vid:
con = self.connection
- con.set_context({'zone_id': zid})
+ con.set_context({'zone_id': record.zone.id})
con.request('domain.zone.record.delete',
zid, vid, filter_opts)
res = con.request('domain.zone.record.add',
@@ -264,7 +259,7 @@ class GandiDNSDriver(BaseGandiDriver, DN
with NewZoneVersion(self, record.zone) as vid:
con = self.connection
- con.set_context({'zone_id': zid})
+ con.set_context({'zone_id': record.zone.id})
count = con.request('domain.zone.record.delete',
zid, vid, filter_opts).object
Added:
libcloud/trunk/libcloud/test/compute/fixtures/softlayer/SoftLayer_Account.xml
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/fixtures/softlayer/SoftLayer_Account.xml?rev=1442533&view=auto
==============================================================================
---
libcloud/trunk/libcloud/test/compute/fixtures/softlayer/SoftLayer_Account.xml
(added)
+++
libcloud/trunk/libcloud/test/compute/fixtures/softlayer/SoftLayer_Account.xml
Tue Feb 5 10:59:52 2013
@@ -0,0 +1,17 @@
+<?xml version='1.0'?>
+<methodResponse>
+ <fault>
+ <value>
+ <struct>
+ <member>
+ <name>faultCode</name>
+ <value><string>SoftLayer_Account</string></value>
+ </member>
+ <member>
+ <name>faultString</name>
+ <value><string>Failed Call</string></value>
+ </member>
+ </struct>
+ </value>
+ </fault>
+</methodResponse>
Added: libcloud/trunk/libcloud/test/compute/fixtures/softlayer/fail.xml
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/fixtures/softlayer/fail.xml?rev=1442533&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/compute/fixtures/softlayer/fail.xml (added)
+++ libcloud/trunk/libcloud/test/compute/fixtures/softlayer/fail.xml Tue Feb 5
10:59:52 2013
@@ -0,0 +1,17 @@
+<?xml version='1.0'?>
+<methodResponse>
+ <fault>
+ <value>
+ <struct>
+ <member>
+ <name>faultCode</name>
+ <value><string>fail</string></value>
+ </member>
+ <member>
+ <name>faultString</name>
+ <value><string>Failed Call</string></value>
+ </member>
+ </struct>
+ </value>
+ </fault>
+</methodResponse>
Modified: libcloud/trunk/libcloud/test/compute/test_softlayer.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/test_softlayer.py?rev=1442533&r1=1442532&r2=1442533&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/compute/test_softlayer.py (original)
+++ libcloud/trunk/libcloud/test/compute/test_softlayer.py Tue Feb 5 10:59:52
2013
@@ -34,33 +34,12 @@ from libcloud.test.file_fixtures import
from libcloud.test.secrets import SOFTLAYER_PARAMS
-class MockSoftLayerTransport(xmlrpclib.Transport):
-
- def request(self, host, handler, request_body, verbose=0):
- self.verbose = 0
-
- if 'SOFTLAYEREXCEPTION' in u(request_body):
- raise xmlrpclib.Fault('fail', 'Failed Call')
- if 'INVALIDCREDSERROR' in u(request_body):
- raise xmlrpclib.Fault('SoftLayer_Account', 'Failed Call')
-
- method = ET.XML(request_body).find('methodName').text
- mock = SoftLayerMockHttp(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 SoftLayerTests(unittest.TestCase):
def setUp(self):
- SoftLayer.connectionCls.proxyCls.transportCls = [
- MockSoftLayerTransport, MockSoftLayerTransport]
+ SoftLayer.connectionCls.conn_classes = (
+ SoftLayerMockHttp, SoftLayerMockHttp)
+ SoftLayerMockHttp.type = None
self.driver = SoftLayer(*SOFTLAYER_PARAMS)
def test_list_nodes(self):
@@ -94,6 +73,7 @@ class SoftLayerTests(unittest.TestCase):
image=self.driver.list_images()[0])
def test_create_fail(self):
+ SoftLayerMockHttp.type = "SOFTLAYEREXCEPTION"
self.assertRaises(
SoftLayerException,
self.driver.create_node,
@@ -103,6 +83,7 @@ class SoftLayerTests(unittest.TestCase):
image=self.driver.list_images()[0])
def test_create_creds_error(self):
+ SoftLayerMockHttp.type = "INVALIDCREDSERROR"
self.assertRaises(
InvalidCredsError,
self.driver.create_node,
@@ -153,6 +134,15 @@ class SoftLayerTests(unittest.TestCase):
class SoftLayerMockHttp(MockHttp):
fixtures = ComputeFileFixtures('softlayer')
+ def _get_method_name(self, type, use_param, qs, path):
+ return "_xmlrpc"
+
+ def _xmlrpc(self, method, url, body, headers):
+ params, meth_name = xmlrpclib.loads(body)
+ url = url.replace("/", "_")
+ meth_name = "%s_%s" % (url, meth_name)
+ return getattr(self, meth_name)(method, url, body, headers)
+
def _xmlrpc_v3__SoftLayer_Virtual_Guest_getCreateObjectOptions(
self, method, url, body, headers):
body = self.fixtures.load(
@@ -172,8 +162,12 @@ class SoftLayerMockHttp(MockHttp):
def _xmlrpc_v3__SoftLayer_Virtual_Guest_createObject(
self, method, url, body, headers):
- body = self.fixtures.load(
- 'v3__SoftLayer_Virtual_Guest_createObject.xml')
+ fixture = {
+ None: 'v3__SoftLayer_Virtual_Guest_createObject.xml',
+ 'INVALIDCREDSERROR': 'SoftLayer_Account.xml',
+ 'SOFTLAYEREXCEPTION': 'fail.xml',
+ }[self.type]
+ body = self.fixtures.load(fixture)
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _xmlrpc_v3__SoftLayer_Virtual_Guest_getObject(
@@ -192,5 +186,6 @@ class SoftLayerMockHttp(MockHttp):
body = self.fixtures.load('empty.xml')
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
if __name__ == '__main__':
sys.exit(unittest.main())
Modified: libcloud/trunk/libcloud/test/compute/test_vcl.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/test_vcl.py?rev=1442533&r1=1442532&r2=1442533&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/compute/test_vcl.py (original)
+++ libcloud/trunk/libcloud/test/compute/test_vcl.py Tue Feb 5 10:59:52 2013
@@ -29,31 +29,13 @@ from libcloud.test import MockHttp
from libcloud.test.file_fixtures import ComputeFileFixtures
from libcloud.test.secrets import VCL_PARAMS
-class MockVCLTransport(xmlrpclib.Transport):
-
- def __init__(self, datetime, user, passwd, host):
- self._use_datetime = datetime
- self._connection = (None, None)
- self._extra_headers = []
- self._use_builtin_types = False
-
- def request(self, host, handler, request_body, verbose=0):
- self.verbose = 0
- method = ET.XML(request_body).find('methodName').text
- mock = VCLMockHttp(host, 80)
- mock.request('POST', 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 VCLTests(unittest.TestCase):
def setUp(self):
- VCL.connectionCls.proxyCls.transportCls = MockVCLTransport
+ VCL.connectionCls.conn_classes = (
+ VCLMockHttp, VCLMockHttp)
+ VCLMockHttp.type = None
self.driver = VCL(*VCL_PARAMS)
def test_list_nodes(self):
@@ -98,50 +80,48 @@ class VCLTests(unittest.TestCase):
1334168100
)
+
class VCLMockHttp(MockHttp):
fixtures = ComputeFileFixtures('vcl')
+ def _get_method_name(self, type, use_param, qs, path):
+ return "_xmlrpc"
+
+ def _xmlrpc(self, method, url, body, headers):
+ params, meth_name = xmlrpclib.loads(body)
+ if self.type:
+ meth_name = "%s_%s" % (meth_name, self.type)
+ return getattr(self, meth_name)(method, url, body, headers)
+
def XMLRPCgetImages(self, method, url, body, headers):
body = self.fixtures.load('XMLRPCgetImages.xml')
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- def XMLRPCextendRequest(
- self, method, url, body, headers):
-
+ def XMLRPCextendRequest(self, method, url, body, headers):
body = self.fixtures.load('XMLRPCextendRequest.xml')
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- def XMLRPCgetRequestIds(
- self, method, url, body, headers):
-
+ def XMLRPCgetRequestIds(self, method, url, body, headers):
body = self.fixtures.load(
'XMLRPCgetRequestIds.xml')
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- def XMLRPCgetRequestStatus(
- self, method, url, body, headers):
-
+ def XMLRPCgetRequestStatus(self, method, url, body, headers):
body = self.fixtures.load(
'XMLRPCgetRequestStatus.xml')
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- def XMLRPCendRequest(
- self, method, url, body, headers):
-
+ def XMLRPCendRequest(self, method, url, body, headers):
body = self.fixtures.load(
'XMLRPCendRequest.xml')
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- def XMLRPCaddRequest(
- self, method, url, body, headers):
-
+ def XMLRPCaddRequest(self, method, url, body, headers):
body = self.fixtures.load(
'XMLRPCaddRequest.xml')
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- def XMLRPCgetRequestConnectData(
- self, method, url, body, headers):
-
+ def XMLRPCgetRequestConnectData(self, method, url, body, headers):
body = self.fixtures.load(
'XMLRPCgetRequestConnectData.xml')
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
Modified: libcloud/trunk/libcloud/test/dns/test_gandi.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/dns/test_gandi.py?rev=1442533&r1=1442532&r2=1442533&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/dns/test_gandi.py (original)
+++ libcloud/trunk/libcloud/test/dns/test_gandi.py Tue Feb 5 10:59:52 2013
@@ -247,39 +247,48 @@ class GandiMockHttp(BaseGandiMockHttp):
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):
+ 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):
+ 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):
+ 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):
+ 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):
+ 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):
+ 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):
+ 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):
+ 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):
+ 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])