From b1eeaa4b9550388a3d289cb09a695a67845c1fcd Mon Sep 17 00:00:00 2001 From: Tim Simpson Date: Wed, 2 May 2012 07:40:13 -0500 Subject: [PATCH] Changing Reddwarf client to use its own request function. * Nova client changed in a way that broke our client, so copying the code from there is necessary. * Adding InstanceStatus class with the status strings. * Moved the Dbaas and ReddwarfHTTPClient into their own module. * Changed exceptions module to check Nova's exception map after first looking in Reddwarf's. --- reddwarfclient/__init__.py | 102 +---------------------- reddwarfclient/client.py | 152 +++++++++++++++++++++++++++++++++++ reddwarfclient/common.py | 3 +- reddwarfclient/exceptions.py | 5 +- reddwarfclient/instances.py | 11 +++ 5 files changed, 170 insertions(+), 103 deletions(-) create mode 100644 reddwarfclient/client.py diff --git a/reddwarfclient/__init__.py b/reddwarfclient/__init__.py index 01dee13a..e981aa53 100644 --- a/reddwarfclient/__init__.py +++ b/reddwarfclient/__init__.py @@ -13,106 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import time -import urlparse - -try: - import json -except ImportError: - import simplejson as json - - -from novaclient.client import HTTPClient -from novaclient.v1_1.client import Client - - -# To write this test from an end user perspective, we have to create a client -# similar to the CloudServers one. -# For now we will work on it here. - - -class ReddwarfHTTPClient(HTTPClient): - """ - Class for overriding the HTTP authenticate call and making it specific to - reddwarf - """ - - def __init__(self, user, apikey, tenant, auth_url, service_name, - service_type=None, service_url=None, timeout=None): - super(ReddwarfHTTPClient, self).__init__(user, apikey, tenant, - auth_url, - service_type=service_type, - timeout=timeout) - self.api_key = apikey - self.tenant = tenant - self.service = service_name - self.management_url = service_url - - - def _get_token(self, path, req_body): - """Set the management url and auth token""" - token_url = urlparse.urljoin(self.auth_url, path) - resp, body = self.request(token_url, "POST", body=req_body) - if 'access' in body: - if not self.management_url: - # Assume the new Keystone lite: - catalog = body['access']['serviceCatalog'] - for service in catalog: - if service['name'] == self.service: - self.management_url = service['adminURL'] - self.auth_token = body['access']['token']['id'] - else: - # Assume pre-Keystone Light: - try: - if not self.management_url: - self.management_url = body['auth']['serviceCatalog'] \ - [self.service][0]['publicURL'] - self.auth_token = body['auth']['token']['id'] - except KeyError: - raise NotImplementedError("Service: %s is not available" - % self.service) - - -class Dbaas(Client): - """ - Top-level object to access the Rackspace Database as a Service API. - - Create an instance with your creds:: - - >>> red = Dbaas(USERNAME, API_KEY, TENANT, AUTH_URL, SERVICE_NAME, - SERVICE_URL) - - Then call methods on its managers:: - - >>> red.instances.list() - ... - >>> red.flavors.list() - ... - - &c. - """ - - def __init__(self, username, api_key, tenant=None, auth_url=None, - service_type='reddwarf', service_name='Reddwarf Service', - service_url=None): - super(Dbaas, self).__init__(self, username, api_key, tenant, auth_url) - self.client = ReddwarfHTTPClient(username, api_key, tenant, auth_url, - service_type=service_type, - service_name=service_name, - service_url=service_url) - self.versions = Versions(self) - self.databases = Databases(self) - self.flavors = Flavors(self) - self.instances = Instances(self) - self.users = Users(self) - self.root = Root(self) - self.hosts = Hosts(self) - self.storage = StorageInfo(self) - self.management = Management(self) - self.accounts = Accounts(self) - self.configs = Configs(self) - self.diagnostics = Interrogator(self) - from reddwarfclient.accounts import Accounts from reddwarfclient.config import Configs @@ -126,3 +26,5 @@ from reddwarfclient.storage import StorageInfo from reddwarfclient.users import Users from reddwarfclient.versions import Versions from reddwarfclient.diagnostics import Interrogator +from reddwarfclient.client import Dbaas +from reddwarfclient.client import ReddwarfHTTPClient diff --git a/reddwarfclient/client.py b/reddwarfclient/client.py new file mode 100644 index 00000000..09e86f9a --- /dev/null +++ b/reddwarfclient/client.py @@ -0,0 +1,152 @@ +# Copyright (c) 2011 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. + +import time +import urlparse + +try: + import json +except ImportError: + import simplejson as json + + +from novaclient.client import HTTPClient +from novaclient.v1_1.client import Client + +from reddwarfclient import exceptions + +class ReddwarfHTTPClient(HTTPClient): + """ + Class for overriding the HTTP authenticate call and making it specific to + reddwarf + """ + + def __init__(self, user, apikey, tenant, auth_url, service_name, + service_type=None, service_url=None, timeout=None): + super(ReddwarfHTTPClient, self).__init__(user, apikey, tenant, + auth_url, + service_type=service_type, + timeout=timeout) + self.api_key = apikey + self.tenant = tenant + self.service = service_name + self.management_url = service_url + + + def _get_token(self, path, req_body): + """Set the management url and auth token""" + token_url = urlparse.urljoin(self.auth_url, path) + resp, body = self.request(token_url, "POST", body=req_body) + if 'access' in body: + if not self.management_url: + # Assume the new Keystone lite: + catalog = body['access']['serviceCatalog'] + for service in catalog: + if service['name'] == self.service: + self.management_url = service['adminURL'] + self.auth_token = body['access']['token']['id'] + else: + # Assume pre-Keystone Light: + try: + if not self.management_url: + self.management_url = body['auth']['serviceCatalog'] \ + [self.service][0]['publicURL'] + self.auth_token = body['auth']['token']['id'] + except KeyError: + raise NotImplementedError("Service: %s is not available" + % self.service) + + def request(self, *args, **kwargs): + #TODO(tim.simpson): Copy and pasted from novaclient, since we raise + # extra exception subclasses not raised there. + kwargs.setdefault('headers', kwargs.get('headers', {})) + kwargs['headers']['User-Agent'] = self.USER_AGENT + kwargs['headers']['Accept'] = 'application/json' + if 'body' in kwargs: + kwargs['headers']['Content-Type'] = 'application/json' + kwargs['body'] = json.dumps(kwargs['body']) + + resp, body = super(HTTPClient, self).request(*args, **kwargs) + + self.http_log(args, kwargs, resp, body) + + if body: + try: + body = json.loads(body) + except ValueError: + pass + else: + body = None + + if resp.status in (400, 401, 403, 404, 408, 409, 413, 500, 501): + raise exceptions.from_response(resp, body) + + return resp, body + + +class Dbaas(Client): + """ + Top-level object to access the Rackspace Database as a Service API. + + Create an instance with your creds:: + + >>> red = Dbaas(USERNAME, API_KEY, TENANT, AUTH_URL, SERVICE_NAME, + SERVICE_URL) + + Then call methods on its managers:: + + >>> red.instances.list() + ... + >>> red.flavors.list() + ... + + &c. + """ + + def __init__(self, username, api_key, tenant=None, auth_url=None, + service_type='reddwarf', service_name='Reddwarf Service', + service_url=None): + from reddwarfclient.versions import Versions + from reddwarfclient.databases import Databases + from reddwarfclient.flavors import Flavors + from reddwarfclient.instances import Instances + from reddwarfclient.users import Users + from reddwarfclient.root import Root + from reddwarfclient.hosts import Hosts + from reddwarfclient.storage import StorageInfo + from reddwarfclient.management import Management + from reddwarfclient.accounts import Accounts + from reddwarfclient.config import Configs + from reddwarfclient.diagnostics import Interrogator + + super(Dbaas, self).__init__(self, username, api_key, tenant, auth_url) + self.client = ReddwarfHTTPClient(username, api_key, tenant, auth_url, + service_type=service_type, + service_name=service_name, + service_url=service_url) + self.versions = Versions(self) + self.databases = Databases(self) + self.flavors = Flavors(self) + self.instances = Instances(self) + self.users = Users(self) + self.root = Root(self) + self.hosts = Hosts(self) + self.storage = StorageInfo(self) + self.management = Management(self) + self.accounts = Accounts(self) + self.configs = Configs(self) + self.diagnostics = Interrogator(self) + + diff --git a/reddwarfclient/common.py b/reddwarfclient/common.py index eee5bc2f..5a3c6169 100644 --- a/reddwarfclient/common.py +++ b/reddwarfclient/common.py @@ -16,8 +16,7 @@ import os import pickle import sys - -from reddwarfclient import Dbaas +from reddwarfclient.client import Dbaas import exceptions diff --git a/reddwarfclient/exceptions.py b/reddwarfclient/exceptions.py index 7a0efbaa..8f0d8f15 100644 --- a/reddwarfclient/exceptions.py +++ b/reddwarfclient/exceptions.py @@ -37,7 +37,10 @@ def from_response(response, body): if resp.status != 200: raise exception_from_response(resp, body) """ - cls = _code_map.get(response.status, exceptions.ClientException) + cls = _code_map.get(response.status, None) + if not cls: + cls = exceptions._code_map.get(response.status, + exceptions.ClientException) if body: message = "n/a" details = "n/a" diff --git a/reddwarfclient/instances.py b/reddwarfclient/instances.py index 7336751a..73645dd8 100644 --- a/reddwarfclient/instances.py +++ b/reddwarfclient/instances.py @@ -147,3 +147,14 @@ class Instances(base.ManagerWithFind): """ body = {'restart': {}} self._action(instance_id, body) + + +class InstanceStatus(object): + + ACTIVE = "ACTIVE" + BLOCKED = "BLOCKED" + BUILD = "BUILD" + FAILED = "FAILED" + REBOOT = "REBOOT" + RESIZE = "RESIZE" + SHUTDOWN = "SHUTDOWN"