libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From anthonys...@apache.org
Subject [23/51] [abbrv] libcloud git commit: merge update
Date Mon, 09 Jan 2017 04:52:43 GMT
merge update


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

Branch: refs/heads/trunk
Commit: 68bf8534721f2af37dbff71a00f9efcfd608130a
Parents: da4327a c06497f
Author: Anthony Shaw <anthonyshaw@apache.org>
Authored: Wed Jan 4 14:42:10 2017 +1100
Committer: Anthony Shaw <anthonyshaw@apache.org>
Committed: Wed Jan 4 14:42:10 2017 +1100

----------------------------------------------------------------------
 .travis.yml                                     |    15 +-
 CHANGES.rst                                     |   209 +-
 doap_libcloud.rdf                               |    14 +
 .../images/provider_logos/cloudscale.png        |   Bin 0 -> 61533 bytes
 .../_supported_methods_block_storage.rst        |     4 +
 .../_supported_methods_image_management.rst     |     4 +
 .../_supported_methods_key_pair_management.rst  |     4 +
 docs/compute/_supported_methods_main.rst        |     4 +
 docs/compute/_supported_providers.rst           |   120 +-
 docs/compute/drivers/cloudscale.rst             |    75 +
 .../cloudscale/cloudscale_compute_simple.py     |    27 +
 libcloud/__init__.py                            |     2 +-
 libcloud/common/azure_arm.py                    |     6 +-
 libcloud/common/base.py                         |    10 +-
 libcloud/common/dimensiondata.py                |    56 +-
 libcloud/common/openstack_identity.py           |   140 +-
 libcloud/common/ovh.py                          |     2 +-
 libcloud/compute/drivers/azure_arm.py           |    34 +
 libcloud/compute/drivers/cloudscale.py          |   246 +
 libcloud/compute/drivers/dimensiondata.py       |   610 +-
 libcloud/compute/drivers/ec2.py                 |    39 +-
 libcloud/compute/drivers/gce.py                 |    56 +-
 libcloud/compute/providers.py                   |     2 +
 libcloud/compute/types.py                       |     3 +
 libcloud/container/drivers/rancher.py           |    71 +-
 libcloud/httplib_ssl.py                         |     7 +-
 libcloud/loadbalancer/drivers/gce.py            |     5 +-
 libcloud/test/backup/test_dimensiondata.py      |   502 -
 libcloud/test/backup/test_dimensiondata_v2_3.py |   503 +
 libcloud/test/backup/test_dimensiondata_v2_4.py |   503 +
 libcloud/test/common/test_base_driver.py        |     6 -
 libcloud/test/common/test_openstack_identity.py |    49 +-
 ...9_providers_Microsoft_Commerce_RateCard.json | 84270 +++++++++++++++++
 .../fixtures/cloudscale/create_node.json        |    46 +
 .../fixtures/cloudscale/list_images.json        |     1 +
 .../compute/fixtures/cloudscale/list_nodes.json |    48 +
 .../compute/fixtures/cloudscale/list_sizes.json |     2 +
 .../2.4/change_nic_networkadapter_response.xml  |     7 +
 .../2.4/exchange_nic_vlans_response.xml         |     8 +
 .../dimensiondata/2.4/image_customerImage.xml   |    57 +
 ...age_2ffa36c8_1848_49eb_b4fa_9d908775f68c.xml |    19 +
 ...age_5234e5c7_01de_4411_8b6e_baeb8d91cf5d.xml |    19 +
 .../dimensiondata/2.4/image_osImage.xml         |    40 +
 ...age_6b4fb0c7_a57b_4f58_b59c_9958f94f971a.xml |    13 +
 ...age_c14b1a46_2428_44c1_9c1a_b20e6418d08c.xml |    14 +
 .../dimensiondata/2.4/import_image_response.xml |     8 +
 .../dimensiondata/2.4/server_GetServer.xml      |    42 +
 .../dimensiondata/2.4/server_cleanServer.xml    |     9 +
 .../dimensiondata/2.4/server_clone_response.xml |     9 +
 .../dimensiondata/2.4/server_server.xml         |   192 +
 .../dimensiondata/2.4/server_server_NA3.xml     |    56 +
 ...ver_e75ead52_692f_4314_8725_c8a4f4d13a87.xml |    30 +
 .../2.4/server_server_paginated.xml             |    58 +
 .../compute/fixtures/fcu/ex_describe_quota.xml  |    53 -
 .../compute/fixtures/fcu/ex_describe_quotas.xml |    53 +
 .../projects_coreos-cloud_global_images.json    |     3 +
 ...al1-b_instances_libcloud-lb-nopubip-001.json |    49 +
 libcloud/test/compute/test_azure_arm.py         |    11 +-
 libcloud/test/compute/test_cloudscale.py        |   122 +
 libcloud/test/compute/test_dimensiondata.py     |  3279 -
 .../test/compute/test_dimensiondata_v2_3.py     |  3283 +
 .../test/compute/test_dimensiondata_v2_4.py     |  3359 +
 libcloud/test/compute/test_ec2.py               |    22 +-
 libcloud/test/compute/test_gce.py               |    18 +-
 libcloud/test/container/test_rancher.py         |   215 +-
 libcloud/test/loadbalancer/test_alb.py          |     2 +-
 .../test/loadbalancer/test_dimensiondata.py     |   619 -
 .../loadbalancer/test_dimensiondata_v2_3.py     |   620 +
 .../loadbalancer/test_dimensiondata_v2_4.py     |   620 +
 libcloud/test/loadbalancer/test_gce.py          |     7 +
 libcloud/test/secrets.py-dist                   |     3 +-
 libcloud/test/test_httplib_ssl.py               |    63 -
 libcloud/test/test_utils.py                     |     2 +-
 libcloud/utils/loggingconnection.py             |     4 +-
 requirements-tests.txt                          |     2 +-
 setup.py                                        |     7 +-
 tox.ini                                         |    13 +-
 77 files changed, 95837 insertions(+), 4868 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/.travis.yml
----------------------------------------------------------------------
diff --cc .travis.yml
index 88c38dc,6ca1e49..e78aeb1
--- a/.travis.yml
+++ b/.travis.yml
@@@ -4,7 -4,7 +4,6 @@@ sudo: fals
  python:
      - 2.6
      - 2.7
--    - 3.2
      - 3.3
      - 3.4
      - 3.5
@@@ -29,9 -30,9 +29,6 @@@ matrix
      - env: ENV=2.7-lxml
        python: 2.7
        before_script: TOX_ENV=py2.7-lxml
--    - env: ENV=3.2-lxml
--      python: 3.2
--      before_script: TOX_ENV=py3.2-lxml
      - env: ENV=3.3-lxml
        python: 3.3
        before_script: TOX_ENV=py3.3-lxml
@@@ -69,8 -73,8 +69,6 @@@
      - env: ENV=2.7-lxml
      - env: ENV=pypy-lxml
      - env: ENV=pypy3-lxml
--    - env: ENV=py3.2-lxml
--    - env: ENV=3.2-lxml
      - env: ENV=3.3-lxml
      - env: ENV=3.4-lxml
      - env: ENV=3.5-lxml

http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/__init__.py
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/common/azure_arm.py
----------------------------------------------------------------------
diff --cc libcloud/common/azure_arm.py
index 89b004f,89b004f..7ad64dc
--- a/libcloud/common/azure_arm.py
+++ b/libcloud/common/azure_arm.py
@@@ -23,7 -23,7 +23,7 @@@ import tim
  from libcloud.common.base import (ConnectionUserAndKey,
                                    JsonResponse,
                                    RawResponse)
--from libcloud.httplib_ssl import LibcloudHTTPSConnection
++from libcloud.httplib_ssl import LibcloudConnection
  from libcloud.utils.py3 import basestring, urlencode
  
  
@@@ -61,7 -61,7 +61,7 @@@ class AzureResourceManagementConnection
      Represents a single connection to Azure
      """
  
--    conn_classes = (None, LibcloudHTTPSConnection)
++    conn_class = LibcloudConnection
      driver = AzureBaseDriver
      name = 'Azure AD Auth'
      responseCls = AzureJsonResponse
@@@ -91,7 -91,7 +91,7 @@@
          Log in and get bearer token used to authorize API requests.
          """
  
