summaryrefslogtreecommitdiffstats
path: root/nova/api/openstack/compute/plugins/v3/images.py
diff options
context:
space:
mode:
Diffstat (limited to 'nova/api/openstack/compute/plugins/v3/images.py')
-rw-r--r--nova/api/openstack/compute/plugins/v3/images.py242
1 files changed, 242 insertions, 0 deletions
diff --git a/nova/api/openstack/compute/plugins/v3/images.py b/nova/api/openstack/compute/plugins/v3/images.py
new file mode 100644
index 000000000..dde22488d
--- /dev/null
+++ b/nova/api/openstack/compute/plugins/v3/images.py
@@ -0,0 +1,242 @@
+# Copyright 2011 OpenStack Foundation
+# 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 webob.exc
+
+from nova.api.openstack import common
+from nova.api.openstack.compute.views import images as views_images
+from nova.api.openstack import extensions
+from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
+from nova import exception
+import nova.image.glance
+import nova.utils
+
+
+ALIAS = "os-images"
+authorize = extensions.extension_authorizer('compute', 'v3:' + ALIAS)
+
+SUPPORTED_FILTERS = {
+ 'name': 'name',
+ 'status': 'status',
+ 'changes-since': 'changes-since',
+ 'server': 'property-instance_uuid',
+ 'type': 'property-image_type',
+ 'minRam': 'min_ram',
+ 'minDisk': 'min_disk',
+}
+
+
+def make_image(elem, detailed=False):
+ elem.set('name')
+ elem.set('id')
+
+ if detailed:
+ elem.set('updated')
+ elem.set('created')
+ elem.set('status')
+ elem.set('progress')
+ elem.set('minRam')
+ elem.set('minDisk')
+
+ server = xmlutil.SubTemplateElement(elem, 'server', selector='server')
+ server.set('id')
+ xmlutil.make_links(server, 'links')
+
+ elem.append(common.MetadataTemplate())
+
+ xmlutil.make_links(elem, 'links')
+
+
+image_nsmap = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM}
+
+
+class ImageTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('image', selector='image')
+ make_image(root, detailed=True)
+ return xmlutil.MasterTemplate(root, 1, nsmap=image_nsmap)
+
+
+class MinimalImagesTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('images')
+ elem = xmlutil.SubTemplateElement(root, 'image', selector='images')
+ make_image(elem)
+ xmlutil.make_links(root, 'images_links')
+ return xmlutil.MasterTemplate(root, 1, nsmap=image_nsmap)
+
+
+class ImagesTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('images')
+ elem = xmlutil.SubTemplateElement(root, 'image', selector='images')
+ make_image(elem, detailed=True)
+ return xmlutil.MasterTemplate(root, 1, nsmap=image_nsmap)
+
+
+class ImagesController(wsgi.Controller):
+ """Base controller for retrieving/displaying images."""
+
+ _view_builder_class = views_images.ViewBuilder
+
+ def __init__(self, image_service=None, **kwargs):
+ """Initialize new `ImageController`.
+
+ :param image_service: `nova.image.glance:GlanceImageService`
+
+ """
+ super(ImagesController, self).__init__(**kwargs)
+ self._image_service = (image_service or
+ nova.image.glance.get_default_image_service())
+
+ def _get_filters(self, req):
+ """
+ Return a dictionary of query param filters from the request
+
+ :param req: the Request object coming from the wsgi layer
+ :retval a dict of key/value filters
+ """
+ filters = {}
+ for param in req.params:
+ if param in SUPPORTED_FILTERS or param.startswith('property-'):
+ # map filter name or carry through if property-*
+ filter_name = SUPPORTED_FILTERS.get(param, param)
+ filters[filter_name] = req.params.get(param)
+
+ # ensure server filter is the instance uuid
+ filter_name = 'property-instance_uuid'
+ try:
+ filters[filter_name] = filters[filter_name].rsplit('/', 1)[1]
+ except (AttributeError, IndexError, KeyError):
+ pass
+
+ filter_name = 'status'
+ if filter_name in filters:
+ # The Image API expects us to use lowercase strings for status
+ filters[filter_name] = filters[filter_name].lower()
+
+ return filters
+
+ @wsgi.serializers(xml=ImageTemplate)
+ def show(self, req, id):
+ """Return detailed information about a specific image.
+
+ :param req: `wsgi.Request` object
+ :param id: Image identifier
+ """
+ context = req.environ['nova.context']
+ authorize(context)
+
+ try:
+ image = self._image_service.show(context, id)
+ except (exception.NotFound, exception.InvalidImageRef):
+ explanation = _("Image not found.")
+ raise webob.exc.HTTPNotFound(explanation=explanation)
+
+ req.cache_db_items('images', [image], 'id')
+ return self._view_builder.show(req, image)
+
+ def delete(self, req, id):
+ """Delete an image, if allowed.
+
+ :param req: `wsgi.Request` object
+ :param id: Image identifier (integer)
+ """
+ context = req.environ['nova.context']
+ authorize(context)
+
+ try:
+ self._image_service.delete(context, id)
+ except exception.ImageNotFound:
+ explanation = _("Image not found.")
+ raise webob.exc.HTTPNotFound(explanation=explanation)
+ except exception.ImageNotAuthorized:
+ # The image service raises this exception on delete if glanceclient
+ # raises HTTPForbidden.
+ explanation = _("You are not allowed to delete the image.")
+ raise webob.exc.HTTPForbidden(explanation=explanation)
+ return webob.exc.HTTPNoContent()
+
+ @wsgi.serializers(xml=MinimalImagesTemplate)
+ def index(self, req):
+ """Return an index listing of images available to the request.
+
+ :param req: `wsgi.Request` object
+
+ """
+ context = req.environ['nova.context']
+ authorize(context)
+
+ filters = self._get_filters(req)
+ params = req.GET.copy()
+ page_params = common.get_pagination_params(req)
+ for key, val in page_params.iteritems():
+ params[key] = val
+
+ try:
+ images = self._image_service.detail(context, filters=filters,
+ **page_params)
+ except exception.Invalid as e:
+ raise webob.exc.HTTPBadRequest(explanation=e.format_message())
+ return self._view_builder.index(req, images)
+
+ @wsgi.serializers(xml=ImagesTemplate)
+ def detail(self, req):
+ """Return a detailed index listing of images available to the request.
+
+ :param req: `wsgi.Request` object.
+
+ """
+ context = req.environ['nova.context']
+ authorize(context)
+
+ filters = self._get_filters(req)
+ params = req.GET.copy()
+ page_params = common.get_pagination_params(req)
+ for key, val in page_params.iteritems():
+ params[key] = val
+ try:
+ images = self._image_service.detail(context, filters=filters,
+ **page_params)
+ except exception.Invalid as e:
+ raise webob.exc.HTTPBadRequest(explanation=e.format_message())
+
+ req.cache_db_items('images', images, 'id')
+ return self._view_builder.detail(req, images)
+
+ def create(self, *args, **kwargs):
+ raise webob.exc.HTTPMethodNotAllowed()
+
+
+class Images(extensions.V3APIExtensionBase):
+ """Server addresses."""
+
+ name = "Images"
+ alias = ALIAS
+ namespace = "http://docs.openstack.org/compute/ext/images/v3"
+ version = 1
+
+ def get_resources(self):
+ collection_actions = {'detail': 'GET'}
+ resources = [
+ extensions.ResourceExtension(
+ ALIAS, ImagesController(),
+ collection_actions=collection_actions)]
+
+ return resources
+
+ def get_controller_extensions(self):
+ return []