From d2b553030e29802f01baa0cac9cfb332aab0eb92 Mon Sep 17 00:00:00 2001 From: Andrew Laski Date: Tue, 19 Feb 2013 18:43:23 -0500 Subject: [PATCH] Add an extension to show image size. Adds image size data that is returned from Glance into the API response. This becomes really useful for users when they take a snapshot and want to track the storage used by it. Since this information is already being pulled by Nova we should display it. DocImpact Change-Id: I8a2d4001c21bcc54cac1a2992034dfc9fbe39d7e --- .../OS-EXT-IMG-SIZE/image-get-resp.json | 34 +++ .../OS-EXT-IMG-SIZE/image-get-resp.xml | 12 + .../images-details-get-resp.json | 219 ++++++++++++++++++ .../images-details-get-resp.xml | 71 ++++++ .../all_extensions/extensions-get-resp.json | 16 +- .../all_extensions/extensions-get-resp.xml | 11 +- etc/nova/policy.json | 1 + .../openstack/compute/contrib/image_size.py | 88 +++++++ nova/api/openstack/compute/images.py | 2 + .../compute/contrib/test_image_size.py | 130 +++++++++++ .../api/openstack/compute/test_extensions.py | 1 + nova/tests/api/openstack/fakes.py | 4 +- nova/tests/fake_policy.py | 1 + nova/tests/image/fake.py | 7 + nova/tests/image/test_fake.py | 3 +- .../OS-EXT-IMG-SIZE/image-get-resp.json.tpl | 34 +++ .../OS-EXT-IMG-SIZE/image-get-resp.xml.tpl | 12 + .../images-details-get-resp.json.tpl | 219 ++++++++++++++++++ .../images-details-get-resp.xml.tpl | 71 ++++++ .../extensions-get-resp.json.tpl | 8 + .../extensions-get-resp.xml.tpl | 3 + nova/tests/integrated/test_api_samples.py | 25 ++ 22 files changed, 961 insertions(+), 11 deletions(-) create mode 100644 doc/api_samples/OS-EXT-IMG-SIZE/image-get-resp.json create mode 100644 doc/api_samples/OS-EXT-IMG-SIZE/image-get-resp.xml create mode 100644 doc/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.json create mode 100644 doc/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.xml create mode 100644 nova/api/openstack/compute/contrib/image_size.py create mode 100644 nova/tests/api/openstack/compute/contrib/test_image_size.py create mode 100644 nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/image-get-resp.json.tpl create mode 100644 nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/image-get-resp.xml.tpl create mode 100644 nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.json.tpl create mode 100644 nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.xml.tpl diff --git a/doc/api_samples/OS-EXT-IMG-SIZE/image-get-resp.json b/doc/api_samples/OS-EXT-IMG-SIZE/image-get-resp.json new file mode 100644 index 000000000000..1548aeb59061 --- /dev/null +++ b/doc/api_samples/OS-EXT-IMG-SIZE/image-get-resp.json @@ -0,0 +1,34 @@ +{ + "image": { + "OS-EXT-IMG-SIZE:size": "74185822", + "created": "2011-01-01T01:02:03Z", + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + }, + { + "href": "http://glance.openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "architecture": "x86_64", + "auto_disk_config": "True", + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage7", + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + } +} \ No newline at end of file diff --git a/doc/api_samples/OS-EXT-IMG-SIZE/image-get-resp.xml b/doc/api_samples/OS-EXT-IMG-SIZE/image-get-resp.xml new file mode 100644 index 000000000000..49fe2ee31b3d --- /dev/null +++ b/doc/api_samples/OS-EXT-IMG-SIZE/image-get-resp.xml @@ -0,0 +1,12 @@ + + + + nokernel + True + nokernel + x86_64 + + + + + \ No newline at end of file diff --git a/doc/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.json b/doc/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.json new file mode 100644 index 000000000000..bbd9dcfb1961 --- /dev/null +++ b/doc/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.json @@ -0,0 +1,219 @@ +{ + "images": [ + { + "OS-EXT-IMG-SIZE:size": "74185822", + "created": "2011-01-01T01:02:03Z", + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + }, + { + "href": "http://glance.openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "architecture": "x86_64", + "auto_disk_config": "True", + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage7", + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + }, + { + "OS-EXT-IMG-SIZE:size": "25165824", + "created": "2011-01-01T01:02:03Z", + "id": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/images/155d900f-4e14-4e4c-a73d-069cbf4541e6", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/images/155d900f-4e14-4e4c-a73d-069cbf4541e6", + "rel": "bookmark" + }, + { + "href": "http://glance.openstack.example.com/openstack/images/155d900f-4e14-4e4c-a73d-069cbf4541e6", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "architecture": "x86_64", + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage123456", + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + }, + { + "OS-EXT-IMG-SIZE:size": "58145823", + "created": "2011-01-01T01:02:03Z", + "id": "a2459075-d96c-40d5-893e-577ff92e721c", + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/images/a2459075-d96c-40d5-893e-577ff92e721c", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/images/a2459075-d96c-40d5-893e-577ff92e721c", + "rel": "bookmark" + }, + { + "href": "http://glance.openstack.example.com/openstack/images/a2459075-d96c-40d5-893e-577ff92e721c", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage123456", + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + }, + { + "OS-EXT-IMG-SIZE:size": "49163826", + "created": "2011-01-01T01:02:03Z", + "id": "a440c04b-79fa-479c-bed1-0b816eaec379", + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/images/a440c04b-79fa-479c-bed1-0b816eaec379", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/images/a440c04b-79fa-479c-bed1-0b816eaec379", + "rel": "bookmark" + }, + { + "href": "http://glance.openstack.example.com/openstack/images/a440c04b-79fa-479c-bed1-0b816eaec379", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "architecture": "x86_64", + "auto_disk_config": "False", + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage6", + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + }, + { + "OS-EXT-IMG-SIZE:size": "26360814", + "created": "2011-01-01T01:02:03Z", + "id": "c905cedb-7281-47e4-8a62-f26bc5fc4c77", + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/images/c905cedb-7281-47e4-8a62-f26bc5fc4c77", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/images/c905cedb-7281-47e4-8a62-f26bc5fc4c77", + "rel": "bookmark" + }, + { + "href": "http://glance.openstack.example.com/openstack/images/c905cedb-7281-47e4-8a62-f26bc5fc4c77", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "kernel_id": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "ramdisk_id": null + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage123456", + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + }, + { + "OS-EXT-IMG-SIZE:size": "84035174", + "created": "2011-01-01T01:02:03Z", + "id": "cedef40a-ed67-4d10-800e-17455edce175", + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/images/cedef40a-ed67-4d10-800e-17455edce175", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/images/cedef40a-ed67-4d10-800e-17455edce175", + "rel": "bookmark" + }, + { + "href": "http://glance.openstack.example.com/openstack/images/cedef40a-ed67-4d10-800e-17455edce175", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage123456", + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + }, + { + "OS-EXT-IMG-SIZE:size": "83594576", + "created": "2011-01-01T01:02:03Z", + "id": "76fa36fc-c930-4bf3-8c8a-ea2a2420deb6", + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/images/76fa36fc-c930-4bf3-8c8a-ea2a2420deb6", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/images/76fa36fc-c930-4bf3-8c8a-ea2a2420deb6", + "rel": "bookmark" + }, + { + "href": "http://glance.openstack.example.com/openstack/images/76fa36fc-c930-4bf3-8c8a-ea2a2420deb6", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage123456", + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + } + ] +} \ No newline at end of file diff --git a/doc/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.xml b/doc/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.xml new file mode 100644 index 000000000000..d0b5787ca879 --- /dev/null +++ b/doc/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.xml @@ -0,0 +1,71 @@ + + + + + nokernel + True + nokernel + x86_64 + + + + + + + + nokernel + nokernel + x86_64 + + + + + + + + nokernel + nokernel + + + + + + + + nokernel + False + nokernel + x86_64 + + + + + + + + 155d900f-4e14-4e4c-a73d-069cbf4541e6 + None + + + + + + + + nokernel + nokernel + + + + + + + + nokernel + nokernel + + + + + + \ No newline at end of file diff --git a/doc/api_samples/all_extensions/extensions-get-resp.json b/doc/api_samples/all_extensions/extensions-get-resp.json index 14e9062ca37b..4ee0da1cfc33 100644 --- a/doc/api_samples/all_extensions/extensions-get-resp.json +++ b/doc/api_samples/all_extensions/extensions-get-resp.json @@ -24,6 +24,14 @@ "namespace": "http://docs.openstack.org/compute/ext/extended_availability_zone/api/v2", "updated": "2013-01-30T00:00:00+00:00" }, + { + "alias": "OS-EXT-IMG-SIZE", + "description": "Adds image size to image listings.", + "links": [], + "name": "ImageSize", + "namespace": "http://docs.openstack.org/compute/ext/image_size/api/v1.1", + "updated": "2013-02-19T00:00:00+00:00" + }, { "alias": "OS-EXT-IPS", "description": "Adds type parameter to the ip list.", @@ -194,11 +202,11 @@ }, { "alias": "os-evacuate", - "description": "Enables server evacuation", + "description": "Enables server evacuation.", "links": [], "name": "Evacuate", "namespace": "http://docs.openstack.org/compute/ext/evacuate/api/v2", - "updated": "2012-12-05T00:00:00+00:00" + "updated": "2013-01-06T00:00:00+00:00" }, { "alias": "os-fixed-ips", @@ -314,7 +322,7 @@ }, { "alias": "os-instance-actions", - "description": "View a log of actions taken on an instance", + "description": "View a log of actions and events taken on an instance.", "links": [], "name": "InstanceActions", "namespace": "http://docs.openstack.org/compute/ext/instance-actions/api/v1.1", @@ -481,4 +489,4 @@ "updated": "2011-03-25T00:00:00+00:00" } ] -} +} \ No newline at end of file diff --git a/doc/api_samples/all_extensions/extensions-get-resp.xml b/doc/api_samples/all_extensions/extensions-get-resp.xml index 133b0570ad0a..39f9ffa0683a 100644 --- a/doc/api_samples/all_extensions/extensions-get-resp.xml +++ b/doc/api_samples/all_extensions/extensions-get-resp.xml @@ -9,6 +9,9 @@ Extended Server Attributes support. + + Adds image size to image listings. + Adds type parameter to the ip list. @@ -88,8 +91,8 @@ Instance deferred delete. - - Enables server evacuation + + Enables server evacuation. Fixed IPs support. @@ -136,7 +139,7 @@ Admin-only hypervisor administration. - View a log of actions taken on an instance + View a log of actions and events taken on an instance. Admin-only Task Log Monitoring. @@ -198,4 +201,4 @@ Volumes support. - + \ No newline at end of file diff --git a/etc/nova/policy.json b/etc/nova/policy.json index a8bc6f1414fd..7ff98d736218 100644 --- a/etc/nova/policy.json +++ b/etc/nova/policy.json @@ -66,6 +66,7 @@ "compute_extension:hide_server_addresses": "is_admin:False", "compute_extension:hosts": "rule:admin_api", "compute_extension:hypervisors": "rule:admin_api", + "compute_extension:image_size": "", "compute_extension:instance_actions": "", "compute_extension:instance_actions:events": "rule:admin_api", "compute_extension:instance_usage_audit_log": "rule:admin_api", diff --git a/nova/api/openstack/compute/contrib/image_size.py b/nova/api/openstack/compute/contrib/image_size.py new file mode 100644 index 000000000000..21998738f2f9 --- /dev/null +++ b/nova/api/openstack/compute/contrib/image_size.py @@ -0,0 +1,88 @@ +# Copyright 2013 Rackspace Hosting +# 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. + +from nova.api.openstack import extensions +from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil + +authorize = extensions.soft_extension_authorizer('compute', 'image_size') + + +def make_image(elem): + elem.set('{%s}size' % Image_size.namespace, '%s:size' % Image_size.alias) + + +class ImagesSizeTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('images') + elem = xmlutil.SubTemplateElement(root, 'image', selector='images') + make_image(elem) + return xmlutil.SlaveTemplate(root, 1, nsmap={ + Image_size.alias: Image_size.namespace}) + + +class ImageSizeTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('image', selector='image') + make_image(root) + return xmlutil.SlaveTemplate(root, 1, nsmap={ + Image_size.alias: Image_size.namespace}) + + +class ImageSizeController(wsgi.Controller): + + def _extend_image(self, image, image_cache): + key = "%s:size" % Image_size.alias + image[key] = image_cache['size'] + + @wsgi.extends + def show(self, req, resp_obj, id): + context = req.environ["nova.context"] + if authorize(context): + # Attach our slave template to the response object + resp_obj.attach(xml=ImageSizeTemplate()) + image_resp = resp_obj.obj['image'] + # image guaranteed to be in the cache due to the core API adding + # it in its 'show' method + image_cached = req.get_db_item('images', image_resp['id']) + self._extend_image(image_resp, image_cached) + + @wsgi.extends + def detail(self, req, resp_obj): + context = req.environ['nova.context'] + if authorize(context): + # Attach our slave template to the response object + resp_obj.attach(xml=ImagesSizeTemplate()) + images_resp = list(resp_obj.obj['images']) + # images guaranteed to be in the cache due to the core API adding + # it in its 'detail' method + for image in images_resp: + image_cached = req.get_db_item('images', image['id']) + self._extend_image(image, image_cached) + + +class Image_size(extensions.ExtensionDescriptor): + """Adds image size to image listings.""" + + name = "ImageSize" + alias = "OS-EXT-IMG-SIZE" + namespace = ("http://docs.openstack.org/compute/ext/" + "image_size/api/v1.1") + updated = "2013-02-19T00:00:00+00:00" + + def get_controller_extensions(self): + controller = ImageSizeController() + extension = extensions.ControllerExtension(self, 'images', controller) + return [extension] diff --git a/nova/api/openstack/compute/images.py b/nova/api/openstack/compute/images.py index 7dda64f87f96..703d2fe2d255 100644 --- a/nova/api/openstack/compute/images.py +++ b/nova/api/openstack/compute/images.py @@ -144,6 +144,7 @@ class Controller(wsgi.Controller): 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): @@ -200,6 +201,7 @@ class Controller(wsgi.Controller): except exception.Invalid as e: raise webob.exc.HTTPBadRequest(explanation=str(e)) + req.cache_db_items('images', images, 'id') return self._view_builder.detail(req, images) def create(self, *args, **kwargs): diff --git a/nova/tests/api/openstack/compute/contrib/test_image_size.py b/nova/tests/api/openstack/compute/contrib/test_image_size.py new file mode 100644 index 000000000000..886bccfa7351 --- /dev/null +++ b/nova/tests/api/openstack/compute/contrib/test_image_size.py @@ -0,0 +1,130 @@ +# Copyright 2013 Rackspace Hosting +# 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. + +from lxml import etree +import webob + +from nova.api.openstack.compute.contrib import image_size +from nova.image import glance +from nova.openstack.common import jsonutils +from nova import test +from nova.tests.api.openstack import fakes + +NOW_API_FORMAT = "2010-10-11T10:30:22Z" +IMAGES = [{ + 'id': '123', + 'name': 'public image', + 'metadata': {'key1': 'value1'}, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'ACTIVE', + 'progress': 100, + 'minDisk': 10, + 'minRam': 128, + 'size': 12345678, + "links": [{ + "rel": "self", + "href": "http://localhost/v2/fake/images/123", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/123", + }], + }, + { + 'id': '124', + 'name': 'queued snapshot', + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'SAVING', + 'progress': 25, + 'minDisk': 0, + 'minRam': 0, + 'size': 87654321, + "links": [{ + "rel": "self", + "href": "http://localhost/v2/fake/images/124", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/124", + }], + }] + + +def fake_show(*args, **kwargs): + return IMAGES[0] + + +def fake_detail(*args, **kwargs): + return IMAGES + + +class ImageSizeTest(test.TestCase): + content_type = 'application/json' + prefix = 'OS-EXT-IMG-SIZE' + + def setUp(self): + super(ImageSizeTest, self).setUp() + self.stubs.Set(glance.GlanceImageService, 'show', fake_show) + self.stubs.Set(glance.GlanceImageService, 'detail', fake_detail) + self.flags(osapi_compute_extension=['nova.api.openstack.compute' + '.contrib.image_size.Image_size']) + + def _make_request(self, url): + req = webob.Request.blank(url) + req.headers['Accept'] = self.content_type + res = req.get_response(fakes.wsgi_app()) + return res + + def _get_image(self, body): + return jsonutils.loads(body).get('image') + + def _get_images(self, body): + return jsonutils.loads(body).get('images') + + def assertImageSize(self, image, size): + self.assertEqual(image.get('%s:size' % self.prefix), size) + + def test_show(self): + url = '/v2/fake/images/1' + res = self._make_request(url) + + self.assertEqual(res.status_int, 200) + image = self._get_image(res.body) + self.assertImageSize(image, 12345678) + + def test_detail(self): + url = '/v2/fake/images/detail' + res = self._make_request(url) + + self.assertEqual(res.status_int, 200) + images = self._get_images(res.body) + self.assertImageSize(images[0], 12345678) + self.assertImageSize(images[1], 87654321) + + +class ImageSizeXmlTest(ImageSizeTest): + content_type = 'application/xml' + prefix = '{%s}' % image_size.Image_size.namespace + + def _get_image(self, body): + return etree.XML(body) + + def _get_images(self, body): + return etree.XML(body).getchildren() + + def assertImageSize(self, image, size): + self.assertEqual(int(image.get('%ssize' % self.prefix)), size) diff --git a/nova/tests/api/openstack/compute/test_extensions.py b/nova/tests/api/openstack/compute/test_extensions.py index 63a6b3c9a2ab..e7a13da5114c 100644 --- a/nova/tests/api/openstack/compute/test_extensions.py +++ b/nova/tests/api/openstack/compute/test_extensions.py @@ -185,6 +185,7 @@ class ExtensionControllerTest(ExtensionTestCase): "FloatingIpsBulk", "Fox In Socks", "Hosts", + "ImageSize", "InstanceActions", "Keypairs", "Multinic", diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index beb99a3f51ec..7da10e73e592 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -227,7 +227,7 @@ def _make_image_fixtures(): # Public image add_fixture(id=image_id, name='public image', is_public=True, status='active', properties={'key1': 'value1'}, - min_ram="128", min_disk="10") + min_ram="128", min_disk="10", size='25165824') image_id += 1 # Snapshot for User 1 @@ -238,7 +238,7 @@ def _make_image_fixtures(): 'deleted', 'pending_delete'): add_fixture(id=image_id, name='%s snapshot' % status, is_public=False, status=status, - properties=snapshot_properties) + properties=snapshot_properties, size='25165824') image_id += 1 # Image without a name diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py index 382667904856..0dc9e699d895 100644 --- a/nova/tests/fake_policy.py +++ b/nova/tests/fake_policy.py @@ -143,6 +143,7 @@ policy_data = """ "compute_extension:hide_server_addresses": "", "compute_extension:hosts": "", "compute_extension:hypervisors": "", + "compute_extension:image_size": "", "compute_extension:instance_actions": "", "compute_extension:instance_actions:events": "is_admin:True", "compute_extension:instance_usage_audit_log": "", diff --git a/nova/tests/image/fake.py b/nova/tests/image/fake.py index 78cd667e4437..7ce16dfa6df3 100644 --- a/nova/tests/image/fake.py +++ b/nova/tests/image/fake.py @@ -52,6 +52,7 @@ class _FakeImageService(object): 'is_public': False, 'container_format': 'raw', 'disk_format': 'raw', + 'size': '25165824', 'properties': {'kernel_id': CONF.null_kernel, 'ramdisk_id': CONF.null_kernel, 'architecture': 'x86_64'}} @@ -66,6 +67,7 @@ class _FakeImageService(object): 'is_public': True, 'container_format': 'ami', 'disk_format': 'ami', + 'size': '58145823', 'properties': {'kernel_id': CONF.null_kernel, 'ramdisk_id': CONF.null_kernel}} @@ -79,6 +81,7 @@ class _FakeImageService(object): 'is_public': True, 'container_format': None, 'disk_format': None, + 'size': '83594576', 'properties': {'kernel_id': CONF.null_kernel, 'ramdisk_id': CONF.null_kernel}} @@ -92,6 +95,7 @@ class _FakeImageService(object): 'is_public': True, 'container_format': 'ami', 'disk_format': 'ami', + 'size': '84035174', 'properties': {'kernel_id': CONF.null_kernel, 'ramdisk_id': CONF.null_kernel}} @@ -105,6 +109,7 @@ class _FakeImageService(object): 'is_public': True, 'container_format': 'ami', 'disk_format': 'ami', + 'size': '26360814', 'properties': {'kernel_id': '155d900f-4e14-4e4c-a73d-069cbf4541e6', 'ramdisk_id': None}} @@ -119,6 +124,7 @@ class _FakeImageService(object): 'is_public': False, 'container_format': 'ova', 'disk_format': 'vhd', + 'size': '49163826', 'properties': {'kernel_id': CONF.null_kernel, 'ramdisk_id': CONF.null_kernel, 'architecture': 'x86_64', @@ -134,6 +140,7 @@ class _FakeImageService(object): 'is_public': False, 'container_format': 'ova', 'disk_format': 'vhd', + 'size': '74185822', 'properties': {'kernel_id': CONF.null_kernel, 'ramdisk_id': CONF.null_kernel, 'architecture': 'x86_64', diff --git a/nova/tests/image/test_fake.py b/nova/tests/image/test_fake.py index 614201b672a8..c63bb53890fb 100644 --- a/nova/tests/image/test_fake.py +++ b/nova/tests/image/test_fake.py @@ -41,7 +41,8 @@ class FakeImageServiceTestCase(test.TestCase): self.assertEquals(keys, set(['id', 'name', 'created_at', 'updated_at', 'deleted_at', 'deleted', 'status', 'is_public', 'properties', - 'disk_format', 'container_format'])) + 'disk_format', 'container_format', + 'size'])) self.assertTrue(isinstance(image['created_at'], datetime.datetime)) self.assertTrue(isinstance(image['updated_at'], datetime.datetime)) diff --git a/nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/image-get-resp.json.tpl b/nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/image-get-resp.json.tpl new file mode 100644 index 000000000000..f5f470cbcedb --- /dev/null +++ b/nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/image-get-resp.json.tpl @@ -0,0 +1,34 @@ +{ + "image": { + "created": "2011-01-01T01:02:03Z", + "id": "%(image_id)s", + "links": [ + { + "href": "%(host)s/v2/openstack/images/%(image_id)s", + "rel": "self" + }, + { + "href": "%(host)s/openstack/images/%(image_id)s", + "rel": "bookmark" + }, + { + "href": "%(glance_host)s/openstack/images/%(image_id)s", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "architecture": "x86_64", + "auto_disk_config": "True", + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage7", + "OS-EXT-IMG-SIZE:size": %(int)s, + "progress": 100, + "status": "ACTIVE", + "updated": "%(timestamp)s" + } +} diff --git a/nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/image-get-resp.xml.tpl b/nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/image-get-resp.xml.tpl new file mode 100644 index 000000000000..e36ddc76c848 --- /dev/null +++ b/nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/image-get-resp.xml.tpl @@ -0,0 +1,12 @@ + + + + nokernel + True + nokernel + x86_64 + + + + + diff --git a/nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.json.tpl b/nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.json.tpl new file mode 100644 index 000000000000..a29172bf4230 --- /dev/null +++ b/nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.json.tpl @@ -0,0 +1,219 @@ +{ + "images": [ + { + "created": "2011-01-01T01:02:03Z", + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "%(host)s/v2/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "self" + }, + { + "href": "%(host)s/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + }, + { + "href": "%(glance_host)s/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "architecture": "x86_64", + "auto_disk_config": "True", + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage7", + "OS-EXT-IMG-SIZE:size": %(int)s, + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + }, + { + "created": "2011-01-01T01:02:03Z", + "id": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "links": [ + { + "href": "%(host)s/v2/openstack/images/155d900f-4e14-4e4c-a73d-069cbf4541e6", + "rel": "self" + }, + { + "href": "%(host)s/openstack/images/155d900f-4e14-4e4c-a73d-069cbf4541e6", + "rel": "bookmark" + }, + { + "href": "%(glance_host)s/openstack/images/155d900f-4e14-4e4c-a73d-069cbf4541e6", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "architecture": "x86_64", + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage123456", + "progress": 100, + "OS-EXT-IMG-SIZE:size": %(int)s, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + }, + { + "created": "2011-01-01T01:02:03Z", + "id": "a2459075-d96c-40d5-893e-577ff92e721c", + "links": [ + { + "href": "%(host)s/v2/openstack/images/a2459075-d96c-40d5-893e-577ff92e721c", + "rel": "self" + }, + { + "href": "%(host)s/openstack/images/a2459075-d96c-40d5-893e-577ff92e721c", + "rel": "bookmark" + }, + { + "href": "%(glance_host)s/openstack/images/a2459075-d96c-40d5-893e-577ff92e721c", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage123456", + "OS-EXT-IMG-SIZE:size": %(int)s, + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + }, + { + "created": "2011-01-01T01:02:03Z", + "id": "a440c04b-79fa-479c-bed1-0b816eaec379", + "links": [ + { + "href": "%(host)s/v2/openstack/images/a440c04b-79fa-479c-bed1-0b816eaec379", + "rel": "self" + }, + { + "href": "%(host)s/openstack/images/a440c04b-79fa-479c-bed1-0b816eaec379", + "rel": "bookmark" + }, + { + "href": "%(glance_host)s/openstack/images/a440c04b-79fa-479c-bed1-0b816eaec379", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "architecture": "x86_64", + "auto_disk_config": "False", + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage6", + "OS-EXT-IMG-SIZE:size": %(int)s, + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + }, + { + "created": "2011-01-01T01:02:03Z", + "id": "c905cedb-7281-47e4-8a62-f26bc5fc4c77", + "links": [ + { + "href": "%(host)s/v2/openstack/images/c905cedb-7281-47e4-8a62-f26bc5fc4c77", + "rel": "self" + }, + { + "href": "%(host)s/openstack/images/c905cedb-7281-47e4-8a62-f26bc5fc4c77", + "rel": "bookmark" + }, + { + "href": "%(glance_host)s/openstack/images/c905cedb-7281-47e4-8a62-f26bc5fc4c77", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "kernel_id": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "ramdisk_id": null + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage123456", + "OS-EXT-IMG-SIZE:size": %(int)s, + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + }, + { + "created": "2011-01-01T01:02:03Z", + "id": "cedef40a-ed67-4d10-800e-17455edce175", + "links": [ + { + "href": "%(host)s/v2/openstack/images/cedef40a-ed67-4d10-800e-17455edce175", + "rel": "self" + }, + { + "href": "%(host)s/openstack/images/cedef40a-ed67-4d10-800e-17455edce175", + "rel": "bookmark" + }, + { + "href": "%(glance_host)s/openstack/images/cedef40a-ed67-4d10-800e-17455edce175", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage123456", + "OS-EXT-IMG-SIZE:size": %(int)s, + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + }, + { + "created": "2011-01-01T01:02:03Z", + "id": "76fa36fc-c930-4bf3-8c8a-ea2a2420deb6", + "links": [ + { + "href": "%(host)s/v2/openstack/images/76fa36fc-c930-4bf3-8c8a-ea2a2420deb6", + "rel": "self" + }, + { + "href": "%(host)s/openstack/images/76fa36fc-c930-4bf3-8c8a-ea2a2420deb6", + "rel": "bookmark" + }, + { + "href": "%(glance_host)s/openstack/images/76fa36fc-c930-4bf3-8c8a-ea2a2420deb6", + "rel": "alternate", + "type": "application/vnd.openstack.image" + } + ], + "metadata": { + "kernel_id": "nokernel", + "ramdisk_id": "nokernel" + }, + "minDisk": 0, + "minRam": 0, + "name": "fakeimage123456", + "OS-EXT-IMG-SIZE:size": %(int)s, + "progress": 100, + "status": "ACTIVE", + "updated": "2011-01-01T01:02:03Z" + } + ] +} diff --git a/nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.xml.tpl b/nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.xml.tpl new file mode 100644 index 000000000000..4c1e4c4beda1 --- /dev/null +++ b/nova/tests/integrated/api_samples/OS-EXT-IMG-SIZE/images-details-get-resp.xml.tpl @@ -0,0 +1,71 @@ + + + + + nokernel + True + nokernel + x86_64 + + + + + + + + nokernel + nokernel + x86_64 + + + + + + + + nokernel + nokernel + + + + + + + + nokernel + False + nokernel + x86_64 + + + + + + + + 155d900f-4e14-4e4c-a73d-069cbf4541e6 + None + + + + + + + + nokernel + nokernel + + + + + + + + nokernel + nokernel + + + + + + diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl index 6aeedbe56a31..1d2042c07c37 100644 --- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl +++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl @@ -32,6 +32,14 @@ "namespace": "http://docs.openstack.org/compute/ext/extended_ips/api/v1.1", "updated": "%(timestamp)s" }, + { + "alias": "OS-EXT-IMG-SIZE", + "description": "%(text)s", + "links": [], + "name": "ImageSize", + "namespace": "http://docs.openstack.org/compute/ext/image_size/api/v1.1", + "updated": "%(timestamp)s" + }, { "alias": "OS-EXT-SRV-ATTR", "description": "%(text)s", diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl index 421cc2233819..f70cad3ba817 100644 --- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl +++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl @@ -12,6 +12,9 @@ %(text)s + + %(text)s + %(text)s diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py index a61ac3b8d626..c91c6137644e 100644 --- a/nova/tests/integrated/test_api_samples.py +++ b/nova/tests/integrated/test_api_samples.py @@ -325,6 +325,7 @@ class ApiSampleTestBase(integrated_helpers._IntegratedTestBase): 'glance_host': self._get_glance_host(), 'compute_host': self.compute.host, 'text': text, + 'int': '[0-9]+', } def _get_response(self, url, method, body=None, strip_version=False): @@ -3206,6 +3207,30 @@ class InstanceActionsSampleXmlTest(InstanceActionsSampleJsonTest): ctype = 'xml' +class ImageSizeSampleJsonTests(ApiSampleTestBase): + extension_name = ("nova.api.openstack.compute.contrib" + ".image_size.Image_size") + + def test_show(self): + # Get api sample of one single image details request. + image_id = fake.get_valid_image_id() + response = self._do_get('images/%s' % image_id) + self.assertEqual(response.status, 200) + subs = self._get_regexes() + subs['image_id'] = image_id + return self._verify_response('image-get-resp', subs, response) + + def test_detail(self): + # Get api sample of all images details request. + response = self._do_get('images/detail') + subs = self._get_regexes() + return self._verify_response('images-details-get-resp', subs, response) + + +class ImageSizeSampleXmlTests(ImageSizeSampleJsonTests): + ctype = 'xml' + + class ConfigDriveSampleJsonTest(ServersSampleBase): extension_name = ("nova.api.openstack.compute.contrib.config_drive." "Config_drive")