]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
allow using image uuid for instance resource
authorLiang Chen <cbjchen@cn.ibm.com>
Fri, 7 Jun 2013 02:12:30 +0000 (10:12 +0800)
committerLiang Chen <cbjchen@cn.ibm.com>
Thu, 13 Jun 2013 03:54:54 +0000 (11:54 +0800)
make the instance resource accept image uuid for its ImageId
properties in addition to unique image name.

Fixes bug #1186410

Change-Id: Iae7705e73fe223d80e85d261a04f88f8c5207487

heat/common/exception.py
heat/engine/resources/instance.py
heat/tests/test_engine_service.py
heat/tests/test_instance.py

index 419685e137c385f910fb7023a67069c9114fab3c..17418367ba121990996d4428c7325b979158952a 100644 (file)
@@ -213,6 +213,10 @@ class ImageNotFound(OpenstackException):
     message = _("The Image (%(image_name)s) could not be found.")
 
 
+class NoUniqueImageFound(OpenstackException):
+    message = _("Multiple images were found with name (%(image_name)s).")
+
+
 class InvalidTenant(OpenstackException):
     message = _("Searching Tenant %(target)s "
                 "from Tenant %(actual)s forbidden.")
index 31ba372c0e7d5a6a1ba027f19ee4cc145968fcf8..4801c7866573e8ea134d57f6f8a68932d11c9600 100644 (file)
@@ -31,6 +31,7 @@ from heat.common import exception
 from heat.engine.resources.network_interface import NetworkInterface
 
 from heat.openstack.common import log as logging
+from heat.openstack.common import uuidutils
 
 logger = logging.getLogger(__name__)
 
@@ -293,16 +294,8 @@ class Instance(resource.Resource):
             raise exception.UserKeyPairMissing(key_name=key_name)
 
         image_name = self.properties['ImageId']
-        image_id = None
-        image_list = self.nova().images.list()
-        for o in image_list:
-            if o.name == image_name:
-                image_id = o.id
-                break
 
-        if image_id is None:
-            logger.info("Image %s was not found in glance" % image_name)
-            raise exception.ImageNotFound(image_name=image_name)
+        image_id = self._get_image_id(image_name)
 
         flavor_id = None
         flavor_list = self.nova().flavors.list()
@@ -424,6 +417,17 @@ class Instance(resource.Resource):
                     'Cannot define both SecurityGroups/SecurityGroupIds and '
                     'NetworkInterfaces properties.'}
 
+        # make sure the image exists.
+        image_identifier = self.properties['ImageId']
+        try:
+            self._get_image_id(image_identifier)
+        except exception.ImageNotFound:
+            return {'Error': 'Image %s was not found in glance' %
+                    image_identifier}
+        except exception.NoUniqueImageFound:
+            return {'Error': 'Multiple images were found with name %s' %
+                    image_identifier}
+
         return
 
     def _delete_server(self, server):
@@ -464,6 +468,31 @@ class Instance(resource.Resource):
 
         self.resource_id = None
 
