http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/openstack_identity.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/openstack_identity.py b/apache-libcloud-1.0.0rc2/libcloud/common/openstack_identity.py deleted file mode 100644 index e8cc6a8..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/openstack_identity.py +++ /dev/null @@ -1,1394 +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 / shared code for handling authentication against OpenStack identity -service (Keystone). -""" - -import sys -import datetime - -from libcloud.utils.py3 import httplib -from libcloud.utils.iso8601 import parse_date - -from libcloud.common.base import ConnectionUserAndKey, Response -from libcloud.compute.types import (LibcloudError, InvalidCredsError, - MalformedResponseError) - -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.0', - '3.x_password' -] - -# How many seconds to subtract from the auth token expiration time before -# testing if the token is still valid. -# The time is subtracted to account for the HTTP request latency and prevent -# user from getting "InvalidCredsError" if token is about to expire. -AUTH_TOKEN_EXPIRES_GRACE_SECONDS = 5 - - -__all__ = [ - 'OpenStackIdentityVersion', - 'OpenStackIdentityDomain', - 'OpenStackIdentityProject', - 'OpenStackIdentityUser', - 'OpenStackIdentityRole', - - 'OpenStackServiceCatalog', - 'OpenStackServiceCatalogEntry', - 'OpenStackServiceCatalogEntryEndpoint', - 'OpenStackIdentityEndpointType', - - 'OpenStackIdentityConnection', - 'OpenStackIdentity_1_0_Connection', - 'OpenStackIdentity_1_1_Connection', - 'OpenStackIdentity_2_0_Connection', - 'OpenStackIdentity_3_0_Connection', - - 'get_class_for_auth_version' -] - - -class OpenStackIdentityEndpointType(object): - """ - Enum class for openstack identity endpoint type. - """ - INTERNAL = 'internal' - EXTERNAL = 'external' - ADMIN = 'admin' - - -class OpenStackIdentityTokenScope(object): - """ - Enum class for openstack identity token scope. - """ - PROJECT = 'project' - DOMAIN = 'domain' - UNSCOPED = 'unscoped' - - -class OpenStackIdentityVersion(object): - def __init__(self, version, status, updated, url): - self.version = version - self.status = status - self.updated = updated - self.url = url - - def __repr__(self): - return (('<OpenStackIdentityVersion version=%s, status=%s, ' - 'updated=%s, url=%s>' % - (self.version, self.status, self.updated, self.url))) - - -class OpenStackIdentityDomain(object): - def __init__(self, id, name, enabled): - self.id = id - self.name = name - self.enabled = enabled - - def __repr__(self): - return (('<OpenStackIdentityDomain id=%s, name=%s, enabled=%s>' % - (self.id, self.name, self.enabled))) - - -class OpenStackIdentityProject(object): - def __init__(self, id, name, description, enabled, domain_id=None): - self.id = id - self.name = name - self.description = description - self.enabled = enabled - self.domain_id = domain_id - - def __repr__(self): - return (('<OpenStackIdentityProject id=%s, domain_id=%s, name=%s, ' - 'enabled=%s>' % - (self.id, self.domain_id, self.name, self.enabled))) - - -class OpenStackIdentityRole(object): - def __init__(self, id, name, description, enabled): - self.id = id - self.name = name - self.description = description - self.enabled = enabled - - def __repr__(self): - return (('<OpenStackIdentityRole id=%s, name=%s, description=%s, ' - 'enabled=%s>' % (self.id, self.name, self.description, - self.enabled))) - - -class OpenStackIdentityUser(object): - def __init__(self, id, domain_id, name, email, description, enabled): - self.id = id - self.domain_id = domain_id - self.name = name - self.email = email - self.description = description - self.enabled = enabled - - def __repr__(self): - return (('<OpenStackIdentityUser id=%s, domain_id=%s, name=%s, ' - 'email=%s, enabled=%s>' % (self.id, self.domain_id, self.name, - self.email, self.enabled))) - - -class OpenStackServiceCatalog(object): - """ - http://docs.openstack.org/api/openstack-identity-service/2.0/content/ - - This class should be instantiated with the contents of the - 'serviceCatalog' in the auth response. This will do the work of figuring - out which services actually exist in the catalog as well as split them up - by type, name, and region if available - """ - - _auth_version = None - _service_catalog = None - - def __init__(self, service_catalog, auth_version=AUTH_API_VERSION): - self._auth_version = auth_version - - # Check this way because there are a couple of different 2.0_* - # auth types. - if '3.x' in self._auth_version: - entries = self._parse_service_catalog_auth_v3( - service_catalog=service_catalog) - elif '2.0' in self._auth_version: - entries = self._parse_service_catalog_auth_v2( - service_catalog=service_catalog) - elif ('1.1' in self._auth_version) or ('1.0' in self._auth_version): - entries = self._parse_service_catalog_auth_v1( - service_catalog=service_catalog) - else: - raise LibcloudError('auth version "%s" not supported' - % (self._auth_version)) - - # Force consistent ordering by sorting the entries - entries = sorted(entries, - key=lambda x: x.service_type + (x.service_name or '')) - self._entries = entries # stories all the service catalog entries - - def get_entries(self): - """ - Return all the entries for this service catalog. - - :rtype: ``list`` of :class:`.OpenStackServiceCatalogEntry` - """ - return self._entries - - def get_catalog(self): - """ - Deprecated in the favor of ``get_entries`` method. - """ - return self.get_entries() - - def get_public_urls(self, service_type=None, name=None): - """ - Retrieve all the available public (external) URLs for the provided - service type and name. - """ - endpoints = self.get_endpoints(service_type=service_type, - name=name) - - result = [] - for endpoint in endpoints: - endpoint_type = endpoint.endpoint_type - if endpoint_type == OpenStackIdentityEndpointType.EXTERNAL: - result.append(endpoint.url) - - return result - - def get_endpoints(self, service_type=None, name=None): - """ - Retrieve all the endpoints for the provided service type and name. - - :rtype: ``list`` of :class:`.OpenStackServiceCatalogEntryEndpoint` - """ - endpoints = [] - - for entry in self._entries: - # Note: "if XXX and YYY != XXX" comparison is used to support - # partial lookups. - # This allows user to pass in only one argument to the method (only - # service_type or name), both of them or neither. - if service_type and entry.service_type != service_type: - continue - - if name and entry.service_name != name: - continue - - for endpoint in entry.endpoints: - endpoints.append(endpoint) - - return endpoints - - def get_endpoint(self, service_type=None, name=None, region=None, - endpoint_type=OpenStackIdentityEndpointType.EXTERNAL): - """ - Retrieve a single endpoint using the provided criteria. - - Note: If no or more than one matching endpoint is found, an exception - is thrown. - """ - endpoints = [] - - for entry in self._entries: - if service_type and entry.service_type != service_type: - continue - - if name and entry.service_name != name: - continue - - for endpoint in entry.endpoints: - if region and endpoint.region != region: - continue - - if endpoint_type and endpoint.endpoint_type != endpoint_type: - continue - - endpoints.append(endpoint) - - if len(endpoints) == 1: - return endpoints[0] - elif len(endpoints) > 1: - raise ValueError('Found more than 1 matching endpoint') - else: - raise LibcloudError('Could not find specified endpoint') - - def get_regions(self, service_type=None): - """ - Retrieve a list of all the available regions. - - :param service_type: If specified, only return regions for this - service type. - :type service_type: ``str`` - - :rtype: ``list`` of ``str`` - """ - regions = set() - - for entry in self._entries: - if service_type and entry.service_type != service_type: - continue - - for endpoint in entry.endpoints: - if endpoint.region: - regions.add(endpoint.region) - - return sorted(list(regions)) - - def get_service_types(self, region=None): - """ - Retrieve all the available service types. - - :param region: Optional region to retrieve service types for. - :type region: ``str`` - - :rtype: ``list`` of ``str`` - """ - service_types = set() - - for entry in self._entries: - include = True - - for endpoint in entry.endpoints: - if region and endpoint.region != region: - include = False - break - - if include: - service_types.add(entry.service_type) - - return sorted(list(service_types)) - - def get_service_names(self, service_type=None, region=None): - """ - Retrieve list of service names that match service type and region. - - :type service_type: ``str`` - :type region: ``str`` - - :rtype: ``list`` of ``str`` - """ - names = set() - - if '2.0' not in self._auth_version: - raise ValueError('Unsupported version: %s' % (self._auth_version)) - - for entry in self._entries: - if service_type and entry.service_type != service_type: - continue - - include = True - for endpoint in entry.endpoints: - if region and endpoint.region != region: - include = False - break - - if include and entry.service_name: - names.add(entry.service_name) - - return sorted(list(names)) - - def _parse_service_catalog_auth_v1(self, service_catalog): - entries = [] - - for service, endpoints in service_catalog.items(): - entry_endpoints = [] - for endpoint in endpoints: - region = endpoint.get('region', None) - - public_url = endpoint.get('publicURL', None) - private_url = endpoint.get('internalURL', None) - - if public_url: - entry_endpoint = OpenStackServiceCatalogEntryEndpoint( - region=region, url=public_url, - endpoint_type=OpenStackIdentityEndpointType.EXTERNAL) - entry_endpoints.append(entry_endpoint) - - if private_url: - entry_endpoint = OpenStackServiceCatalogEntryEndpoint( - region=region, url=private_url, - endpoint_type=OpenStackIdentityEndpointType.INTERNAL) - entry_endpoints.append(entry_endpoint) - - entry = OpenStackServiceCatalogEntry(service_type=service, - endpoints=entry_endpoints) - entries.append(entry) - - return entries - - def _parse_service_catalog_auth_v2(self, service_catalog): - entries = [] - - for service in service_catalog: - service_type = service['type'] - service_name = service.get('name', None) - - entry_endpoints = [] - for endpoint in service.get('endpoints', []): - region = endpoint.get('region', None) - - public_url = endpoint.get('publicURL', None) - private_url = endpoint.get('internalURL', None) - - if public_url: - entry_endpoint = OpenStackServiceCatalogEntryEndpoint( - region=region, url=public_url, - endpoint_type=OpenStackIdentityEndpointType.EXTERNAL) - entry_endpoints.append(entry_endpoint) - - if private_url: - entry_endpoint = OpenStackServiceCatalogEntryEndpoint( - region=region, url=private_url, - endpoint_type=OpenStackIdentityEndpointType.INTERNAL) - entry_endpoints.append(entry_endpoint) - - entry = OpenStackServiceCatalogEntry(service_type=service_type, - endpoints=entry_endpoints, - service_name=service_name) - entries.append(entry) - - return entries - - def _parse_service_catalog_auth_v3(self, service_catalog): - entries = [] - - for item in service_catalog: - service_type = item['type'] - service_name = item.get('name', None) - - entry_endpoints = [] - for endpoint in item['endpoints']: - region = endpoint.get('region', None) - url = endpoint['url'] - endpoint_type = endpoint['interface'] - - if endpoint_type == 'internal': - endpoint_type = OpenStackIdentityEndpointType.INTERNAL - elif endpoint_type == 'public': - endpoint_type = OpenStackIdentityEndpointType.EXTERNAL - elif endpoint_type == 'admin': - endpoint_type = OpenStackIdentityEndpointType.ADMIN - - entry_endpoint = OpenStackServiceCatalogEntryEndpoint( - region=region, url=url, endpoint_type=endpoint_type) - entry_endpoints.append(entry_endpoint) - - entry = OpenStackServiceCatalogEntry(service_type=service_type, - service_name=service_name, - endpoints=entry_endpoints) - entries.append(entry) - - return entries - - -class OpenStackServiceCatalogEntry(object): - def __init__(self, service_type, endpoints=None, service_name=None): - """ - :param service_type: Service type. - :type service_type: ``str`` - - :param endpoints: Endpoints belonging to this entry. - :type endpoints: ``list`` - - :param service_name: Optional service name. - :type service_name: ``str`` - """ - self.service_type = service_type - self.endpoints = endpoints or [] - self.service_name = service_name - - # For consistency, sort the endpoints - self.endpoints = sorted(self.endpoints, key=lambda x: x.url or '') - - def __eq__(self, other): - return (self.service_type == other.service_type and - self.endpoints == other.endpoints and - other.service_name == self.service_name) - - def __ne__(self, other): - return not self.__eq__(other=other) - - def __repr__(self): - return (('<OpenStackServiceCatalogEntry service_type=%s, ' - 'service_name=%s, endpoints=%s' % - (self.service_type, self.service_name, repr(self.endpoints)))) - - -class OpenStackServiceCatalogEntryEndpoint(object): - VALID_ENDPOINT_TYPES = [ - OpenStackIdentityEndpointType.INTERNAL, - OpenStackIdentityEndpointType.EXTERNAL, - OpenStackIdentityEndpointType.ADMIN, - ] - - def __init__(self, region, url, endpoint_type='external'): - """ - :param region: Endpoint region. - :type region: ``str`` - - :param url: Endpoint URL. - :type url: ``str`` - - :param endpoint_type: Endpoint type (external / internal / admin). - :type endpoint_type: ``str`` - """ - if endpoint_type not in self.VALID_ENDPOINT_TYPES: - raise ValueError('Invalid type: %s' % (endpoint_type)) - - # TODO: Normalize / lowercase all the region names - self.region = region - self.url = url - self.endpoint_type = endpoint_type - - def __eq__(self, other): - return (self.region == other.region and self.url == other.url and - self.endpoint_type == other.endpoint_type) - - def __ne__(self, other): - return not self.__eq__(other=other) - - def __repr__(self): - return (('<OpenStackServiceCatalogEntryEndpoint region=%s, url=%s, ' - 'type=%s' % (self.region, self.url, self.endpoint_type))) - - -class OpenStackAuthResponse(Response): - def success(self): - return self.status in [httplib.OK, httplib.CREATED, - httplib.ACCEPTED, httplib.NO_CONTENT, - httplib.MULTIPLE_CHOICES, - httplib.UNAUTHORIZED, - httplib.INTERNAL_SERVER_ERROR] - - def parse_body(self): - if not self.body: - return None - - if 'content-type' in self.headers: - key = 'content-type' - elif 'Content-Type' in self.headers: - key = 'Content-Type' - else: - raise LibcloudError('Missing content-type header', - driver=OpenStackIdentityConnection) - - content_type = self.headers[key] - if content_type.find(';') != -1: - content_type = content_type.split(';')[0] - - if content_type == 'application/json': - try: - data = json.loads(self.body) - except: - driver = OpenStackIdentityConnection - raise MalformedResponseError('Failed to parse JSON', - body=self.body, - driver=driver) - elif content_type == 'text/plain': - data = self.body - else: - data = self.body - - return data - - -class OpenStackIdentityConnection(ConnectionUserAndKey): - """ - Base identity connection class which contains common / shared logic. - - Note: This class shouldn't be instantiated directly. - """ - responseCls = OpenStackAuthResponse - timeout = None - - def __init__(self, auth_url, user_id, key, tenant_name=None, - timeout=None, parent_conn=None): - super(OpenStackIdentityConnection, self).__init__(user_id=user_id, - key=key, - url=auth_url, - timeout=timeout) - - self.auth_url = auth_url - self.tenant_name = tenant_name - self.parent_conn = parent_conn - - # enable tests to use the same mock connection classes. - if parent_conn: - self.conn_classes = parent_conn.conn_classes - self.driver = parent_conn.driver - else: - self.driver = None - - self.auth_url = auth_url - self.tenant_name = tenant_name - self.timeout = timeout - - self.urls = {} - self.auth_token = None - self.auth_token_expires = None - self.auth_user_info = None - - def authenticated_request(self, action, params=None, data=None, - headers=None, method='GET', raw=False): - """ - Perform an authenticated request against the identity API. - """ - if not self.auth_token: - raise ValueError('Not to be authenticated to perform this request') - - headers = headers or {} - headers['X-Auth-Token'] = self.auth_token - - return self.request(action=action, params=params, data=data, - headers=headers, method=method, raw=raw) - - def morph_action_hook(self, action): - (_, _, _, request_path) = self._tuple_from_url(self.auth_url) - - if request_path == '': - # No path is provided in the auth_url, use action passed to this - # method. - return action - - return request_path - - def add_default_headers(self, headers): - headers['Accept'] = 'application/json' - headers['Content-Type'] = 'application/json; charset=UTF-8' - return headers - - def is_token_valid(self): - """ - Return True if the current auth token is already cached and hasn't - expired yet. - - :return: ``True`` if the token is still valid, ``False`` otherwise. - :rtype: ``bool`` - """ - if not self.auth_token: - return False - - if not self.auth_token_expires: - return False - - expires = self.auth_token_expires - \ - datetime.timedelta(seconds=AUTH_TOKEN_EXPIRES_GRACE_SECONDS) - - time_tuple_expires = expires.utctimetuple() - time_tuple_now = datetime.datetime.utcnow().utctimetuple() - - if time_tuple_now < time_tuple_expires: - return True - - return False - - def authenticate(self, force=False): - """ - Authenticate against the identity API. - - :param force: Forcefully update the token even if it's already cached - and still valid. - :type force: ``bool`` - """ - raise NotImplementedError('authenticate not implemented') - - def list_supported_versions(self): - """ - Retrieve a list of all the identity versions which are supported by - this installation. - - :rtype: ``list`` of :class:`.OpenStackIdentityVersion` - """ - response = self.request('/', method='GET') - result = self._to_versions(data=response.object['versions']['values']) - result = sorted(result, key=lambda x: x.version) - return result - - def _to_versions(self, data): - result = [] - for item in data: - version = self._to_version(data=item) - result.append(version) - - return result - - def _to_version(self, data): - try: - updated = parse_date(data['updated']) - except Exception: - updated = None - - try: - url = data['links'][0]['href'] - except IndexError: - url = None - - version = OpenStackIdentityVersion(version=data['id'], - status=data['status'], - updated=updated, - url=url) - return version - - def _is_authentication_needed(self, force=False): - """ - Determine if the authentication is needed or if the existing token (if - any exists) is still valid. - """ - if force: - return True - - if self.auth_version not in AUTH_VERSIONS_WITH_EXPIRES: - return True - - if self.is_token_valid(): - return False - - return True - - def _to_projects(self, data): - result = [] - for item in data: - project = self._to_project(data=item) - result.append(project) - - return result - - def _to_project(self, data): - project = OpenStackIdentityProject(id=data['id'], - name=data['name'], - description=data['description'], - enabled=data['enabled'], - domain_id=data.get('domain_id', - None)) - return project - - -class OpenStackIdentity_1_0_Connection(OpenStackIdentityConnection): - """ - Connection class for Keystone API v1.0. - """ - - responseCls = OpenStackAuthResponse - name = 'OpenStack Identity API v1.0' - auth_version = '1.0' - - def authenticate(self, force=False): - if not self._is_authentication_needed(force=force): - return self - - headers = { - 'X-Auth-User': self.user_id, - 'X-Auth-Key': self.key, - } - - resp = self.request('/v1.0', headers=headers, method='GET') - - if resp.status == httplib.UNAUTHORIZED: - # HTTP UNAUTHORIZED (401): auth failed - raise InvalidCredsError() - elif resp.status not in [httplib.NO_CONTENT, httplib.OK]: - body = 'code: %s body:%s headers:%s' % (resp.status, - resp.body, - resp.headers) - raise MalformedResponseError('Malformed response', body=body, - driver=self.driver) - else: - headers = resp.headers - # emulate the auth 1.1 URL list - self.urls = {} - self.urls['cloudServers'] = \ - [{'publicURL': headers.get('x-server-management-url', None)}] - self.urls['cloudFilesCDN'] = \ - [{'publicURL': headers.get('x-cdn-management-url', None)}] - self.urls['cloudFiles'] = \ - [{'publicURL': headers.get('x-storage-url', None)}] - self.auth_token = headers.get('x-auth-token', None) - self.auth_user_info = None - - if not self.auth_token: - raise MalformedResponseError('Missing X-Auth-Token in \ - response headers') - - return self - - -class OpenStackIdentity_1_1_Connection(OpenStackIdentityConnection): - """ - Connection class for Keystone API v1.1. - """ - - responseCls = OpenStackAuthResponse - name = 'OpenStack Identity API v1.1' - auth_version = '1.1' - - def authenticate(self, force=False): - if not self._is_authentication_needed(force=force): - return self - - reqbody = json.dumps({'credentials': {'username': self.user_id, - 'key': self.key}}) - resp = self.request('/v1.1/auth', data=reqbody, headers={}, - method='POST') - - if resp.status == httplib.UNAUTHORIZED: - # HTTP UNAUTHORIZED (401): auth failed - raise InvalidCredsError() - elif resp.status != httplib.OK: - body = 'code: %s body:%s' % (resp.status, resp.body) - raise MalformedResponseError('Malformed response', body=body, - driver=self.driver) - else: - try: - body = json.loads(resp.body) - except Exception: - e = sys.exc_info()[1] - raise MalformedResponseError('Failed to parse JSON', e) - - try: - expires = body['auth']['token']['expires'] - - self.auth_token = body['auth']['token']['id'] - self.auth_token_expires = parse_date(expires) - self.urls = body['auth']['serviceCatalog'] - self.auth_user_info = None - except KeyError: - e = sys.exc_info()[1] - raise MalformedResponseError('Auth JSON response is \ - missing required elements', e) - - return self - - -class OpenStackIdentity_2_0_Connection(OpenStackIdentityConnection): - """ - Connection class for Keystone API v2.0. - """ - - responseCls = OpenStackAuthResponse - name = 'OpenStack Identity API v1.0' - auth_version = '2.0' - - def authenticate(self, auth_type='api_key', force=False): - if not self._is_authentication_needed(force=force): - return self - - if auth_type == 'api_key': - return self._authenticate_2_0_with_api_key() - elif auth_type == 'password': - return self._authenticate_2_0_with_password() - else: - raise ValueError('Invalid value for auth_type argument') - - def _authenticate_2_0_with_api_key(self): - # API Key based authentication uses the RAX-KSKEY extension. - # http://s.apache.org/oAi - data = {'auth': - {'RAX-KSKEY:apiKeyCredentials': - {'username': self.user_id, 'apiKey': self.key}}} - if self.tenant_name: - data['auth']['tenantName'] = self.tenant_name - reqbody = json.dumps(data) - return self._authenticate_2_0_with_body(reqbody) - - def _authenticate_2_0_with_password(self): - # Password based authentication is the only 'core' authentication - # method in Keystone at this time. - # 'keystone' - http://s.apache.org/e8h - data = {'auth': - {'passwordCredentials': - {'username': self.user_id, 'password': self.key}}} - if self.tenant_name: - data['auth']['tenantName'] = self.tenant_name - reqbody = json.dumps(data) - return self._authenticate_2_0_with_body(reqbody) - - def _authenticate_2_0_with_body(self, reqbody): - resp = self.request('/v2.0/tokens', data=reqbody, - headers={'Content-Type': 'application/json'}, - method='POST') - - if resp.status == httplib.UNAUTHORIZED: - raise InvalidCredsError() - elif resp.status not in [httplib.OK, - httplib.NON_AUTHORITATIVE_INFORMATION]: - body = 'code: %s body: %s' % (resp.status, resp.body) - raise MalformedResponseError('Malformed response', body=body, - driver=self.driver) - else: - body = resp.object - - try: - access = body['access'] - expires = access['token']['expires'] - - self.auth_token = access['token']['id'] - self.auth_token_expires = parse_date(expires) - self.urls = access['serviceCatalog'] - self.auth_user_info = access.get('user', {}) - except KeyError: - e = sys.exc_info()[1] - raise MalformedResponseError('Auth JSON response is \ - missing required elements', e) - - return self - - def list_projects(self): - response = self.authenticated_request('/v2.0/tenants', method='GET') - result = self._to_projects(data=response.object['tenants']) - return result - - def list_tenants(self): - return self.list_projects() - - -class OpenStackIdentity_3_0_Connection(OpenStackIdentityConnection): - """ - Connection class for Keystone API v3.x. - """ - - responseCls = OpenStackAuthResponse - name = 'OpenStack Identity API v3.x' - auth_version = '3.0' - - VALID_TOKEN_SCOPES = [ - OpenStackIdentityTokenScope.PROJECT, - OpenStackIdentityTokenScope.DOMAIN, - OpenStackIdentityTokenScope.UNSCOPED - ] - - def __init__(self, auth_url, user_id, key, tenant_name=None, - domain_name='Default', - token_scope=OpenStackIdentityTokenScope.PROJECT, - timeout=None, parent_conn=None): - """ - :param tenant_name: Name of the project this user belongs to. Note: - When token_scope is set to project, this argument - control to which project to scope the token to. - :type tenant_name: ``str`` - - :param domain_name: Domain the user belongs to. Note: Then token_scope - is set to token, this argument controls to which - domain to scope the token to. - :type domain_name: ``str`` - - :param token_scope: Whether to scope a token to a "project" or a - "domain" - :type token_scope: ``str`` - """ - super(OpenStackIdentity_3_0_Connection, - self).__init__(auth_url=auth_url, - user_id=user_id, - key=key, - tenant_name=tenant_name, - timeout=timeout, - parent_conn=parent_conn) - if token_scope not in self.VALID_TOKEN_SCOPES: - raise ValueError('Invalid value for "token_scope" argument: %s' % - (token_scope)) - - if (token_scope == OpenStackIdentityTokenScope.PROJECT and - (not tenant_name or not domain_name)): - raise ValueError('Must provide tenant_name and domain_name ' - 'argument') - elif (token_scope == OpenStackIdentityTokenScope.DOMAIN and - not domain_name): - raise ValueError('Must provide domain_name argument') - - self.tenant_name = tenant_name - self.domain_name = domain_name - self.token_scope = token_scope - self.auth_user_roles = None - - def authenticate(self, force=False): - """ - Perform authentication. - """ - if not self._is_authentication_needed(force=force): - return self - - data = { - 'auth': { - 'identity': { - 'methods': ['password'], - 'password': { - 'user': { - 'domain': { - 'name': self.domain_name - }, - 'name': self.user_id, - 'password': self.key - } - } - } - } - } - - if self.token_scope == OpenStackIdentityTokenScope.PROJECT: - # Scope token to project (tenant) - data['auth']['scope'] = { - 'project': { - 'domain': { - 'name': self.domain_name - }, - 'name': self.tenant_name - } - } - elif self.token_scope == OpenStackIdentityTokenScope.DOMAIN: - # Scope token to domain - data['auth']['scope'] = { - 'domain': { - 'name': self.domain_name - } - } - elif self.token_scope == OpenStackIdentityTokenScope.UNSCOPED: - pass - else: - raise ValueError('Token needs to be scoped either to project or ' - 'a domain') - - data = json.dumps(data) - response = self.request('/v3/auth/tokens', data=data, - headers={'Content-Type': 'application/json'}, - method='POST') - - if response.status == httplib.UNAUTHORIZED: - # Invalid credentials - raise InvalidCredsError() - elif response.status in [httplib.OK, httplib.CREATED]: - headers = response.headers - - try: - body = json.loads(response.body) - except Exception: - e = sys.exc_info()[1] - raise MalformedResponseError('Failed to parse JSON', e) - - try: - roles = self._to_roles(body['token']['roles']) - except Exception: - e = sys.exc_info()[1] - roles = [] - - try: - expires = body['token']['expires_at'] - - self.auth_token = headers['x-subject-token'] - self.auth_token_expires = parse_date(expires) - # Note: catalog is not returned for unscoped tokens - self.urls = body['token'].get('catalog', None) - self.auth_user_info = None - self.auth_user_roles = roles - except KeyError: - e = sys.exc_info()[1] - raise MalformedResponseError('Auth JSON response is \ - missing required elements', e) - body = 'code: %s body:%s' % (response.status, response.body) - else: - raise MalformedResponseError('Malformed response', body=body, - driver=self.driver) - - return self - - def list_domains(self): - """ - List the available domains. - - :rtype: ``list`` of :class:`OpenStackIdentityDomain` - """ - response = self.authenticated_request('/v3/domains', method='GET') - result = self._to_domains(data=response.object['domains']) - return result - - def list_projects(self): - """ - List the available projects. - - Note: To perform this action, user you are currently authenticated with - needs to be an admin. - - :rtype: ``list`` of :class:`OpenStackIdentityProject` - """ - response = self.authenticated_request('/v3/projects', method='GET') - result = self._to_projects(data=response.object['projects']) - return result - - def list_users(self): - """ - List the available users. - - :rtype: ``list`` of :class:`.OpenStackIdentityUser` - """ - response = self.authenticated_request('/v3/users', method='GET') - result = self._to_users(data=response.object['users']) - return result - - def list_roles(self): - """ - List the available roles. - - :rtype: ``list`` of :class:`.OpenStackIdentityRole` - """ - response = self.authenticated_request('/v3/roles', method='GET') - result = self._to_roles(data=response.object['roles']) - return result - - def get_domain(self, domain_id): - """ - Retrieve information about a single domain. - - :param domain_id: ID of domain to retrieve information for. - :type domain_id: ``str`` - - :rtype: :class:`.OpenStackIdentityDomain` - """ - response = self.authenticated_request('/v3/domains/%s' % (domain_id), - method='GET') - result = self._to_domain(data=response.object['domain']) - return result - - def list_user_projects(self, user): - """ - Retrieve all the projects user belongs to. - - :rtype: ``list`` of :class:`.OpenStackIdentityProject` - """ - path = '/v3/users/%s/projects' % (user.id) - response = self.authenticated_request(path, method='GET') - result = self._to_projects(data=response.object['projects']) - return result - - def list_user_domain_roles(self, domain, user): - """ - Retrieve all the roles for a particular user on a domain. - - :rtype: ``list`` of :class:`.OpenStackIdentityRole` - """ - # TODO: Also add "get users roles" and "get assginements" which are - # available in 3.1 and 3.3 - path = '/v3/domains/%s/users/%s/roles' % (domain.id, user.id) - response = self.authenticated_request(path, method='GET') - result = self._to_roles(data=response.object['roles']) - return result - - def grant_domain_role_to_user(self, domain, role, user): - """ - Grant domain role to a user. - - Note: This function appears to be idempotent. - - :param domain: Domain to grant the role to. - :type domain: :class:`.OpenStackIdentityDomain` - - :param role: Role to grant. - :type role: :class:`.OpenStackIdentityRole` - - :param user: User to grant the role to. - :type user: :class:`.OpenStackIdentityUser` - - :return: ``True`` on success. - :rtype: ``bool`` - """ - path = ('/v3/domains/%s/users/%s/roles/%s' % - (domain.id, user.id, role.id)) - response = self.authenticated_request(path, method='PUT') - return response.status == httplib.NO_CONTENT - - def revoke_domain_role_from_user(self, domain, user, role): - """ - Revoke domain role from a user. - - :param domain: Domain to revoke the role from. - :type domain: :class:`.OpenStackIdentityDomain` - - :param role: Role to revoke. - :type role: :class:`.OpenStackIdentityRole` - - :param user: User to revoke the role from. - :type user: :class:`.OpenStackIdentityUser` - - :return: ``True`` on success. - :rtype: ``bool`` - """ - path = ('/v3/domains/%s/users/%s/roles/%s' % - (domain.id, user.id, role.id)) - response = self.authenticated_request(path, method='DELETE') - return response.status == httplib.NO_CONTENT - - def grant_project_role_to_user(self, project, role, user): - """ - Grant project role to a user. - - Note: This function appears to be idempotent. - - :param project: Project to grant the role to. - :type project: :class:`.OpenStackIdentityDomain` - - :param role: Role to grant. - :type role: :class:`.OpenStackIdentityRole` - - :param user: User to grant the role to. - :type user: :class:`.OpenStackIdentityUser` - - :return: ``True`` on success. - :rtype: ``bool`` - """ - path = ('/v3/projects/%s/users/%s/roles/%s' % - (project.id, user.id, role.id)) - response = self.authenticated_request(path, method='PUT') - return response.status == httplib.NO_CONTENT - - def revoke_project_role_from_user(self, project, role, user): - """ - Revoke project role from a user. - - :param project: Project to revoke the role from. - :type project: :class:`.OpenStackIdentityDomain` - - :param role: Role to revoke. - :type role: :class:`.OpenStackIdentityRole` - - :param user: User to revoke the role from. - :type user: :class:`.OpenStackIdentityUser` - - :return: ``True`` on success. - :rtype: ``bool`` - """ - path = ('/v3/projects/%s/users/%s/roles/%s' % - (project.id, user.id, role.id)) - response = self.authenticated_request(path, method='DELETE') - return response.status == httplib.NO_CONTENT - - def create_user(self, email, password, name, description=None, - domain_id=None, default_project_id=None, enabled=True): - """ - Create a new user account. - - :param email: User's mail address. - :type email: ``str`` - - :param password: User's password. - :type password: ``str`` - - :param name: User's name. - :type name: ``str`` - - :param description: Optional description. - :type description: ``str`` - - :param domain_id: ID of the domain to add the user to (optional). - :type domain_id: ``str`` - - :param default_project_id: ID of the default user project (optional). - :type default_project_id: ``str`` - - :param enabled: True to enable user after creation. - :type enabled: ``bool`` - - :return: Created user. - :rtype: :class:`.OpenStackIdentityUser` - """ - data = { - 'email': email, - 'password': password, - 'name': name, - 'enabled': enabled - } - - if description: - data['description'] = description - - if domain_id: - data['domain_id'] = domain_id - - if default_project_id: - data['default_project_id'] = default_project_id - - data = json.dumps({'user': data}) - response = self.authenticated_request('/v3/users', data=data, - method='POST') - - user = self._to_user(data=response.object['user']) - return user - - def enable_user(self, user): - """ - Enable user account. - - Note: This operation appears to be idempotent. - - :param user: User to enable. - :type user: :class:`.OpenStackIdentityUser` - - :return: User account which has been enabled. - :rtype: :class:`.OpenStackIdentityUser` - """ - data = { - 'enabled': True - } - data = json.dumps({'user': data}) - response = self.authenticated_request('/v3/users/%s' % (user.id), - data=data, - method='PATCH') - - user = self._to_user(data=response.object['user']) - return user - - def disable_user(self, user): - """ - Disable user account. - - Note: This operation appears to be idempotent. - - :param user: User to disable. - :type user: :class:`.OpenStackIdentityUser` - - :return: User account which has been disabled. - :rtype: :class:`.OpenStackIdentityUser` - """ - data = { - 'enabled': False - } - data = json.dumps({'user': data}) - response = self.authenticated_request('/v3/users/%s' % (user.id), - data=data, - method='PATCH') - - user = self._to_user(data=response.object['user']) - return user - - def _to_domains(self, data): - result = [] - for item in data: - domain = self._to_domain(data=item) - result.append(domain) - - return result - - def _to_domain(self, data): - domain = OpenStackIdentityDomain(id=data['id'], - name=data['name'], - enabled=data['enabled']) - return domain - - def _to_users(self, data): - result = [] - for item in data: - user = self._to_user(data=item) - result.append(user) - - return result - - def _to_user(self, data): - user = OpenStackIdentityUser(id=data['id'], - domain_id=data['domain_id'], - name=data['name'], - email=data['email'], - description=data.get('description', - None), - enabled=data['enabled']) - return user - - def _to_roles(self, data): - result = [] - for item in data: - user = self._to_role(data=item) - result.append(user) - - return result - - def _to_role(self, data): - role = OpenStackIdentityRole(id=data['id'], - name=data['name'], - description=data.get('description', - None), - enabled=data.get('enabled', True)) - return role - - -def get_class_for_auth_version(auth_version): - """ - Retrieve class for the provided auth version. - """ - if auth_version == '1.0': - cls = OpenStackIdentity_1_0_Connection - elif auth_version == '1.1': - cls = OpenStackIdentity_1_1_Connection - elif auth_version == '2.0' or auth_version == '2.0_apikey': - cls = OpenStackIdentity_2_0_Connection - elif auth_version == '2.0_password': - cls = OpenStackIdentity_2_0_Connection - elif auth_version == '3.x_password': - cls = OpenStackIdentity_3_0_Connection - else: - raise LibcloudError('Unsupported Auth Version requested') - - return cls
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/pointdns.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/pointdns.py b/apache-libcloud-1.0.0rc2/libcloud/common/pointdns.py deleted file mode 100644 index cfb911f..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/pointdns.py +++ /dev/null @@ -1,55 +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.utils.py3 import b -from libcloud.utils.py3 import httplib - -from libcloud.common.base import ConnectionUserAndKey -from libcloud.common.base import JsonResponse - - -class PointDNSDNSResponse(JsonResponse): - - 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`` - """ - # response.success() only checks for 200 and 201 codes. Should we - # add 202? - return self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED] - - -class PointDNSConnection(ConnectionUserAndKey): - host = 'pointhq.com' - responseCls = PointDNSDNSResponse - - def add_default_headers(self, headers): - """ - Add headers that are necessary for every request - - This method adds ``token`` to the request. - """ - b64string = b('%s:%s' % (self.user_id, self.key)) - token = base64.b64encode(b64string) - headers['Authorization'] = 'Basic %s' % token - headers['Accept'] = 'application/json' - 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/providers.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/providers.py b/apache-libcloud-1.0.0rc2/libcloud/common/providers.py deleted file mode 100644 index b8ad271..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/providers.py +++ /dev/null @@ -1,107 +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 methods for obtaining a reference to the provider driver class. -""" - -import sys - -__all__ = [ - 'get_driver', - 'set_driver' -] - - -def get_driver(drivers, provider, deprecated_providers=None, - deprecated_constants=None): - """ - Get a driver. - - :param drivers: Dictionary containing valid providers. - :type drivers: ``dict`` - - :param provider: Id (constant) of provider to get the driver for. - :type provider: :class:`libcloud.types.Provider` - - :param: deprecated_providers: Dictionary with information about the - deprecated drivers. - :type deprecated_providers: ``dict`` - - :param: deprecated_constants: Dictionary with information about the - deprecated provider constants. - :type deprecated_constants: ``dict`` - """ - # Those providers have been shut down or similar. - deprecated_providers = deprecated_providers or {} - if provider in deprecated_providers: - url = deprecated_providers[provider]['url'] - reason = deprecated_providers[provider]['reason'] - msg = ('Provider no longer supported: %s, please visit: %s' % - (url, reason)) - raise Exception(msg) - - # Those drivers have moved to "region" constructor argument model - deprecated_constants = deprecated_constants or {} - if provider in deprecated_constants: - old_name = provider.upper() - new_name = deprecated_constants[provider].upper() - - url = 'https://s.apache.org/lc0140un' - msg = ('Provider constant "%s" has been removed. New constant ' - 'is now called "%s".\n' - 'For more information on this change and how to modify your ' - 'code to work with it, please visit: %s' % - (old_name, new_name, url)) - raise Exception(msg) - - if provider in drivers: - mod_name, driver_name = drivers[provider] - _mod = __import__(mod_name, globals(), locals(), [driver_name]) - return getattr(_mod, driver_name) - - raise AttributeError('Provider %s does not exist' % (provider)) - - -def set_driver(drivers, provider, module, klass): - """ - Sets a driver. - - :param drivers: Dictionary to store providers. - :param provider: Id of provider to set driver for - - :type provider: :class:`libcloud.types.Provider` - :param module: The module which contains the driver - - :type module: L - :param klass: The driver class name - - :type klass: - """ - - if provider in drivers: - raise AttributeError('Provider %s already registered' % (provider)) - - drivers[provider] = (module, klass) - - # Check if this driver is valid - try: - driver = get_driver(drivers, provider) - except (ImportError, AttributeError): - exp = sys.exc_info()[1] - drivers.pop(provider) - raise exp - - return driver http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/rackspace.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/rackspace.py b/apache-libcloud-1.0.0rc2/libcloud/common/rackspace.py deleted file mode 100644 index 294255d..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/rackspace.py +++ /dev/null @@ -1,24 +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 settings for Rackspace Cloud Servers and Cloud Files -""" - -__all__ = [ - 'AUTH_URL' -] - -AUTH_URL = 'https://identity.api.rackspacecloud.com' http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/runabove.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/runabove.py b/apache-libcloud-1.0.0rc2/libcloud/common/runabove.py deleted file mode 100644 index 4d45241..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/runabove.py +++ /dev/null @@ -1,164 +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 - -try: - import simplejson as json -except ImportError: - import json - -from libcloud.utils.py3 import httplib -from libcloud.utils.connection import get_response_object -from libcloud.common.types import InvalidCredsError -from libcloud.common.base import ConnectionUserAndKey, JsonResponse -from libcloud.httplib_ssl import LibcloudHTTPSConnection - -__all__ = [ - 'RunAboveResponse', - 'RunAboveConnection' -] - -API_HOST = 'api.runabove.com' -API_ROOT = '/1.0' -LOCATIONS = { - 'SBG-1': {'id': 'SBG-1', 'name': 'Strasbourg 1', 'country': 'FR'}, - 'BHS-1': {'id': 'BHS-1', 'name': 'Montreal 1', 'country': 'CA'} -} -DEFAULT_ACCESS_RULES = [ - {'method': 'GET', 'path': '/*'}, - {'method': 'POST', 'path': '/*'}, - {'method': 'PUT', 'path': '/*'}, - {'method': 'DELETE', 'path': '/*'}, -] - - -class RunAboveException(Exception): - pass - - -class RunAboveResponse(JsonResponse): - def parse_error(self): - response = super(RunAboveResponse, self).parse_body() - - if response.get('errorCode', None) == 'INVALID_SIGNATURE': - raise InvalidCredsError('Signature validation failed, probably ' - 'using invalid credentials') - - return self.body - - -class RunAboveConnection(ConnectionUserAndKey): - """ - A connection to the RunAbove API - - Wraps SSL connections to the RunAbove API, automagically injecting the - parameters that the API needs for each request. - """ - host = API_HOST - request_path = API_ROOT - responseCls = RunAboveResponse - timestamp = None - ua = [] - LOCATIONS = LOCATIONS - _timedelta = None - - allow_insecure = True - - def __init__(self, user_id, *args, **kwargs): - self.consumer_key = kwargs.pop('ex_consumer_key', None) - if self.consumer_key is None: - consumer_key_json = self.request_consumer_key(user_id) - msg = ("Your consumer key isn't validated, " - "go to '%(validationUrl)s' for valid it. After instantiate " - "your driver with \"ex_consumer_key='%(consumerKey)s'\"." % - consumer_key_json) - raise RunAboveException(msg) - super(RunAboveConnection, self).__init__(user_id, *args, **kwargs) - - def request_consumer_key(self, user_id): - action = self.request_path + '/auth/credential' - data = json.dumps({ - 'accessRules': DEFAULT_ACCESS_RULES, - 'redirection': 'http://runabove.com', - }) - headers = { - 'Content-Type': 'application/json', - 'X-Ra-Application': user_id, - } - httpcon = LibcloudHTTPSConnection(self.host) - httpcon.request(method='POST', url=action, body=data, headers=headers) - response = httpcon.getresponse() - - if response.status == httplib.UNAUTHORIZED: - raise InvalidCredsError() - - body = response.read() - json_response = json.loads(body) - httpcon.close() - return json_response - - def get_timestamp(self): - if not self._timedelta: - url = 'https://%s/%s/auth/time' % (API_HOST, API_ROOT) - response = get_response_object(url=url, method='GET', headers={}) - if not response or not response.body: - raise Exception('Failed to get current time from RunAbove API') - - timestamp = int(response.body) - self._timedelta = timestamp - int(time.time()) - return int(time.time()) + self._timedelta - - def make_signature(self, method, action, data, timestamp): - full_url = 'https://%s%s' % (API_HOST, action) - sha1 = hashlib.sha1() - base_signature = "+".join([ - self.key, - self.consumer_key, - method.upper(), - full_url, - data if data else '', - str(timestamp), - ]) - sha1.update(base_signature.encode()) - signature = '$1$' + sha1.hexdigest() - return signature - - def add_default_params(self, params): - return params - - def add_default_headers(self, headers): - headers.update({ - 'X-Ra-Application': self.user_id, - 'X-Ra-Consumer': self.consumer_key, - 'Content-type': 'application/json', - }) - return headers - - def request(self, action, params=None, data=None, headers=None, - method='GET', raw=False): - data = json.dumps(data) if data else None - timestamp = self.get_timestamp() - signature = self.make_signature(method, action, data, timestamp) - headers = headers or {} - headers.update({ - 'X-Ra-Timestamp': timestamp, - 'X-Ra-Signature': signature - }) - return super(RunAboveConnection, 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/softlayer.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/softlayer.py b/apache-libcloud-1.0.0rc2/libcloud/common/softlayer.py deleted file mode 100644 index d41b431..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/softlayer.py +++ /dev/null @@ -1,88 +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. -""" -Softlayer connection -""" - -from libcloud.common.base import ConnectionUserAndKey -from libcloud.common.xmlrpc import XMLRPCResponse, XMLRPCConnection -from libcloud.common.types import InvalidCredsError, LibcloudError - - -class SoftLayerException(LibcloudError): - """ - Exception class for SoftLayer driver - """ - pass - - -class SoftLayerObjectDoesntExist(LibcloudError): - """ - Exception class for SoftLayer driver object doesnt exist - """ - pass - - -class SoftLayerResponse(XMLRPCResponse): - defaultExceptionCls = SoftLayerException - exceptions = { - 'SoftLayer_Account': InvalidCredsError, - 'SoftLayer_Exception_ObjectNotFound': SoftLayerObjectDoesntExist - } - - -class SoftLayerConnection(XMLRPCConnection, ConnectionUserAndKey): - responseCls = SoftLayerResponse - host = 'api.softlayer.com' - endpoint = '/xmlrpc/v3' - - def request(self, service, method, *args, **kwargs): - headers = {} - headers.update(self._get_auth_headers()) - headers.update(self._get_init_params(service, kwargs.get('id'))) - headers.update( - self._get_object_mask(service, kwargs.get('object_mask'))) - headers.update( - self._get_object_mask(service, kwargs.get('object_mask'))) - - args = ({'headers': headers}, ) + args - endpoint = '%s/%s' % (self.endpoint, service) - return super(SoftLayerConnection, self).request(method, *args, - **{'endpoint': - endpoint}) - - def _get_auth_headers(self): - return { - 'authenticate': { - 'username': self.user_id, - 'apiKey': self.key - } - } - - def _get_init_params(self, service, id): - if id is not None: - return { - '%sInitParameters' % service: {'id': id} - } - else: - return {} - - def _get_object_mask(self, service, mask): - if mask is not None: - return { - '%sObjectMask' % service: {'mask': mask} - } - else: - return {} http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/types.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/types.py b/apache-libcloud-1.0.0rc2/libcloud/common/types.py deleted file mode 100644 index b5ff512..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/types.py +++ /dev/null @@ -1,143 +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.utils.py3 import httplib - -__all__ = [ - "LibcloudError", - "MalformedResponseError", - "ProviderError", - "InvalidCredsError", - "InvalidCredsException", - "LazyList" -] - - -class LibcloudError(Exception): - """The base class for other libcloud exceptions""" - - def __init__(self, value, driver=None): - super(LibcloudError, self).__init__(value) - self.value = value - self.driver = driver - - def __str__(self): - return self.__repr__() - - def __repr__(self): - return ("<LibcloudError in " + - repr(self.driver) + - " " + - repr(self.value) + ">") - - -class MalformedResponseError(LibcloudError): - """Exception for the cases when a provider returns a malformed - response, e.g. you request JSON and provider returns - '<h3>something</h3>' due to some error on their side.""" - - def __init__(self, value, body=None, driver=None): - self.value = value - self.driver = driver - self.body = body - - def __str__(self): - return self.__repr__() - - def __repr__(self): - return ("<MalformedResponseException in " + - repr(self.driver) + - " " + - repr(self.value) + - ">: " + - repr(self.body)) - - -class ProviderError(LibcloudError): - """ - Exception used when provider gives back - error response (HTTP 4xx, 5xx) for a request. - - Specific sub types can be derieved for errors like - HTTP 401 : InvalidCredsError - HTTP 404 : NodeNotFoundError, ContainerDoesNotExistError - """ - - def __init__(self, value, http_code, driver=None): - super(ProviderError, self).__init__(value=value, driver=driver) - self.http_code = http_code - - def __str__(self): - return self.__repr__() - - def __repr__(self): - return repr(self.value) - - -class InvalidCredsError(ProviderError): - """Exception used when invalid credentials are used on a provider.""" - - def __init__(self, value='Invalid credentials with the provider', - driver=None): - super(InvalidCredsError, self).__init__(value, - http_code=httplib.UNAUTHORIZED, - driver=driver) - - -# Deprecated alias of :class:`InvalidCredsError` -InvalidCredsException = InvalidCredsError - - -class LazyList(object): - - def __init__(self, get_more, value_dict=None): - self._data = [] - self._last_key = None - self._exhausted = False - self._all_loaded = False - self._get_more = get_more - self._value_dict = value_dict or {} - - def __iter__(self): - if not self._all_loaded: - self._load_all() - - data = self._data - for i in data: - yield i - - def __getitem__(self, index): - if index >= len(self._data) and not self._all_loaded: - self._load_all() - - return self._data[index] - - def __len__(self): - self._load_all() - return len(self._data) - - def __repr__(self): - self._load_all() - repr_string = ', ' .join([repr(item) for item in self._data]) - repr_string = '[%s]' % (repr_string) - return repr_string - - def _load_all(self): - while not self._exhausted: - newdata, self._last_key, self._exhausted = \ - self._get_more(last_key=self._last_key, - value_dict=self._value_dict) - self._data.extend(newdata) - self._all_loaded = True http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/vultr.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/vultr.py b/apache-libcloud-1.0.0rc2/libcloud/common/vultr.py deleted file mode 100644 index aa57105..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/vultr.py +++ /dev/null @@ -1,121 +0,0 @@ -from libcloud.common.base import ConnectionKey, JsonResponse - - -__all__ = [ - 'API_HOST', - 'VultrConnection', - 'VultrException', - 'VultrResponse', -] - -# Endpoint for the Vultr API -API_HOST = 'api.vultr.com' - - -class VultrResponse(JsonResponse): - - objects = None - error_dict = {} - errors = None - ERROR_CODE_MAP = { - - 400: "Invalid API location. Check the URL that you are using.", - 403: "Invalid or missing API key. Check that your API key is present" + - " and matches your assigned key.", - 405: "Invalid HTTP method. Check that the method (POST|GET) matches" + - " what the documentation indicates.", - 412: "Request failed. Check the response body for a more detailed" + - " description.", - 500: "Internal server error. Try again at a later time.", - 503: "Rate limit hit. API requests are limited to an average of 1/s." + - " Try your request again later.", - - } - - def __init__(self, response, connection): - - self.errors = [] - super(VultrResponse, self).__init__(response=response, - connection=connection) - self.objects, self.errors = self.parse_body_and_errors() - if not self.success(): - raise self._make_excp(self.errors[0]) - - def parse_body_and_errors(self): - """ - Returns JSON data in a python list. - """ - json_objects = [] - errors = [] - - if self.status in self.ERROR_CODE_MAP: - self.error_dict['ERRORCODE'] = self.status - self.error_dict['ERRORMESSAGE'] = self.ERROR_CODE_MAP[self.status] - errors.append(self.error_dict) - - js = super(VultrResponse, self).parse_body() - if isinstance(js, dict): - js = [js] - - json_objects.append(js) - - return (json_objects, errors) - - def _make_excp(self, error): - """ - Convert API error to a VultrException instance - """ - - return VultrException(error['ERRORCODE'], error['ERRORMESSAGE']) - - def success(self): - - return len(self.errors) == 0 - - -class VultrConnection(ConnectionKey): - """ - A connection to the Vultr API - """ - host = API_HOST - responseCls = VultrResponse - - def add_default_params(self, params): - """ - Returns default params such as api_key which is - needed to perform an action.Returns a dictionary. - Example:/v1/server/upgrade_plan?api_key=self.key - """ - params['api_key'] = self.key - - return params - - def add_default_headers(self, headers): - """ - Returns default headers such as content-type. - Returns a dictionary. - """ - headers["Content-Type"] = "application/x-www-form-urlencoded" - headers["Accept"] = "text/plain" - - return headers - - def set_path(self): - self.path = '/v/' - return self.path - - -class VultrException(Exception): - """ - Error originating from the Vultr API - """ - 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 "VultrException code %u '%s'" % (self.code, self.message) http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/worldwidedns.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/worldwidedns.py b/apache-libcloud-1.0.0rc2/libcloud/common/worldwidedns.py deleted file mode 100644 index 1c02a12..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/worldwidedns.py +++ /dev/null @@ -1,195 +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 re - -from libcloud.common.base import ConnectionUserAndKey -from libcloud.common.base import Response -from libcloud.common.types import ProviderError - - -OK_CODES = ['200', '211', '212', '213'] -ERROR_CODES = ['401', '403', '405', '406', '407', '408', '409', '410', '411', - '412', '413', '414', '450', '451'] - - -class WorldWideDNSException(ProviderError): - def __init__(self, value, http_code, code, driver=None): - self.code = code - super(WorldWideDNSException, self).__init__(value, http_code, driver) - - -class SuspendedAccount(WorldWideDNSException): - def __init__(self, http_code, driver=None): - value = "Login ID you supplied is SUSPENDED, you need to renew" + \ - " your account" - super(SuspendedAccount, self).__init__(value, http_code, 401, - driver) - - -class LoginOrPasswordNotMatch(WorldWideDNSException): - def __init__(self, http_code, driver=None): - value = "Login ID and/or Password you supplied is not on file or" + \ - " does not match" - super(LoginOrPasswordNotMatch, self).__init__(value, http_code, 403, - driver) - - -class NonExistentDomain(WorldWideDNSException): - def __init__(self, http_code, driver=None): - value = "Domain name supplied is not in your account" - super(NonExistentDomain, self).__init__(value, http_code, 405, - driver) - - -class CouldntRemoveDomain(WorldWideDNSException): - def __init__(self, http_code, driver=None): - value = "Error occured removing domain from name server, try again" - super(CouldntRemoveDomain, self).__init__(value, http_code, 406, - driver) - - -class LimitExceeded(WorldWideDNSException): - def __init__(self, http_code, driver=None): - value = "Your limit was exceeded, you need to upgrade your account" - super(LimitExceeded, self).__init__(value, http_code, 407, - driver) - - -class ExistentDomain(WorldWideDNSException): - def __init__(self, http_code, driver=None): - value = "Domain already exists on our servers" - super(ExistentDomain, self).__init__(value, http_code, 408, - driver) - - -class DomainBanned(WorldWideDNSException): - def __init__(self, http_code, driver=None): - value = "Domain is listed in DNSBL and is banned from our servers" - super(DomainBanned, self).__init__(value, http_code, 409, - driver) - - -class InvalidDomainName(WorldWideDNSException): - def __init__(self, http_code, driver=None): - value = "Invalid domain name" - super(InvalidDomainName, self).__init__(value, http_code, 410, - driver) - - -class ErrorOnReloadInNameServer(WorldWideDNSException): - def __init__(self, server, http_code, driver=None): - if server == 1: - value = "Name server #1 kicked an error on reload, contact support" - code = 411 - elif server == 2: - value = "Name server #2 kicked an error on reload, contact support" - code = 412 - elif server == 3: - value = "Name server #3 kicked an error on reload, contact support" - code = 413 - super(ErrorOnReloadInNameServer, self).__init__(value, http_code, code, - driver) - - -class NewUserNotValid(WorldWideDNSException): - def __init__(self, http_code, driver=None): - value = "New userid is not valid" - super(NewUserNotValid, self).__init__(value, http_code, 414, - driver) - - -class CouldntReachNameServer(WorldWideDNSException): - def __init__(self, http_code, driver=None): - value = "Couldn't reach the name server, try again later" - super(CouldntReachNameServer, self).__init__(value, http_code, 450, - driver) - - -class NoZoneFile(WorldWideDNSException): - def __init__(self, http_code, driver=None): - value = "No zone file in the name server queried" - super(NoZoneFile, self).__init__(value, http_code, 451, - driver) - - -ERROR_CODE_TO_EXCEPTION_CLS = { - '401': SuspendedAccount, - '403': LoginOrPasswordNotMatch, - '405': NonExistentDomain, - '406': CouldntRemoveDomain, - '407': LimitExceeded, - '408': ExistentDomain, - '409': DomainBanned, - '410': InvalidDomainName, - '411': ErrorOnReloadInNameServer, - '412': ErrorOnReloadInNameServer, - '413': ErrorOnReloadInNameServer, - '414': NewUserNotValid, - '450': CouldntReachNameServer, - '451': NoZoneFile, -} - - -class WorldWideDNSResponse(Response): - - def parse_body(self): - """ - Parse response body. - - :return: Parsed body. - :rtype: ``str`` - """ - if self._code_response(self.body): - codes = re.split('\r?\n', self.body) - for code in codes: - if code in OK_CODES: - continue - elif code in ERROR_CODES: - exception = ERROR_CODE_TO_EXCEPTION_CLS.get(code) - if code in ['411', '412', '413']: - server = int(code[2]) - raise exception(server, self.status) - raise exception(self.status) - return self.body - - def _code_response(self, body): - """ - Checks if the response body contains code status. - - :rtype: ``bool`` - """ - available_response_codes = OK_CODES + ERROR_CODES - codes = re.split('\r?\n', body) - if codes[0] in available_response_codes: - return True - return False - - -class WorldWideDNSConnection(ConnectionUserAndKey): - host = 'www.worldwidedns.net' - responseCls = WorldWideDNSResponse - - def add_default_params(self, params): - """ - Add parameters that are necessary for every request - - This method adds ``NAME`` and ``PASSWORD`` to - the request. - """ - params["NAME"] = self.user_id - params["PASSWORD"] = self.key - if hasattr(self, 'reseller_id'): - params["ID"] = self.reseller_id - return params http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/xmlrpc.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/xmlrpc.py b/apache-libcloud-1.0.0rc2/libcloud/common/xmlrpc.py deleted file mode 100644 index 2502ea6..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/common/xmlrpc.py +++ /dev/null @@ -1,108 +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. -""" -Base classes for working with xmlrpc APIs -""" - -import sys - -from libcloud.utils.py3 import xmlrpclib -from libcloud.utils.py3 import httplib -from libcloud.common.base import Response, Connection - - -class ProtocolError(Exception): - pass - - -class ErrorCodeMixin(object): - """ - This is a helper for API's that have a well defined collection of error - codes that are easily parsed out of error messages. It acts as a factory: - it finds the right exception for the error code, fetches any parameters it - needs from the context and raises it. - """ - - exceptions = {} - - def raise_exception_for_error(self, error_code, message): - exceptionCls = self.exceptions.get(error_code, None) - if exceptionCls is None: - return - context = self.connection.context - driver = self.connection.driver - params = {} - if hasattr(exceptionCls, 'kwargs'): - for key in exceptionCls.kwargs: - if key in context: - params[key] = context[key] - raise exceptionCls(value=message, driver=driver, **params) - - -class XMLRPCResponse(ErrorCodeMixin, Response): - - defaultExceptionCls = Exception - - def success(self): - return self.status == httplib.OK - - def parse_body(self): - try: - params, methodname = xmlrpclib.loads(self.body) - if len(params) == 1: - params = params[0] - return params - except xmlrpclib.Fault: - e = sys.exc_info()[1] - self.raise_exception_for_error(e.faultCode, e.faultString) - error_string = '%s: %s' % (e.faultCode, e.faultString) - raise self.defaultExceptionCls(error_string) - - def parse_error(self): - msg = 'Server returned an invalid xmlrpc response (%d)' % (self.status) - raise ProtocolError(msg) - - -class XMLRPCConnection(Connection): - """ - Connection class which can call XMLRPC based API's. - - This class uses the xmlrpclib marshalling and demarshalling code but uses - the http transports provided by libcloud giving it better certificate - validation and debugging helpers than the core client library. - """ - - responseCls = XMLRPCResponse - - def add_default_headers(self, headers): - headers['Content-Type'] = 'text/xml' - return headers - - def request(self, method_name, *args, **kwargs): - """ - Call a given `method_name`. - - :type method_name: ``str`` - :param method_name: A method exposed by the xmlrpc endpoint that you - are connecting to. - - :type args: ``tuple`` - :param args: Arguments to invoke with method with. - """ - endpoint = kwargs.get('endpoint', self.endpoint) - data = xmlrpclib.dumps(args, methodname=method_name, allow_none=True) - return super(XMLRPCConnection, self).request(endpoint, - data=data, - method='POST')