--        conn = self.conn_classes[1](self.login_host, 443)
++        conn = self.conn_class(self.login_host, 443)
          conn.connect()
          params = urlencode({
              "grant_type": "client_credentials",

http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/common/base.py
----------------------------------------------------------------------
diff --cc libcloud/common/base.py
index f686fac,3bf4dbd..eb3875f
--- a/libcloud/common/base.py
+++ b/libcloud/common/base.py
@@@ -1,977 -1,1209 +1,977 @@@
 -# 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 os
 -import sys
 -import ssl
 -import socket
 -import copy
 -import binascii
 -import time
 -
 -import xml.dom.minidom
 -
 -try:
 -    from lxml import etree as ET
 -except ImportError:
 -    from xml.etree import ElementTree as ET
 -
 -from pipes import quote as pquote
 -
 -try:
 -    import simplejson as json
 -except:
 -    import json
 -
 -import libcloud
 -
 -from libcloud.utils.py3 import PY3, PY25
 -from libcloud.utils.py3 import httplib
 -from libcloud.utils.py3 import urlparse
 -from libcloud.utils.py3 import urlencode
 -from libcloud.utils.py3 import StringIO
 -from libcloud.utils.py3 import u
 -from libcloud.utils.py3 import b
 -
 -from libcloud.utils.misc import lowercase_keys, retry
 -from libcloud.utils.compression import decompress_data
 -
 -from libcloud.common.exceptions import exception_from_message
 -from libcloud.common.types import LibcloudError, MalformedResponseError
 -from libcloud.httplib_ssl import LibcloudHTTPConnection
 -from libcloud.httplib_ssl import LibcloudHTTPSConnection
 -
 -__all__ = [
 -    'RETRY_FAILED_HTTP_REQUESTS',
 -
 -    'BaseDriver',
 -
 -    'Connection',
 -    'PollingConnection',
 -    'ConnectionKey',
 -    'ConnectionUserAndKey',
 -    'CertificateConnection',
 -    'LoggingHTTPConnection',
 -    'LoggingHTTPSConnection',
 -
 -    'Response',
 -    'HTTPResponse',
 -    'JsonResponse',
 -    'XmlResponse',
 -    'RawResponse'
 -]
 -
 -# Module level variable indicates if the failed HTTP requests should be retried
 -RETRY_FAILED_HTTP_REQUESTS = False
 -
 -
 -class LazyObject(object):
 -    """An object that doesn't get initialized until accessed."""
 -
 -    @classmethod
 -    def _proxy(cls, *lazy_init_args, **lazy_init_kwargs):
 -        class Proxy(cls, object):
 -            _lazy_obj = None
 -
 -            def __init__(self):
 -                # Must override the lazy_cls __init__
 -                pass
 -
 -            def __getattribute__(self, attr):
 -                lazy_obj = object.__getattribute__(self, '_get_lazy_obj')()
 -                return getattr(lazy_obj, attr)
 -
 -            def __setattr__(self, attr, value):
 -                lazy_obj = object.__getattribute__(self, '_get_lazy_obj')()
 -                setattr(lazy_obj, attr, value)
 -
 -            def _get_lazy_obj(self):
 -                lazy_obj = object.__getattribute__(self, '_lazy_obj')
 -                if lazy_obj is None:
 -                    lazy_obj = cls(*lazy_init_args, **lazy_init_kwargs)
 -                    object.__setattr__(self, '_lazy_obj', lazy_obj)
 -                return lazy_obj
 -
 -        return Proxy()
 -
 -    @classmethod
 -    def lazy(cls, *lazy_init_args, **lazy_init_kwargs):
 -        """Create a lazily instantiated instance of the subclass, cls."""
 -        return cls._proxy(*lazy_init_args, **lazy_init_kwargs)
 -
 -
 -class HTTPResponse(httplib.HTTPResponse):
 -    # On python 2.6 some calls can hang because HEAD isn't quite properly
 -    # supported.
 -    # In particular this happens on S3 when calls are made to get_object to
 -    # objects that don't exist.
 -    # This applies the behaviour from 2.7, fixing the hangs.
 -
 -    def read(self, amt=None):
 -        if self.fp is None:
 -            return ''
 -
 -        if self._method == 'HEAD':
 -            self.close()
 -            return ''
 -
 -        return httplib.HTTPResponse.read(self, amt)
 -
 -
 -class Response(object):
 -    """
 -    A base Response class to derive from.
 -    """
 -
 -    status = httplib.OK  # Response status code
 -    headers = {}  # Response headers
 -    body = None  # Raw response body
 -    object = None  # Parsed response body
 -
 -    error = None  # Reason returned by the server.
 -    connection = None  # Parent connection class
 -    parse_zero_length_body = False
 -
 -    def __init__(self, response, connection):
 -        """
 -        :param response: HTTP response object. (optional)
 -        :type response: :class:`httplib.HTTPResponse`
 -
 -        :param connection: Parent connection object.
 -        :type connection: :class:`.Connection`
 -        """
 -        self.connection = connection
 -
 -        # http.client In Python 3 doesn't automatically lowercase the header
 -        # names
 -        self.headers = lowercase_keys(dict(response.getheaders()))
 -        self.error = response.reason
 -        self.status = response.status
 -
 -        # This attribute is set when using LoggingConnection.
 -        original_data = getattr(response, '_original_data', None)
 -
 -        if original_data:
 -            # LoggingConnection already decompresses data so it can log it
 -            # which means we don't need to decompress it here.
 -            self.body = response._original_data
 -        else:
 -            self.body = self._decompress_response(body=response.read(),
 -                                                  headers=self.headers)
 -
 -        if PY3:
 -            self.body = b(self.body).decode('utf-8')
 -
 -        if not self.success():
 -            raise exception_from_message(code=self.status,
 -                                         message=self.parse_error(),
 -                                         headers=self.headers)
 -
 -        self.object = self.parse_body()
 -
 -    def parse_body(self):
 -        """
 -        Parse response body.
 -
 -        Override in a provider's subclass.
 -
 -        :return: Parsed body.
 -        :rtype: ``str``
 -        """
 -        return self.body
 -
 -    def parse_error(self):
 -        """
 -        Parse the error messages.
 -
 -        Override in a provider's subclass.
 -
 -        :return: Parsed error.
 -        :rtype: ``str``
 -        """
 -        return self.body
 -
 -    def success(self):
 -        """
 -        Determine if our request was successful.
 -
 -        The meaning of this can be arbitrary; did we receive OK status? Did
 -        the node get created? Were we authenticated?
 -
 -        :rtype: ``bool``
 -        :return: ``True`` or ``False``
 -        """
 -        return self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED]
 -
 -    def _decompress_response(self, body, headers):
 -        """
 -        Decompress a response body if it is using deflate or gzip encoding.
 -
 -        :param body: Response body.
 -        :type body: ``str``
 -
 -        :param headers: Response headers.
 -        :type headers: ``dict``
 -
 -        :return: Decompressed response
 -        :rtype: ``str``
 -        """
 -        encoding = headers.get('content-encoding', None)
 -
 -        if encoding in ['zlib', 'deflate']:
 -            body = decompress_data('zlib', body)
 -        elif encoding in ['gzip', 'x-gzip']:
 -            body = decompress_data('gzip', body)
 -        else:
 -            body = body.strip()
 -
 -        return body
 -
 -
 -class JsonResponse(Response):
 -    """
 -    A Base JSON Response class to derive from.
 -    """
 -
 -    def parse_body(self):
 -        if len(self.body) == 0 and not self.parse_zero_length_body:
 -            return self.body
 -
 -        try:
 -            body = json.loads(self.body)
 -        except:
 -            raise MalformedResponseError(
 -                'Failed to parse JSON',
 -                body=self.body,
 -                driver=self.connection.driver)
 -        return body
 -
 -    parse_error = parse_body
 -
 -
 -class XmlResponse(Response):
 -    """
 -    A Base XML Response class to derive from.
 -    """
 -
 -    def parse_body(self):
 -        if len(self.body) == 0 and not self.parse_zero_length_body:
 -            return self.body
 -
 -        try:
 -            try:
 -                body = ET.XML(self.body)
 -            except ValueError:
 -                # lxml wants a bytes and tests are basically hard-coded to str
 -                body = ET.XML(self.body.encode('utf-8'))
 -        except:
 -            raise MalformedResponseError('Failed to parse XML',
 -                                         body=self.body,
 -                                         driver=self.connection.driver)
 -        return body
 -
 -    parse_error = parse_body
 -
 -
 -class RawResponse(Response):
 -
 -    def __init__(self, connection):
 -        """
 -        :param connection: Parent connection object.
 -        :type connection: :class:`.Connection`
 -        """
 -        self._status = None
 -        self._response = None
 -        self._headers = {}
 -        self._error = None
 -        self._reason = None
 -        self.connection = connection
 -
 -    @property
 -    def response(self):
 -        if not self._response:
 -            response = self.connection.connection.getresponse()
 -            self._response, self.body = response, response
 -            if not self.success():
 -                self.parse_error()
 -        return self._response
 -
 -    @property
 -    def status(self):
 -        if not self._status:
 -            self._status = self.response.status
 -        return self._status
 -
 -    @property
 -    def headers(self):
 -        if not self._headers:
 -            self._headers = lowercase_keys(dict(self.response.getheaders()))
 -        return self._headers
 -
 -    @property
 -    def reason(self):
 -        if not self._reason:
 -            self._reason = self.response.reason
 -        return self._reason
 -
 -
 -# TODO: Move this to a better location/package
 -class LoggingConnection():
 -    """
 -    Debug class to log all HTTP(s) requests as they could be made
 -    with the curl command.
 -
 -    :cvar log: file-like object that logs entries are written to.
 -    """
 -
 -    log = None
 -    http_proxy_used = False
 -
 -    def _log_response(self, r):
 -        rv = "# -------- begin %d:%d response ----------\n" % (id(self), id(r))
 -        ht = ""
 -        v = r.version
 -        if r.version == 10:
 -            v = "HTTP/1.0"
 -        if r.version == 11:
 -            v = "HTTP/1.1"
 -        ht += "%s %s %s\r\n" % (v, r.status, r.reason)
 -        body = r.read()
 -        for h in r.getheaders():
 -            ht += "%s: %s\r\n" % (h[0].title(), h[1])
 -        ht += "\r\n"
 -
 -        # this is evil. laugh with me. ha arharhrhahahaha
 -        class fakesock(object):
 -
 -            def __init__(self, s):
 -                self.s = s
 -
 -            def makefile(self, *args, **kwargs):
 -                if PY3:
 -                    from io import BytesIO
 -                    cls = BytesIO
 -                else:
 -                    cls = StringIO
 -
 -                return cls(b(self.s))
 -        rr = r
 -        headers = lowercase_keys(dict(r.getheaders()))
 -
 -        encoding = headers.get('content-encoding', None)
 -        content_type = headers.get('content-type', None)
 -
 -        if encoding in ['zlib', 'deflate']:
 -            body = decompress_data('zlib', body)
 -        elif encoding in ['gzip', 'x-gzip']:
 -            body = decompress_data('gzip', body)
 -
 -        pretty_print = os.environ.get('LIBCLOUD_DEBUG_PRETTY_PRINT_RESPONSE',
 -                                      False)
 -
 -        if r.chunked:
 -            ht += "%x\r\n" % (len(body))
 -            ht += body.decode('utf-8')
 -            ht += "\r\n0\r\n"
 -        else:
 -            if pretty_print and content_type == 'application/json':
 -                try:
 -                    body = json.loads(body.decode('utf-8'))
 -                    body = json.dumps(body, sort_keys=True, indent=4)
 -                except:
 -                    # Invalid JSON or server is lying about content-type
 -                    pass
 -            elif pretty_print and content_type == 'text/xml':
 -                try:
 -                    elem = xml.dom.minidom.parseString(body.decode('utf-8'))
 -                    body = elem.toprettyxml()
 -                except Exception:
 -                    # Invalid XML
 -                    pass
 -
 -            ht += u(body)
 -
 -        if sys.version_info >= (2, 6) and sys.version_info < (2, 7):
 -            cls = HTTPResponse
 -        else:
 -            cls = httplib.HTTPResponse
 -
 -        rr = cls(sock=fakesock(ht), method=r._method,
 -                 debuglevel=r.debuglevel)
 -        rr.begin()
 -        rv += ht
 -        rv += ("\n# -------- end %d:%d response ----------\n"
 -               % (id(self), id(r)))
 -
 -        rr._original_data = body
 -        return (rr, rv)
 -
 -    def _log_curl(self, method, url, body, headers):
 -        # pylint: disable=no-member
 -
 -        cmd = ["curl"]
 -
 -        if self.http_proxy_used:
 -            if self.proxy_username and self.proxy_password:
 -                proxy_url = 'http://%s:%s@%s:%s' % (self.proxy_username,
 -                                                    self.proxy_password,
 -                                                    self.proxy_host,
 -                                                    self.proxy_port)
 -            else:
 -                proxy_url = 'http://%s:%s' % (self.proxy_host,
 -                                              self.proxy_port)
 -            proxy_url = pquote(proxy_url)
 -            cmd.extend(['--proxy', proxy_url])
 -
 -        cmd.extend(['-i'])
 -
 -        if method.lower() == 'head':
 -            # HEAD method need special handling
 -            cmd.extend(["--head"])
 -        else:
 -            cmd.extend(["-X", pquote(method)])
 -
 -        for h in headers:
 -            cmd.extend(["-H", pquote("%s: %s" % (h, headers[h]))])
 -
 -        cert_file = getattr(self, 'cert_file', None)
 -
 -        if cert_file:
 -            cmd.extend(["--cert", pquote(cert_file)])
 -
 -        # TODO: in python 2.6, body can be a file-like object.
 -        if body is not None and len(body) > 0:
 -            cmd.extend(["--data-binary", pquote(body)])
 -
 -        cmd.extend(["--compress"])
 -        cmd.extend([pquote("%s://%s:%d%s" % (self.protocol, self.host,
 -                                             self.port, url))])
 -        return " ".join(cmd)
 -
 -
 -class LoggingHTTPSConnection(LoggingConnection, LibcloudHTTPSConnection):
 -    """
 -    Utility Class for logging HTTPS connections
 -    """
 -
 -    protocol = 'https'
 -
 -    def getresponse(self):
 -        r = LibcloudHTTPSConnection.getresponse(self)
 -        if self.log is not None:
 -            r, rv = self._log_response(r)
 -            self.log.write(rv + "\n")
 -            self.log.flush()
 -        return r
 -
 -    def request(self, method, url, body=None, headers=None):
 -        headers.update({'X-LC-Request-ID': str(id(self))})
 -        if self.log is not None:
 -            pre = "# -------- begin %d request ----------\n" % id(self)
 -            self.log.write(pre +
 -                           self._log_curl(method, url, body, headers) + "\n")
 -            self.log.flush()
 -        return LibcloudHTTPSConnection.request(self, method, url, body,
 -                                               headers)
 -
 -
 -class LoggingHTTPConnection(LoggingConnection, LibcloudHTTPConnection):
 -    """
 -    Utility Class for logging HTTP connections
 -    """
 -
 -    protocol = 'http'
 -
 -    def getresponse(self):
 -        r = LibcloudHTTPConnection.getresponse(self)
 -        if self.log is not None:
 -            r, rv = self._log_response(r)
 -            self.log.write(rv + "\n")
 -            self.log.flush()
 -        return r
 -
 -    def request(self, method, url, body=None, headers=None):
 -        headers.update({'X-LC-Request-ID': str(id(self))})
 -        if self.log is not None:
 -            pre = '# -------- begin %d request ----------\n' % id(self)
 -            self.log.write(pre +
 -                           self._log_curl(method, url, body, headers) + "\n")
 -            self.log.flush()
 -        return LibcloudHTTPConnection.request(self, method, url,
 -                                              body, headers)
 -
 -
 -class Connection(object):
 -    """
 -    A Base Connection class to derive from.
 -    """
 -    # conn_classes = (LoggingHTTPSConnection)
 -    conn_classes = (LibcloudHTTPConnection, LibcloudHTTPSConnection)
 -
 -    responseCls = Response
 -    rawResponseCls = RawResponse
 -    connection = None
 -    host = '127.0.0.1'
 -    port = 443
 -    timeout = None
 -    secure = 1
 -    driver = None
 -    action = None
 -    cache_busting = False
 -    backoff = None
 -    retry_delay = None
 -
 -    allow_insecure = True
 -    skip_host = True
 -    skip_accept_encoding = True
 -
 -    def __init__(self, secure=True, host=None, port=None, url=None,
 -                 timeout=None, proxy_url=None, retry_delay=None, backoff=None):
 -        self.secure = secure and 1 or 0
 -        self.ua = []
 -        self.context = {}
 -
 -        if not self.allow_insecure and not secure:
 -            # TODO: We should eventually switch to whitelist instead of
 -            # blacklist approach
 -            raise ValueError('Non https connections are not allowed (use '
 -                             'secure=True)')
 -
 -        self.request_path = ''
 -
 -        if host:
 -            self.host = host
 -
 -        if port is not None:
 -            self.port = port
 -        else:
 -            if self.secure == 1:
 -                self.port = 443
 -            else:
 -                self.port = 80
 -
 -        if url:
 -            (self.host, self.port, self.secure,
 -             self.request_path) = self._tuple_from_url(url)
 -
 -        self.timeout = timeout or self.timeout
 -        self.retry_delay = retry_delay
 -        self.backoff = backoff
 -        self.proxy_url = proxy_url
 -
 -    def set_http_proxy(self, proxy_url):
 -        """
 -        Set a HTTP proxy which will be used with this connection.
 -
 -        :param proxy_url: Proxy URL (e.g. http://<hostname>:<port> without
 -                          authentication and
 -                          http://<username>:<password>@<hostname>:<port> for
 -                          basic auth authentication information.
 -        :type proxy_url: ``str``
 -        """
 -        self.proxy_url = proxy_url
 -
 -    def set_context(self, context):
 -        if not isinstance(context, dict):
 -            raise TypeError('context needs to be a dictionary')
 -
 -        self.context = context
 -
 -    def reset_context(self):
 -        self.context = {}
 -
 -    def _tuple_from_url(self, url):
 -        secure = 1
 -        port = None
 -        (scheme, netloc, request_path, param,
 -         query, fragment) = urlparse.urlparse(url)
 -
 -        if scheme not in ['http', 'https']:
 -            raise LibcloudError('Invalid scheme: %s in url %s' % (scheme, url))
 -
 -        if scheme == "http":
 -            secure = 0
 -
 -        if ":" in netloc:
 -            netloc, port = netloc.rsplit(":")
 -            port = int(port)
 -
 -        if not port:
 -            if scheme == "http":
 -                port = 80
 -            else:
 -                port = 443
 -
 -        host = netloc
 -        port = int(port)
 -
 -        return (host, port, secure, request_path)
 -
 -    def connect(self, host=None, port=None, base_url=None, **kwargs):
 -        """
 -        Establish a connection with the API server.
 -
 -        :type host: ``str``
 -        :param host: Optional host to override our default
 -
 -        :type port: ``int``
 -        :param port: Optional port to override our default
 -
 -        :returns: A connection
 -        """
 -        # prefer the attribute base_url if its set or sent
 -        connection = None
 -        secure = self.secure
 -
 -        # pylint: disable=no-member
 -
 -        if getattr(self, 'base_url', None) and base_url is None:
 -            (host, port,
 -             secure, request_path) = self._tuple_from_url(self.base_url)
 -        elif base_url is not None:
 -            (host, port,
 -             secure, request_path) = self._tuple_from_url(base_url)
 -        else:
 -            host = host or self.host
 -            port = port or self.port
 -
 -        # Make sure port is an int
 -        port = int(port)
 -
 -        if not hasattr(kwargs, 'host'):
 -            kwargs.update({'host': host})
 -
 -        if not hasattr(kwargs, 'port'):
 -            kwargs.update({'port': port})
 -
 -        if not hasattr(kwargs, 'key_file') and hasattr(self, 'key_file'):
 -            kwargs.update({'key_file': self.key_file})
 -
 -        if not hasattr(kwargs, 'cert_file') and hasattr(self, 'cert_file'):
 -            kwargs.update({'cert_file': self.cert_file})
 -
 -        #  kwargs = {'host': host, 'port': int(port)}
 -
 -        # Timeout is only supported in Python 2.6 and later
 -        # http://docs.python.org/library/httplib.html#httplib.HTTPConnection
 -        if self.timeout and not PY25:
 -            kwargs.update({'timeout': self.timeout})
 -
 -        if self.proxy_url:
 -            kwargs.update({'proxy_url': self.proxy_url})
 -
 -        connection = self.conn_classes[secure](**kwargs)
 -        # You can uncoment this line, if you setup a reverse proxy server
 -        # which proxies to your endpoint, and lets you easily capture
 -        # connections in cleartext when you setup the proxy to do SSL
 -        # for you
 -        # connection = self.conn_classes[False]("127.0.0.1", 8080)
 -
 -        self.connection = connection
 -
 -    def _user_agent(self):
 -        user_agent_suffix = ' '.join(['(%s)' % x for x in self.ua])
 -
 -        if self.driver:
 -            user_agent = 'libcloud/%s (%s) %s' % (
 -                libcloud.__version__,
 -                self.driver.name, user_agent_suffix)
 -        else:
 -            user_agent = 'libcloud/%s %s' % (
 -                libcloud.__version__, user_agent_suffix)
 -
 -        return user_agent
 -
 -    def user_agent_append(self, token):
 -        """
 -        Append a token to a user agent string.
 -
 -        Users of the library should call this to uniquely identify their
 -        requests to a provider.
 -
 -        :type token: ``str``
 -        :param token: Token to add to the user agent.
 -        """
 -        self.ua.append(token)
 -
 -    def request(self, action, params=None, data=None, headers=None,
 -                method='GET', raw=False):
 -        """
 -        Request a given `action`.
 -
 -        Basically a wrapper around the connection
 -        object's `request` that does some helpful pre-processing.
 -
 -        :type action: ``str``
 -        :param action: A path. This can include arguments. If included,
 -            any extra parameters are appended to the existing ones.
 -
 -        :type params: ``dict``
 -        :param params: Optional mapping of additional parameters to send. If
 -            None, leave as an empty ``dict``.
 -
 -        :type data: ``unicode``
 -        :param data: A body of data to send with the request.
 -
 -        :type headers: ``dict``
 -        :param headers: Extra headers to add to the request
 -            None, leave as an empty ``dict``.
 -
 -        :type method: ``str``
 -        :param method: An HTTP method such as "GET" or "POST".
 -
 -        :type raw: ``bool``
 -        :param raw: True to perform a "raw" request aka only send the headers
 -                     and use the rawResponseCls class. This is used with
 -                     storage API when uploading a file.
 -
 -        :return: An :class:`Response` instance.
 -        :rtype: :class:`Response` instance
 -
 -        """
 -        if params is None:
 -            params = {}
 -        else:
 -            params = copy.copy(params)
 -
 -        if headers is None:
 -            headers = {}
 -        else:
 -            headers = copy.copy(headers)
 -
 -        retry_enabled = os.environ.get('LIBCLOUD_RETRY_FAILED_HTTP_REQUESTS',
 -                                       False) or RETRY_FAILED_HTTP_REQUESTS
 -
 -        action = self.morph_action_hook(action)
 -        self.action = action
 -        self.method = method
 -        self.data = data
 -
 -        # Extend default parameters
 -        params = self.add_default_params(params)
 -
 -        # Add cache busting parameters (if enabled)
 -        if self.cache_busting and method == 'GET':
 -            params = self._add_cache_busting_to_params(params=params)
 -
 -        # Extend default headers
 -        headers = self.add_default_headers(headers)
 -
 -        # We always send a user-agent header
 -        headers.update({'User-Agent': self._user_agent()})
 -
 -        # Indicate that we support gzip and deflate compression
 -        headers.update({'Accept-Encoding': 'gzip,deflate'})
 -
 -        port = int(self.port)
 -
 -        if port not in (80, 443):
 -            headers.update({'Host': "%s:%d" % (self.host, port)})
 -        else:
 -            headers.update({'Host': self.host})
 -
 -        skip_host = self.skip_host
 -        skip_accept_encoding = self.skip_accept_encoding
 -
 -        if data:
 -            data = self.encode_data(data)
 -            headers['Content-Length'] = str(len(data))
 -        elif method.upper() in ['POST', 'PUT'] and not raw:
 -            # Only send Content-Length 0 with POST and PUT request.
 -            #
 -            # Note: Content-Length is not added when using "raw" mode means
 -            # means that headers are upfront and the body is sent at some point
 -            # later on. With raw mode user can specify Content-Length with
 -            # "data" not being set.
 -            headers['Content-Length'] = '0'
 -
 -        params, headers = self.pre_connect_hook(params, headers)
 -
 -        if params:
 -            if '?' in action:
 -                url = '&'.join((action, urlencode(params, doseq=True)))
 -            else:
 -                url = '?'.join((action, urlencode(params, doseq=True)))
 -        else:
 -            url = action
 -
 -        # Removed terrible hack...this a less-bad hack that doesn't execute a
 -        # request twice, but it's still a hack.
 -        self.connect()
 -        try:
 -            # @TODO: Should we just pass File object as body to request method
 -            # instead of dealing with splitting and sending the file ourselves?
 -            if raw:
 -                self.connection.putrequest(
 -                    method,
 -                    url,
 -                    skip_host=skip_host,
 -                    skip_accept_encoding=skip_accept_encoding)
 -
 -                for key, value in list(headers.items()):
 -                    self.connection.putheader(key, str(value))
 -
 -                self.connection.endheaders()
 -            else:
 -                if retry_enabled:
 -                    retry_request = retry(timeout=self.timeout,
 -                                          retry_delay=self.retry_delay,
 -                                          backoff=self.backoff)
 -                    retry_request(self.connection.request)(method=method,
 -                                                           url=url,
 -                                                           body=data,
 -                                                           headers=headers)
 -                else:
 -                    self.connection.request(method=method, url=url, body=data,
 -                                            headers=headers)
 -        except socket.gaierror:
 -            e = sys.exc_info()[1]
 -            message = str(e)
 -            errno = getattr(e, 'errno', None)
 -
 -            if errno == -5:
 -                # Throw a more-friendly exception on "no address associated
 -                # with hostname" error. This error could simpli indicate that
 -                # "host" Connection class attribute is set to an incorrect
 -                # value
 -                class_name = self.__class__.__name__
 -                msg = ('%s. Perhaps "host" Connection class attribute '
 -                       '(%s.connection) is set to an invalid, non-hostname '
 -                       'value (%s)?' %
 -                       (message, class_name, self.host))
 -                raise socket.gaierror(msg)
 -            self.reset_context()
 -            raise e
 -        except ssl.SSLError:
 -            e = sys.exc_info()[1]
 -            self.reset_context()
 -            raise ssl.SSLError(str(e))
 -
 -        if raw:
 -            responseCls = self.rawResponseCls
 -            kwargs = {'connection': self}
 -        else:
 -            responseCls = self.responseCls
 -            kwargs = {'connection': self,
 -                      'response': self.connection.getresponse()}
 -
 -        try:
 -            response = responseCls(**kwargs)
 -        finally:
 -            # Always reset the context after the request has completed
 -            self.reset_context()
 -
 -        return response
 -
 -    def morph_action_hook(self, action):
 -        if not action.startswith("/"):
 -            action = "/" + action
 -        return self.request_path + action
 -
 -    def add_default_params(self, params):
 -        """
 -        Adds default parameters (such as API key, version, etc.)
 -        to the passed `params`
 -
 -        Should return a dictionary.
 -        """
 -        return params
 -
 -    def add_default_headers(self, headers):
 -        """
 -        Adds default headers (such as Authorization, X-Foo-Bar)
 -        to the passed `headers`
 -
 -        Should return a dictionary.
 -        """
 -        return headers
 -
 -    def pre_connect_hook(self, params, headers):
 -        """
 -        A hook which is called before connecting to the remote server.
 -        This hook can perform a final manipulation on the params, headers and
 -        url parameters.
 -
 -        :type params: ``dict``
 -        :param params: Request parameters.
 -
 -        :type headers: ``dict``
 -        :param headers: Request headers.
 -        """
 -        return params, headers
 -
 -    def encode_data(self, data):
 -        """
 -        Encode body data.
 -
 -        Override in a provider's subclass.
 -        """
 -        return data
 -
 -    def _add_cache_busting_to_params(self, params):
 -        """
 -        Add cache busting parameter to the query parameters of a GET request.
 -
 -        Parameters are only added if "cache_busting" class attribute is set to
 -        True.
 -
 -        Note: This should only be used with *naughty* providers which use
 -        excessive caching of responses.
 -        """
 -        cache_busting_value = binascii.hexlify(os.urandom(8)).decode('ascii')
 -
 -        if isinstance(params, dict):
 -            params['cache-busting'] = cache_busting_value
 -        else:
 -            params.append(('cache-busting', cache_busting_value))
 -
 -        return params
 -
 -
 -class PollingConnection(Connection):
 -    """
 -    Connection class which can also work with the async APIs.
 -
 -    After initial requests, this class periodically polls for jobs status and
 -    waits until the job has finished.
 -    If job doesn't finish in timeout seconds, an Exception thrown.
 -    """
 -    poll_interval = 0.5
 -    timeout = 200
 -    request_method = 'request'
 -
 -    def async_request(self, action, params=None, data=None, headers=None,
 -                      method='GET', context=None):
 -        """
 -        Perform an 'async' request to the specified path. Keep in mind that
 -        this function is *blocking* and 'async' in this case means that the
 -        hit URL only returns a job ID which is the periodically polled until
 -        the job has completed.
 -
 -        This function works like this:
 -
 -        - Perform a request to the specified path. Response should contain a
 -          'job_id'.
 -
 -        - Returned 'job_id' is then used to construct a URL which is used for
 -          retrieving job status. Constructed URL is then periodically polled
 -          until the response indicates that the job has completed or the
 -          timeout of 'self.timeout' seconds has been reached.
 -
 -        :type action: ``str``
 -        :param action: A path
 -
 -        :type params: ``dict``
 -        :param params: Optional mapping of additional parameters to send. If
 -            None, leave as an empty ``dict``.
 -
 -        :type data: ``unicode``
 -        :param data: A body of data to send with the request.
 -
 -        :type headers: ``dict``
 -        :param headers: Extra headers to add to the request
 -            None, leave as an empty ``dict``.
 -
 -        :type method: ``str``
 -        :param method: An HTTP method such as "GET" or "POST".
 -
 -        :type context: ``dict``
 -        :param context: Context dictionary which is passed to the functions
 -                        which construct initial and poll URL.
 -
 -        :return: An :class:`Response` instance.
 -        :rtype: :class:`Response` instance
 -        """
 -
 -        request = getattr(self, self.request_method)
 -        kwargs = self.get_request_kwargs(action=action, params=params,
 -                                         data=data, headers=headers,
 -                                         method=method,
 -                                         context=context)
 -        response = request(**kwargs)
 -        kwargs = self.get_poll_request_kwargs(response=response,
 -                                              context=context,
 -                                              request_kwargs=kwargs)
 -
 -        end = time.time() + self.timeout
 -        completed = False
 -        while time.time() < end and not completed:
 -            response = request(**kwargs)
 -            completed = self.has_completed(response=response)
 -            if not completed:
 -                time.sleep(self.poll_interval)
 -
 -        if not completed:
 -            raise LibcloudError('Job did not complete in %s seconds' %
 -                                (self.timeout))
 -
 -        return response
 -
 -    def get_request_kwargs(self, action, params=None, data=None, headers=None,
 -                           method='GET', context=None):
 -        """
 -        Arguments which are passed to the initial request() call inside
 -        async_request.
 -        """
 -        kwargs = {'action': action, 'params': params, 'data': data,
 -                  'headers': headers, 'method': method}
 -        return kwargs
 -
 -    def get_poll_request_kwargs(self, response, context, request_kwargs):
 -        """
 -        Return keyword arguments which are passed to the request() method when
 -        polling for the job status.
 -
 -        :param response: Response object returned by poll request.
 -        :type response: :class:`HTTPResponse`
 -
 -        :param request_kwargs: Kwargs previously used to initiate the
 -                                  poll request.
 -        :type response: ``dict``
 -
 -        :return ``dict`` Keyword arguments
 -        """
 -        raise NotImplementedError('get_poll_request_kwargs not implemented')
 -
 -    def has_completed(self, response):
 -        """
 -        Return job completion status.
 -
 -        :param response: Response object returned by poll request.
 -        :type response: :class:`HTTPResponse`
 -
 -        :return ``bool`` True if the job has completed, False otherwise.
 -        """
 -        raise NotImplementedError('has_completed not implemented')
 -
 -
 -class ConnectionKey(Connection):
 -    """
 -    Base connection class which accepts a single ``key`` argument.
 -    """
 -
 -    def __init__(self, key, secure=True, host=None, port=None, url=None,
 -                 timeout=None, proxy_url=None, backoff=None, retry_delay=None):
 -        """
 -        Initialize `user_id` and `key`; set `secure` to an ``int`` based on
 -        passed value.
 -        """
 -        super(ConnectionKey, self).__init__(secure=secure, host=host,
 -                                            port=port, url=url,
 -                                            timeout=timeout,
 -                                            proxy_url=proxy_url,
 -                                            backoff=backoff,
 -                                            retry_delay=retry_delay)
 -        self.key = key
 -
 -
 -class CertificateConnection(Connection):
 -    """
 -    Base connection class which accepts a single ``cert_file`` argument.
 -    """
 -
 -    def __init__(self, cert_file, secure=True, host=None, port=None, url=None,
 -                 proxy_url=None, timeout=None, backoff=None, retry_delay=None):
 -        """
 -        Initialize `cert_file`; set `secure` to an ``int`` based on
 -        passed value.
 -        """
 -        super(CertificateConnection, self).__init__(secure=secure, host=host,
 -                                                    port=port, url=url,
 -                                                    timeout=timeout,
 -                                                    backoff=backoff,
 -                                                    retry_delay=retry_delay,
 -                                                    proxy_url=proxy_url)
 -
 -        self.cert_file = cert_file
 -
 -
 -class ConnectionUserAndKey(ConnectionKey):
 -    """
 -    Base connection class which accepts a ``user_id`` and ``key`` argument.
 -    """
 -
 -    user_id = None
 -
 -    def __init__(self, user_id, key, secure=True, host=None, port=None,
 -                 url=None, timeout=None, proxy_url=None,
 -                 backoff=None, retry_delay=None):
 -        super(ConnectionUserAndKey, self).__init__(key, secure=secure,
 -                                                   host=host, port=port,
 -                                                   url=url, timeout=timeout,
 -                                                   backoff=backoff,
 -                                                   retry_delay=retry_delay,
 -                                                   proxy_url=proxy_url)
 -        self.user_id = user_id
 -
 -
 -class BaseDriver(object):
 -    """
 -    Base driver class from which other classes can inherit from.
 -    """
 -
 -    connectionCls = ConnectionKey
 -
 -    def __init__(self, key, secret=None, secure=True, host=None, port=None,
 -                 api_version=None, region=None, **kwargs):
 -        """
 -        :param    key:    API key or username to be used (required)
 -        :type     key:    ``str``
 -
 -        :param    secret: Secret password to be used (required)
 -        :type     secret: ``str``
 -
 -        :param    secure: Whether to use HTTPS or HTTP. Note: Some providers
 -                            only support HTTPS, and it is on by default.
 -        :type     secure: ``bool``
 -
 -        :param    host: Override hostname used for connections.
 -        :type     host: ``str``
 -
 -        :param    port: Override port used for connections.
 -        :type     port: ``int``
 -
 -        :param    api_version: Optional API version. Only used by drivers
 -                                 which support multiple API versions.
 -        :type     api_version: ``str``
 -
 -        :param region: Optional driver region. Only used by drivers which
 -                       support multiple regions.
 -        :type region: ``str``
 -
 -        :rtype: ``None``
 -        """
 -
 -        self.key = key
 -        self.secret = secret
 -        self.secure = secure
 -        args = [self.key]
 -
 -        if self.secret is not None:
 -            args.append(self.secret)
 -
 -        args.append(secure)
 -
 -        if host is not None:
 -            args.append(host)
 -
 -        if port is not None:
 -            args.append(port)
 -
 -        self.api_version = api_version
 -        self.region = region
 -
 -        conn_kwargs = self._ex_connection_class_kwargs()
 -
 -        # Note: We do that to make sure those additional arguments which are
 -        # provided via "_ex_connection_class_kwargs" are not overriden with
 -        # None
 -        additional_kwargs = ['timeout', 'retry_delay', 'backoff', 'proxy_url']
 -        for kwarg_name in additional_kwargs:
 -            value = kwargs.pop(kwarg_name, None)
 -
 -            # Constructor argument has precedence over
 -            # _ex_connection_class_kwargs kwarg
 -            if value is not None or kwarg_name not in conn_kwargs:
 -                conn_kwargs[kwarg_name] = value
 -
 -        self.connection = self.connectionCls(*args, **conn_kwargs)
 -
 -        self.connection.driver = self
 -        self.connection.connect()
 -
 -    @classmethod
 -    def list_regions(cls):
 -        """
 -        Method which returns a list of the available / supported regions.
 -
 -        :rtype: ``list`` of ``str``
 -        """
 -        return []
 -
 -    def _ex_connection_class_kwargs(self):
 -        """
 -        Return extra connection keyword arguments which are passed to the
 -        Connection class constructor.
 -        """
 -        return {}
 +# 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 os
 +import sys
 +import ssl
 +import socket
 +import copy
 +import binascii
 +import time
 +
 +try:
 +    from lxml import etree as ET
 +except ImportError:
 +    from xml.etree import ElementTree as ET
 +
 +
 +try:
 +    import simplejson as json
 +except:
 +    import json
 +
 +import requests
 +
 +import libcloud
 +
 +from libcloud.utils.py3 import PY25
 +from libcloud.utils.py3 import httplib
 +from libcloud.utils.py3 import urlparse
 +from libcloud.utils.py3 import urlencode
 +
 +from libcloud.utils.misc import lowercase_keys, retry
 +from libcloud.utils.compression import decompress_data
 +
 +from libcloud.common.exceptions import exception_from_message
 +from libcloud.common.types import LibcloudError, MalformedResponseError
 +from libcloud.httplib_ssl import LibcloudConnection
 +
 +__all__ = [
 +    'RETRY_FAILED_HTTP_REQUESTS',
 +
 +    'BaseDriver',
 +
 +    'Connection',
 +    'PollingConnection',
 +    'ConnectionKey',
 +    'ConnectionUserAndKey',
 +    'CertificateConnection',
 +
 +    'Response',
 +    'HTTPResponse',
 +    'JsonResponse',
 +    'XmlResponse',
 +    'RawResponse'
 +]
 +
 +# Module level variable indicates if the failed HTTP requests should be retried
 +RETRY_FAILED_HTTP_REQUESTS = False
 +
 +
 +class LazyObject(object):
 +    """An object that doesn't get initialized until accessed."""
 +
 +    @classmethod
 +    def _proxy(cls, *lazy_init_args, **lazy_init_kwargs):
 +        class Proxy(cls, object):
 +            _lazy_obj = None
 +
 +            def __init__(self):
 +                # Must override the lazy_cls __init__
 +                pass
 +
 +            def __getattribute__(self, attr):
 +                lazy_obj = object.__getattribute__(self, '_get_lazy_obj')()
 +                return getattr(lazy_obj, attr)
 +
 +            def __setattr__(self, attr, value):
 +                lazy_obj = object.__getattribute__(self, '_get_lazy_obj')()
 +                setattr(lazy_obj, attr, value)
 +
 +            def _get_lazy_obj(self):
 +                lazy_obj = object.__getattribute__(self, '_lazy_obj')
 +                if lazy_obj is None:
 +                    lazy_obj = cls(*lazy_init_args, **lazy_init_kwargs)
 +                    object.__setattr__(self, '_lazy_obj', lazy_obj)
 +                return lazy_obj
 +
 +        return Proxy()
 +
 +    @classmethod
 +    def lazy(cls, *lazy_init_args, **lazy_init_kwargs):
 +        """Create a lazily instantiated instance of the subclass, cls."""
 +        return cls._proxy(*lazy_init_args, **lazy_init_kwargs)
 +
 +
 +class HTTPResponse(httplib.HTTPResponse):
 +    # On python 2.6 some calls can hang because HEAD isn't quite properly
 +    # supported.
 +    # In particular this happens on S3 when calls are made to get_object to
 +    # objects that don't exist.
 +    # This applies the behaviour from 2.7, fixing the hangs.
 +    def read(self, amt=None):
 +        if self.fp is None:
 +            return ''
 +
 +        if self._method == 'HEAD':
 +            self.close()
 +            return ''
 +
 +        return httplib.HTTPResponse.read(self, amt)
 +
 +
 +class Response(object):
 +    """
 +    A base Response class to derive from.
 +    """
 +
 +    status = httplib.OK  # Response status code
 +    headers = {}  # Response headers
 +    body = None  # Raw response body
 +    object = None  # Parsed response body
 +
 +    error = None  # Reason returned by the server.
 +    connection = None  # Parent connection class
 +    parse_zero_length_body = False
 +
 +    def __init__(self, response, connection):
 +        """
 +        :param response: HTTP response object. (optional)
 +        :type response: :class:`httplib.HTTPResponse`
 +
 +        :param connection: Parent connection object.
 +        :type connection: :class:`.Connection`
 +        """
 +        self.connection = connection
 +
 +        # http.client In Python 3 doesn't automatically lowercase the header
 +        # names
 +        self.headers = lowercase_keys(dict(response.headers))
 +        self.error = response.reason
 +        self.status = response.status_code
 +
 +        self.body = response.text.strip() \
 +            if response.text is not None else ''
 +
 +        if not self.success():
 +            raise exception_from_message(code=self.status,
 +                                         message=self.parse_error())
 +
 +        self.object = self.parse_body()
 +
 +    def parse_body(self):
 +        """
 +        Parse response body.
 +
 +        Override in a provider's subclass.
 +
 +        :return: Parsed body.
 +        :rtype: ``str``
 +        """
 +        return self.body if self.body is not None else ''
 +
 +    def parse_error(self):
 +        """
 +        Parse the error messages.
 +
 +        Override in a provider's subclass.
 +
 +        :return: Parsed error.
 +        :rtype: ``str``
 +        """
 +        return self.body
 +
 +    def success(self):
 +        """
 +        Determine if our request was successful.
 +
 +        The meaning of this can be arbitrary; did we receive OK status? Did
 +        the node get created? Were we authenticated?
 +
 +        :rtype: ``bool``
 +        :return: ``True`` or ``False``
 +        """
++        # pylint: disable=E1101
 +        return self.status in [requests.codes.ok, requests.codes.created,
 +                               httplib.OK, httplib.CREATED, httplib.ACCEPTED]
 +
 +    def _decompress_response(self, body, headers):
 +        """
 +        Decompress a response body if it is using deflate or gzip encoding.
 +
 +        :param body: Response body.
 +        :type body: ``str``
 +
 +        :param headers: Response headers.
 +        :type headers: ``dict``
 +
 +        :return: Decompressed response
 +        :rtype: ``str``
 +        """
 +        encoding = headers.get('content-encoding', None)
 +
 +        if encoding in ['zlib', 'deflate']:
 +            body = decompress_data('zlib', body)
 +        elif encoding in ['gzip', 'x-gzip']:
 +            body = decompress_data('gzip', body)
 +        else:
 +            body = body.strip()
 +
 +        return body
 +
 +
 +class JsonResponse(Response):
 +    """
 +    A Base JSON Response class to derive from.
 +    """
 +
 +    def parse_body(self):
 +        if len(self.body) == 0 and not self.parse_zero_length_body:
 +            return self.body
 +
 +        try:
 +            body = json.loads(self.body)
 +        except:
 +            raise MalformedResponseError(
 +                'Failed to parse JSON',
 +                body=self.body,
 +                driver=self.connection.driver)
 +        return body
 +
 +    parse_error = parse_body
 +
 +
 +class XmlResponse(Response):
 +    """
 +    A Base XML Response class to derive from.
 +    """
 +
 +    def parse_body(self):
 +        if len(self.body) == 0 and not self.parse_zero_length_body:
 +            return self.body
 +
 +        try:
 +            try:
 +                body = ET.XML(self.body)
 +            except ValueError:
 +                # lxml wants a bytes and tests are basically hard-coded to str
 +                body = ET.XML(self.body.encode('utf-8'))
 +        except:
 +            raise MalformedResponseError('Failed to parse XML',
 +                                         body=self.body,
 +                                         driver=self.connection.driver)
 +        return body
 +
 +    parse_error = parse_body
 +
 +
 +class RawResponse(Response):
 +
 +    def __init__(self, connection):
 +        """
 +        :param connection: Parent connection object.
 +        :type connection: :class:`.Connection`
 +        """
 +        self._status = None
 +        self._response = None
 +        self._headers = {}
 +        self._error = None
 +        self._reason = None
 +        self.connection = connection
 +
 +    @property
 +    def response(self):
 +        if not self._response:
 +            response = self.connection.connection.getresponse()
 +            self._response, self.body = response, response
 +            if not self.success():
 +                self.parse_error()
 +        return self._response
 +
 +    @property
 +    def status(self):
 +        if not self._status:
 +            self._status = self.response.status
 +        return self._status
 +
 +    @property
 +    def headers(self):
 +        if not self._headers:
 +            self._headers = lowercase_keys(dict(self.response.getheaders()))
 +        return self._headers
 +
 +    @property
 +    def reason(self):
 +        if not self._reason:
 +            self._reason = self.response.reason
 +        return self._reason
 +
 +
 +class Connection(object):
 +    """
 +    A Base Connection class to derive from.
 +    """
 +    conn_class = LibcloudConnection
-     # backward compat to pre 1.3
-     conn_classes = (conn_class, conn_class)
 +
 +    responseCls = Response
 +    rawResponseCls = RawResponse
 +    connection = None
 +    host = '127.0.0.1'
 +    port = 443
 +    timeout = None
 +    secure = 1
 +    driver = None
 +    action = None
 +    cache_busting = False
 +    backoff = None
 +    retry_delay = None
 +
 +    allow_insecure = True
 +
 +    def __init__(self, secure=True, host=None, port=None, url=None,
 +                 timeout=None, proxy_url=None, retry_delay=None, backoff=None):
 +        self.secure = secure and 1 or 0
 +        self.ua = []
 +        self.context = {}
 +
 +        if not self.allow_insecure and not secure:
 +            # TODO: We should eventually switch to whitelist instead of
 +            # blacklist approach
 +            raise ValueError('Non https connections are not allowed (use '
 +                             'secure=True)')
 +
 +        self.request_path = ''
 +
 +        if host:
 +            self.host = host
 +
 +        if port is not None:
 +            self.port = port
 +        else:
 +            if self.secure == 1:
 +                self.port = 443
 +            else:
 +                self.port = 80
 +
 +        if url:
 +            (self.host, self.port, self.secure,
 +             self.request_path) = self._tuple_from_url(url)
 +
 +        self.timeout = timeout or self.timeout
 +        self.retry_delay = retry_delay
 +        self.backoff = backoff
 +        self.proxy_url = proxy_url
 +
 +    def set_http_proxy(self, proxy_url):
 +        """
 +        Set a HTTP proxy which will be used with this connection.
 +
 +        :param proxy_url: Proxy URL (e.g. http://<hostname>:<port> without
 +                          authentication and
 +                          http://<username>:<password>@<hostname>:<port> for
 +                          basic auth authentication information.
 +        :type proxy_url: ``str``
 +        """
 +        self.proxy_url = proxy_url
 +
 +    def set_context(self, context):
 +        if not isinstance(context, dict):
 +            raise TypeError('context needs to be a dictionary')
 +
 +        self.context = context
 +
 +    def reset_context(self):
 +        self.context = {}
 +
 +    def _tuple_from_url(self, url):
 +        secure = 1
 +        port = None
 +        (scheme, netloc, request_path, param,
 +         query, fragment) = urlparse.urlparse(url)
 +
 +        if scheme not in ['http', 'https']:
 +            raise LibcloudError('Invalid scheme: %s in url %s' % (scheme, url))
 +
 +        if scheme == "http":
 +            secure = 0
 +
 +        if ":" in netloc:
 +            netloc, port = netloc.rsplit(":")
 +            port = int(port)
 +
 +        if not port:
 +            if scheme == "http":
 +                port = 80
 +            else:
 +                port = 443
 +
 +        host = netloc
 +        port = int(port)
 +
 +        return (host, port, secure, request_path)
 +
 +    def connect(self, host=None, port=None, base_url=None, **kwargs):
 +        """
 +        Establish a connection with the API server.
 +
 +        :type host: ``str``
 +        :param host: Optional host to override our default
 +
 +        :type port: ``int``
 +        :param port: Optional port to override our default
 +
 +        :returns: A connection
 +        """
 +        # prefer the attribute base_url if its set or sent
 +        connection = None
 +        secure = self.secure
 +
 +        if getattr(self, 'base_url', None) and base_url is None:
 +            (host, port,
-              secure, request_path) = self._tuple_from_url(self.base_url)
++             secure, request_path) = \
++                self._tuple_from_url(getattr(self, 'base_url'))
 +        elif base_url is not None:
 +            (host, port,
 +             secure, request_path) = self._tuple_from_url(base_url)
 +        else:
 +            host = host or self.host
 +            port = port or self.port
 +
 +        # Make sure port is an int
 +        port = int(port)
 +
 +        if not hasattr(kwargs, 'host'):
 +            kwargs.update({'host': host})
 +
 +        if not hasattr(kwargs, 'port'):
 +            kwargs.update({'port': port})
 +
 +        if not hasattr(kwargs, 'key_file') and hasattr(self, 'key_file'):
