diff options
Diffstat (limited to 'quantumclient/client.py')
| -rw-r--r-- | quantumclient/client.py | 250 |
1 files changed, 2 insertions, 248 deletions
diff --git a/quantumclient/client.py b/quantumclient/client.py index a4669c4..b816bee 100644 --- a/quantumclient/client.py +++ b/quantumclient/client.py @@ -1,249 +1,3 @@ -# Copyright 2012 OpenStack LLC. -# All Rights Reserved -# -# Licensed 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. -# -# vim: tabstop=4 shiftwidth=4 softtabstop=4 +from neutronclient import client -try: - import json -except ImportError: - import simplejson as json -import logging -import os -import urlparse -# Python 2.5 compat fix -if not hasattr(urlparse, 'parse_qsl'): - import cgi - urlparse.parse_qsl = cgi.parse_qsl - -import httplib2 - -from quantumclient.common import exceptions -from quantumclient.common import utils - -_logger = logging.getLogger(__name__) - -if 'QUANTUMCLIENT_DEBUG' in os.environ and os.environ['QUANTUMCLIENT_DEBUG']: - ch = logging.StreamHandler() - _logger.setLevel(logging.DEBUG) - _logger.addHandler(ch) - - -class ServiceCatalog(object): - """Helper methods for dealing with a Keystone Service Catalog.""" - - def __init__(self, resource_dict): - self.catalog = resource_dict - - def get_token(self): - """Fetch token details fron service catalog.""" - token = {'id': self.catalog['access']['token']['id'], - 'expires': self.catalog['access']['token']['expires'], } - try: - token['user_id'] = self.catalog['access']['user']['id'] - token['tenant_id'] = ( - self.catalog['access']['token']['tenant']['id']) - except Exception: - # just leave the tenant and user out if it doesn't exist - pass - return token - - def url_for(self, attr=None, filter_value=None, - service_type='network', endpoint_type='publicURL'): - """Fetch the URL from the Quantum service for - a particular endpoint type. If none given, return - publicURL. - """ - - catalog = self.catalog['access'].get('serviceCatalog', []) - matching_endpoints = [] - for service in catalog: - if service['type'] != service_type: - continue - - endpoints = service['endpoints'] - for endpoint in endpoints: - if not filter_value or endpoint.get(attr) == filter_value: - matching_endpoints.append(endpoint) - - if not matching_endpoints: - raise exceptions.EndpointNotFound() - elif len(matching_endpoints) > 1: - raise exceptions.AmbiguousEndpoints(message=matching_endpoints) - else: - if endpoint_type not in matching_endpoints[0]: - raise exceptions.EndpointTypeNotFound(message=endpoint_type) - - return matching_endpoints[0][endpoint_type] - - -class HTTPClient(httplib2.Http): - """Handles the REST calls and responses, include authn.""" - - USER_AGENT = 'python-quantumclient' - - def __init__(self, username=None, tenant_name=None, - password=None, auth_url=None, - token=None, region_name=None, timeout=None, - endpoint_url=None, insecure=False, - endpoint_type='publicURL', - auth_strategy='keystone', **kwargs): - super(HTTPClient, self).__init__(timeout=timeout) - self.username = username - self.tenant_name = tenant_name - self.password = password - self.auth_url = auth_url.rstrip('/') if auth_url else None - self.endpoint_type = endpoint_type - self.region_name = region_name - self.auth_token = token - self.content_type = 'application/json' - self.endpoint_url = endpoint_url - self.auth_strategy = auth_strategy - # httplib2 overrides - self.force_exception_to_status_code = True - self.disable_ssl_certificate_validation = insecure - - def _cs_request(self, *args, **kwargs): - kargs = {} - kargs.setdefault('headers', kwargs.get('headers', {})) - kargs['headers']['User-Agent'] = self.USER_AGENT - - if 'content_type' in kwargs: - kargs['headers']['Content-Type'] = kwargs['content_type'] - kargs['headers']['Accept'] = kwargs['content_type'] - else: - kargs['headers']['Content-Type'] = self.content_type - kargs['headers']['Accept'] = self.content_type - - if 'body' in kwargs: - kargs['body'] = kwargs['body'] - args = utils.safe_encode_list(args) - kargs = utils.safe_encode_dict(kargs) - utils.http_log_req(_logger, args, kargs) - resp, body = self.request(*args, **kargs) - utils.http_log_resp(_logger, resp, body) - status_code = self.get_status_code(resp) - if status_code == 401: - raise exceptions.Unauthorized(message=body) - elif status_code == 403: - raise exceptions.Forbidden(message=body) - return resp, body - - def authenticate_and_fetch_endpoint_url(self): - if not self.auth_token: - self.authenticate() - elif not self.endpoint_url: - self.endpoint_url = self._get_endpoint_url() - - def do_request(self, url, method, **kwargs): - self.authenticate_and_fetch_endpoint_url() - # Perform the request once. If we get a 401 back then it - # might be because the auth token expired, so try to - # re-authenticate and try again. If it still fails, bail. - try: - kwargs.setdefault('headers', {}) - kwargs['headers']['X-Auth-Token'] = self.auth_token - resp, body = self._cs_request(self.endpoint_url + url, method, - **kwargs) - return resp, body - except exceptions.Unauthorized: - self.authenticate() - kwargs.setdefault('headers', {}) - kwargs['headers']['X-Auth-Token'] = self.auth_token - resp, body = self._cs_request( - self.endpoint_url + url, method, **kwargs) - return resp, body - - def _extract_service_catalog(self, body): - """Set the client's service catalog from the response data.""" - self.service_catalog = ServiceCatalog(body) - try: - sc = self.service_catalog.get_token() - self.auth_token = sc['id'] - self.auth_tenant_id = sc.get('tenant_id') - self.auth_user_id = sc.get('user_id') - except KeyError: - raise exceptions.Unauthorized() - self.endpoint_url = self.service_catalog.url_for( - attr='region', filter_value=self.region_name, - endpoint_type=self.endpoint_type) - - def authenticate(self): - if self.auth_strategy != 'keystone': - raise exceptions.Unauthorized(message='unknown auth strategy') - body = {'auth': {'passwordCredentials': - {'username': self.username, - 'password': self.password, }, - 'tenantName': self.tenant_name, }, } - - token_url = self.auth_url + "/tokens" - - # Make sure we follow redirects when trying to reach Keystone - tmp_follow_all_redirects = self.follow_all_redirects - self.follow_all_redirects = True - try: - resp, body = self._cs_request(token_url, "POST", - body=json.dumps(body), - content_type="application/json") - finally: - self.follow_all_redirects = tmp_follow_all_redirects - status_code = self.get_status_code(resp) - if status_code != 200: - raise exceptions.Unauthorized(message=body) - if body: - try: - body = json.loads(body) - except ValueError: - pass - else: - body = None - self._extract_service_catalog(body) - - def _get_endpoint_url(self): - url = self.auth_url + '/tokens/%s/endpoints' % self.auth_token - try: - resp, body = self._cs_request(url, "GET") - except exceptions.Unauthorized: - # rollback to authenticate() to handle case when quantum client - # is initialized just before the token is expired - self.authenticate() - return self.endpoint_url - - body = json.loads(body) - for endpoint in body.get('endpoints', []): - if (endpoint['type'] == 'network' and - endpoint.get('region') == self.region_name): - if self.endpoint_type not in endpoint: - raise exceptions.EndpointTypeNotFound( - message=self.endpoint_type) - return endpoint[self.endpoint_type] - - raise exceptions.EndpointNotFound() - - def get_auth_info(self): - return {'auth_token': self.auth_token, - 'auth_tenant_id': self.auth_tenant_id, - 'auth_user_id': self.auth_user_id, - 'endpoint_url': self.endpoint_url} - - def get_status_code(self, response): - """Returns the integer status code from the response. - - Either a Webob.Response (used in testing) or httplib.Response - is returned. - """ - if hasattr(response, 'status_int'): - return response.status_int - else: - return response.status +HTTPClient = client.HTTPClient |
