commit 98273f0e605d42b78c649cd0d2392d3b640b2cb2 Author: Michael Still Date: Sun Sep 23 20:35:20 2012 +1000 Check that an image is active before spawning instances. Resolves bug/1054163. Change-Id: I4095fcac300a161e6da93bdc14eb43257d4f1aee diff --git a/nova/compute/api.py b/nova/compute/api.py index 13d6631..c69046c 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -401,6 +401,8 @@ class API(base.Base): (image_service, image_id) = glance.get_remote_image_service(context, image_href) image = image_service.show(context, image_id) + if image['status'] != 'active': + raise exception.ImageNotActive(image_id=image_id) if instance_type['memory_mb'] < int(image.get('min_ram') or 0): QUOTAS.rollback(context, quota_reservations) diff --git a/nova/exception.py b/nova/exception.py index 28fb1ee..0a088cb 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -213,6 +213,10 @@ class PolicyNotAuthorized(NotAuthorized): message = _("Policy doesn't allow %(action)s to be performed.") +class ImageNotActive(NovaException): + message = _("Image %(image_id)s is not active.") + + class ImageNotAuthorized(NovaException): message = _("Not authorized for image %(image_id)s.") diff --git a/nova/tests/api/ec2/test_cinder_cloud.py b/nova/tests/api/ec2/test_cinder_cloud.py index 738e13b..389f71b 100644 --- a/nova/tests/api/ec2/test_cinder_cloud.py +++ b/nova/tests/api/ec2/test_cinder_cloud.py @@ -92,6 +92,7 @@ class CinderCloudTestCase(test.TestCase): return {'id': id, 'name': 'fake_name', 'container_format': 'ami', + 'status': 'active', 'properties': { 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', @@ -530,6 +531,7 @@ class CinderCloudTestCase(test.TestCase): image1 = { 'id': 'cedef40a-ed67-4d10-800e-17455edce175', 'name': 'fake_name', + 'status': 'active', 'properties': { 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'type': 'machine', @@ -545,6 +547,7 @@ class CinderCloudTestCase(test.TestCase): image2 = { 'id': '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', 'name': 'fake_name', + 'status': 'active', 'properties': { 'kernel_id': '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', 'type': 'machine', diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py index 9794090..3d7b29f 100644 --- a/nova/tests/api/ec2/test_cloud.py +++ b/nova/tests/api/ec2/test_cloud.py @@ -106,6 +106,7 @@ class CloudTestCase(test.TestCase): return {'id': id, 'name': 'fake_name', 'container_format': 'ami', + 'status': 'active', 'properties': { 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', @@ -1202,6 +1203,7 @@ class CloudTestCase(test.TestCase): return [{'id': 'cedef40a-ed67-4d10-800e-17455edce175', 'name': 'fake_name', 'container_format': 'ami', + 'status': 'active', 'properties': { 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', @@ -1269,6 +1271,7 @@ class CloudTestCase(test.TestCase): image1 = { 'id': 'cedef40a-ed67-4d10-800e-17455edce175', 'name': 'fake_name', + 'status': 'active', 'properties': { 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'type': 'machine', @@ -1284,6 +1287,7 @@ class CloudTestCase(test.TestCase): image2 = { 'id': '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', 'name': 'fake_name', + 'status': 'active', 'properties': { 'kernel_id': '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', 'type': 'machine', @@ -1394,6 +1398,7 @@ class CloudTestCase(test.TestCase): def fake_show(meh, context, id): return {'id': 'cedef40a-ed67-4d10-800e-17455edce175', 'name': 'fake_name', + 'status': 'active', 'properties': { 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', @@ -1451,6 +1456,7 @@ class CloudTestCase(test.TestCase): 'id': 'cedef40a-ed67-4d10-800e-17455edce175', 'name': 'fake_name', 'container_format': 'ami', + 'status': 'active', 'properties': { 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', @@ -1490,13 +1496,14 @@ class CloudTestCase(test.TestCase): # NOTE(vish): We are mocking s3 so make sure we have converted # to ids instead of uuids. return {'id': 1, - 'name': 'fake_name', - 'container_format': 'ami', - 'properties': { - 'kernel_id': 1, - 'ramdisk_id': 1, - 'type': 'machine'}, - 'is_public': False} + 'name': 'fake_name', + 'container_format': 'ami', + 'properties': {'kernel_id': 1, + 'ramdisk_id': 1, + 'type': 'machine' + }, + 'is_public': False + } self.stubs.Set(s3.S3ImageService, 'create', fake_create) image_location = 'fake_bucket/fake.img.manifest.xml' @@ -1812,6 +1819,7 @@ class CloudTestCase(test.TestCase): return {'id': 'cedef40a-ed67-4d10-800e-17455edce175', 'name': 'fake_name', 'container_format': 'ami', + 'status': 'active', 'properties': { 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', @@ -1832,6 +1840,7 @@ class CloudTestCase(test.TestCase): return {'id': 'cedef40a-ed67-4d10-800e-17455edce175', 'name': 'fake_name', 'container_format': 'ami', + 'status': 'active', 'properties': { 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index 0a56a9b..491d109 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -111,6 +111,7 @@ class BaseTestCase(test.TestCase): def fake_show(meh, context, id): return {'id': id, 'min_disk': None, 'min_ram': None, 'name': 'fake_name', + 'status': 'active', 'properties': {'kernel_id': 'fake_kernel_id', 'ramdisk_id': 'fake_ramdisk_id', 'something_else': 'meow'}} @@ -2651,6 +2652,7 @@ class ComputeAPITestCase(BaseTestCase): self.fake_image = { 'id': 1, 'name': 'fake_name', + 'status': 'active', 'properties': {'kernel_id': 'fake_kernel_id', 'ramdisk_id': 'fake_ramdisk_id'}, } @@ -5352,3 +5354,27 @@ class ComputeReschedulingExceptionTestCase(BaseTestCase): self.assertRaises(test.TestingException, self.compute._run_instance, self.context, None, {}, None, None, None, None, self.fake_instance) + + +class ComputeInactiveImageTestCase(BaseTestCase): + def setUp(self): + super(ComputeInactiveImageTestCase, self).setUp() + + def fake_show(meh, context, id): + return {'id': id, 'min_disk': None, 'min_ram': None, + 'name': 'fake_name', + 'status': 'deleted', + 'properties': {'kernel_id': 'fake_kernel_id', + 'ramdisk_id': 'fake_ramdisk_id', + 'something_else': 'meow'}} + + fake_image.stub_out_image_service(self.stubs) + self.stubs.Set(fake_image._FakeImageService, 'show', fake_show) + self.compute_api = compute.API() + + def test_create_instance_with_deleted_image(self): + """Make sure we can't start an instance with a deleted image.""" + inst_type = instance_types.get_instance_type_by_name('m1.tiny') + self.assertRaises(exception.ImageNotActive, + self.compute_api.create, + self.context, inst_type, None)