+    def _get_image_id(self, image_identifier):
+        image_id = None
+        if uuidutils.is_uuid_like(image_identifier):
+            try:
+                image_id = self.nova().images.get(image_identifier).id
+            except clients.novaclient.exceptions.NotFound:
+                logger.info("Image %s was not found in glance"
+                            % image_identifier)
+                raise exception.ImageNotFound(image_name=image_identifier)
+        else:
+            image_list = self.nova().images.list()
+            image_names = dict(
+                (o.id, o.name)
+                for o in image_list if o.name == image_identifier)
+            if len(image_names) == 0:
+                logger.info("Image %s was not found in glance" %
+                            image_identifier)
+                raise exception.ImageNotFound(image_name=image_identifier)
+            elif len(image_names) > 1:
+                logger.info("Mulitple images %s were found in glance with name"
+                            % image_identifier)
+                raise exception.NoUniqueImageFound(image_name=image_identifier)
+            image_id = image_names.popitem()[0]
+        return image_id
+
 
 def resource_mapping():
     return {
index 3d51cd2615bcdd9644f3e4c9335a4f0ac4217e19..c0a913c552de0c8ce7e10f021df5ba53ec895454 100644 (file)
@@ -331,7 +331,7 @@ class stackServiceCreateUpdateDeleteTest(HeatTestCase):
         resource.properties = Properties(
             resource.properties_schema,
             {
-                'ImageId': 'foo',
+                'ImageId': 'CentOS 5.2',
                 'KeyName': 'test',
                 'InstanceType': 'm1.large'
             })
index c9aacecbca00a43aa79f8a0475d0ba53389f3f15..9fc7e6c6c3d4be756e1107949d4228fc20ad7079 100644 (file)
@@ -19,6 +19,7 @@ import mox
 
 from heat.tests.v1_1 import fakes
 from heat.engine.resources import instance as instances
+from heat.common import exception
 from heat.common import template_format
 from heat.engine import parser
 from heat.engine import resource
@@ -60,15 +61,20 @@ class instancesTest(HeatTestCase):
         self.fc = fakes.FakeClient()
         setup_dummy_db()
 
-    def _setup_test_instance(self, return_server, name):
-        stack_name = '%s_stack' % name
+    def _setup_test_stack(self, stack_name):
         t = template_format.parse(wp_template)
         template = parser.Template(t)
         params = parser.Parameters(stack_name, template, {'KeyName': 'test'})
         stack = parser.Stack(None, stack_name, template, params,
                              stack_id=uuidutils.generate_uuid())
+        return (t, stack)
 
-        t['Resources']['WebServer']['Properties']['ImageId'] = 'CentOS 5.2'
+    def _setup_test_instance(self, return_server, name, image_id=None):
+        stack_name = '%s_stack' % name
+        (t, stack) = self._setup_test_stack(stack_name)
+
+        t['Resources']['WebServer']['Properties']['ImageId'] = \
+            image_id or 'CentOS 5.2'
         t['Resources']['WebServer']['Properties']['InstanceType'] = \
             '256 MB Server'
         instance = instances.Instance('%s_name' % name,
@@ -114,6 +120,108 @@ class instancesTest(HeatTestCase):
 
         self.m.VerifyAll()
 
+    def test_instance_create_with_image_id(self):
+        return_server = self.fc.servers.list()[1]
+        instance = self._setup_test_instance(return_server,
+                                             'test_instance_create_image_id',
+                                             image_id='1')
+        self.m.StubOutWithMock(uuidutils, "is_uuid_like")
+        uuidutils.is_uuid_like('1').AndReturn(True)
+
+        self.m.ReplayAll()
+        scheduler.TaskRunner(instance.create)()
+
+        # this makes sure the auto increment worked on instance creation
+        self.assertTrue(instance.id > 0)
+
+        expected_ip = return_server.networks['public'][0]
+        self.assertEqual(instance.FnGetAtt('PublicIp'), expected_ip)
+        self.assertEqual(instance.FnGetAtt('PrivateIp'), expected_ip)
+        self.assertEqual(instance.FnGetAtt('PrivateDnsName'), expected_ip)
+        self.assertEqual(instance.FnGetAtt('PrivateDnsName'), expected_ip)
+
+        self.m.VerifyAll()
+
+    def test_instance_create_image_name_err(self):
+        stack_name = 'test_instance_create_image_name_err_stack'
+        (t, stack) = self._setup_test_stack(stack_name)
+
+        # create an instance with non exist image name
+        t['Resources']['WebServer']['Properties']['ImageId'] = 'Slackware'
+        instance = instances.Instance('instance_create_image_err',
+                                      t['Resources']['WebServer'], stack)
+
+        self.m.StubOutWithMock(instance, 'nova')
+        instance.nova().MultipleTimes().AndReturn(self.fc)
+        self.m.ReplayAll()
+
+        self.assertRaises(exception.ImageNotFound, instance.handle_create)
+
+        self.m.VerifyAll()
+
+    def test_instance_create_duplicate_image_name_err(self):
+        stack_name = 'test_instance_create_image_name_err_stack'
+        (t, stack) = self._setup_test_stack(stack_name)
+
+        # create an instance with a non unique image name
+        t['Resources']['WebServer']['Properties']['ImageId'] = 'CentOS 5.2'
+        instance = instances.Instance('instance_create_image_err',
+                                      t['Resources']['WebServer'], stack)
+
+        self.m.StubOutWithMock(instance, 'nova')
+        instance.nova().MultipleTimes().AndReturn(self.fc)
+        self.m.StubOutWithMock(self.fc.client, "get_images_detail")
+        self.fc.client.get_images_detail().AndReturn((
+            200, {'images': [{'id': 1, 'name': 'CentOS 5.2'},
+                             {'id': 4, 'name': 'CentOS 5.2'}]}))
+        self.m.ReplayAll()
+
+        self.assertRaises(exception.NoUniqueImageFound, instance.handle_create)
+
+        self.m.VerifyAll()
+
+    def test_instance_create_image_id_err(self):
+        stack_name = 'test_instance_create_image_id_err_stack'
+        (t, stack) = self._setup_test_stack(stack_name)
+
+        # create an instance with non exist image Id
+        t['Resources']['WebServer']['Properties']['ImageId'] = '1'
+        instance = instances.Instance('instance_create_image_err',
+                                      t['Resources']['WebServer'], stack)
+
+        self.m.StubOutWithMock(instance, 'nova')
+        instance.nova().MultipleTimes().AndReturn(self.fc)
+        self.m.StubOutWithMock(uuidutils, "is_uuid_like")
+        uuidutils.is_uuid_like('1').AndReturn(True)
+        self.m.StubOutWithMock(self.fc.client, "get_images_1")
+        self.fc.client.get_images_1().AndRaise(
+            instances.clients.novaclient.exceptions.NotFound(404))
+        self.m.ReplayAll()
+
+        self.assertRaises(exception.ImageNotFound, instance.handle_create)
+
+        self.m.VerifyAll()
+
+    def test_instance_validate(self):
+        stack_name = 'test_instance_validate_stack'
+        (t, stack) = self._setup_test_stack(stack_name)
+
+        # create an instance with non exist image Id
+        t['Resources']['WebServer']['Properties']['ImageId'] = '1'
+        instance = instances.Instance('instance_create_image_err',
+                                      t['Resources']['WebServer'], stack)
+
+        self.m.StubOutWithMock(instance, 'nova')
+        instance.nova().MultipleTimes().AndReturn(self.fc)
+
+        self.m.StubOutWithMock(uuidutils, "is_uuid_like")
+        uuidutils.is_uuid_like('1').AndReturn(True)
+        self.m.ReplayAll()
+
+        self.assertEqual(instance.validate(), None)
+
+        self.m.VerifyAll()
+
     def test_instance_create_delete(self):
         return_server = self.fc.servers.list()[1]
         instance = self._create_test_instance(return_server,