http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/aws.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/aws.py b/apache-libcloud-1.0.0rc2/libcloud/common/aws.py deleted file mode 100644 index 74922a6..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/aws.py +++ /dev/null @@ -1,426 +0,0 @@ -# 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 base64 -from datetime import datetime -import hashlib -import hmac -import time -from hashlib import sha256 - -try: - import simplejson as json -except ImportError: - import json - -try: - from lxml import etree as ET -except ImportError: - from xml.etree import ElementTree as ET - -from libcloud.common.base import ConnectionUserAndKey, XmlResponse, BaseDriver -from libcloud.common.base import JsonResponse -from libcloud.common.types import InvalidCredsError, MalformedResponseError -from libcloud.utils.py3 import b, httplib, urlquote -from libcloud.utils.xml import findtext, findall - -__all__ = [ - 'AWSBaseResponse', - 'AWSGenericResponse', - - 'AWSTokenConnection', - 'SignedAWSConnection', - - 'AWSRequestSignerAlgorithmV2', - 'AWSRequestSignerAlgorithmV4', - - 'AWSDriver' -] - -DEFAULT_SIGNATURE_VERSION = '2' -UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD' - - -class AWSBaseResponse(XmlResponse): - namespace = None - - def _parse_error_details(self, element): - """ - Parse code and message from the provided error element. - - :return: ``tuple`` with two elements: (code, message) - :rtype: ``tuple`` - """ - code = findtext(element=element, xpath='Code', - namespace=self.namespace) - message = findtext(element=element, xpath='Message', - namespace=self.namespace) - - return code, message - - -class AWSGenericResponse(AWSBaseResponse): - # There are multiple error messages in AWS, but they all have an Error node - # with Code and Message child nodes. Xpath to select them - # None if the root node *is* the Error node - xpath = None - - # This dict maps <Error><Code>CodeName</Code></Error> to a specific - # exception class that is raised immediately. - # If a custom exception class is not defined, errors are accumulated and - # returned from the parse_error method. - expections = {} - - def success(self): - return self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED] - - def parse_error(self): - context = self.connection.context - status = int(self.status) - - # FIXME: Probably ditch this as the forbidden message will have - # corresponding XML. - if status == httplib.FORBIDDEN: - if not self.body: - raise InvalidCredsError(str(self.status) + ': ' + self.error) - else: - raise InvalidCredsError(self.body) - - try: - body = ET.XML(self.body) - except Exception: - raise MalformedResponseError('Failed to parse XML', - body=self.body, - driver=self.connection.driver) - - if self.xpath: - errs = findall(element=body, xpath=self.xpath, - namespace=self.namespace) - else: - errs = [body] - - msgs = [] - for err in errs: - code, message = self._parse_error_details(element=err) - exceptionCls = self.exceptions.get(code, None) - - if exceptionCls is None: - msgs.append('%s: %s' % (code, message)) - continue - - # Custom exception class is defined, immediately throw an exception - params = {} - if hasattr(exceptionCls, 'kwargs'): - for key in exceptionCls.kwargs: - if key in context: - params[key] = context[key] - - raise exceptionCls(value=message, driver=self.connection.driver, - **params) - - return "\n".join(msgs) - - -class AWSTokenConnection(ConnectionUserAndKey): - def __init__(self, user_id, key, secure=True, - host=None, port=None, url=None, timeout=None, proxy_url=None, - token=None, retry_delay=None, backoff=None): - self.token = token - super(AWSTokenConnection, self).__init__(user_id, key, secure=secure, - host=host, port=port, url=url, - timeout=timeout, - retry_delay=retry_delay, - backoff=backoff, - proxy_url=proxy_url) - - def add_default_params(self, params): - # Even though we are adding it to the headers, we need it here too - # so that the token is added to the signature. - if self.token: - params['x-amz-security-token'] = self.token - return super(AWSTokenConnection, self).add_default_params(params) - - def add_default_headers(self, headers): - if self.token: - headers['x-amz-security-token'] = self.token - return super(AWSTokenConnection, self).add_default_headers(headers) - - -class AWSRequestSigner(object): - """ - Class which handles signing the outgoing AWS requests. - """ - - def __init__(self, access_key, access_secret, version, connection): - """ - :param access_key: Access key. - :type access_key: ``str`` - - :param access_secret: Access secret. - :type access_secret: ``str`` - - :param version: API version. - :type version: ``str`` - - :param connection: Connection instance. - :type connection: :class:`Connection` - """ - self.access_key = access_key - self.access_secret = access_secret - self.version = version - # TODO: Remove cycling dependency between connection and signer - self.connection = connection - - def get_request_params(self, params, method='GET', path='/'): - return params - - def get_request_headers(self, params, headers, method='GET', path='/', - data=None): - return params, headers - - -class AWSRequestSignerAlgorithmV2(AWSRequestSigner): - def get_request_params(self, params, method='GET', path='/'): - params['SignatureVersion'] = '2' - params['SignatureMethod'] = 'HmacSHA256' - params['AWSAccessKeyId'] = self.access_key - params['Version'] = self.version - params['Timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%SZ', - time.gmtime()) - params['Signature'] = self._get_aws_auth_param( - params=params, - secret_key=self.access_secret, - path=path) - return params - - def _get_aws_auth_param(self, params, secret_key, path='/'): - """ - Creates the signature required for AWS, per - http://bit.ly/aR7GaQ [docs.amazonwebservices.com]: - - StringToSign = HTTPVerb + "\n" + - ValueOfHostHeaderInLowercase + "\n" + - HTTPRequestURI + "\n" + - CanonicalizedQueryString <from the preceding step> - """ - connection = self.connection - - keys = list(params.keys()) - keys.sort() - pairs = [] - for key in keys: - value = str(params[key]) - pairs.append(urlquote(key, safe='') + '=' + - urlquote(value, safe='-_~')) - - qs = '&'.join(pairs) - - hostname = connection.host - if (connection.secure and connection.port != 443) or \ - (not connection.secure and connection.port != 80): - hostname += ':' + str(connection.port) - - string_to_sign = '\n'.join(('GET', hostname, path, qs)) - - b64_hmac = base64.b64encode( - hmac.new(b(secret_key), b(string_to_sign), - digestmod=sha256).digest() - ) - - return b64_hmac.decode('utf-8') - - -class AWSRequestSignerAlgorithmV4(AWSRequestSigner): - def get_request_params(self, params, method='GET', path='/'): - if method == 'GET': - params['Version'] = self.version - return params - - def get_request_headers(self, params, headers, method='GET', path='/', - data=None): - now = datetime.utcnow() - headers['X-AMZ-Date'] = now.strftime('%Y%m%dT%H%M%SZ') - headers['X-AMZ-Content-SHA256'] = self._get_payload_hash(method, data) - headers['Authorization'] = \ - self._get_authorization_v4_header(params=params, headers=headers, - dt=now, method=method, path=path, - data=data) - - return params, headers - - def _get_authorization_v4_header(self, params, headers, dt, method='GET', - path='/', data=None): - credentials_scope = self._get_credential_scope(dt=dt) - signed_headers = self._get_signed_headers(headers=headers) - signature = self._get_signature(params=params, headers=headers, - dt=dt, method=method, path=path, - data=data) - - return 'AWS4-HMAC-SHA256 Credential=%(u)s/%(c)s, ' \ - 'SignedHeaders=%(sh)s, Signature=%(s)s' % { - 'u': self.access_key, - 'c': credentials_scope, - 'sh': signed_headers, - 's': signature - } - - def _get_signature(self, params, headers, dt, method, path, data): - key = self._get_key_to_sign_with(dt) - string_to_sign = self._get_string_to_sign(params=params, - headers=headers, dt=dt, - method=method, path=path, - data=data) - return _sign(key=key, msg=string_to_sign, hex=True) - - def _get_key_to_sign_with(self, dt): - return _sign( - _sign( - _sign( - _sign(('AWS4' + self.access_secret), - dt.strftime('%Y%m%d')), - self.connection.driver.region_name), - self.connection.service_name), - 'aws4_request') - - def _get_string_to_sign(self, params, headers, dt, method, path, data): - canonical_request = self._get_canonical_request(params=params, - headers=headers, - method=method, - path=path, - data=data) - - return '\n'.join(['AWS4-HMAC-SHA256', - dt.strftime('%Y%m%dT%H%M%SZ'), - self._get_credential_scope(dt), - _hash(canonical_request)]) - - def _get_credential_scope(self, dt): - return '/'.join([dt.strftime('%Y%m%d'), - self.connection.driver.region_name, - self.connection.service_name, - 'aws4_request']) - - def _get_signed_headers(self, headers): - return ';'.join([k.lower() for k in sorted(headers.keys())]) - - def _get_canonical_headers(self, headers): - return '\n'.join([':'.join([k.lower(), str(v).strip()]) - for k, v in sorted(headers.items())]) + '\n' - - def _get_payload_hash(self, method, data=None): - if method in ('POST', 'PUT'): - if data: - return _hash(data) - else: - # When upload file, we can't know payload here even if given - return UNSIGNED_PAYLOAD - else: - return _hash('') - - def _get_request_params(self, params): - # For self.method == GET - return '&'.join(["%s=%s" % - (urlquote(k, safe=''), urlquote(str(v), safe='~')) - for k, v in sorted(params.items())]) - - def _get_canonical_request(self, params, headers, method, path, data): - return '\n'.join([ - method, - path, - self._get_request_params(params), - self._get_canonical_headers(headers), - self._get_signed_headers(headers), - self._get_payload_hash(method, data) - ]) - - -class SignedAWSConnection(AWSTokenConnection): - def __init__(self, user_id, key, secure=True, host=None, port=None, - url=None, timeout=None, proxy_url=None, token=None, - retry_delay=None, backoff=None, - signature_version=DEFAULT_SIGNATURE_VERSION): - super(SignedAWSConnection, self).__init__(user_id=user_id, key=key, - secure=secure, host=host, - port=port, url=url, - timeout=timeout, token=token, - retry_delay=retry_delay, - backoff=backoff, - proxy_url=proxy_url) - self.signature_version = str(signature_version) - - if self.signature_version == '2': - signer_cls = AWSRequestSignerAlgorithmV2 - elif self.signature_version == '4': - signer_cls = AWSRequestSignerAlgorithmV4 - else: - raise ValueError('Unsupported signature_version: %s' % - (signature_version)) - - self.signer = signer_cls(access_key=self.user_id, - access_secret=self.key, - version=self.version, - connection=self) - - def add_default_params(self, params): - params = self.signer.get_request_params(params=params, - method=self.method, - path=self.action) - return params - - def pre_connect_hook(self, params, headers): - params, headers = self.signer.get_request_headers(params=params, - headers=headers, - method=self.method, - path=self.action, - data=self.data) - return params, headers - - -class AWSJsonResponse(JsonResponse): - """ - Amazon ECS response class. - ECS API uses JSON unlike the s3, elb drivers - """ - def parse_error(self): - response = json.loads(self.body) - code = response['__type'] - message = response.get('Message', response['message']) - return ('%s: %s' % (code, message)) - - -def _sign(key, msg, hex=False): - if hex: - return hmac.new(b(key), b(msg), hashlib.sha256).hexdigest() - else: - return hmac.new(b(key), b(msg), hashlib.sha256).digest() - - -def _hash(msg): - return hashlib.sha256(b(msg)).hexdigest() - - -class AWSDriver(BaseDriver): - def __init__(self, key, secret=None, secure=True, host=None, port=None, - api_version=None, region=None, token=None, **kwargs): - self.token = token - super(AWSDriver, self).__init__(key, secret=secret, secure=secure, - host=host, port=port, - api_version=api_version, region=region, - token=token, **kwargs) - - def _ex_connection_class_kwargs(self): - kwargs = super(AWSDriver, self)._ex_connection_class_kwargs() - kwargs['token'] = self.token - return kwargs
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/azure.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/azure.py b/apache-libcloud-1.0.0rc2/libcloud/common/azure.py deleted file mode 100644 index bd3c504..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/azure.py +++ /dev/null @@ -1,294 +0,0 @@ -# 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 copy -import os -import time -import base64 -import hmac - -from hashlib import sha256 -from libcloud.utils.py3 import httplib -from libcloud.utils.py3 import b -from libcloud.utils.xml import fixxpath - -try: - from lxml import etree as ET -except ImportError: - from xml.etree import ElementTree as ET - -from libcloud.common.types import InvalidCredsError -from libcloud.common.types import LibcloudError, MalformedResponseError -from libcloud.common.base import ConnectionUserAndKey, RawResponse -from libcloud.common.base import CertificateConnection -from libcloud.common.base import XmlResponse - -# Azure API version -API_VERSION = '2012-02-12' - -# The time format for headers in Azure requests -AZURE_TIME_FORMAT = '%a, %d %b %Y %H:%M:%S GMT' - - -class AzureRedirectException(Exception): - - def __init__(self, response): - self.location = response.headers['location'] - - -class AzureResponse(XmlResponse): - - valid_response_codes = [ - httplib.NOT_FOUND, - httplib.CONFLICT, - httplib.BAD_REQUEST, - httplib.TEMPORARY_REDIRECT - # added TEMPORARY_REDIRECT as this can sometimes be - # sent by azure instead of a success or fail response - ] - - def success(self): - i = int(self.status) - return 200 <= i <= 299 or i in self.valid_response_codes - - def parse_error(self, msg=None): - error_msg = 'Unknown error' - - try: - # Azure does give some meaningful errors, but is inconsistent - # Some APIs respond with an XML error. Others just dump HTML - body = self.parse_body() - - if type(body) == ET.Element: - code = body.findtext(fixxpath(xpath='Code')) - message = body.findtext(fixxpath(xpath='Message')) - message = message.split('\n')[0] - error_msg = '%s: %s' % (code, message) - - except MalformedResponseError: - pass - - if msg: - error_msg = '%s - %s' % (msg, error_msg) - - if self.status in [httplib.UNAUTHORIZED, httplib.FORBIDDEN]: - raise InvalidCredsError(error_msg) - - raise LibcloudError( - '%s Status code: %d.' % (error_msg, self.status), - driver=self - ) - - def parse_body(self): - is_redirect = int(self.status) == httplib.TEMPORARY_REDIRECT - - if is_redirect and self.connection.driver.follow_redirects: - raise AzureRedirectException(self) - else: - return super(AzureResponse, self).parse_body() - - -class AzureRawResponse(RawResponse): - pass - - -class AzureConnection(ConnectionUserAndKey): - """ - Represents a single connection to Azure - """ - - responseCls = AzureResponse - rawResponseCls = AzureRawResponse - - def add_default_params(self, params): - return params - - def pre_connect_hook(self, params, headers): - headers = copy.deepcopy(headers) - - # We have to add a date header in GMT - headers['x-ms-date'] = time.strftime(AZURE_TIME_FORMAT, time.gmtime()) - headers['x-ms-version'] = API_VERSION - - # Add the authorization header - headers['Authorization'] = self._get_azure_auth_signature( - method=self.method, - headers=headers, - params=params, - account=self.user_id, - secret_key=self.key, - path=self.action - ) - - # Azure cribs about this in 'raw' connections - headers.pop('Host', None) - - return params, headers - - def _get_azure_auth_signature(self, - method, - headers, - params, - account, - secret_key, - path='/'): - """ - Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID, - UTF-8-Encoding-Of( StringToSign ) ) ) ); - - StringToSign = HTTP-VERB + "\n" + - Content-Encoding + "\n" + - Content-Language + "\n" + - Content-Length + "\n" + - Content-MD5 + "\n" + - Content-Type + "\n" + - Date + "\n" + - If-Modified-Since + "\n" + - If-Match + "\n" + - If-None-Match + "\n" + - If-Unmodified-Since + "\n" + - Range + "\n" + - CanonicalizedHeaders + - CanonicalizedResource; - """ - special_header_values = [] - xms_header_values = [] - param_list = [] - special_header_keys = [ - 'content-encoding', - 'content-language', - 'content-length', - 'content-md5', - 'content-type', - 'date', - 'if-modified-since', - 'if-match', - 'if-none-match', - 'if-unmodified-since', - 'range' - ] - - # Split the x-ms headers and normal headers and make everything - # lower case - headers_copy = {} - for header, value in headers.items(): - header = header.lower() - value = str(value).strip() - if header.startswith('x-ms-'): - xms_header_values.append((header, value)) - else: - headers_copy[header] = value - - # Get the values for the headers in the specific order - for header in special_header_keys: - header = header.lower() # Just for safety - if header in headers_copy: - special_header_values.append(headers_copy[header]) - else: - special_header_values.append('') - - # Prepare the first section of the string to be signed - values_to_sign = [method] + special_header_values - # string_to_sign = '\n'.join([method] + special_header_values) - - # The x-ms-* headers have to be in lower case and sorted - xms_header_values.sort() - - for header, value in xms_header_values: - values_to_sign.append('%s:%s' % (header, value)) - - # Add the canonicalized path - values_to_sign.append('/%s%s' % (account, path)) - - # URL query parameters (sorted and lower case) - for key, value in params.items(): - param_list.append((key.lower(), str(value).strip())) - - param_list.sort() - - for key, value in param_list: - values_to_sign.append('%s:%s' % (key, value)) - - string_to_sign = b('\n'.join(values_to_sign)) - secret_key = b(secret_key) - b64_hmac = base64.b64encode( - hmac.new(secret_key, string_to_sign, digestmod=sha256).digest() - ) - - return 'SharedKey %s:%s' % (self.user_id, b64_hmac.decode('utf-8')) - - -class AzureBaseDriver(object): - name = "Microsoft Azure Service Management API" - - -class AzureServiceManagementConnection(CertificateConnection): - # This needs the following approach - - # 1. Make request using LibcloudHTTPSConnection which is a overloaded - # class which takes in a client certificate - # 2. Depending on the type of operation use a PollingConnection - # when the response id is returned - # 3. The Response can be used in an AzureServiceManagementResponse - - """ - Authentication class for "Service Account" authentication. - """ - - driver = AzureBaseDriver - responseCls = AzureResponse - rawResponseCls = AzureRawResponse - name = 'Azure Service Management API Connection' - host = 'management.core.windows.net' - keyfile = "" - - def __init__(self, subscription_id, key_file, *args, **kwargs): - """ - Check to see if PyCrypto is available, and convert key file path into a - key string if the key is in a file. - - :param subscription_id: Azure subscription ID. - :type subscription_id: ``str`` - - :param key_file: The PEM file used to authenticate with the service. - :type key_file: ``str`` - """ - - super(AzureServiceManagementConnection, self).__init__( - key_file, - *args, - **kwargs - ) - - self.subscription_id = subscription_id - - keypath = os.path.expanduser(key_file) - self.keyfile = keypath - is_file_path = os.path.exists(keypath) and os.path.isfile(keypath) - if not is_file_path: - raise InvalidCredsError( - 'You need an certificate PEM file to authenticate with ' - 'Microsoft Azure. This can be found in the portal.' - ) - self.key_file = key_file - - def add_default_headers(self, headers): - """ - @inherits: :class:`Connection.add_default_headers` - TODO: move to constant.. - """ - headers['x-ms-version'] = "2014-05-01" - headers['x-ms-date'] = time.strftime(AZURE_TIME_FORMAT, time.gmtime()) - # headers['host'] = self.host - return headers http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/azure_arm.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/azure_arm.py b/apache-libcloud-1.0.0rc2/libcloud/common/azure_arm.py deleted file mode 100644 index 2611921..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/azure_arm.py +++ /dev/null @@ -1,124 +0,0 @@ -# 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. - -try: - import simplejson as json -except ImportError: - import json - -import time - -from libcloud.common.base import (ConnectionUserAndKey, - JsonResponse, - RawResponse) -from libcloud.httplib_ssl import LibcloudHTTPSConnection -from libcloud.utils.py3 import basestring, urlencode - - -class AzureBaseDriver(object): - name = "Microsoft Azure Resource Management API" - - -class AzureJsonResponse(JsonResponse): - def parse_error(self): - b = self.parse_body() - - if isinstance(b, basestring): - return b - elif isinstance(b, dict) and "error" in b: - return "[%s] %s" % (b["error"].get("code"), - b["error"].get("message")) - else: - return str(b) - - -class AzureAuthJsonResponse(JsonResponse): - def parse_error(self): - b = self.parse_body() - - if isinstance(b, basestring): - return b - elif isinstance(b, dict) and "error_description" in b: - return b["error_description"] - else: - return str(b) - - -class AzureResourceManagementConnection(ConnectionUserAndKey): - """ - Represents a single connection to Azure - """ - - conn_classes = (None, LibcloudHTTPSConnection) - driver = AzureBaseDriver - name = 'Azure AD Auth' - responseCls = AzureJsonResponse - rawResponseCls = RawResponse - host = 'management.azure.com' - login_host = 'login.windows.net' - login_resource = 'https://management.core.windows.net/' - - def __init__(self, key, secret, secure=True, tenant_id=None, - subscription_id=None, **kwargs): - super(AzureResourceManagementConnection, self) \ - .__init__(key, secret, **kwargs) - self.tenant_id = tenant_id - self.subscription_id = subscription_id - - def add_default_headers(self, headers): - headers['Content-Type'] = "application/json" - headers['Authorization'] = "Bearer %s" % self.access_token - return headers - - def encode_data(self, data): - """Encode data to JSON""" - return json.dumps(data) - - def get_token_from_credentials(self): - """ - Log in and get bearer token used to authorize API requests. - """ - - conn = self.conn_classes[1](self.login_host, 443) - conn.connect() - params = urlencode({ - "grant_type": "client_credentials", - "client_id": self.user_id, - "client_secret": self.key, - "resource": self.login_resource - }) - headers = {"Content-type": "application/x-www-form-urlencoded"} - conn.request("POST", "/%s/oauth2/token" % self.tenant_id, - params, headers) - js = AzureAuthJsonResponse(conn.getresponse(), conn) - self.access_token = js.object["access_token"] - self.expires_on = js.object["expires_on"] - - def connect(self, **kwargs): - self.get_token_from_credentials() - return super(AzureResourceManagementConnection, self).connect(**kwargs) - - def request(self, action, params=None, data=None, headers=None, - method='GET', raw=False): - - # Log in again if the token has expired or is going to expire soon - # (next 5 minutes). - if (time.time() + 300) >= self.expires_on: - self.get_token_from_credentials(self) - - return super(AzureResourceManagementConnection, self) \ - .request(action, params=params, - data=data, headers=headers, - method=method, raw=raw) http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/base.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/base.py b/apache-libcloud-1.0.0rc2/libcloud/common/base.py deleted file mode 100644 index 0cdb257..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/base.py +++ /dev/null @@ -1,1179 +0,0 @@ -# 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] - - 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: - body = ET.XML(self.body) - 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): - 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 - - 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) - 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}) - - 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=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): - 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() - - @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 {} http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/brightbox.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/brightbox.py b/apache-libcloud-1.0.0rc2/libcloud/common/brightbox.py deleted file mode 100644 index 1943dda..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/brightbox.py +++ /dev/null @@ -1,101 +0,0 @@ -# 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 base64 - -from libcloud.common.base import ConnectionUserAndKey, JsonResponse -from libcloud.compute.types import InvalidCredsError - -from libcloud.utils.py3 import b -from libcloud.utils.py3 import httplib - -try: - import simplejson as json -except ImportError: - import json - - -class BrightboxResponse(JsonResponse): - def success(self): - return self.status >= httplib.OK and self.status < httplib.BAD_REQUEST - - def parse_body(self): - if self.headers['content-type'].split(';')[0] == 'application/json': - return super(BrightboxResponse, self).parse_body() - else: - return self.body - - def parse_error(self): - response = super(BrightboxResponse, self).parse_body() - - if 'error' in response: - if response['error'] in ['invalid_client', 'unauthorized_client']: - raise InvalidCredsError(response['error']) - - return response['error'] - elif 'error_name' in response: - return '%s: %s' % (response['error_name'], response['errors'][0]) - - return self.body - - -class BrightboxConnection(ConnectionUserAndKey): - """ - Connection class for the Brightbox driver - """ - - host = 'api.gb1.brightbox.com' - responseCls = BrightboxResponse - - def _fetch_oauth_token(self): - body = json.dumps({'client_id': self.user_id, 'grant_type': 'none'}) - - authorization = 'Basic ' + str(base64.encodestring(b('%s:%s' % - (self.user_id, self.key)))).rstrip() - - self.connect() - - headers = { - 'Host': self.host, - 'User-Agent': self._user_agent(), - 'Authorization': authorization, - 'Content-Type': 'application/json', - 'Content-Length': str(len(body)) - } - - response = self.connection.request(method='POST', url='/token', - body=body, headers=headers) - - response = self.connection.getresponse() - - if response.status == httplib.OK: - return json.loads(response.read())['access_token'] - else: - responseCls = BrightboxResponse(response=response, connection=self) - message = responseCls.parse_error() - raise InvalidCredsError(message) - - def add_default_headers(self, headers): - try: - headers['Authorization'] = 'OAuth ' + self.token - except AttributeError: - self.token = self._fetch_oauth_token() - - headers['Authorization'] = 'OAuth ' + self.token - - return headers - - def encode_data(self, data): - return json.dumps(data) http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/buddyns.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/buddyns.py b/apache-libcloud-1.0.0rc2/libcloud/common/buddyns.py deleted file mode 100644 index ae312ae..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/buddyns.py +++ /dev/null @@ -1,77 +0,0 @@ -# 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. - -from libcloud.common.base import ConnectionKey, JsonResponse - - -__all__ = [ - 'API_HOST', - 'BuddyNSException', - 'BuddyNSResponse', - 'BuddyNSConnection' -] - -# Endpoint for buddyns api -API_HOST = 'www.buddyns.com' - - -class BuddyNSResponse(JsonResponse): - errors = [] - objects = [] - - def __init__(self, response, connection): - super(BuddyNSResponse, self).__init__(response=response, - connection=connection) - self.errors, self.objects = self.parse_body_and_errors() - if not self.success(): - raise BuddyNSException(code=self.status, - message=self.errors.pop()['detail']) - - def parse_body_and_errors(self): - js = super(BuddyNSResponse, self).parse_body() - if 'detail' in js: - self.errors.append(js) - else: - self.objects.append(js) - - return self.errors, self.objects - - def success(self): - return len(self.errors) == 0 - - -class BuddyNSConnection(ConnectionKey): - host = API_HOST - responseCls = BuddyNSResponse - - def add_default_headers(self, headers): - headers['content-type'] = 'application/json' - headers['Authorization'] = 'Token' + ' ' + self.key - - return headers - - -class BuddyNSException(Exception): - - def __init__(self, code, message): - self.code = code - self.message = message - self.args = (code, message) - - def __str__(self): - return "%s %s" % (self.code, self.message) - - def __repr__(self): - return "BuddyNSException %s %s" % (self.code, self.message) http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/cloudsigma.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/cloudsigma.py b/apache-libcloud-1.0.0rc2/libcloud/common/cloudsigma.py deleted file mode 100644 index cb5c263..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/cloudsigma.py +++ /dev/null @@ -1,165 +0,0 @@ -# -*- coding: utf-8 -*- -# 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. - -__all__ = [ - 'API_ENDPOINTS_1_0', - 'API_ENDPOINTS_2_0', - 'API_VERSIONS', - 'INSTANCE_TYPES' -] - -# API end-points -API_ENDPOINTS_1_0 = { - 'zrh': { - 'name': 'Zurich', - 'country': 'Switzerland', - 'host': 'api.zrh.cloudsigma.com' - }, - 'lvs': { - 'name': 'Las Vegas', - 'country': 'United States', - 'host': 'api.lvs.cloudsigma.com' - } -} - -API_ENDPOINTS_2_0 = { - 'zrh': { - 'name': 'Zurich', - 'country': 'Switzerland', - 'host': 'zrh.cloudsigma.com' - }, - 'sjc': { - 'name': 'San Jose, CA', - 'country': 'United States', - 'host': 'sjc.cloudsigma.com' - }, - 'mia': { - 'name': 'Miami, FL', - 'country': 'United States', - 'host': 'mia.cloudsigma.com' - }, - 'wdc': { - 'name': 'Washington, DC', - 'country': 'United States', - 'host': 'wdc.cloudsigma.com' - }, - 'hnl': { - 'name': 'Honolulu, HI', - 'country': 'United States', - 'host': 'hnl.cloudsigma.com' - }, - 'per': { - 'name': 'Perth, Australia', - 'country': 'Australia', - 'host': 'per.cloudsigma.com' - }, - 'mnl': { - 'name': 'Manila, Philippines', - 'country': 'Philippines', - 'host': 'mnl.cloudsigma.com' - } -} - -DEFAULT_REGION = 'zrh' - -# Supported API versions. -API_VERSIONS = [ - '1.0' # old and deprecated - '2.0' -] - -DEFAULT_API_VERSION = '2.0' - -# CloudSigma doesn't specify special instance types. -# Basically for CPU any value between 0.5 GHz and 20.0 GHz should work, -# 500 MB to 32000 MB for ram -# and 1 GB to 1024 GB for hard drive size. -# Plans in this file are based on examples listed on http://www.cloudsigma -# .com/en/pricing/price-schedules -INSTANCE_TYPES = [ - { - 'id': 'micro-regular', - 'name': 'Micro/Regular instance', - 'cpu': 1100, - 'memory': 640, - 'disk': 10 + 3, - 'bandwidth': None, - }, - { - 'id': 'micro-high-cpu', - 'name': 'Micro/High CPU instance', - 'cpu': 2200, - 'memory': 640, - 'disk': 80, - 'bandwidth': None, - }, - { - 'id': 'standard-small', - 'name': 'Standard/Small instance', - 'cpu': 1100, - 'memory': 1741, - 'disk': 50, - 'bandwidth': None, - }, - { - 'id': 'standard-large', - 'name': 'Standard/Large instance', - 'cpu': 4400, - 'memory': 7680, - 'disk': 250, - 'bandwidth': None, - }, - { - 'id': 'standard-extra-large', - 'name': 'Standard/Extra Large instance', - 'cpu': 8800, - 'memory': 15360, - 'disk': 500, - 'bandwidth': None, - }, - { - 'id': 'high-memory-extra-large', - 'name': 'High Memory/Extra Large instance', - 'cpu': 7150, - 'memory': 17510, - 'disk': 250, - 'bandwidth': None, - }, - { - 'id': 'high-memory-double-extra-large', - 'name': 'High Memory/Double Extra Large instance', - 'cpu': 14300, - 'memory': 32768, - 'disk': 500, - 'bandwidth': None, - }, - { - 'id': 'high-cpu-medium', - 'name': 'High CPU/Medium instance', - 'cpu': 5500, - 'memory': 1741, - 'disk': 150, - 'bandwidth': None, - }, - { - 'id': 'high-cpu-extra-large', - 'name': 'High CPU/Extra Large instance', - 'cpu': 20000, - 'memory': 7168, - 'disk': 500, - 'bandwidth': None, - } -]