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
This commit is contained in:
Brian Waldon 2012-07-12 18:30:54 -07:00
parent 49bc6f94f2
commit 53acf1a0ca
7 changed files with 161 additions and 149 deletions

@ -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()

@ -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 *

@ -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)

@ -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):

131
glanceclient/exc.py Normal file

@ -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)

@ -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)

@ -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)