http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/gogrid.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/gogrid.py b/apache-libcloud-1.0.0rc2/libcloud/common/gogrid.py deleted file mode 100644 index e2448de..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/gogrid.py +++ /dev/null @@ -1,183 +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 hashlib -import time - -from libcloud.utils.py3 import b - -from libcloud.common.types import InvalidCredsError, LibcloudError -from libcloud.common.types import MalformedResponseError -from libcloud.common.base import ConnectionUserAndKey, JsonResponse -from libcloud.compute.base import NodeLocation - -HOST = 'api.gogrid.com' -PORTS_BY_SECURITY = {True: 443, False: 80} -API_VERSION = '1.8' - -__all__ = [ - "GoGridResponse", - "GoGridConnection", - "GoGridIpAddress", - "BaseGoGridDriver", -] - - -class GoGridResponse(JsonResponse): - - def __init__(self, *args, **kwargs): - self.driver = BaseGoGridDriver - super(GoGridResponse, self).__init__(*args, **kwargs) - - def success(self): - if self.status == 403: - raise InvalidCredsError('Invalid credentials', self.driver) - if self.status == 401: - raise InvalidCredsError('API Key has insufficient rights', - self.driver) - if not self.body: - return None - try: - return self.parse_body()['status'] == 'success' - except ValueError: - raise MalformedResponseError('Malformed reply', - body=self.body, - driver=self.driver) - - def parse_error(self): - try: - return self.parse_body()["list"][0]["message"] - except (ValueError, KeyError): - return None - - -class GoGridConnection(ConnectionUserAndKey): - """ - Connection class for the GoGrid driver - """ - - host = HOST - responseCls = GoGridResponse - - def add_default_params(self, params): - params["api_key"] = self.user_id - params["v"] = API_VERSION - params["format"] = 'json' - params["sig"] = self.get_signature(self.user_id, self.key) - - return params - - def get_signature(self, key, secret): - """ create sig from md5 of key + secret + time """ - m = hashlib.md5(b(key + secret + str(int(time.time())))) - return m.hexdigest() - - def request(self, action, params=None, data='', headers=None, method='GET', - raw=False): - return super(GoGridConnection, self).request(action, params, data, - headers, method, raw) - - -class GoGridIpAddress(object): - """ - IP Address - """ - - def __init__(self, id, ip, public, state, subnet): - self.id = id - self.ip = ip - self.public = public - self.state = state - self.subnet = subnet - - -class BaseGoGridDriver(object): - """GoGrid has common object model for services they - provide, like locations and IP, so keep handling of - these things in a single place.""" - - name = "GoGrid" - - def _get_ip(self, element): - return element.get('ip').get('ip') - - def _to_ip(self, element): - ip = GoGridIpAddress(id=element['id'], - ip=element['ip'], - public=element['public'], - subnet=element['subnet'], - state=element["state"]["name"]) - ip.location = self._to_location(element['datacenter']) - return ip - - def _to_ips(self, object): - return [self._to_ip(el) - for el in object['list']] - - def _to_location(self, element): - location = NodeLocation(id=element['id'], - name=element['name'], - country="US", - driver=self.connection.driver) - return location - - def _to_locations(self, object): - return [self._to_location(el) - for el in object['list']] - - def ex_list_ips(self, **kwargs): - """Return list of IP addresses assigned to - the account. - - :keyword public: set to True to list only - public IPs or False to list only - private IPs. Set to None or not specify - at all not to filter by type - :type public: ``bool`` - - :keyword assigned: set to True to list only addresses - assigned to servers, False to list unassigned - addresses and set to None or don't set at all - not no filter by state - :type assigned: ``bool`` - - :keyword location: filter IP addresses by location - :type location: :class:`NodeLocation` - - :rtype: ``list`` of :class:`GoGridIpAddress` - """ - - params = {} - - if "public" in kwargs and kwargs["public"] is not None: - params["ip.type"] = {True: "Public", - False: "Private"}[kwargs["public"]] - if "assigned" in kwargs and kwargs["assigned"] is not None: - params["ip.state"] = {True: "Assigned", - False: "Unassigned"}[kwargs["assigned"]] - if "location" in kwargs and kwargs['location'] is not None: - params['datacenter'] = kwargs['location'].id - - response = self.connection.request('/api/grid/ip/list', params=params) - ips = self._to_ips(response.object) - return ips - - def _get_first_ip(self, location=None): - ips = self.ex_list_ips(public=True, assigned=False, location=location) - try: - return ips[0].ip - except IndexError: - raise LibcloudError('No public unassigned IPs left', - self.driver)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/google.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/google.py b/apache-libcloud-1.0.0rc2/libcloud/common/google.py deleted file mode 100644 index ca248d5..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/google.py +++ /dev/null @@ -1,828 +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. - -""" -Module for Google Connection and Authentication classes. - -Information about setting up your Google OAUTH2 credentials: - -For libcloud, there are two basic methods for authenticating to Google using -OAUTH2: Service Accounts and Client IDs for Installed Applications. - -Both are initially set up from the Cloud Console Console - -https://cloud.google.com/console - -Setting up Service Account authentication (note that you need the PyCrypto -package installed to use this): - -- Go to the Console -- Go to your project and then to "APIs & auth" on the left -- Click on "Credentials" -- Click on "Create New Client ID..." -- Select "Service account" and click on "Create Client ID" -- Download the Private Key (should happen automatically). The key you download - is in JSON format. -- Move the .json file to a safe location. -- Optionally, you may choose to Generate a PKCS12 key from the Console. - It needs to be converted to the PEM format. Please note, the PKCS12 format - is deprecated and may be removed in a future release. - - Convert the key using OpenSSL (the default password is 'notasecret'). - - Move the .pem file to a safe location. -- To Authenticate, you will need to pass the Service Account's "Email - address" in as the user_id and the path to the .pem file as the key. - -Setting up Installed Application authentication: - -- Go to the Console -- Go to your project and then to "APIs & auth" on the left -- Click on "Credentials" -- Select "Installed application" and "Other" then click on - "Create Client ID" -- To Authenticate, pass in the "Client ID" as the user_id and the "Client - secret" as the key -- The first time that you do this, the libcloud will give you a URL to - visit. Copy and paste the URL into a browser. -- When you go to the URL it will ask you to log in (if you aren't already) - and ask you if you want to allow the project access to your account. -- Click on Accept and you will be given a code. -- Paste that code at the prompt given to you by the Google libcloud - connection. -- At that point, a token & refresh token will be stored in your home - directory and will be used for authentication. - -Please remember to secure your keys and access tokens. -""" - -from __future__ import with_statement - -try: - import simplejson as json -except ImportError: - import json - -import base64 -import errno -import time -import datetime -import os -import socket -import sys - -from libcloud.utils.connection import get_response_object -from libcloud.utils.py3 import b, httplib, urlencode, urlparse, PY3 -from libcloud.common.base import (ConnectionUserAndKey, JsonResponse, - PollingConnection) -from libcloud.common.types import (ProviderError, - LibcloudError) - -try: - from Crypto.Hash import SHA256 - from Crypto.PublicKey import RSA - from Crypto.Signature import PKCS1_v1_5 - import Crypto.Random - Crypto.Random.atfork() -except ImportError: - # The pycrypto library is unavailable - SHA256 = None - RSA = None - PKCS1_v1_5 = None - -UTC_TIMESTAMP_FORMAT = '%Y-%m-%dT%H:%M:%SZ' - - -def _utcnow(): - """ - Mocked in libcloud.test.common.google.GoogleTestCase. - """ - return datetime.datetime.utcnow() - - -def _utc_timestamp(datetime_obj): - return datetime_obj.strftime(UTC_TIMESTAMP_FORMAT) - - -def _from_utc_timestamp(timestamp): - return datetime.datetime.strptime(timestamp, UTC_TIMESTAMP_FORMAT) - - -def _get_gce_metadata(path=''): - try: - url = 'http://metadata/computeMetadata/v1/' + path.lstrip('/') - headers = {'Metadata-Flavor': 'Google'} - response = get_response_object(url, headers=headers) - return response.status, '', response.body - except Exception as e: - return -1, str(e), None - - -class GoogleAuthError(LibcloudError): - """Generic Error class for various authentication errors.""" - def __init__(self, value): - self.value = value - - def __repr__(self): - return repr(self.value) - - -class GoogleBaseError(ProviderError): - def __init__(self, value, http_code, code, driver=None): - self.code = code - super(GoogleBaseError, self).__init__(value, http_code, driver) - - -class InvalidRequestError(GoogleBaseError): - pass - - -class JsonParseError(GoogleBaseError): - pass - - -class ResourceNotFoundError(GoogleBaseError): - def __init__(self, value, http_code, code, driver=None): - self.code = code - if isinstance(value, dict) and 'message' in value and \ - value['message'].count('/') == 1 and \ - value['message'].count('projects/') == 1: - value['message'] = value['message'] + ". A missing project " \ - "error may be an authentication issue. " \ - "Please ensure your auth credentials match " \ - "your project. " - super(GoogleBaseError, self).__init__(value, http_code, driver) - - -class QuotaExceededError(GoogleBaseError): - pass - - -class ResourceExistsError(GoogleBaseError): - pass - - -class ResourceInUseError(GoogleBaseError): - pass - - -class GoogleResponse(JsonResponse): - """ - Google Base Response class. - """ - def success(self): - """ - Determine if the request was successful. - - For the Google response class, tag all responses as successful and - raise appropriate Exceptions from parse_body. - - :return: C{True} - """ - return True - - def _get_error(self, body): - """ - Get the error code and message from a JSON response. - - Return just the first error if there are multiple errors. - - :param body: The body of the JSON response dictionary - :type body: ``dict`` - - :return: Tuple containing error code and message - :rtype: ``tuple`` of ``str`` or ``int`` - """ - if 'errors' in body['error']: - err = body['error']['errors'][0] - else: - err = body['error'] - - if 'code' in err: - code = err.get('code') - message = err.get('message') - else: - code = err.get('reason', None) - message = body.get('error_description', err) - - return (code, message) - - def parse_body(self): - """ - Parse the JSON response body, or raise exceptions as appropriate. - - :return: JSON dictionary - :rtype: ``dict`` - """ - if len(self.body) == 0 and not self.parse_zero_length_body: - return self.body - - json_error = False - try: - body = json.loads(self.body) - except: - # If there is both a JSON parsing error and an unsuccessful http - # response (like a 404), we want to raise the http error and not - # the JSON one, so don't raise JsonParseError here. - body = self.body - json_error = True - - valid_http_codes = [ - httplib.OK, - httplib.CREATED, - httplib.ACCEPTED, - httplib.CONFLICT, - ] - if self.status in valid_http_codes: - if json_error: - raise JsonParseError(body, self.status, None) - elif 'error' in body: - (code, message) = self._get_error(body) - if code == 'QUOTA_EXCEEDED': - raise QuotaExceededError(message, self.status, code) - elif code == 'RESOURCE_ALREADY_EXISTS': - raise ResourceExistsError(message, self.status, code) - elif code == 'alreadyExists': - raise ResourceExistsError(message, self.status, code) - elif code.startswith('RESOURCE_IN_USE'): - raise ResourceInUseError(message, self.status, code) - else: - raise GoogleBaseError(message, self.status, code) - else: - return body - - elif self.status == httplib.NOT_FOUND: - if (not json_error) and ('error' in body): - (code, message) = self._get_error(body) - else: - message = body - code = None - raise ResourceNotFoundError(message, self.status, code) - - elif self.status == httplib.BAD_REQUEST: - if (not json_error) and ('error' in body): - (code, message) = self._get_error(body) - else: - message = body - code = None - raise InvalidRequestError(message, self.status, code) - - else: - if (not json_error) and ('error' in body): - (code, message) = self._get_error(body) - else: - message = body - code = None - raise GoogleBaseError(message, self.status, code) - - -class GoogleBaseDriver(object): - name = "Google API" - - -class GoogleBaseAuthConnection(ConnectionUserAndKey): - """ - Base class for Google Authentication. Should be subclassed for specific - types of authentication. - """ - driver = GoogleBaseDriver - responseCls = GoogleResponse - name = 'Google Auth' - host = 'accounts.google.com' - auth_path = '/o/oauth2/auth' - - def __init__(self, user_id, key=None, scopes=None, - redirect_uri='urn:ietf:wg:oauth:2.0:oob', - login_hint=None, **kwargs): - """ - :param user_id: The email address (for service accounts) or Client ID - (for installed apps) to be used for authentication. - :type user_id: ``str`` - - :param key: The RSA Key (for service accounts) or file path containing - key or Client Secret (for installed apps) to be used for - authentication. - :type key: ``str`` - - :param scopes: A list of urls defining the scope of authentication - to grant. - :type scopes: ``list`` - - :keyword redirect_uri: The Redirect URI for the authentication - request. See Google OAUTH2 documentation for - more info. - :type redirect_uri: ``str`` - - :keyword login_hint: Login hint for authentication request. Useful - for Installed Application authentication. - :type login_hint: ``str`` - """ - scopes = scopes or [] - - self.scopes = " ".join(scopes) - self.redirect_uri = redirect_uri - self.login_hint = login_hint - - super(GoogleBaseAuthConnection, self).__init__(user_id, key, **kwargs) - - def add_default_headers(self, headers): - headers['Content-Type'] = "application/x-www-form-urlencoded" - headers['Host'] = self.host - return headers - - def _token_request(self, request_body): - """ - Return an updated token from a token request body. - - :param request_body: A dictionary of values to send in the body of the - token request. - :type request_body: ``dict`` - - :return: A dictionary with updated token information - :rtype: ``dict`` - """ - data = urlencode(request_body) - try: - response = self.request('/o/oauth2/token', method='POST', - data=data) - except AttributeError: - raise GoogleAuthError('Invalid authorization response, please ' - 'check your credentials and time drift.') - token_info = response.object - if 'expires_in' in token_info: - expire_time = _utcnow() + datetime.timedelta( - seconds=token_info['expires_in']) - token_info['expire_time'] = _utc_timestamp(expire_time) - return token_info - - def refresh_token(self, token_info): - """ - Refresh the current token. - - Fetch an updated refresh token from internal metadata service. - - :param token_info: Dictionary containing token information. - (Not used, but here for compatibility) - :type token_info: ``dict`` - - :return: A dictionary containing updated token information. - :rtype: ``dict`` - """ - return self.get_new_token() - - -class GoogleInstalledAppAuthConnection(GoogleBaseAuthConnection): - """Authentication connection for "Installed Application" authentication.""" - def get_code(self): - """ - Give the user a URL that they can visit to authenticate and obtain a - code. This method will ask for that code that the user can paste in. - - Mocked in libcloud.test.common.google.GoogleTestCase. - - :return: Code supplied by the user after authenticating - :rtype: ``str`` - """ - auth_params = {'response_type': 'code', - 'client_id': self.user_id, - 'redirect_uri': self.redirect_uri, - 'scope': self.scopes, - 'state': 'Libcloud Request'} - if self.login_hint: - auth_params['login_hint'] = self.login_hint - - data = urlencode(auth_params) - - url = 'https://%s%s?%s' % (self.host, self.auth_path, data) - print('\nPlease Go to the following URL and sign in:') - print(url) - if PY3: - code = input('Enter Code: ') - else: - code = raw_input('Enter Code: ') - return code - - def get_new_token(self): - """ - Get a new token. Generally used when no previous token exists or there - is no refresh token - - :return: Dictionary containing token information - :rtype: ``dict`` - """ - # Ask the user for a code - code = self.get_code() - - token_request = {'code': code, - 'client_id': self.user_id, - 'client_secret': self.key, - 'redirect_uri': self.redirect_uri, - 'grant_type': 'authorization_code'} - - return self._token_request(token_request) - - def refresh_token(self, token_info): - """ - Use the refresh token supplied in the token info to get a new token. - - :param token_info: Dictionary containing current token information - :type token_info: ``dict`` - - :return: A dictionary containing updated token information. - :rtype: ``dict`` - """ - if 'refresh_token' not in token_info: - return self.get_new_token() - refresh_request = {'refresh_token': token_info['refresh_token'], - 'client_id': self.user_id, - 'client_secret': self.key, - 'grant_type': 'refresh_token'} - - new_token = self._token_request(refresh_request) - if 'refresh_token' not in new_token: - new_token['refresh_token'] = token_info['refresh_token'] - return new_token - - -class GoogleServiceAcctAuthConnection(GoogleBaseAuthConnection): - """Authentication class for "Service Account" authentication.""" - def __init__(self, user_id, key, *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 user_id: Email address to be used for Service Account - authentication. - :type user_id: ``str`` - - :param key: The RSA Key or path to file containing the key. - :type key: ``str`` - """ - if SHA256 is None: - raise GoogleAuthError('PyCrypto library required for ' - 'Service Account Authentication.') - # Check to see if 'key' is a file and read the file if it is. - if key.find("PRIVATE KEY---") == -1: - # key is a file - keypath = os.path.expanduser(key) - is_file_path = os.path.exists(keypath) and os.path.isfile(keypath) - if not is_file_path: - raise ValueError("Missing (or not readable) key " - "file: '%s'" % key) - with open(keypath, 'r') as f: - contents = f.read() - try: - key = json.loads(contents) - key = key['private_key'] - except ValueError: - key = contents - - super(GoogleServiceAcctAuthConnection, self).__init__( - user_id, key, *args, **kwargs) - - def get_new_token(self): - """ - Get a new token using the email address and RSA Key. - - :return: Dictionary containing token information - :rtype: ``dict`` - """ - # The header is always the same - header = {'alg': 'RS256', 'typ': 'JWT'} - header_enc = base64.urlsafe_b64encode(b(json.dumps(header))) - - # Construct a claim set - claim_set = {'iss': self.user_id, - 'scope': self.scopes, - 'aud': 'https://accounts.google.com/o/oauth2/token', - 'exp': int(time.time()) + 3600, - 'iat': int(time.time())} - claim_set_enc = base64.urlsafe_b64encode(b(json.dumps(claim_set))) - - # The message contains both the header and claim set - message = b'.'.join((header_enc, claim_set_enc)) - # Then the message is signed using the key supplied - key = RSA.importKey(self.key) - hash_func = SHA256.new(message) - signer = PKCS1_v1_5.new(key) - signature = base64.urlsafe_b64encode(signer.sign(hash_func)) - - # Finally the message and signature are sent to get a token - jwt = b'.'.join((message, signature)) - request = {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', - 'assertion': jwt} - - return self._token_request(request) - - -class GoogleGCEServiceAcctAuthConnection(GoogleBaseAuthConnection): - """Authentication class for self-authentication when used with a GCE - instance that supports serviceAccounts. - """ - def get_new_token(self): - """ - Get a new token from the internal metadata service. - - :return: Dictionary containing token information - :rtype: ``dict`` - """ - path = '/instance/service-accounts/default/token' - http_code, http_reason, token_info = _get_gce_metadata(path) - if http_code == httplib.NOT_FOUND: - raise ValueError("Service Accounts are not enabled for this " - "GCE instance.") - if http_code != httplib.OK: - raise ValueError("Internal GCE Authorization failed: " - "'%s'" % str(http_reason)) - token_info = json.loads(token_info) - if 'expires_in' in token_info: - expire_time = _utcnow() + datetime.timedelta( - seconds=token_info['expires_in']) - token_info['expire_time'] = _utc_timestamp(expire_time) - return token_info - - -class GoogleAuthType(object): - """ - SA (Service Account), - IA (Installed Application), - GCE (Auth from a GCE instance with service account enabled) - GCS_S3 (Cloud Storage S3 interoperability authentication) - """ - SA = 'SA' - IA = 'IA' - GCE = 'GCE' - GCS_S3 = 'GCS_S3' - - ALL_TYPES = [SA, IA, GCE, GCS_S3] - OAUTH2_TYPES = [SA, IA, GCE] - - @classmethod - def guess_type(cls, user_id): - if cls._is_sa(user_id): - return cls.SA - elif cls._is_gce(): - return cls.GCE - elif cls._is_gcs_s3(user_id): - return cls.GCS_S3 - else: - return cls.IA - - @classmethod - def is_oauth2(cls, auth_type): - return auth_type in cls.OAUTH2_TYPES - - @staticmethod - def _is_gce(): - """ - Checks if we can access the GCE metadata server. - Mocked in libcloud.test.common.google.GoogleTestCase. - """ - http_code, http_reason, body = _get_gce_metadata() - if http_code == httplib.OK and body: - return True - return False - - @staticmethod - def _is_gcs_s3(user_id): - """ - Checks S3 key format: 20 alphanumeric chars starting with GOOG. - """ - return len(user_id) == 20 and user_id.startswith('GOOG') - - @staticmethod - def _is_sa(user_id): - return user_id.endswith('.gserviceaccount.com') - - -class GoogleOAuth2Credential(object): - default_credential_file = '~/.google_libcloud_auth' - - def __init__(self, user_id, key, auth_type=None, credential_file=None, - scopes=None, **kwargs): - self.auth_type = auth_type or GoogleAuthType.guess_type(user_id) - if self.auth_type not in GoogleAuthType.ALL_TYPES: - raise GoogleAuthError('Invalid auth type: %s' % self.auth_type) - if not GoogleAuthType.is_oauth2(self.auth_type): - raise GoogleAuthError(('Auth type %s cannot be used with OAuth2' % - self.auth_type)) - self.user_id = user_id - self.key = key - - default_credential_file = '.'.join([self.default_credential_file, - user_id]) - self.credential_file = credential_file or default_credential_file - # Default scopes to read/write for compute, storage, and dns. - self.scopes = scopes or [ - 'https://www.googleapis.com/auth/compute', - 'https://www.googleapis.com/auth/devstorage.full_control', - 'https://www.googleapis.com/auth/ndev.clouddns.readwrite', - ] - - self.token = self._get_token_from_file() - - if self.auth_type == GoogleAuthType.GCE: - self.oauth2_conn = GoogleGCEServiceAcctAuthConnection( - self.user_id, self.scopes, **kwargs) - elif self.auth_type == GoogleAuthType.SA: - self.oauth2_conn = GoogleServiceAcctAuthConnection( - self.user_id, self.key, self.scopes, **kwargs) - elif self.auth_type == GoogleAuthType.IA: - self.oauth2_conn = GoogleInstalledAppAuthConnection( - self.user_id, self.key, self.scopes, **kwargs) - else: - raise GoogleAuthError('Invalid auth_type: %s' % - str(self.auth_type)) - - if self.token is None: - self.token = self.oauth2_conn.get_new_token() - self._write_token_to_file() - - @property - def access_token(self): - if self.token_expire_utc_datetime < _utcnow(): - self._refresh_token() - return self.token['access_token'] - - @property - def token_expire_utc_datetime(self): - return _from_utc_timestamp(self.token['expire_time']) - - def _refresh_token(self): - self.token = self.oauth2_conn.refresh_token(self.token) - self._write_token_to_file() - - def _get_token_from_file(self): - """ - Read credential file and return token information. - Mocked in libcloud.test.common.google.GoogleTestCase. - - :return: Token information dictionary, or None - :rtype: ``dict`` or ``None`` - """ - token = None - filename = os.path.realpath(os.path.expanduser(self.credential_file)) - - try: - with open(filename, 'r') as f: - data = f.read() - token = json.loads(data) - except IOError: - pass - return token - - def _write_token_to_file(self): - """ - Write token to credential file. - Mocked in libcloud.test.common.google.GoogleTestCase. - """ - filename = os.path.realpath(os.path.expanduser(self.credential_file)) - data = json.dumps(self.token) - with os.fdopen(os.open(filename, os.O_CREAT | os.O_WRONLY, - int('600', 8)), 'w') as f: - f.write(data) - - -class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): - """Base connection class for interacting with Google APIs.""" - driver = GoogleBaseDriver - responseCls = GoogleResponse - host = 'www.googleapis.com' - poll_interval = 2.0 - timeout = 180 - - def __init__(self, user_id, key=None, auth_type=None, - credential_file=None, scopes=None, **kwargs): - """ - Determine authentication type, set up appropriate authentication - connection and get initial authentication information. - - :param user_id: The email address (for service accounts) or Client ID - (for installed apps) to be used for authentication. - :type user_id: ``str`` - - :param key: The RSA Key (for service accounts) or file path containing - key or Client Secret (for installed apps) to be used for - authentication. - :type key: ``str`` - - :keyword auth_type: See GoogleAuthType class for list and description - of accepted values. - If not supplied, auth_type will be guessed based - on value of user_id or if the code is running - on a GCE instance. - :type auth_type: ``str`` - - :keyword credential_file: Path to file for caching authentication - information. - :type credential_file: ``str`` - - :keyword scopes: List of OAuth2 scope URLs. The empty default sets - read/write access to Compute, Storage, and DNS. - :type scopes: ``list`` - """ - super(GoogleBaseConnection, self).__init__(user_id, key, **kwargs) - - self.oauth2_credential = GoogleOAuth2Credential( - user_id, key, auth_type, credential_file, scopes, **kwargs) - - python_ver = '%s.%s.%s' % (sys.version_info[0], sys.version_info[1], - sys.version_info[2]) - ver_platform = 'Python %s/%s' % (python_ver, sys.platform) - self.user_agent_append(ver_platform) - - def add_default_headers(self, headers): - """ - @inherits: :class:`Connection.add_default_headers` - """ - headers['Content-Type'] = 'application/json' - headers['Host'] = self.host - return headers - - def pre_connect_hook(self, params, headers): - """ - Check to make sure that token hasn't expired. If it has, get an - updated token. Also, add the token to the headers. - - @inherits: :class:`Connection.pre_connect_hook` - """ - headers['Authorization'] = ('Bearer ' + - self.oauth2_credential.access_token) - return params, headers - - def encode_data(self, data): - """Encode data to JSON""" - return json.dumps(data) - - def request(self, *args, **kwargs): - """ - @inherits: :class:`Connection.request` - """ - # Adds some retry logic for the occasional - # "Connection Reset by peer" error. - retries = 4 - tries = 0 - while tries < (retries - 1): - try: - return super(GoogleBaseConnection, self).request( - *args, **kwargs) - except socket.error: - e = sys.exc_info()[1] - if e.errno == errno.ECONNRESET: - tries = tries + 1 - else: - raise e - # One more time, then give up. - return super(GoogleBaseConnection, self).request(*args, **kwargs) - - def has_completed(self, response): - """ - Determine if operation has completed based on response. - - :param response: JSON response - :type response: I{responseCls} - - :return: True if complete, False otherwise - :rtype: ``bool`` - """ - if response.object['status'] == 'DONE': - return True - else: - return False - - def get_poll_request_kwargs(self, response, context, request_kwargs): - """ - @inherits: :class:`PollingConnection.get_poll_request_kwargs` - """ - return {'action': response.object['selfLink']} - - def morph_action_hook(self, action): - """ - Update action to correct request path. - - In many places, the Google API returns a full URL to a resource. - This will strip the scheme and host off of the path and just return - the request. Otherwise, it will prepend the base request_path to - the action. - - :param action: The action to be called in the http request - :type action: ``str`` - - :return: The modified request based on the action - :rtype: ``str`` - """ - if action.startswith('https://'): - u = urlparse.urlsplit(action) - request = urlparse.urlunsplit(('', '', u[2], u[3], u[4])) - else: - request = self.request_path + action - return request http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/hostvirtual.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/hostvirtual.py b/apache-libcloud-1.0.0rc2/libcloud/common/hostvirtual.py deleted file mode 100644 index e7ce14d..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/hostvirtual.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. - -try: - import simplejson as json -except ImportError: - import json - -from libcloud.utils.py3 import httplib -from libcloud.common.base import ConnectionKey, JsonResponse -from libcloud.compute.types import InvalidCredsError -from libcloud.common.types import LibcloudError - -API_HOST = 'vapi.vr.org' - - -class HostVirtualException(LibcloudError): - def __init__(self, code, message): - self.code = code - self.message = message - self.args = (code, message) - - def __str__(self): - return self.__repr__() - - def __repr__(self): - return '<HostVirtualException in %d: %s>' % (self.code, self.message) - - -class HostVirtualConnection(ConnectionKey): - host = API_HOST - - allow_insecure = False - - def add_default_params(self, params): - params['key'] = self.key - return params - - -class HostVirtualResponse(JsonResponse): - valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED, - httplib.NO_CONTENT] - - def parse_body(self): - if not self.body: - return None - - data = json.loads(self.body) - return data - - def parse_error(self): - data = self.parse_body() - - if self.status == httplib.UNAUTHORIZED: - raise InvalidCredsError('%(code)s:%(message)s' % (data['error'])) - elif self.status == httplib.PRECONDITION_FAILED: - raise HostVirtualException( - data['error']['code'], data['error']['message']) - elif self.status == httplib.NOT_FOUND: - raise HostVirtualException( - data['error']['code'], data['error']['message']) - - return self.body - - def success(self): - return self.status in self.valid_response_codes http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/linode.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/linode.py b/apache-libcloud-1.0.0rc2/libcloud/common/linode.py deleted file mode 100644 index c991695..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/linode.py +++ /dev/null @@ -1,186 +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 -from libcloud.common.types import InvalidCredsError - -from libcloud.utils.py3 import PY3 -from libcloud.utils.py3 import b - -__all__ = [ - 'API_HOST', - 'API_ROOT', - 'LinodeException', - 'LinodeResponse', - 'LinodeConnection' -] - -# Endpoint for the Linode API -API_HOST = 'api.linode.com' -API_ROOT = '/' - -# Constants that map a RAM figure to a PlanID (updated 2014-08-25) -LINODE_PLAN_IDS = {1024: '1', - 2048: '2', - 4096: '4', - 8192: '6', - 16384: '7', - 32768: '8', - 49152: '9', - 65536: '10', - 98304: '12'} - -# Available filesystems for disk creation -LINODE_DISK_FILESYSTEMS = ['ext3', 'ext4', 'swap', 'raw'] - - -class LinodeException(Exception): - """Error originating from the Linode API - - This class wraps a Linode API error, a list of which is available in the - API documentation. All Linode API errors are a numeric code and a - human-readable description. - """ - def __init__(self, code, message): - self.code = code - self.message = message - self.args = (code, message) - - def __str__(self): - return "(%u) %s" % (self.code, self.message) - - def __repr__(self): - return "<LinodeException code %u '%s'>" % (self.code, self.message) - - -class LinodeResponse(JsonResponse): - """ - Linode API response - - Wraps the HTTP response returned by the Linode API. - - libcloud does not take advantage of batching, so a response will always - reflect the above format. A few weird quirks are caught here as well. - """ - - objects = None - - def __init__(self, response, connection): - """Instantiate a LinodeResponse from the HTTP response - - :keyword response: The raw response returned by urllib - :return: parsed :class:`LinodeResponse`""" - - self.connection = connection - - self.headers = 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') - - self.invalid = LinodeException(0xFF, - "Invalid JSON received from server") - - # Move parse_body() to here; we can't be sure of failure until we've - # parsed the body into JSON. - self.objects, self.errors = self.parse_body() - - if not self.success(): - # Raise the first error, as there will usually only be one - raise self.errors[0] - - def parse_body(self): - """Parse the body of the response into JSON objects - - If the response chokes the parser, action and data will be returned as - None and errorarray will indicate an invalid JSON exception. - - :return: ``list`` of objects and ``list`` of errors""" - js = super(LinodeResponse, self).parse_body() - - try: - if isinstance(js, dict): - # solitary response - promote to list - js = [js] - ret = [] - errs = [] - for obj in js: - if ("DATA" not in obj or "ERRORARRAY" not in obj or - "ACTION" not in obj): - ret.append(None) - errs.append(self.invalid) - continue - ret.append(obj["DATA"]) - errs.extend(self._make_excp(e) for e in obj["ERRORARRAY"]) - return (ret, errs) - except: - return (None, [self.invalid]) - - def success(self): - """Check the response for success - - The way we determine success is by the presence of an error in - ERRORARRAY. If one is there, we assume the whole request failed. - - :return: ``bool`` indicating a successful request""" - return len(self.errors) == 0 - - def _make_excp(self, error): - """Convert an API error to a LinodeException instance - - :keyword error: JSON object containing ``ERRORCODE`` and - ``ERRORMESSAGE`` - :type error: dict""" - if "ERRORCODE" not in error or "ERRORMESSAGE" not in error: - return None - if error["ERRORCODE"] == 4: - return InvalidCredsError(error["ERRORMESSAGE"]) - return LinodeException(error["ERRORCODE"], error["ERRORMESSAGE"]) - - -class LinodeConnection(ConnectionKey): - """ - A connection to the Linode API - - Wraps SSL connections to the Linode API, automagically injecting the - parameters that the API needs for each request. - """ - host = API_HOST - responseCls = LinodeResponse - - def add_default_params(self, params): - """ - Add parameters that are necessary for every request - - This method adds ``api_key`` and ``api_responseFormat`` to - the request. - """ - params["api_key"] = self.key - # Be explicit about this in case the default changes. - params["api_responseFormat"] = "json" - return params http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/liquidweb.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/liquidweb.py b/apache-libcloud-1.0.0rc2/libcloud/common/liquidweb.py deleted file mode 100644 index 123e82f..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/liquidweb.py +++ /dev/null @@ -1,229 +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 JsonResponse -from libcloud.common.base import ConnectionUserAndKey -from libcloud.utils.py3 import b -from libcloud.common.types import ProviderError - - -__all__ = [ - 'API_HOST', - 'LiquidWebException', - 'LiquidWebResponse', - 'LiquidWebConnection', -] - - -# Endpoint for liquidweb api. -API_HOST = 'api.stormondemand.com' - - -class LiquidWebException(ProviderError): - """The base class for other Liquidweb exceptions""" - - def __init__(self, value, http_code, extra=None): - """ - :param value: message contained in error - :type value: ``str`` - - :param http_code: error code - :type http_code: ``int`` - - :param extra: extra fields specific to error type - :type extra: ``list`` - """ - self.extra = extra - super(LiquidWebException, self).__init__(value, http_code, driver=None) - - def __str__(self): - return "%s %s" % (self.code, self.value) - - def __repr__(self): - return "LiquidWebException %s %s" % (self.code, self.value) - - -class APIException(LiquidWebException): - - def __init__(self, error_class, full_msg, http_code, extra=None): - self.error_class = error_class - super(APIException, self).__init__(full_msg, http_code, extra=extra) - - def __str__(self): - return "%s: %s" % (self.error_class, self.value) - - def __repr__(self): - return "%s: %s" % (self.error_class, self.value) - - -EXCEPTIONS_FIELDS = { - 'LW::Exception::API::Internal': { - 'fields': [] - }, - 'LW::Exception::API::InvalidEncoding': { - 'fields': ['encoding'] - }, - 'LW::Exception::API::InvalidMethod': { - 'fields': ['method'] - }, - 'LW::Exception::API::Maintenance': { - 'fields': [] - }, - 'LW::Exception::API::RateLimit': { - 'fields': ['account', 'ip', 'method'] - }, - 'LW::Exception::Authorization': { - 'fields': ['username'] - }, - 'LW::Exception::DNS::NoResponse': { - 'fields': ['nameservers'] - }, - 'LW::Exception::DNS::Servfail': { - 'fields': ['nameservers'] - }, - 'LW::Exception::Deserialize': { - 'fields': ['data', 'encoding'] - }, - 'LW::Exception::DuplicateRecord': { - 'fields': ['field', 'input', 'statement'] - }, - 'LW::Exception::Forbidden': { - 'fields': [] - }, - 'LW::Exception::Incapable': { - 'fields': ['capability', 'thing'] - }, - 'LW::Exception::Input': { - 'fields': ['field'] - }, - 'LW::Exception::Input::Disallowed': { - 'fields': ['field'] - }, - 'LW::Exception::Input::Multiple': { - 'fields': ['errors', 'field', 'type'] - }, - 'LW::Exception::Input::NotInRealm': { - 'fields': ['field', 'valid', 'value'] - }, - 'LW::Exception::Input::OutOfBounds': { - 'fields': ['field', 'max', 'min', 'value'] - }, - 'LW::Exception::Input::Required': { - 'fields': ['field', 'position'] - }, - 'LW::Exception::Input::Unknown': { - 'fields': ['field', 'value'] - }, - 'LW::Exception::Input::Validation': { - 'fields': ['field', 'type', 'value'] - }, - 'LW::Exception::Permission': { - 'fields': ['account', 'identifier'] - }, - 'LW::Exception::RecordNotFound': { - 'fields': ['field', 'input'] - }, - 'LW::Exception::RemoteService::Authorization': { - 'fields': ['url'] - }, - 'LW::Exception::Resource': { - 'fields': ['resource'] - }, - 'LW::Exception::Resource::Insufficient': { - 'fields': ['available', 'requested', 'resource'] - }, - 'LW::Exception::Resource::Unavailable': { - 'fields': ['resource'] - }, - 'LW::Exception::Serialize': { - 'fields': ['data', 'encoding'] - }, - 'LW::Exception::Workflow::Conflict': { - 'fields': ['conflict', 'workflow'] - } -} - - -class LiquidWebResponse(JsonResponse): - objects = None - errors = None - error_dict = {} - - def __init__(self, response, connection): - self.errors = [] - super(LiquidWebResponse, self).__init__(response=response, - connection=connection) - self.objects, self.errors = self.parse_body_and_errors() - if self.errors: - error = self.errors.pop() - raise self._make_excp(error, self.status) - - def parse_body_and_errors(self): - data = [] - errors = [] - js = super(LiquidWebResponse, self).parse_body() - if 'items' in js: - data.append(js['items']) - - if 'name' in js: - data.append(js) - - if 'deleted' in js: - data.append(js['deleted']) - - if 'error_class' in js: - errors.append(js) - - return (data, errors) - - def success(self): - """ - Returns ``True`` if our request is successful. - """ - return (len(self.errors) == 0) - - def _make_excp(self, error, status): - """ - Raise LiquidWebException. - """ - exc_type = error.get('error_class') - message = error.get('full_message') - try: - _type = EXCEPTIONS_FIELDS[exc_type] - fields = _type.get('fields') - extra = {} - except KeyError: - fields = [] - - for field in fields: - extra[field] = error.get(field) - return APIException(exc_type, message, status, extra=extra) - - -class LiquidWebConnection(ConnectionUserAndKey): - host = API_HOST - responseCls = LiquidWebResponse - - def add_default_headers(self, headers): - b64string = b('%s:%s' % (self.user_id, self.key)) - encoded = base64.b64encode(b64string).decode('utf-8') - authorization = 'Basic ' + encoded - - headers['Authorization'] = authorization - headers['Content-Type'] = 'application/json' - - return headers http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/luadns.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/luadns.py b/apache-libcloud-1.0.0rc2/libcloud/common/luadns.py deleted file mode 100644 index e079835..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/luadns.py +++ /dev/null @@ -1,69 +0,0 @@ -import base64 - - -from libcloud.common.base import ConnectionUserAndKey, JsonResponse -from libcloud.utils.py3 import b - -__all__ = [ - 'API_HOST', - 'LuadnsException', - 'LuadnsResponse', - 'LuadnsConnection' -] - -# Endpoint for luadns api -API_HOST = 'api.luadns.com' - - -class LuadnsResponse(JsonResponse): - errors = [] - objects = [] - - def __init__(self, response, connection): - super(LuadnsResponse, self).__init__(response=response, - connection=connection) - self.errors, self.objects = self.parse_body_and_errors() - if not self.success(): - raise LuadnsException(code=self.status, - message=self.errors.pop()['message']) - - def parse_body_and_errors(self): - js = super(LuadnsResponse, self).parse_body() - if 'message' 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 LuadnsConnection(ConnectionUserAndKey): - host = API_HOST - responseCls = LuadnsResponse - - def add_default_headers(self, headers): - b64string = b('%s:%s' % (self.user_id, self.key)) - encoded = base64.b64encode(b64string).decode('utf-8') - authorization = 'Basic ' + encoded - - headers['Accept'] = 'application/json' - headers['Authorization'] = authorization - - return headers - - -class LuadnsException(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 "Luadns %s %s" % (self.code, self.message) http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/nfsn.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/nfsn.py b/apache-libcloud-1.0.0rc2/libcloud/common/nfsn.py deleted file mode 100644 index 4286f0c..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/nfsn.py +++ /dev/null @@ -1,114 +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 hashlib -import random -import string -import time - -from libcloud.common.base import ConnectionUserAndKey -from libcloud.common.base import JsonResponse -from libcloud.common.types import InvalidCredsError, ProviderError -from libcloud.utils.py3 import basestring, httplib, urlencode - - -SALT_CHARACTERS = string.ascii_letters + string.digits - - -class NFSNException(ProviderError): - def __init__(self, value, http_code, code, driver=None): - self.code = code - super(NFSNException, self).__init__(value, http_code, driver) - - -class NFSNResponse(JsonResponse): - - def parse_error(self): - if self.status == httplib.UNAUTHORIZED: - raise InvalidCredsError('Invalid provider credentials') - - body = self.parse_body() - - if isinstance(body, basestring): - return body + ' (HTTP Code: %d)' % self.status - - error = body.get('error', None) - debug = body.get('debug', None) - # If we only have one of "error" or "debug", use the one that we have. - # If we have both, use both, with a space character in between them. - value = 'No message specified' - if error is not None: - value = error - if debug is not None: - value = debug - if error is not None and value is not None: - value = error + ' ' + value - value = value + ' (HTTP Code: %d)' % self.status - - return value - - -class NFSNConnection(ConnectionUserAndKey): - host = 'api.nearlyfreespeech.net' - responseCls = NFSNResponse - allow_insecure = False - - def _header(self, action, data): - """ Build the contents of the X-NFSN-Authentication HTTP header. See - https://members.nearlyfreespeech.net/wiki/API/Introduction for - more explanation. """ - login = self.user_id - timestamp = self._timestamp() - salt = self._salt() - api_key = self.key - data = urlencode(data) - data_hash = hashlib.sha1(data.encode('utf-8')).hexdigest() - - string = ';'.join((login, timestamp, salt, api_key, action, data_hash)) - string_hash = hashlib.sha1(string.encode('utf-8')).hexdigest() - - return ';'.join((login, timestamp, salt, string_hash)) - - def request(self, action, params=None, data='', headers=None, - method='GET'): - """ Add the X-NFSN-Authentication header to an HTTP request. """ - if not headers: - headers = {} - if not params: - params = {} - header = self._header(action, data) - - headers['X-NFSN-Authentication'] = header - if method == 'POST': - headers['Content-Type'] = 'application/x-www-form-urlencoded' - - return ConnectionUserAndKey.request(self, action, params, data, - headers, method) - - def encode_data(self, data): - """ NFSN expects the body to be regular key-value pairs that are not - JSON-encoded. """ - if data: - data = urlencode(data) - return data - - def _salt(self): - """ Return a 16-character alphanumeric string. """ - r = random.SystemRandom() - return ''.join(r.choice(SALT_CHARACTERS) for _ in range(16)) - - def _timestamp(self): - """ Return the current number of seconds since the Unix epoch, - as a string. """ - return str(int(time.time())) http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/nsone.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/nsone.py b/apache-libcloud-1.0.0rc2/libcloud/common/nsone.py deleted file mode 100644 index 5eb4e17..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/nsone.py +++ /dev/null @@ -1,62 +0,0 @@ -from libcloud.common.base import ConnectionKey, JsonResponse - - -__all__ = [ - 'API_HOST', - 'NsOneException', - 'NsOneResponse', - 'NsOneConnection' -] - -# Endpoint for nsone api -API_HOST = 'api.nsone.net' - - -class NsOneResponse(JsonResponse): - errors = [] - objects = [] - - def __init__(self, response, connection): - super(NsOneResponse, self).__init__(response=response, - connection=connection) - self.errors, self.objects = self.parse_body_and_errors() - if not self.success(): - raise NsOneException(code=self.status, - message=self.errors.pop()['message']) - - def parse_body_and_errors(self): - js = super(NsOneResponse, self).parse_body() - if 'message' 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 NsOneConnection(ConnectionKey): - host = API_HOST - responseCls = NsOneResponse - - def add_default_headers(self, headers): - headers['Content-Type'] = 'application/json' - headers['X-NSONE-KEY'] = self.key - - return headers - - -class NsOneException(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 "NsOneException %s %s" % (self.code, self.message) http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/onapp.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/onapp.py b/apache-libcloud-1.0.0rc2/libcloud/common/onapp.py deleted file mode 100644 index 259e315..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/onapp.py +++ /dev/null @@ -1,49 +0,0 @@ -from base64 import b64encode - -from libcloud.utils.py3 import b -from libcloud.utils.py3 import httplib -from libcloud.common.base import ConnectionUserAndKey, JsonResponse - - -class OnAppResponse(JsonResponse): - """ - OnApp response class - """ - - 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.NO_CONTENT] - - -class OnAppConnection(ConnectionUserAndKey): - """ - OnApp connection class - """ - - responseCls = OnAppResponse - - def add_default_headers(self, headers): - """ - Add Basic Authentication header to all the requests. - It injects the "Authorization: Basic Base64String===" header - in each request - - :type headers: ``dict`` - :param headers: Default input headers - - :rtype: ``dict`` - :return: Default input headers with the "Authorization" header. - """ - b64string = b("%s:%s" % (self.user_id, self.key)) - encoded = b64encode(b64string).decode("utf-8") - - headers["Authorization"] = "Basic " + encoded - return headers http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/openstack.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/openstack.py b/apache-libcloud-1.0.0rc2/libcloud/common/openstack.py deleted file mode 100644 index e63ee8a..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/openstack.py +++ /dev/null @@ -1,430 +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. - -""" -Common utilities for OpenStack -""" - -try: - from lxml import etree as ET -except ImportError: - from xml.etree import ElementTree as ET - -from libcloud.utils.py3 import httplib - -from libcloud.common.base import ConnectionUserAndKey, Response -from libcloud.common.types import ProviderError -from libcloud.compute.types import (LibcloudError, MalformedResponseError) -from libcloud.compute.types import KeyPairDoesNotExistError -from libcloud.common.openstack_identity import get_class_for_auth_version - -# Imports for backward compatibility reasons -from libcloud.common.openstack_identity import OpenStackServiceCatalog - - -try: - import simplejson as json -except ImportError: - import json - -AUTH_API_VERSION = '1.1' - -# Auth versions which contain token expiration information. -AUTH_VERSIONS_WITH_EXPIRES = [ - '1.1', - '2.0', - '2.0_apikey', - '2.0_password', - '3.x', - '3.x_password' -] - -__all__ = [ - 'OpenStackBaseConnection', - 'OpenStackResponse', - 'OpenStackException', - 'OpenStackDriverMixin' -] - - -class OpenStackBaseConnection(ConnectionUserAndKey): - - """ - Base class for OpenStack connections. - - :param user_id: User name to use when authenticating - :type user_id: ``str`` - - :param key: Secret to use when authenticating. - :type key: ``str`` - - :param secure: Use HTTPS? (True by default.) - :type secure: ``bool`` - - :param ex_force_base_url: Base URL for connection requests. If - not specified, this will be determined by - authenticating. - :type ex_force_base_url: ``str`` - - :param ex_force_auth_url: Base URL for authentication requests. - :type ex_force_auth_url: ``str`` - - :param ex_force_auth_version: Authentication version to use. If - not specified, defaults to AUTH_API_VERSION. - :type ex_force_auth_version: ``str`` - - :param ex_force_auth_token: Authentication token to use for connection - requests. If specified, the connection will - not attempt to authenticate, and the value - of ex_force_base_url will be used to - determine the base request URL. If - ex_force_auth_token is passed in, - ex_force_base_url must also be provided. - :type ex_force_auth_token: ``str`` - - :param ex_tenant_name: When authenticating, provide this tenant name to the - identity service. A scoped token will be returned. - Some cloud providers require the tenant name to be - provided at authentication time. Others will use a - default tenant if none is provided. - :type ex_tenant_name: ``str`` - - :param ex_force_service_type: Service type to use when selecting an - service. If not specified, a provider - specific default will be used. - :type ex_force_service_type: ``str`` - - :param ex_force_service_name: Service name to use when selecting an - service. If not specified, a provider - specific default will be used. - :type ex_force_service_name: ``str`` - - :param ex_force_service_region: Region to use when selecting an service. - If not specified, a provider specific - default will be used. - :type ex_force_service_region: ``str`` - """ - - auth_url = None - auth_token = None - auth_token_expires = None - auth_user_info = None - service_catalog = None - service_type = None - service_name = None - service_region = None - _auth_version = None - - def __init__(self, user_id, key, secure=True, - host=None, port=None, timeout=None, proxy_url=None, - ex_force_base_url=None, - ex_force_auth_url=None, - ex_force_auth_version=None, - ex_force_auth_token=None, - ex_tenant_name=None, - ex_force_service_type=None, - ex_force_service_name=None, - ex_force_service_region=None, - retry_delay=None, backoff=None): - super(OpenStackBaseConnection, self).__init__( - user_id, key, secure=secure, timeout=timeout, - retry_delay=retry_delay, backoff=backoff, proxy_url=proxy_url) - - if ex_force_auth_version: - self._auth_version = ex_force_auth_version - - self._ex_force_base_url = ex_force_base_url - self._ex_force_auth_url = ex_force_auth_url - self._ex_force_auth_token = ex_force_auth_token - self._ex_tenant_name = ex_tenant_name - self._ex_force_service_type = ex_force_service_type - self._ex_force_service_name = ex_force_service_name - self._ex_force_service_region = ex_force_service_region - self._osa = None - - if ex_force_auth_token and not ex_force_base_url: - raise LibcloudError( - 'Must also provide ex_force_base_url when specifying ' - 'ex_force_auth_token.') - - if ex_force_auth_token: - self.auth_token = ex_force_auth_token - - if not self._auth_version: - self._auth_version = AUTH_API_VERSION - - auth_url = self._get_auth_url() - - if not auth_url: - raise LibcloudError('OpenStack instance must ' + - 'have auth_url set') - - def get_auth_class(self): - """ - Retrieve identity / authentication class instance. - - :rtype: :class:`OpenStackIdentityConnection` - """ - if not self._osa: - auth_url = self._get_auth_url() - - cls = get_class_for_auth_version(auth_version=self._auth_version) - self._osa = cls(auth_url=auth_url, - user_id=self.user_id, - key=self.key, - tenant_name=self._ex_tenant_name, - timeout=self.timeout, - parent_conn=self) - - return self._osa - - def request(self, action, params=None, data='', headers=None, - method='GET', raw=False): - headers = headers or {} - params = params or {} - - # Include default content-type for POST and PUT request (if available) - default_content_type = getattr(self, 'default_content_type', None) - if method.upper() in ['POST', 'PUT'] and default_content_type: - headers = {'Content-Type': default_content_type} - - return super(OpenStackBaseConnection, self).request(action=action, - params=params, - data=data, - method=method, - headers=headers, - raw=raw) - - def _get_auth_url(self): - """ - Retrieve auth url for this instance using either "ex_force_auth_url" - constructor kwarg of "auth_url" class variable. - """ - auth_url = self.auth_url - - if self._ex_force_auth_url is not None: - auth_url = self._ex_force_auth_url - - return auth_url - - def get_service_catalog(self): - if self.service_catalog is None: - self._populate_hosts_and_request_paths() - - return self.service_catalog - - def get_service_name(self): - """ - Gets the service name used to look up the endpoint in the service - catalog. - - :return: name of the service in the catalog - """ - if self._ex_force_service_name: - return self._ex_force_service_name - - return self.service_name - - def get_endpoint(self): - """ - Selects the endpoint to use based on provider specific values, - or overrides passed in by the user when setting up the driver. - - :returns: url of the relevant endpoint for the driver - """ - service_type = self.service_type - service_name = self.service_name - service_region = self.service_region - - if self._ex_force_service_type: - service_type = self._ex_force_service_type - if self._ex_force_service_name: - service_name = self._ex_force_service_name - if self._ex_force_service_region: - service_region = self._ex_force_service_region - - endpoint = self.service_catalog.get_endpoint(service_type=service_type, - name=service_name, - region=service_region) - - url = endpoint.url - - if not url: - raise LibcloudError('Could not find specified endpoint') - - return url - - def add_default_headers(self, headers): - headers['X-Auth-Token'] = self.auth_token - headers['Accept'] = self.accept_format - return headers - - def morph_action_hook(self, action): - self._populate_hosts_and_request_paths() - return super(OpenStackBaseConnection, self).morph_action_hook(action) - - def _set_up_connection_info(self, url): - result = self._tuple_from_url(url) - (self.host, self.port, self.secure, self.request_path) = result - - def _populate_hosts_and_request_paths(self): - """ - OpenStack uses a separate host for API calls which is only provided - after an initial authentication request. - """ - osa = self.get_auth_class() - - if self._ex_force_auth_token: - # If ex_force_auth_token is provided we always hit the api directly - # and never try to authenticate. - # - # Note: When ex_force_auth_token is provided, ex_force_base_url - # must be provided as well. - self._set_up_connection_info(url=self._ex_force_base_url) - return - - if not osa.is_token_valid(): - # Token is not available or it has expired. Need to retrieve a - # new one. - if self._auth_version == '2.0_apikey': - kwargs = {'auth_type': 'api_key'} - elif self._auth_version == '2.0_password': - kwargs = {'auth_type': 'password'} - else: - kwargs = {} - - osa = osa.authenticate(**kwargs) # may throw InvalidCreds - - self.auth_token = osa.auth_token - self.auth_token_expires = osa.auth_token_expires - self.auth_user_info = osa.auth_user_info - - # Pull out and parse the service catalog - osc = OpenStackServiceCatalog(service_catalog=osa.urls, - auth_version=self._auth_version) - self.service_catalog = osc - - url = self._ex_force_base_url or self.get_endpoint() - self._set_up_connection_info(url=url) - - -class OpenStackException(ProviderError): - pass - - -class OpenStackResponse(Response): - node_driver = None - - def success(self): - i = int(self.status) - return i >= 200 and i <= 299 - - def has_content_type(self, content_type): - content_type_value = self.headers.get('content-type') or '' - content_type_value = content_type_value.lower() - return content_type_value.find(content_type.lower()) > -1 - - def parse_body(self): - if self.status == httplib.NO_CONTENT or not self.body: - return None - - if self.has_content_type('application/xml'): - try: - return ET.XML(self.body) - except: - raise MalformedResponseError( - 'Failed to parse XML', - body=self.body, - driver=self.node_driver) - - elif self.has_content_type('application/json'): - try: - return json.loads(self.body) - except: - raise MalformedResponseError( - 'Failed to parse JSON', - body=self.body, - driver=self.node_driver) - else: - return self.body - - def parse_error(self): - text = None - body = self.parse_body() - - if self.has_content_type('application/xml'): - text = '; '.join([err.text or '' for err in body.getiterator() - if err.text]) - elif self.has_content_type('application/json'): - values = list(body.values()) - - context = self.connection.context - driver = self.connection.driver - key_pair_name = context.get('key_pair_name', None) - - if len(values) > 0 and values[0]['code'] == 404 and key_pair_name: - raise KeyPairDoesNotExistError(name=key_pair_name, - driver=driver) - elif len(values) > 0 and 'message' in values[0]: - text = ';'.join([fault_data['message'] for fault_data - in values]) - else: - text = body - else: - # while we hope a response is always one of xml or json, we have - # seen html or text in the past, its not clear we can really do - # something to make it more readable here, so we will just pass - # it along as the whole response body in the text variable. - text = body - - return '%s %s %s' % (self.status, self.error, text) - - -class OpenStackDriverMixin(object): - - def __init__(self, *args, **kwargs): - self._ex_force_base_url = kwargs.get('ex_force_base_url', None) - self._ex_force_auth_url = kwargs.get('ex_force_auth_url', None) - self._ex_force_auth_version = kwargs.get('ex_force_auth_version', None) - self._ex_force_auth_token = kwargs.get('ex_force_auth_token', None) - self._ex_tenant_name = kwargs.get('ex_tenant_name', None) - self._ex_force_service_type = kwargs.get('ex_force_service_type', None) - self._ex_force_service_name = kwargs.get('ex_force_service_name', None) - self._ex_force_service_region = kwargs.get('ex_force_service_region', - None) - - def openstack_connection_kwargs(self): - """ - - :rtype: ``dict`` - """ - rv = {} - if self._ex_force_base_url: - rv['ex_force_base_url'] = self._ex_force_base_url - if self._ex_force_auth_token: - rv['ex_force_auth_token'] = self._ex_force_auth_token - if self._ex_force_auth_url: - rv['ex_force_auth_url'] = self._ex_force_auth_url - if self._ex_force_auth_version: - rv['ex_force_auth_version'] = self._ex_force_auth_version - if self._ex_tenant_name: - rv['ex_tenant_name'] = self._ex_tenant_name - if self._ex_force_service_type: - rv['ex_force_service_type'] = self._ex_force_service_type - if self._ex_force_service_name: - rv['ex_force_service_name'] = self._ex_force_service_name - if self._ex_force_service_region: - rv['ex_force_service_region'] = self._ex_force_service_region - return rv