Fixes LP Bug#861650 - Glance client deps
This patch addresses the dependency proliferation in the glance client stuff. It removes references to modules that contain non-client-necessary libraries (like eventlet, xattr, sqlalchemy-migrate and sqlalchemy) by restructuring the modules slightly. Note that the additional httplib2 dependency is because that is used in the authentication strategy stuff in glance.common.auth. This could be rewritten to use httplib instead, further reducing the dependencies of the client lib. IMPORTANT NOTE: This patch changes the default entrypoint for the Images API router application, and therefore this should be merged along with the packaging changes in this branch: https://code.launchpad.net/~jaypipes/glance/ubuntu/+merge/82318 Change-Id: I5dbc8584fb77e3e011fb6ff3532f792f5103e242
This commit is contained in:
parent
6bf61e8ce1
commit
1ab63ff5c9
@ -40,8 +40,8 @@ if os.path.exists(os.path.join(possible_topdir, 'glance', '__init__.py')):
|
||||
|
||||
gettext.install('glance', unicode=1)
|
||||
|
||||
from glance import client as glance_client
|
||||
from glance import version
|
||||
from glance import client as glance_client
|
||||
from glance.common import exception
|
||||
from glance.common import utils
|
||||
|
||||
|
@ -214,7 +214,7 @@ pipeline = versionnegotiation context apiv1app
|
||||
# pipeline = versionnegotiation authtoken auth-context cachemanage apiv1app
|
||||
|
||||
[app:apiv1app]
|
||||
paste.app_factory = glance.api.v1:app_factory
|
||||
paste.app_factory = glance.api.v1.router:app_factory
|
||||
|
||||
[filter:versionnegotiation]
|
||||
paste.filter_factory = glance.api.middleware.version_negotiation:filter_factory
|
||||
|
@ -14,50 +14,3 @@
|
||||
# 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 logging
|
||||
import webob.exc
|
||||
|
||||
from glance import registry
|
||||
from glance.common import exception
|
||||
|
||||
|
||||
logger = logging.getLogger('glance.api')
|
||||
|
||||
|
||||
class BaseController(object):
|
||||
def get_image_meta_or_404(self, request, image_id):
|
||||
"""
|
||||
Grabs the image metadata for an image with a supplied
|
||||
identifier or raises an HTTPNotFound (404) response
|
||||
|
||||
:param request: The WSGI/Webob Request object
|
||||
:param image_id: The opaque image identifier
|
||||
|
||||
:raises HTTPNotFound if image does not exist
|
||||
"""
|
||||
context = request.context
|
||||
try:
|
||||
return registry.get_image_metadata(context, image_id)
|
||||
except exception.NotFound:
|
||||
msg = _("Image with identifier %s not found") % image_id
|
||||
logger.debug(msg)
|
||||
raise webob.exc.HTTPNotFound(
|
||||
msg, request=request, content_type='text/plain')
|
||||
except exception.NotAuthorized:
|
||||
msg = _("Unauthorized image access")
|
||||
logger.debug(msg)
|
||||
raise webob.exc.HTTPForbidden(msg, request=request,
|
||||
content_type='text/plain')
|
||||
|
||||
def get_active_image_meta_or_404(self, request, image_id):
|
||||
"""
|
||||
Same as get_image_meta_or_404 except that it will raise a 404 if the
|
||||
image isn't 'active'.
|
||||
"""
|
||||
image = self.get_image_meta_or_404(request, image_id)
|
||||
if image['status'] != 'active':
|
||||
msg = _("Image %s is not active") % image_id
|
||||
logger.debug(msg)
|
||||
raise webob.exc.HTTPNotFound(
|
||||
msg, request=request, content_type='text/plain')
|
||||
return image
|
||||
|
@ -25,14 +25,14 @@ import webob.exc
|
||||
|
||||
from glance.common import exception
|
||||
from glance.common import wsgi
|
||||
from glance import api
|
||||
from glance.api.v1 import controller
|
||||
from glance import image_cache
|
||||
from glance import registry
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Controller(api.BaseController):
|
||||
class Controller(controller.BaseController):
|
||||
"""
|
||||
A controller for managing cached images.
|
||||
"""
|
||||
|
@ -15,51 +15,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
SUPPORTED_FILTERS = ['name', 'status', 'container_format', 'disk_format',
|
||||
'min_ram', 'min_disk', 'size_min', 'size_max',
|
||||
'is_public', 'changes-since']
|
||||
|
||||
import routes
|
||||
|
||||
from glance.api.v1 import images
|
||||
from glance.api.v1 import members
|
||||
from glance.common import wsgi
|
||||
|
||||
logger = logging.getLogger('glance.api.v1')
|
||||
|
||||
|
||||
class API(wsgi.Router):
|
||||
|
||||
"""WSGI router for Glance v1 API requests."""
|
||||
|
||||
def __init__(self, options):
|
||||
self.options = options
|
||||
mapper = routes.Mapper()
|
||||
|
||||
images_resource = images.create_resource(options)
|
||||
|
||||
mapper.resource("image", "images", controller=images_resource,
|
||||
collection={'detail': 'GET'})
|
||||
mapper.connect("/", controller=images_resource, action="index")
|
||||
mapper.connect("/images/{id}", controller=images_resource,
|
||||
action="meta", conditions=dict(method=["HEAD"]))
|
||||
|
||||
members_resource = members.create_resource(options)
|
||||
|
||||
mapper.resource("member", "members", controller=members_resource,
|
||||
parent_resource=dict(member_name='image',
|
||||
collection_name='images'))
|
||||
mapper.connect("/shared-images/{id}",
|
||||
controller=members_resource,
|
||||
action="index_shared_images")
|
||||
mapper.connect("/images/{image_id}/members",
|
||||
controller=members_resource,
|
||||
action="update_all",
|
||||
conditions=dict(method=["PUT"]))
|
||||
|
||||
super(API, self).__init__(mapper)
|
||||
|
||||
|
||||
def app_factory(global_conf, **local_conf):
|
||||
"""paste.deploy app factory for creating Glance API server apps"""
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
return API(conf)
|
||||
SUPPORTED_PARAMS = ('limit', 'marker', 'sort_key', 'sort_dir')
|
||||
|
64
glance/api/v1/controller.py
Normal file
64
glance/api/v1/controller.py
Normal file
@ -0,0 +1,64 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 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 logging
|
||||
|
||||
import webob.exc
|
||||
|
||||
from glance import registry
|
||||
from glance.common import exception
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseController(object):
|
||||
def get_image_meta_or_404(self, request, image_id):
|
||||
"""
|
||||
Grabs the image metadata for an image with a supplied
|
||||
identifier or raises an HTTPNotFound (404) response
|
||||
|
||||
:param request: The WSGI/Webob Request object
|
||||
:param image_id: The opaque image identifier
|
||||
|
||||
:raises HTTPNotFound if image does not exist
|
||||
"""
|
||||
context = request.context
|
||||
try:
|
||||
return registry.get_image_metadata(context, image_id)
|
||||
except exception.NotFound:
|
||||
msg = _("Image with identifier %s not found") % image_id
|
||||
logger.debug(msg)
|
||||
raise webob.exc.HTTPNotFound(
|
||||
msg, request=request, content_type='text/plain')
|
||||
except exception.NotAuthorized:
|
||||
msg = _("Unauthorized image access")
|
||||
logger.debug(msg)
|
||||
raise webob.exc.HTTPForbidden(msg, request=request,
|
||||
content_type='text/plain')
|
||||
|
||||
def get_active_image_meta_or_404(self, request, image_id):
|
||||
"""
|
||||
Same as get_image_meta_or_404 except that it will raise a 404 if the
|
||||
image isn't 'active'.
|
||||
"""
|
||||
image = self.get_image_meta_or_404(request, image_id)
|
||||
if image['status'] != 'active':
|
||||
msg = _("Image %s is not active") % image_id
|
||||
logger.debug(msg)
|
||||
raise webob.exc.HTTPNotFound(
|
||||
msg, request=request, content_type='text/plain')
|
||||
return image
|
@ -31,11 +31,13 @@ from webob.exc import (HTTPNotFound,
|
||||
HTTPNoContent,
|
||||
HTTPUnauthorized)
|
||||
|
||||
from glance import api
|
||||
import glance.api.v1
|
||||
from glance.api.v1 import controller
|
||||
from glance import image_cache
|
||||
from glance.common import exception
|
||||
from glance.common import notifier
|
||||
from glance.common import wsgi
|
||||
from glance.common import utils
|
||||
import glance.store
|
||||
import glance.store.filesystem
|
||||
import glance.store.http
|
||||
@ -50,16 +52,12 @@ from glance.store import (get_from_backend,
|
||||
from glance import registry
|
||||
|
||||
|
||||
logger = logging.getLogger('glance.api.v1.images')
|
||||
|
||||
SUPPORTED_FILTERS = ['name', 'status', 'container_format', 'disk_format',
|
||||
'min_ram', 'min_disk', 'size_min', 'size_max',
|
||||
'is_public', 'changes-since']
|
||||
|
||||
SUPPORTED_PARAMS = ('limit', 'marker', 'sort_key', 'sort_dir')
|
||||
logger = logging.getLogger(__name__)
|
||||
SUPPORTED_PARAMS = glance.api.v1.SUPPORTED_PARAMS
|
||||
SUPPORTED_FILTERS = glance.api.v1.SUPPORTED_FILTERS
|
||||
|
||||
|
||||
class Controller(api.BaseController):
|
||||
class Controller(controller.BaseController):
|
||||
"""
|
||||
WSGI controller for images resource in Glance v1 API
|
||||
|
||||
@ -596,7 +594,7 @@ class ImageDeserializer(wsgi.JSONRequestDeserializer):
|
||||
|
||||
def _deserialize(self, request):
|
||||
result = {}
|
||||
result['image_meta'] = wsgi.get_image_meta_from_headers(request)
|
||||
result['image_meta'] = utils.get_image_meta_from_headers(request)
|
||||
data = request.body_file if self.has_body(request) else None
|
||||
result['image_data'] = data
|
||||
return result
|
||||
@ -630,7 +628,7 @@ class ImageSerializer(wsgi.JSONResponseSerializer):
|
||||
:param response: The Webob Response object
|
||||
:param image_meta: Mapping of image metadata
|
||||
"""
|
||||
headers = wsgi.image_meta_to_http_headers(image_meta)
|
||||
headers = utils.image_meta_to_http_headers(image_meta)
|
||||
|
||||
for k, v in headers.items():
|
||||
response.headers[k] = v
|
||||
|
65
glance/api/v1/router.py
Normal file
65
glance/api/v1/router.py
Normal file
@ -0,0 +1,65 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 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 logging
|
||||
|
||||
import routes
|
||||
|
||||
from glance.api.v1 import images
|
||||
from glance.api.v1 import members
|
||||
from glance.common import wsgi
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class API(wsgi.Router):
|
||||
|
||||
"""WSGI router for Glance v1 API requests."""
|
||||
|
||||
def __init__(self, options):
|
||||
self.options = options
|
||||
mapper = routes.Mapper()
|
||||
|
||||
images_resource = images.create_resource(options)
|
||||
|
||||
mapper.resource("image", "images", controller=images_resource,
|
||||
collection={'detail': 'GET'})
|
||||
mapper.connect("/", controller=images_resource, action="index")
|
||||
mapper.connect("/images/{id}", controller=images_resource,
|
||||
action="meta", conditions=dict(method=["HEAD"]))
|
||||
|
||||
members_resource = members.create_resource(options)
|
||||
|
||||
mapper.resource("member", "members", controller=members_resource,
|
||||
parent_resource=dict(member_name='image',
|
||||
collection_name='images'))
|
||||
mapper.connect("/shared-images/{id}",
|
||||
controller=members_resource,
|
||||
action="index_shared_images")
|
||||
mapper.connect("/images/{image_id}/members",
|
||||
controller=members_resource,
|
||||
action="update_all",
|
||||
conditions=dict(method=["PUT"]))
|
||||
|
||||
super(API, self).__init__(mapper)
|
||||
|
||||
|
||||
def app_factory(global_conf, **local_conf):
|
||||
"""paste.deploy app factory for creating Glance API server apps"""
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
return API(conf)
|
@ -21,14 +21,17 @@ Client classes for callers of a Glance system
|
||||
|
||||
import errno
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
from glance.api.v1 import images as v1_images
|
||||
import glance.api.v1
|
||||
from glance.common import client as base_client
|
||||
from glance.common import exception
|
||||
from glance.common import wsgi
|
||||
from glance.common import utils
|
||||
|
||||
#TODO(jaypipes) Allow a logger param for client classes
|
||||
logger = logging.getLogger(__name__)
|
||||
SUPPORTED_PARAMS = glance.api.v1.SUPPORTED_PARAMS
|
||||
SUPPORTED_FILTERS = glance.api.v1.SUPPORTED_FILTERS
|
||||
|
||||
|
||||
class V1Client(base_client.BaseClient):
|
||||
@ -49,7 +52,7 @@ class V1Client(base_client.BaseClient):
|
||||
:param sort_key: results will be ordered by this image attribute
|
||||
:param sort_dir: direction in which to to order results (asc, desc)
|
||||
"""
|
||||
params = self._extract_params(kwargs, v1_images.SUPPORTED_PARAMS)
|
||||
params = self._extract_params(kwargs, SUPPORTED_PARAMS)
|
||||
res = self.do_request("GET", "/images", params=params)
|
||||
data = json.loads(res.read())['images']
|
||||
return data
|
||||
@ -65,7 +68,7 @@ class V1Client(base_client.BaseClient):
|
||||
:param sort_key: results will be ordered by this image attribute
|
||||
:param sort_dir: direction in which to to order results (asc, desc)
|
||||
"""
|
||||
params = self._extract_params(kwargs, v1_images.SUPPORTED_PARAMS)
|
||||
params = self._extract_params(kwargs, SUPPORTED_PARAMS)
|
||||
res = self.do_request("GET", "/images/detail", params=params)
|
||||
data = json.loads(res.read())['images']
|
||||
return data
|
||||
@ -82,7 +85,7 @@ class V1Client(base_client.BaseClient):
|
||||
"""
|
||||
res = self.do_request("GET", "/images/%s" % image_id)
|
||||
|
||||
image = wsgi.get_image_meta_from_headers(res)
|
||||
image = utils.get_image_meta_from_headers(res)
|
||||
return image, base_client.ImageBodyIterator(res)
|
||||
|
||||
def get_image_meta(self, image_id):
|
||||
@ -93,7 +96,7 @@ class V1Client(base_client.BaseClient):
|
||||
"""
|
||||
res = self.do_request("HEAD", "/images/%s" % image_id)
|
||||
|
||||
image = wsgi.get_image_meta_from_headers(res)
|
||||
image = utils.get_image_meta_from_headers(res)
|
||||
return image
|
||||
|
||||
def _get_image_size(self, image_data):
|
||||
@ -136,7 +139,7 @@ class V1Client(base_client.BaseClient):
|
||||
|
||||
:retval The newly-stored image's metadata.
|
||||
"""
|
||||
headers = wsgi.image_meta_to_http_headers(image_meta or {})
|
||||
headers = utils.image_meta_to_http_headers(image_meta or {})
|
||||
|
||||
if image_data:
|
||||
body = image_data
|
||||
@ -159,7 +162,7 @@ class V1Client(base_client.BaseClient):
|
||||
if image_meta is None:
|
||||
image_meta = {}
|
||||
|
||||
headers = wsgi.image_meta_to_http_headers(image_meta)
|
||||
headers = utils.image_meta_to_http_headers(image_meta)
|
||||
|
||||
if image_data:
|
||||
body = image_data
|
||||
|
@ -26,13 +26,11 @@ import os
|
||||
import urllib
|
||||
import urlparse
|
||||
|
||||
from eventlet.green import socket, ssl
|
||||
|
||||
# See http://code.google.com/p/python-nose/issues/detail?id=373
|
||||
# The code below enables glance.client standalone to work with i18n _() blocks
|
||||
import __builtin__
|
||||
if not hasattr(__builtin__, '_'):
|
||||
setattr(__builtin__, '_', lambda x: x)
|
||||
try:
|
||||
from eventlet.green import socket, ssl
|
||||
except ImportError:
|
||||
import socket
|
||||
import ssl
|
||||
|
||||
from glance.common import auth
|
||||
from glance.common import exception
|
||||
|
@ -34,7 +34,7 @@ import uuid
|
||||
from glance.common import exception
|
||||
|
||||
|
||||
logger = logging.getLogger('glance.utils')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
|
||||
|
||||
@ -54,6 +54,76 @@ def chunkiter(fp, chunk_size=65536):
|
||||
break
|
||||
|
||||
|
||||
def image_meta_to_http_headers(image_meta):
|
||||
"""
|
||||
Returns a set of image metadata into a dict
|
||||
of HTTP headers that can be fed to either a Webob
|
||||
Request object or an httplib.HTTP(S)Connection object
|
||||
|
||||
:param image_meta: Mapping of image metadata
|
||||
"""
|
||||
headers = {}
|
||||
for k, v in image_meta.items():
|
||||
if v is None:
|
||||
v = ''
|
||||
if k == 'properties':
|
||||
for pk, pv in v.items():
|
||||
if pv is None:
|
||||
pv = ''
|
||||
headers["x-image-meta-property-%s"
|
||||
% pk.lower()] = unicode(pv)
|
||||
else:
|
||||
headers["x-image-meta-%s" % k.lower()] = unicode(v)
|
||||
return headers
|
||||
|
||||
|
||||
def get_image_meta_from_headers(response):
|
||||
"""
|
||||
Processes HTTP headers from a supplied response that
|
||||
match the x-image-meta and x-image-meta-property and
|
||||
returns a mapping of image metadata and properties
|
||||
|
||||
:param response: Response to process
|
||||
"""
|
||||
result = {}
|
||||
properties = {}
|
||||
|
||||
if hasattr(response, 'getheaders'): # httplib.HTTPResponse
|
||||
headers = response.getheaders()
|
||||
else: # webob.Response
|
||||
headers = response.headers.items()
|
||||
|
||||
for key, value in headers:
|
||||
key = str(key.lower())
|
||||
if key.startswith('x-image-meta-property-'):
|
||||
field_name = key[len('x-image-meta-property-'):].replace('-', '_')
|
||||
properties[field_name] = value or None
|
||||
elif key.startswith('x-image-meta-'):
|
||||
field_name = key[len('x-image-meta-'):].replace('-', '_')
|
||||
result[field_name] = value or None
|
||||
result['properties'] = properties
|
||||
if 'size' in result:
|
||||
result['size'] = int(result['size'])
|
||||
if 'is_public' in result:
|
||||
result['is_public'] = bool_from_header_value(result['is_public'])
|
||||
if 'deleted' in result:
|
||||
result['deleted'] = bool_from_header_value(result['deleted'])
|
||||
return result
|
||||
|
||||
|
||||
def bool_from_header_value(value):
|
||||
"""
|
||||
Returns True if value is a boolean True or the
|
||||
string 'true', case-insensitive, False otherwise
|
||||
"""
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
elif isinstance(value, (basestring, unicode)):
|
||||
if str(value).lower() == 'true':
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def bool_from_string(subject):
|
||||
"""
|
||||
Interpret a string as a boolean.
|
||||
|
@ -406,73 +406,3 @@ class Resource(object):
|
||||
pass
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def image_meta_to_http_headers(image_meta):
|
||||
"""
|
||||
Returns a set of image metadata into a dict
|
||||
of HTTP headers that can be fed to either a Webob
|
||||
Request object or an httplib.HTTP(S)Connection object
|
||||
|
||||
:param image_meta: Mapping of image metadata
|
||||
"""
|
||||
headers = {}
|
||||
for k, v in image_meta.items():
|
||||
if v is None:
|
||||
v = ''
|
||||
if k == 'properties':
|
||||
for pk, pv in v.items():
|
||||
if pv is None:
|
||||
pv = ''
|
||||
headers["x-image-meta-property-%s"
|
||||
% pk.lower()] = unicode(pv)
|
||||
else:
|
||||
headers["x-image-meta-%s" % k.lower()] = unicode(v)
|
||||
return headers
|
||||
|
||||
|
||||
def get_image_meta_from_headers(response):
|
||||
"""
|
||||
Processes HTTP headers from a supplied response that
|
||||
match the x-image-meta and x-image-meta-property and
|
||||
returns a mapping of image metadata and properties
|
||||
|
||||
:param response: Response to process
|
||||
"""
|
||||
result = {}
|
||||
properties = {}
|
||||
|
||||
if hasattr(response, 'getheaders'): # httplib.HTTPResponse
|
||||
headers = response.getheaders()
|
||||
else: # webob.Response
|
||||
headers = response.headers.items()
|
||||
|
||||
for key, value in headers:
|
||||
key = str(key.lower())
|
||||
if key.startswith('x-image-meta-property-'):
|
||||
field_name = key[len('x-image-meta-property-'):].replace('-', '_')
|
||||
properties[field_name] = value or None
|
||||
elif key.startswith('x-image-meta-'):
|
||||
field_name = key[len('x-image-meta-'):].replace('-', '_')
|
||||
result[field_name] = value or None
|
||||
result['properties'] = properties
|
||||
if 'size' in result:
|
||||
result['size'] = int(result['size'])
|
||||
if 'is_public' in result:
|
||||
result['is_public'] = bool_from_header_value(result['is_public'])
|
||||
if 'deleted' in result:
|
||||
result['deleted'] = bool_from_header_value(result['deleted'])
|
||||
return result
|
||||
|
||||
|
||||
def bool_from_header_value(value):
|
||||
"""
|
||||
Returns True if value is a boolean True or the
|
||||
string 'true', case-insensitive, False otherwise
|
||||
"""
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
elif isinstance(value, (basestring, unicode)):
|
||||
if str(value).lower() == 'true':
|
||||
return True
|
||||
return False
|
||||
|
@ -1,83 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 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 httplib
|
||||
import json
|
||||
import urlparse
|
||||
|
||||
from glance import client
|
||||
|
||||
|
||||
class ImageRegistryException(Exception):
|
||||
""" Base class for all RegistryAdapter exceptions """
|
||||
pass
|
||||
|
||||
|
||||
class UnknownImageRegistry(ImageRegistryException):
|
||||
""" Raised if we don't recognize the requested Registry protocol """
|
||||
pass
|
||||
|
||||
|
||||
class ImageRegistry(object):
|
||||
""" Base class for all image endpoints """
|
||||
|
||||
@classmethod
|
||||
def lookup(cls, parsed_uri):
|
||||
""" Subclasses must define a lookup method which returns an dictionary
|
||||
representing the image.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Parallax(ImageRegistry):
|
||||
"""
|
||||
Parallax stuff
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def lookup(cls, image_id):
|
||||
"""
|
||||
Takes an image ID and checks if that image is registered in Parallax,
|
||||
and if so, returns the image metadata. If the image does not exist,
|
||||
we raise NotFound
|
||||
"""
|
||||
# TODO(jaypipes): Make parallax client configurable via options.
|
||||
# Unfortunately, the decision to make all adapters have no state
|
||||
# hinders this...
|
||||
c = client.ParallaxClient()
|
||||
return c.get_image(image_id)
|
||||
|
||||
|
||||
REGISTRY_ADAPTERS = {
|
||||
'parallax': Parallax
|
||||
}
|
||||
|
||||
|
||||
def lookup_by_registry(registry, image_id):
|
||||
"""
|
||||
Convenience function to lookup image metadata for the given
|
||||
opaque image identifier and registry.
|
||||
|
||||
:param registry: String name of registry to use for lookups
|
||||
:param image_id: Opaque image identifier
|
||||
"""
|
||||
try:
|
||||
adapter = REGISTRY_ADAPTERS[registry]
|
||||
except KeyError:
|
||||
raise UnknownImageRegistry("'%s' not found" % registry)
|
||||
|
||||
return adapter.lookup(image_id)
|
@ -217,7 +217,7 @@ image_cache_driver = %(image_cache_driver)s
|
||||
pipeline = versionnegotiation context %(cache_pipeline)s apiv1app
|
||||
|
||||
[app:apiv1app]
|
||||
paste.app_factory = glance.api.v1:app_factory
|
||||
paste.app_factory = glance.api.v1.router:app_factory
|
||||
|
||||
[filter:versionnegotiation]
|
||||
paste.filter_factory = glance.api.middleware.version_negotiation:filter_factory
|
||||
|
@ -22,8 +22,8 @@ import shutil
|
||||
|
||||
import webob
|
||||
|
||||
from glance.api import v1 as server
|
||||
from glance.api.middleware import version_negotiation
|
||||
from glance.api.v1 import router
|
||||
import glance.common.client
|
||||
from glance.common import context
|
||||
from glance.common import exception
|
||||
@ -154,7 +154,7 @@ def stub_out_registry_and_store_server(stubs):
|
||||
'default_store': 'file',
|
||||
'filesystem_store_datadir': FAKE_FILESYSTEM_ROOTDIR}
|
||||
api = version_negotiation.VersionNegotiationFilter(
|
||||
context.ContextMiddleware(server.API(options), options),
|
||||
context.ContextMiddleware(router.API(options), options),
|
||||
options)
|
||||
res = self.req.get_response(api)
|
||||
|
||||
|
@ -26,7 +26,7 @@ import unittest
|
||||
import stubout
|
||||
import webob
|
||||
|
||||
from glance.api import v1 as server
|
||||
from glance.api.v1 import router
|
||||
from glance.common import context
|
||||
from glance.common import utils
|
||||
from glance.registry import context as rcontext
|
||||
@ -1936,7 +1936,7 @@ class TestGlanceAPI(unittest.TestCase):
|
||||
stubs.stub_out_registry_and_store_server(self.stubs)
|
||||
stubs.stub_out_filesystem_backend()
|
||||
sql_connection = os.environ.get('GLANCE_SQL_CONNECTION', "sqlite://")
|
||||
self.api = context.ContextMiddleware(server.API(OPTIONS), OPTIONS)
|
||||
self.api = context.ContextMiddleware(router.API(OPTIONS), OPTIONS)
|
||||
self.FIXTURES = [
|
||||
{'id': UUID1,
|
||||
'name': 'fake image #1',
|
||||
|
@ -19,6 +19,7 @@ import unittest
|
||||
import webob
|
||||
|
||||
from glance.common import wsgi
|
||||
from glance.common import utils
|
||||
from glance.common import exception
|
||||
|
||||
|
||||
@ -201,7 +202,7 @@ class TestHelpers(unittest.TestCase):
|
||||
'size': 19,
|
||||
'location': "file:///tmp/glance-tests/2",
|
||||
'properties': {'distro': 'Ubuntu 10.04 LTS'}}
|
||||
headers = wsgi.image_meta_to_http_headers(fixture)
|
||||
headers = utils.image_meta_to_http_headers(fixture)
|
||||
for k, v in headers.iteritems():
|
||||
self.assert_(isinstance(v, unicode), "%s is not unicode" % v)
|
||||
|
||||
@ -217,14 +218,14 @@ class TestHelpers(unittest.TestCase):
|
||||
'size': 19,
|
||||
'location': "file:///tmp/glance-tests/2",
|
||||
'properties': {'distro': 'Ubuntu 10.04 LTS'}}
|
||||
headers = wsgi.image_meta_to_http_headers(fixture)
|
||||
headers = utils.image_meta_to_http_headers(fixture)
|
||||
|
||||
class FakeResponse():
|
||||
pass
|
||||
|
||||
response = FakeResponse()
|
||||
response.headers = headers
|
||||
result = wsgi.get_image_meta_from_headers(response)
|
||||
result = utils.get_image_meta_from_headers(response)
|
||||
for k, v in fixture.iteritems():
|
||||
self.assertEqual(v, result[k])
|
||||
|
||||
@ -243,11 +244,11 @@ class TestHelpers(unittest.TestCase):
|
||||
pass
|
||||
|
||||
for fixture in fixtures:
|
||||
headers = wsgi.image_meta_to_http_headers(fixture)
|
||||
headers = utils.image_meta_to_http_headers(fixture)
|
||||
|
||||
response = FakeResponse()
|
||||
response.headers = headers
|
||||
result = wsgi.get_image_meta_from_headers(response)
|
||||
result = utils.get_image_meta_from_headers(response)
|
||||
for k, v in expected.items():
|
||||
self.assertEqual(v, result[k])
|
||||
|
||||
@ -261,10 +262,10 @@ class TestHelpers(unittest.TestCase):
|
||||
expected = {'is_public': False}
|
||||
|
||||
for fixture in fixtures:
|
||||
headers = wsgi.image_meta_to_http_headers(fixture)
|
||||
headers = utils.image_meta_to_http_headers(fixture)
|
||||
|
||||
response = FakeResponse()
|
||||
response.headers = headers
|
||||
result = wsgi.get_image_meta_from_headers(response)
|
||||
result = utils.get_image_meta_from_headers(response)
|
||||
for k, v in expected.items():
|
||||
self.assertEqual(v, result[k])
|
||||
|
Loading…
Reference in New Issue
Block a user