1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2010 OpenStack LLC.
6
# Licensed under the Apache License, Version 2.0 (the "License"); you may
7
# not use this file except in compliance with the License. You may obtain
8
# a copy of the License at
10
# http://www.apache.org/licenses/LICENSE-2.0
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
# License for the specific language governing permissions and limitations
19
from nova import utils
22
class BaseImageService(object):
23
"""Base class for providing image search and retrieval services.
25
ImageService exposes two concepts of metadata:
27
1. First-class attributes: This is metadata that is common to all
28
ImageService subclasses and is shared across all hypervisors. These
29
attributes are defined by IMAGE_ATTRS.
31
2. Properties: This is metdata that is specific to an ImageService,
32
and Image, or a particular hypervisor. Any attribute not present in
33
BASE_IMAGE_ATTRS should be considered an image property.
35
This means that ImageServices will return BASE_IMAGE_ATTRS as keys in the
36
metadata dict, all other attributes will be returned as keys in the nested
41
BASE_IMAGE_ATTRS = ['id', 'name', 'created_at', 'updated_at',
42
'deleted_at', 'deleted', 'status', 'is_public']
44
# NOTE(sirp): ImageService subclasses may override this to aid translation
45
# between BaseImageService attributes and additional metadata stored by
46
# the ImageService subclass
47
SERVICE_IMAGE_ATTRS = []
49
def index(self, context, *args, **kwargs):
52
:returns: a sequence of mappings with the following signature
53
{'id': opaque id of image, 'name': name of image}
56
raise NotImplementedError
58
def detail(self, context, *args, **kwargs):
59
"""Detailed information about an images.
61
:returns: a sequence of mappings with the following signature
62
{'id': opaque id of image,
63
'name': name of image,
64
'created_at': creation datetime object,
65
'updated_at': modification datetime object,
66
'deleted_at': deletion datetime object or None,
67
'deleted': boolean indicating if image has been deleted,
68
'status': string description of image status,
69
'is_public': boolean indicating if image is public
72
If the service does not implement a method that provides a detailed
73
set of information about images, then the method should raise
74
NotImplementedError, in which case Nova will emulate this method
75
with repeated calls to show() for each image received from the
79
raise NotImplementedError
81
def show(self, context, image_id):
82
"""Detailed information about an image.
84
:returns: a mapping with the following signature:
85
{'id': opaque id of image,
86
'name': name of image,
87
'created_at': creation datetime object,
88
'updated_at': modification datetime object,
89
'deleted_at': deletion datetime object or None,
90
'deleted': boolean indicating if image has been deleted,
91
'status': string description of image status,
92
'is_public': boolean indicating if image is public
95
:raises: NotFound if the image does not exist
98
raise NotImplementedError
100
def get(self, context, data):
103
:param data: a file-like object to hold binary image data
104
:returns: a dict containing image metadata, writes image data to data.
105
:raises: NotFound if the image does not exist
108
raise NotImplementedError
110
def create(self, context, metadata, data=None):
111
"""Store the image metadata and data.
113
:returns: the new image metadata.
114
:raises: AlreadyExists if the image already exist.
117
raise NotImplementedError
119
def update(self, context, image_id, metadata, data=None):
120
"""Update the given image metadata and data and return the metadata.
122
:raises: NotFound if the image does not exist.
125
raise NotImplementedError
127
def delete(self, context, image_id):
128
"""Delete the given image.
130
:raises: NotFound if the image does not exist.
133
raise NotImplementedError
136
def _is_image_available(context, image_meta):
137
"""Check image availability.
139
Images are always available if they are public or if the user is an
142
Otherwise, we filter by project_id (if present) and then fall-back to
143
images owned by user.
146
# FIXME(sirp): We should be filtering by user_id on the Glance side
147
# for security; however, we can't do that until we get authn/authz
148
# sorted out. Until then, filtering in Nova.
149
if image_meta['is_public'] or context.is_admin:
152
properties = image_meta['properties']
154
if context.project_id and ('project_id' in properties):
155
return str(properties['project_id']) == str(context.project_id)
158
user_id = properties['user_id']
162
return str(user_id) == str(context.user_id)
165
def _translate_to_base(cls, metadata):
166
"""Return a metadata dictionary that is BaseImageService compliant.
168
This is used by subclasses to expose only a metadata dictionary that
169
is the same across ImageService implementations.
172
return cls._propertify_metadata(metadata, cls.BASE_IMAGE_ATTRS)
175
def _translate_to_service(cls, metadata):
176
"""Return a metadata dict that is usable by the ImageService subclass.
178
As an example, Glance has additional attributes (like 'location'); the
179
BaseImageService considers these properties, but we need to translate
180
these back to first-class attrs for sending to Glance. This method
181
handles this by allowing you to specify the attributes an ImageService
182
considers first-class.
185
if not cls.SERVICE_IMAGE_ATTRS:
186
raise NotImplementedError(_('Cannot use this without specifying '
187
'SERVICE_IMAGE_ATTRS for subclass'))
188
return cls._propertify_metadata(metadata, cls.SERVICE_IMAGE_ATTRS)
191
def _propertify_metadata(metadata, keys):
192
"""Move unknown keys to a nested 'properties' dict.
194
:returns: a new dict with the keys moved.
197
flattened = utils.flatten_dict(metadata)
198
attributes, properties = utils.partition_dict(flattened, keys)
199
attributes['properties'] = properties