From 53acf1a0ca70c900267286a249e476fffe078a9f Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Thu, 12 Jul 2012 18:30:54 -0700 Subject: [PATCH] Establish the supported importable interface * Consumers of this client should not depend on being able to import any module other than glanceclient and glanceclient * The only attributs of the glanceclient module are Client and __version__ * The attributes of the glanceclient.exc modules have yet to be locked down * glanceclient.common.exceptions was replaced with a placeholder module until consumers of it are updated Change-Id: Iea9648cd06906d65764987c1f2ee5a88ebeee748 --- glanceclient/__init__.py | 13 ++- glanceclient/common/exceptions.py | 134 +----------------------------- glanceclient/common/http.py | 4 +- glanceclient/common/utils.py | 10 +-- glanceclient/exc.py | 131 +++++++++++++++++++++++++++++ glanceclient/shell.py | 14 ++-- glanceclient/v2/schemas.py | 4 +- 7 files changed, 161 insertions(+), 149 deletions(-) create mode 100644 glanceclient/exc.py diff --git a/glanceclient/__init__.py b/glanceclient/__init__.py index 33f84275..d71b8a7d 100644 --- a/glanceclient/__init__.py +++ b/glanceclient/__init__.py @@ -14,6 +14,15 @@ # License for the specific language governing permissions and limitations # under the License. -from glanceclient import version +#NOTE(bcwaldon): this try/except block is needed to run setup.py due to +# its need to import local code before installing required dependencies +try: + import glanceclient.client + Client = glanceclient.client.Client +except ImportError: + import warnings + warnings.warn("Could not import glanceclient.client", ImportWarning) -__version__ = version.version_info.deferred_version_string() +import glanceclient.version + +__version__ = glanceclient.version.version_info.deferred_version_string() diff --git a/glanceclient/common/exceptions.py b/glanceclient/common/exceptions.py index d88f94b8..583a1302 100644 --- a/glanceclient/common/exceptions.py +++ b/glanceclient/common/exceptions.py @@ -1,131 +1,3 @@ -""" -Exception definitions. -""" - - -class CommandError(Exception): - pass - - -class NoTokenLookupException(Exception): - """This form of authentication does not support looking up - endpoints from an existing token.""" - pass - - -class EndpointNotFound(Exception): - """Could not find Service or Region in Service Catalog.""" - pass - - -class SchemaNotFound(KeyError): - """Could not find schema""" - pass - - -class ClientException(Exception): - """ - The base exception class for all exceptions this library raises. - """ - def __init__(self, code, message=None, details=None): - self.code = code - self.message = message or self.__class__.message - self.details = details - - def __str__(self): - return "%s (HTTP %s)" % (self.message, self.code) - - -class BadRequest(ClientException): - """ - HTTP 400 - Bad request: you sent some malformed data. - """ - http_status = 400 - message = "Bad request" - - -class Unauthorized(ClientException): - """ - HTTP 401 - Unauthorized: bad credentials. - """ - http_status = 401 - message = "Unauthorized" - - -class Forbidden(ClientException): - """ - HTTP 403 - Forbidden: your credentials don't give you access to this - resource. - """ - http_status = 403 - message = "Forbidden" - - -class NotFound(ClientException): - """ - HTTP 404 - Not found - """ - http_status = 404 - message = "Not found" - - -class Conflict(ClientException): - """ - HTTP 409 - Conflict - """ - http_status = 409 - message = "Conflict" - - -class OverLimit(ClientException): - """ - HTTP 413 - Over limit: you're over the API limits for this time period. - """ - http_status = 413 - message = "Over limit" - - -# NotImplemented is a python keyword. -class HTTPNotImplemented(ClientException): - """ - HTTP 501 - Not Implemented: the server does not support this operation. - """ - http_status = 501 - message = "Not Implemented" - - -# In Python 2.4 Exception is old-style and thus doesn't have a __subclasses__() -# so we can do this: -# _code_map = dict((c.http_status, c) -# for c in ClientException.__subclasses__()) -# -# Instead, we have to hardcode it: -_code_map = dict((c.http_status, c) for c in [BadRequest, Unauthorized, - Forbidden, NotFound, OverLimit, HTTPNotImplemented]) - - -def from_response(response, body): - """ - Return an instance of an ClientException or subclass - based on an httplib2 response. - - Usage:: - - resp, body = http.request(...) - if resp.status != 200: - raise exception_from_response(resp, body) - """ - cls = _code_map.get(response.status, ClientException) - if body: - if hasattr(body, 'keys'): - error = body[body.keys()[0]] - message = error.get('message', None) - details = error.get('details', None) - else: - # If we didn't get back a properly formed error message we - # probably couldn't communicate with Keystone at all. - message = "Unable to communicate with image service: %s." % body - details = None - return cls(code=response.status, message=message, details=details) - else: - return cls(code=response.status) +# This is here for compatability purposes. Once all known OpenStack clients +# are updated to use glanceclient.exc, this file should be removed +from glanceclient.exc import * diff --git a/glanceclient/common/http.py b/glanceclient/common/http.py index 0892a4df..d963be67 100644 --- a/glanceclient/common/http.py +++ b/glanceclient/common/http.py @@ -20,7 +20,7 @@ if not hasattr(urlparse, 'parse_qsl'): urlparse.parse_qsl = cgi.parse_qsl -from glanceclient.common import exceptions +from glanceclient import exc logger = logging.getLogger(__name__) @@ -83,7 +83,7 @@ class HTTPClient(httplib2.Http): if 400 <= resp.status < 600: logger.exception("Request returned failure status.") - raise exceptions.from_response(resp, body) + raise exc.from_response(resp, body) elif resp.status in (301, 302, 305): # Redirected. Reissue the request to the new location. return self._http_request(resp['location'], method, **kwargs) diff --git a/glanceclient/common/utils.py b/glanceclient/common/utils.py index 1b792314..8bbb7acc 100644 --- a/glanceclient/common/utils.py +++ b/glanceclient/common/utils.py @@ -18,7 +18,7 @@ import uuid import prettytable -from glanceclient.common import exceptions +from glanceclient import exc from glanceclient.openstack.common import importutils @@ -67,23 +67,23 @@ def find_resource(manager, name_or_id): try: if isinstance(name_or_id, int) or name_or_id.isdigit(): return manager.get(int(name_or_id)) - except exceptions.NotFound: + except exc.NotFound: pass # now try to get entity as uuid try: uuid.UUID(str(name_or_id)) return manager.get(name_or_id) - except (ValueError, exceptions.NotFound): + except (ValueError, exc.NotFound): pass # finally try to find entity by name try: return manager.find(name=name_or_id) - except exceptions.NotFound: + except exc.NotFound: msg = "No %s with a name or ID of '%s' exists." % \ (manager.resource_class.__name__.lower(), name_or_id) - raise exceptions.CommandError(msg) + raise exc.CommandError(msg) def skip_authentication(f): diff --git a/glanceclient/exc.py b/glanceclient/exc.py new file mode 100644 index 00000000..d88f94b8 --- /dev/null +++ b/glanceclient/exc.py @@ -0,0 +1,131 @@ +""" +Exception definitions. +""" + + +class CommandError(Exception): + pass + + +class NoTokenLookupException(Exception): + """This form of authentication does not support looking up + endpoints from an existing token.""" + pass + + +class EndpointNotFound(Exception): + """Could not find Service or Region in Service Catalog.""" + pass + + +class SchemaNotFound(KeyError): + """Could not find schema""" + pass + + +class ClientException(Exception): + """ + The base exception class for all exceptions this library raises. + """ + def __init__(self, code, message=None, details=None): + self.code = code + self.message = message or self.__class__.message + self.details = details + + def __str__(self): + return "%s (HTTP %s)" % (self.message, self.code) + + +class BadRequest(ClientException): + """ + HTTP 400 - Bad request: you sent some malformed data. + """ + http_status = 400 + message = "Bad request" + + +class Unauthorized(ClientException): + """ + HTTP 401 - Unauthorized: bad credentials. + """ + http_status = 401 + message = "Unauthorized" + + +class Forbidden(ClientException): + """ + HTTP 403 - Forbidden: your credentials don't give you access to this + resource. + """ + http_status = 403 + message = "Forbidden" + + +class NotFound(ClientException): + """ + HTTP 404 - Not found + """ + http_status = 404 + message = "Not found" + + +class Conflict(ClientException): + """ + HTTP 409 - Conflict + """ + http_status = 409 + message = "Conflict" + + +class OverLimit(ClientException): + """ + HTTP 413 - Over limit: you're over the API limits for this time period. + """ + http_status = 413 + message = "Over limit" + + +# NotImplemented is a python keyword. +class HTTPNotImplemented(ClientException): + """ + HTTP 501 - Not Implemented: the server does not support this operation. + """ + http_status = 501 + message = "Not Implemented" + + +# In Python 2.4 Exception is old-style and thus doesn't have a __subclasses__() +# so we can do this: +# _code_map = dict((c.http_status, c) +# for c in ClientException.__subclasses__()) +# +# Instead, we have to hardcode it: +_code_map = dict((c.http_status, c) for c in [BadRequest, Unauthorized, + Forbidden, NotFound, OverLimit, HTTPNotImplemented]) + + +def from_response(response, body): + """ + Return an instance of an ClientException or subclass + based on an httplib2 response. + + Usage:: + + resp, body = http.request(...) + if resp.status != 200: + raise exception_from_response(resp, body) + """ + cls = _code_map.get(response.status, ClientException) + if body: + if hasattr(body, 'keys'): + error = body[body.keys()[0]] + message = error.get('message', None) + details = error.get('details', None) + else: + # If we didn't get back a properly formed error message we + # probably couldn't communicate with Keystone at all. + message = "Unable to communicate with image service: %s." % body + details = None + return cls(code=response.status, message=message, details=details) + else: + return cls(code=response.status) diff --git a/glanceclient/shell.py b/glanceclient/shell.py index 4f2c0053..2b1b5d9f 100644 --- a/glanceclient/shell.py +++ b/glanceclient/shell.py @@ -24,8 +24,8 @@ import sys from keystoneclient.v2_0 import client as ksclient -import glanceclient.client -from glanceclient.common import exceptions as exc +import glanceclient +from glanceclient import exc from glanceclient.common import utils @@ -273,11 +273,11 @@ class OpenStackImagesShell(object): } endpoint, token = self._authenticate(**kwargs) - client = glanceclient.client.Client(api_version, - endpoint, - token, - insecure=args.insecure, - timeout=args.timeout) + client = glanceclient.Client(api_version, + endpoint, + token, + insecure=args.insecure, + timeout=args.timeout) try: args.func(client, args) diff --git a/glanceclient/v2/schemas.py b/glanceclient/v2/schemas.py index 41e94e80..8757da1d 100644 --- a/glanceclient/v2/schemas.py +++ b/glanceclient/v2/schemas.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from glanceclient.common import exceptions +from glanceclient import exc class SchemaProperty(object): @@ -55,4 +55,4 @@ class Controller(object): for link in schema_index['links']: if link['rel'] == schema_name: return link['href'] - raise exceptions.SchemaNotFound(schema_name) + raise exc.SchemaNotFound(schema_name)