-             kwargs.update({'key_file': self.key_file})
++            kwargs.update({'key_file': getattr(self, 'key_file')})
 +
 +        if not hasattr(kwargs, 'cert_file') and hasattr(self, 'cert_file'):
-             kwargs.update({'cert_file': self.cert_file})
++            kwargs.update({'cert_file': getattr(self, 'cert_file')})
 +
 +        #  kwargs = {'host': host, 'port': int(port)}
 +
 +        # Timeout is only supported in Python 2.6 and later
 +        # http://docs.python.org/library/httplib.html#httplib.HTTPConnection
 +        if self.timeout and not PY25:
 +            kwargs.update({'timeout': self.timeout})
 +
 +        if self.proxy_url:
 +            kwargs.update({'proxy_url': self.proxy_url})
 +
 +        connection = self.conn_class(**kwargs)
 +        # You can uncoment this line, if you setup a reverse proxy server
 +        # which proxies to your endpoint, and lets you easily capture
 +        # connections in cleartext when you setup the proxy to do SSL
 +        # for you
 +        # connection = self.conn_class("127.0.0.1", 8080)
 +
 +        self.connection = connection
 +
 +    def _user_agent(self):
 +        user_agent_suffix = ' '.join(['(%s)' % x for x in self.ua])
 +
 +        if self.driver:
 +            user_agent = 'libcloud/%s (%s) %s' % (
 +                libcloud.__version__,
 +                self.driver.name, user_agent_suffix)
 +        else:
 +            user_agent = 'libcloud/%s %s' % (
 +                libcloud.__version__, user_agent_suffix)
 +
 +        return user_agent
 +
 +    def user_agent_append(self, token):
 +        """
 +        Append a token to a user agent string.
 +
 +        Users of the library should call this to uniquely identify their
 +        requests to a provider.
 +
 +        :type token: ``str``
 +        :param token: Token to add to the user agent.
 +        """
 +        self.ua.append(token)
 +
 +    def request(self, action, params=None, data=None, headers=None,
 +                method='GET', raw=False):
 +        """
 +        Request a given `action`.
 +
 +        Basically a wrapper around the connection
 +        object's `request` that does some helpful pre-processing.
 +
 +        :type action: ``str``
 +        :param action: A path. This can include arguments. If included,
 +            any extra parameters are appended to the existing ones.
 +
 +        :type params: ``dict``
 +        :param params: Optional mapping of additional parameters to send. If
 +            None, leave as an empty ``dict``.
 +
 +        :type data: ``unicode``
 +        :param data: A body of data to send with the request.
 +
 +        :type headers: ``dict``
 +        :param headers: Extra headers to add to the request
 +            None, leave as an empty ``dict``.
 +
 +        :type method: ``str``
 +        :param method: An HTTP method such as "GET" or "POST".
 +
 +        :type raw: ``bool``
 +        :param raw: True to perform a "raw" request aka only send the headers
 +                     and use the rawResponseCls class. This is used with
 +                     storage API when uploading a file.
 +
 +        :return: An :class:`Response` instance.
 +        :rtype: :class:`Response` instance
 +
 +        """
 +        if params is None:
 +            params = {}
 +        else:
 +            params = copy.copy(params)
 +
 +        if headers is None:
 +            headers = {}
 +        else:
 +            headers = copy.copy(headers)
 +
 +        retry_enabled = os.environ.get('LIBCLOUD_RETRY_FAILED_HTTP_REQUESTS',
 +                                       False) or RETRY_FAILED_HTTP_REQUESTS
 +
 +        action = self.morph_action_hook(action)
 +        self.action = action
 +        self.method = method
 +        self.data = data
 +
 +        # Extend default parameters
 +        params = self.add_default_params(params)
 +
 +        # Add cache busting parameters (if enabled)
 +        if self.cache_busting and method == 'GET':
 +            params = self._add_cache_busting_to_params(params=params)
 +
 +        # Extend default headers
 +        headers = self.add_default_headers(headers)
 +
 +        # We always send a user-agent header
 +        headers.update({'User-Agent': self._user_agent()})
 +
 +        # Indicate that we support gzip and deflate compression
 +        headers.update({'Accept-Encoding': 'gzip,deflate'})
 +
 +        port = int(self.port)
 +
 +        if port not in (80, 443):
 +            headers.update({'Host': "%s:%d" % (self.host, port)})
 +        else:
 +            headers.update({'Host': self.host})
 +
 +        if data:
 +            data = self.encode_data(data)
 +            headers['Content-Length'] = str(len(data))
 +        elif method.upper() in ['POST', 'PUT'] and not raw:
 +            # Only send Content-Length 0 with POST and PUT request.
 +            #
 +            # Note: Content-Length is not added when using "raw" mode means
 +            # means that headers are upfront and the body is sent at some point
 +            # later on. With raw mode user can specify Content-Length with
 +            # "data" not being set.
 +            headers['Content-Length'] = '0'
 +
 +        params, headers = self.pre_connect_hook(params, headers)
 +
 +        if params:
 +            if '?' in action:
 +                url = '&'.join((action, urlencode(params, doseq=True)))
 +            else:
 +                url = '?'.join((action, urlencode(params, doseq=True)))
 +        else:
 +            url = action
 +
 +        # IF connection has not yet been established
 +        if self.connection is None:
 +            self.connect()
 +
 +        try:
 +            # @TODO: Should we just pass File object as body to request method
 +            # instead of dealing with splitting and sending the file ourselves?
 +            if raw:
 +                self.connection.putrequest(method, url,
 +                                           skip_host=1,
 +                                           skip_accept_encoding=1)
 +
 +                for key, value in list(headers.items()):
 +                    self.connection.putheader(key, str(value))
 +
 +                self.connection.endheaders()
 +            else:
 +                if retry_enabled:
 +                    retry_request = retry(timeout=self.timeout,
 +                                          retry_delay=self.retry_delay,
 +                                          backoff=self.backoff)
 +                    retry_request(self.connection.request)(method=method,
 +                                                           url=url,
 +                                                           body=data,
 +                                                           headers=headers)
 +                else:
 +                    self.connection.request(method=method, url=url, body=data,
 +                                            headers=headers)
 +        except socket.gaierror:
 +            e = sys.exc_info()[1]
 +            message = str(e)
 +            errno = getattr(e, 'errno', None)
 +
 +            if errno == -5:
 +                # Throw a more-friendly exception on "no address associated
 +                # with hostname" error. This error could simpli indicate that
 +                # "host" Connection class attribute is set to an incorrect
 +                # value
 +                class_name = self.__class__.__name__
 +                msg = ('%s. Perhaps "host" Connection class attribute '
 +                       '(%s.connection) is set to an invalid, non-hostname '
 +                       'value (%s)?' %
 +                       (message, class_name, self.host))
 +                raise socket.gaierror(msg)
 +            self.reset_context()
 +            raise e
 +        except ssl.SSLError:
 +            e = sys.exc_info()[1]
 +            self.reset_context()
 +            raise ssl.SSLError(str(e))
 +
 +        if raw:
 +            responseCls = self.rawResponseCls
 +            kwargs = {'connection': self}
 +        else:
 +            responseCls = self.responseCls
 +            kwargs = {'connection': self,
 +                      'response': self.connection.getresponse()}
 +
 +        try:
 +            response = responseCls(**kwargs)
 +        finally:
 +            # Always reset the context after the request has completed
 +            self.reset_context()
 +
 +        return response
 +
 +    def morph_action_hook(self, action):
 +        if not action.startswith("/"):
 +            action = "/" + action
 +        return self.request_path + action
 +
 +    def add_default_params(self, params):
 +        """
 +        Adds default parameters (such as API key, version, etc.)
 +        to the passed `params`
 +
 +        Should return a dictionary.
 +        """
 +        return params
 +
 +    def add_default_headers(self, headers):
 +        """
 +        Adds default headers (such as Authorization, X-Foo-Bar)
 +        to the passed `headers`
 +
 +        Should return a dictionary.
 +        """
 +        return headers
 +
 +    def pre_connect_hook(self, params, headers):
 +        """
 +        A hook which is called before connecting to the remote server.
 +        This hook can perform a final manipulation on the params, headers and
 +        url parameters.
 +
 +        :type params: ``dict``
 +        :param params: Request parameters.
 +
 +        :type headers: ``dict``
 +        :param headers: Request headers.
 +        """
 +        return params, headers
 +
 +    def encode_data(self, data):
 +        """
 +        Encode body data.
 +
 +        Override in a provider's subclass.
 +        """
 +        return data
 +
 +    def _add_cache_busting_to_params(self, params):
 +        """
 +        Add cache busting parameter to the query parameters of a GET request.
 +
 +        Parameters are only added if "cache_busting" class attribute is set to
 +        True.
 +
 +        Note: This should only be used with *naughty* providers which use
 +        excessive caching of responses.
 +        """
 +        cache_busting_value = binascii.hexlify(os.urandom(8)).decode('ascii')
 +
 +        if isinstance(params, dict):
 +            params['cache-busting'] = cache_busting_value
 +        else:
 +            params.append(('cache-busting', cache_busting_value))
 +
 +        return params
 +
 +
 +class PollingConnection(Connection):
 +    """
 +    Connection class which can also work with the async APIs.
 +
 +    After initial requests, this class periodically polls for jobs status and
 +    waits until the job has finished.
 +    If job doesn't finish in timeout seconds, an Exception thrown.
 +    """
 +    poll_interval = 0.5
 +    timeout = 200
 +    request_method = 'request'
 +
 +    def async_request(self, action, params=None, data=None, headers=None,
 +                      method='GET', context=None):
 +        """
 +        Perform an 'async' request to the specified path. Keep in mind that
 +        this function is *blocking* and 'async' in this case means that the
 +        hit URL only returns a job ID which is the periodically polled until
 +        the job has completed.
 +
 +        This function works like this:
 +
 +        - Perform a request to the specified path. Response should contain a
 +          'job_id'.
 +
 +        - Returned 'job_id' is then used to construct a URL which is used for
 +          retrieving job status. Constructed URL is then periodically polled
 +          until the response indicates that the job has completed or the
 +          timeout of 'self.timeout' seconds has been reached.
 +
 +        :type action: ``str``
 +        :param action: A path
 +
 +        :type params: ``dict``
 +        :param params: Optional mapping of additional parameters to send. If
 +            None, leave as an empty ``dict``.
 +
 +        :type data: ``unicode``
 +        :param data: A body of data to send with the request.
 +
 +        :type headers: ``dict``
 +        :param headers: Extra headers to add to the request
 +            None, leave as an empty ``dict``.
 +
 +        :type method: ``str``
 +        :param method: An HTTP method such as "GET" or "POST".
 +
 +        :type context: ``dict``
 +        :param context: Context dictionary which is passed to the functions
 +                        which construct initial and poll URL.
 +
 +        :return: An :class:`Response` instance.
 +        :rtype: :class:`Response` instance
 +        """
 +
 +        request = getattr(self, self.request_method)
 +        kwargs = self.get_request_kwargs(action=action, params=params,
 +                                         data=data, headers=headers,
 +                                         method=method,
 +                                         context=context)
 +        response = request(**kwargs)
 +        kwargs = self.get_poll_request_kwargs(response=response,
 +                                              context=context,
 +                                              request_kwargs=kwargs)
 +
 +        end = time.time() + self.timeout
 +        completed = False
 +        while time.time() < end and not completed:
 +            response = request(**kwargs)
 +            completed = self.has_completed(response=response)
 +            if not completed:
 +                time.sleep(self.poll_interval)
 +
 +        if not completed:
 +            raise LibcloudError('Job did not complete in %s seconds' %
 +                                (self.timeout))
 +
 +        return response
 +
 +    def get_request_kwargs(self, action, params=None, data=None, headers=None,
 +                           method='GET', context=None):
 +        """
 +        Arguments which are passed to the initial request() call inside
 +        async_request.
 +        """
 +        kwargs = {'action': action, 'params': params, 'data': data,
 +                  'headers': headers, 'method': method}
 +        return kwargs
 +
 +    def get_poll_request_kwargs(self, response, context, request_kwargs):
 +        """
 +        Return keyword arguments which are passed to the request() method when
 +        polling for the job status.
 +
 +        :param response: Response object returned by poll request.
 +        :type response: :class:`HTTPResponse`
 +
 +        :param request_kwargs: Kwargs previously used to initiate the
 +                                  poll request.
 +        :type response: ``dict``
 +
 +        :return ``dict`` Keyword arguments
 +        """
 +        raise NotImplementedError('get_poll_request_kwargs not implemented')
 +
 +    def has_completed(self, response):
 +        """
 +        Return job completion status.
 +
 +        :param response: Response object returned by poll request.
 +        :type response: :class:`HTTPResponse`
 +
 +        :return ``bool`` True if the job has completed, False otherwise.
 +        """
 +        raise NotImplementedError('has_completed not implemented')
 +
 +
 +class ConnectionKey(Connection):
 +    """
 +    Base connection class which accepts a single ``key`` argument.
 +    """
 +    def __init__(self, key, secure=True, host=None, port=None, url=None,
 +                 timeout=None, proxy_url=None, backoff=None, retry_delay=None):
 +        """
 +        Initialize `user_id` and `key`; set `secure` to an ``int`` based on
 +        passed value.
 +        """
 +        super(ConnectionKey, self).__init__(secure=secure, host=host,
 +                                            port=port, url=url,
 +                                            timeout=timeout,
 +                                            proxy_url=proxy_url,
 +                                            backoff=backoff,
 +                                            retry_delay=retry_delay)
 +        self.key = key
 +
 +
 +class CertificateConnection(Connection):
 +    """
 +    Base connection class which accepts a single ``cert_file`` argument.
 +    """
 +    def __init__(self, cert_file, secure=True, host=None, port=None, url=None,
 +                 proxy_url=None, timeout=None, backoff=None, retry_delay=None):
 +        """
 +        Initialize `cert_file`; set `secure` to an ``int`` based on
 +        passed value.
 +        """
 +        super(CertificateConnection, self).__init__(secure=secure, host=host,
 +                                                    port=port, url=url,
 +                                                    timeout=timeout,
 +                                                    backoff=backoff,
 +                                                    retry_delay=retry_delay,
 +                                                    proxy_url=proxy_url)
 +
 +        self.cert_file = cert_file
 +
 +
 +class ConnectionUserAndKey(ConnectionKey):
 +    """
 +    Base connection class which accepts a ``user_id`` and ``key`` argument.
 +    """
 +
 +    user_id = None
 +
 +    def __init__(self, user_id, key, secure=True, host=None, port=None,
 +                 url=None, timeout=None, proxy_url=None,
 +                 backoff=None, retry_delay=None):
 +        super(ConnectionUserAndKey, self).__init__(key, secure=secure,
 +                                                   host=host, port=port,
 +                                                   url=url, timeout=timeout,
 +                                                   backoff=backoff,
 +                                                   retry_delay=retry_delay,
 +                                                   proxy_url=proxy_url)
 +        self.user_id = user_id
 +
 +
 +class BaseDriver(object):
 +    """
 +    Base driver class from which other classes can inherit from.
 +    """
 +
 +    connectionCls = ConnectionKey
 +
 +    def __init__(self, key, secret=None, secure=True, host=None, port=None,
 +                 api_version=None, region=None, **kwargs):
 +        """
 +        :param    key:    API key or username to be used (required)
 +        :type     key:    ``str``
 +
 +        :param    secret: Secret password to be used (required)
 +        :type     secret: ``str``
 +
 +        :param    secure: Whether to use HTTPS or HTTP. Note: Some providers
 +                            only support HTTPS, and it is on by default.
 +        :type     secure: ``bool``
 +
 +        :param    host: Override hostname used for connections.
 +        :type     host: ``str``
 +
 +        :param    port: Override port used for connections.
 +        :type     port: ``int``
 +
 +        :param    api_version: Optional API version. Only used by drivers
 +                                 which support multiple API versions.
 +        :type     api_version: ``str``
 +
 +        :param region: Optional driver region. Only used by drivers which
 +                       support multiple regions.
 +        :type region: ``str``
 +
 +        :rtype: ``None``
 +        """
 +
 +        self.key = key
 +        self.secret = secret
 +        self.secure = secure
 +        args = [self.key]
 +
 +        if self.secret is not None:
 +            args.append(self.secret)
 +
 +        args.append(secure)
 +
 +        if host is not None:
 +            args.append(host)
 +
 +        if port is not None:
 +            args.append(port)
 +
 +        self.api_version = api_version
 +        self.region = region
 +
 +        conn_kwargs = self._ex_connection_class_kwargs()
 +        conn_kwargs.update({'timeout': kwargs.pop('timeout', None),
 +                            'retry_delay': kwargs.pop('retry_delay', None),
 +                            'backoff': kwargs.pop('backoff', None),
 +                            'proxy_url': kwargs.pop('proxy_url', None)})
 +        self.connection = self.connectionCls(*args, **conn_kwargs)
 +
 +        self.connection.driver = self
 +        self.connection.connect()
 +
 +    def _ex_connection_class_kwargs(self):
 +        """
 +        Return extra connection keyword arguments which are passed to the
 +        Connection class constructor.
 +        """
 +        return {}


Mime
View raw message