summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Still <mikal@stillhq.com>2012-09-23 20:35:20 +1000
committerMichael Still <mikal@stillhq.com>2012-09-26 16:25:02 +1000
commit4be437da11d41fe7c5495971f3d5408cbfd30bfe (patch)
treefe58452dac5169e4a32b8709cd671c90a8560c51
parentc367fa5e4a5e4712bde9fc319ae6c2f4f2add606 (diff)
downloadnova-4be437da11d41fe7c5495971f3d5408cbfd30bfe.tar.gz
nova-4be437da11d41fe7c5495971f3d5408cbfd30bfe.tar.xz
nova-4be437da11d41fe7c5495971f3d5408cbfd30bfe.zip
Check that an image is active before spawning instances.
Resolves bug/1054163. Change-Id: I4095fcac300a161e6da93bdc14eb43257d4f1aee
-rw-r--r--nova/compute/api.py2
-rw-r--r--nova/exception.py4
-rw-r--r--nova/tests/api/ec2/test_cinder_cloud.py3
-rw-r--r--nova/tests/api/ec2/test_cloud.py23
-rw-r--r--nova/tests/compute/test_compute.py26
5 files changed, 51 insertions, 7 deletions
diff --git a/nova/compute/api.py b/nova/compute/api.py
index c1b5ac379..b54870e8d 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 28fb1ee0a..0a088cb8c 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 738e13b37..389f71b7b 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 979409077..3d7b29fc4 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 51a387d3b..dfec505c6 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'}}
@@ -2663,6 +2664,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'},
}
@@ -5364,3 +5366,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)