diff options
| author | Brian Waldon <bcwaldon@gmail.com> | 2012-07-30 13:12:18 -0700 |
|---|---|---|
| committer | Brian Waldon <bcwaldon@gmail.com> | 2012-08-13 13:28:48 -0700 |
| commit | 95015ad42a1f6355af095a6dca2ecd39ae2cc735 (patch) | |
| tree | c9ad4567af8538db33a631078f10dbae2f11ad3e /nova/image | |
| parent | 3fc3dbe6ab6149f5650f777b3a89c650ae3945e9 (diff) | |
| download | nova-95015ad42a1f6355af095a6dca2ecd39ae2cc735.tar.gz nova-95015ad42a1f6355af095a6dca2ecd39ae2cc735.tar.xz nova-95015ad42a1f6355af095a6dca2ecd39ae2cc735.zip | |
Rewrite image code to use python-glanceclient
This patch rewrites the Glance-specific code within Nova to use
the new python-glanceclient library. The old client is deprecated
in Folsom and should not be used in conjunction with
python-glanceclient.
This removes the dependency on installing 'glance' through pip.
Implements bp integrate-python-glanceclient
Fixes bug 880964
Change-Id: Ifb9e75593abd36a2d1c08773d02f192e5e5627fc
Diffstat (limited to 'nova/image')
| -rw-r--r-- | nova/image/glance.py | 176 | ||||
| -rw-r--r-- | nova/image/s3.py | 12 |
2 files changed, 72 insertions, 116 deletions
diff --git a/nova/image/glance.py b/nova/image/glance.py index 9aa54eb18..aa03ccd49 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -26,8 +26,8 @@ import sys import time import urlparse -import glance.client -from glance.common import exception as glance_exception +import glanceclient +import glanceclient.exc from nova import exception from nova import flags @@ -56,16 +56,12 @@ def _parse_image_ref(image_href): def _create_glance_client(context, host, port): + """Instantiate a new glanceclient.Client object""" params = {} if FLAGS.auth_strategy == 'keystone': - params['creds'] = { - 'strategy': 'keystone', - 'username': context.user_id, - 'tenant': context.project_id, - } - params['auth_tok'] = context.auth_token - - return glance.client.Client(host, port, **params) + params['token'] = context.auth_token + endpoint = 'http://%s:%s' % (host, port) + return glanceclient.Client('1', endpoint, **params) def get_api_servers(): @@ -110,15 +106,15 @@ class GlanceClientWrapper(object): Call a glance client method. If we get a connection error, retry the request according to FLAGS.glance_num_retries. """ - retry_excs = (glance_exception.ClientConnectionError, - glance_exception.ServiceUnavailable) - + retry_excs = (glanceclient.exc.ServiceUnavailable, + glanceclient.exc.InvalidEndpoint, + glanceclient.exc.CommunicationError) num_attempts = 1 + FLAGS.glance_num_retries for attempt in xrange(1, num_attempts + 1): client = self.client or self._create_onetime_client(context) try: - return getattr(client, method)(*args, **kwargs) + return getattr(client.images, method)(*args, **kwargs) except retry_excs as e: host = self.host port = self.port @@ -143,14 +139,17 @@ class GlanceImageService(object): def detail(self, context, **kwargs): """Calls out to Glance for a list of detailed image information.""" params = self._extract_query_params(kwargs) - image_metas = self._get_images(context, **params) + try: + images = self._client.call(context, 'list', **params) + except Exception: + _reraise_translated_exception() - images = [] - for image_meta in image_metas: - if self._is_image_available(context, image_meta): - base_image_meta = self._translate_from_glance(image_meta) - images.append(base_image_meta) - return images + _images = [] + for image in images: + if self._is_image_available(context, image): + _images.append(self._translate_from_glance(image)) + + return _images def _extract_query_params(self, params): _params = {} @@ -159,68 +158,31 @@ class GlanceImageService(object): for param in accepted_params: if param in params: _params[param] = params.get(param) - return _params - def _get_images(self, context, **kwargs): - """Get image entitites from images service""" # ensure filters is a dict - kwargs['filters'] = kwargs.get('filters') or {} + params.setdefault('filters', {}) # NOTE(vish): don't filter out private images - kwargs['filters'].setdefault('is_public', 'none') - - return self._fetch_images(context, 'get_images_detailed', **kwargs) + params['filters'].setdefault('is_public', 'none') - def _fetch_images(self, context, fetch_method, **kwargs): - """Paginate through results from glance server""" - try: - images = self._client.call(context, fetch_method, **kwargs) - except Exception: - _reraise_translated_exception() - - if not images: - # break out of recursive loop to end pagination - return - - for image in images: - yield image - - try: - # attempt to advance the marker in order to fetch next page - kwargs['marker'] = images[-1]['id'] - except KeyError: - raise exception.ImagePaginationFailed() - - try: - kwargs['limit'] = kwargs['limit'] - len(images) - # break if we have reached a provided limit - if kwargs['limit'] <= 0: - return - except KeyError: - # ignore missing limit, just proceed without it - pass - - for image in self._fetch_images(context, fetch_method, **kwargs): - yield image + return _params def show(self, context, image_id): """Returns a dict with image data for the given opaque image id.""" try: - image_meta = self._client.call(context, 'get_image_meta', - image_id) + image = self._client.call(context, 'get', image_id) except Exception: _reraise_translated_image_exception(image_id) - if not self._is_image_available(context, image_meta): + if not self._is_image_available(context, image): raise exception.ImageNotFound(image_id=image_id) - base_image_meta = self._translate_from_glance(image_meta) + base_image_meta = self._translate_from_glance(image) return base_image_meta def download(self, context, image_id, data): """Calls out to Glance for metadata and data and writes data.""" try: - image_meta, image_chunks = self._client.call(context, - 'get_image', image_id) + image_chunks = self._client.call(context, 'data', image_id) except Exception: _reraise_translated_image_exception(image_id) @@ -228,34 +190,34 @@ class GlanceImageService(object): data.write(chunk) def create(self, context, image_meta, data=None): - """Store the image data and return the new image id. - - :raises: AlreadyExists if the image already exist. - - """ + """Store the image data and return the new image object.""" sent_service_image_meta = self._translate_to_glance(image_meta) - recv_service_image_meta = self._client.call(context, - 'add_image', sent_service_image_meta, data) - base_image_meta = self._translate_from_glance(recv_service_image_meta) - return base_image_meta - def update(self, context, image_id, image_meta, data=None, features=None): - """Replace the contents of the given image with the new data. + if data: + sent_service_image_meta['data'] = data - :raises: ImageNotFound if the image does not exist. + recv_service_image_meta = self._client.call(context, 'create', + **sent_service_image_meta) - """ - # NOTE(vish): show is to check if image is available - self.show(context, image_id) + return self._translate_from_glance(recv_service_image_meta) + + def update(self, context, image_id, image_meta, data=None, + purge_props=True): + """Modify the given image with the new data.""" image_meta = self._translate_to_glance(image_meta) + image_meta['purge_props'] = purge_props + #NOTE(bcwaldon): id is not an editable field, but it is likely to be + # passed in by calling code. Let's be nice and ignore it. + image_meta.pop('id', None) + if data: + image_meta['data'] = data try: - image_meta = self._client.call(context, 'update_image', - image_id, image_meta, data, features) + image_meta = self._client.call(context, 'update', + image_id, **image_meta) except Exception: _reraise_translated_image_exception(image_id) - - base_image_meta = self._translate_from_glance(image_meta) - return base_image_meta + else: + return self._translate_from_glance(image_meta) def delete(self, context, image_id): """Delete the given image. @@ -264,13 +226,11 @@ class GlanceImageService(object): :raises: NotAuthorized if the user is not an owner. """ - # NOTE(vish): show is to check if image is available - self.show(context, image_id) try: - result = self._client.call(context, 'delete_image', image_id) - except glance_exception.NotFound: + self._client.call(context, 'delete', image_id) + except glanceclient.exc.NotFound: raise exception.ImageNotFound(image_id=image_id) - return result + return True @staticmethod def _translate_to_glance(image_meta): @@ -279,14 +239,14 @@ class GlanceImageService(object): return image_meta @staticmethod - def _translate_from_glance(image_meta): - image_meta = _limit_attributes(image_meta) + def _translate_from_glance(image): + image_meta = _extract_attributes(image) image_meta = _convert_timestamps_to_datetimes(image_meta) image_meta = _convert_from_string(image_meta) return image_meta @staticmethod - def _is_image_available(context, image_meta): + def _is_image_available(context, image): """Check image availability. This check is needed in case Nova and Glance are deployed @@ -297,10 +257,10 @@ class GlanceImageService(object): if hasattr(context, 'auth_token') and context.auth_token: return True - if image_meta['is_public'] or context.is_admin: + if image.is_public or context.is_admin: return True - properties = image_meta['properties'] + properties = image.properties if context.project_id and ('owner_id' in properties): return str(properties['owner_id']) == str(context.project_id) @@ -359,7 +319,7 @@ def _convert_to_string(metadata): return _convert(_json_dumps, metadata) -def _limit_attributes(image_meta): +def _extract_attributes(image): IMAGE_ATTRIBUTES = ['size', 'disk_format', 'owner', 'container_format', 'checksum', 'id', 'name', 'created_at', 'updated_at', @@ -367,15 +327,15 @@ def _limit_attributes(image_meta): 'min_disk', 'min_ram', 'is_public'] output = {} for attr in IMAGE_ATTRIBUTES: - output[attr] = image_meta.get(attr) + output[attr] = getattr(image, attr, None) - output['properties'] = image_meta.get('properties', {}) + output['properties'] = getattr(image, 'properties', {}) return output def _remove_read_only(image_meta): - IMAGE_ATTRIBUTES = ['updated_at', 'created_at', 'deleted_at'] + IMAGE_ATTRIBUTES = ['status', 'updated_at', 'created_at', 'deleted_at'] output = copy.deepcopy(image_meta) for attr in IMAGE_ATTRIBUTES: if attr in output: @@ -398,25 +358,23 @@ def _reraise_translated_exception(): def _translate_image_exception(image_id, exc_type, exc_value): - if exc_type in (glance_exception.Forbidden, - glance_exception.NotAuthenticated, - glance_exception.MissingCredentialError): + if exc_type in (glanceclient.exc.Forbidden, + glanceclient.exc.Unauthorized): return exception.ImageNotAuthorized(image_id=image_id) - if exc_type is glance_exception.NotFound: + if exc_type is glanceclient.exc.NotFound: return exception.ImageNotFound(image_id=image_id) - if exc_type is glance_exception.Invalid: + if exc_type is glanceclient.exc.BadRequest: return exception.Invalid(exc_value) return exc_value def _translate_plain_exception(exc_type, exc_value): - if exc_type in (glance_exception.Forbidden, - glance_exception.NotAuthenticated, - glance_exception.MissingCredentialError): + if exc_type in (glanceclient.exc.Forbidden, + glanceclient.exc.Unauthorized): return exception.NotAuthorized(exc_value) - if exc_type is glance_exception.NotFound: + if exc_type is glanceclient.exc.NotFound: return exception.NotFound(exc_value) - if exc_type is glance_exception.Invalid: + if exc_type is glanceclient.exc.BadRequest: return exception.Invalid(exc_value) return exc_value diff --git a/nova/image/s3.py b/nova/image/s3.py index 282f7702a..80f94484e 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -281,15 +281,13 @@ class S3ImageService(object): def _update_image_state(context, image_uuid, image_state): metadata = {'properties': {'image_state': image_state}} - headers = {'x-glance-registry-purge-props': False} - self.service.update(context, image_uuid, metadata, None, - headers) + self.service.update(context, image_uuid, metadata, + purge_props=False) def _update_image_data(context, image_uuid, image_data): metadata = {} - headers = {'x-glance-registry-purge-props': False} self.service.update(context, image_uuid, metadata, image_data, - headers) + purge_props=False) _update_image_state(context, image_uuid, 'downloading') @@ -354,8 +352,8 @@ class S3ImageService(object): metadata = {'status': 'active', 'properties': {'image_state': 'available'}} - headers = {'x-glance-registry-purge-props': False} - self.service.update(context, image_uuid, metadata, None, headers) + self.service.update(context, image_uuid, metadata, + purge_props=False) shutil.rmtree(image_path) |
