]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Refactor InstanceGroup to use a nested stack
authorChristopher Armstrong <chris.armstrong@rackspace.com>
Mon, 22 Jul 2013 21:35:13 +0000 (16:35 -0500)
committerChristopher Armstrong <chris.armstrong@rackspace.com>
Tue, 6 Aug 2013 22:06:12 +0000 (22:06 +0000)
Much of the code of InstanceGroup was able to be deleted in
preference for using the native functionality of StackResource.
InstanceGroup now generates a template and updates the nested
stack when it needs to be created or resized.

blueprint instance-group-nested-stack

Fixes bug 1189278

Change-Id: Ic08a55cad1ac34d69080c0ef2dae4877f1fefd29

heat/engine/resources/autoscaling.py
heat/engine/stack_resource.py
heat/tests/test_autoscaling.py
heat/tests/test_instance_group.py
heat/tests/test_stack_resource.py

index 5a8c48f07a8bd9c648d7007734b6c71adc91bbfd..54b15e05087b05dc43f7aaeac4d730e17caba244 100644 (file)
 from heat.common import exception
 from heat.engine import resource
 from heat.engine import signal_responder
-from heat.engine import scheduler
 
 from heat.openstack.common import log as logging
 from heat.openstack.common import timeutils
 from heat.engine.properties import Properties
+from heat.engine import stack_resource
 
 logger = logging.getLogger(__name__)
 
@@ -55,7 +55,7 @@ class CooldownMixin(object):
         self.metadata = metadata
 
 
-class InstanceGroup(resource.Resource):
+class InstanceGroup(stack_resource.StackResource):
     tags_schema = {'Key': {'Type': 'String',
                            'Required': True},
                    'Value': {'Type': 'String',
@@ -79,22 +79,46 @@ class InstanceGroup(resource.Resource):
                          "(Heat extension)")
     }
 
-    def handle_create(self):
-        return self.resize(int(self.properties['Size']), raise_on_error=True)
+    def get_instance_names(self):
+        """Get a list of resource names of the instances in this InstanceGroup.
+
+        Deleted resources will be ignored.
+        """
+        return sorted(x.name for x in self.get_instances())
 
-    def check_create_complete(self, creator):
-        if creator is None:
-            return True
+    def get_instances(self):
+        """Get a set of all the instance resources managed by this group."""
+        return [resource for resource in self.nested()
+                if resource.state[0] != resource.DELETE]
+
+    def handle_create(self):
+        """Create a nested stack and add the initial resources to it."""
+        num_instances = int(self.properties['Size'])
+        initial_template = self._create_template(num_instances)
+        return self.create_with_template(initial_template, {})
 
-        return creator.step()
+    def check_create_complete(self, task):
+        """
+        When stack creation is done, update the load balancer.
 
-    def _wait_for_activation(self, creator):
-        if creator is not None:
-            creator.run_to_completion()
+        If any instances failed to be created, delete them.
+        """
+        try:
+            done = super(InstanceGroup, self).check_create_complete(task)
+        except exception.Error as exc:
+            for resource in self.nested():
+                if resource.state == ('CREATE', 'FAILED'):
+                    resource.destroy()
+            raise
+        if done and len(self.get_instances()):
+            self._lb_reload()
+        return done
 
     def handle_update(self, json_snippet, tmpl_diff, prop_diff):
-        # If Properties has changed, update self.properties, so we
-        # get the new values during any subsequent adjustment
+        """
+        If Properties has changed, update self.properties, so we
+        get the new values during any subsequent adjustment.
+        """
         if prop_diff:
             self.properties = Properties(self.properties_schema,
                                          json_snippet.get('Properties', {}),
@@ -104,39 +128,9 @@ class InstanceGroup(resource.Resource):
             # Get the current capacity, we may need to adjust if
             # Size has changed
             if 'Size' in prop_diff:
-                inst_list = []
-                if self.resource_id is not None:
-                    inst_list = sorted(self.resource_id.split(','))
-
+                inst_list = self.get_instances()
                 if len(inst_list) != int(self.properties['Size']):
-                    creator = self.resize(int(self.properties['Size']),
-                                          raise_on_error=True)
-                    self._wait_for_activation(creator)
-
-    def _make_instance(self, name):
-        # We look up and subclass the class for AWS::EC2::Instance instead of
-        # just importing Instance, so that if someone overrides that resource
-        # we'll use the custom one.
-        Instance = resource.get_class('AWS::EC2::Instance',
-                                      resource_name=name,
-                                      environment=self.stack.env)
-
-        class GroupedInstance(Instance):
-            '''
-            Subclass Instance to suppress event transitions, since the
-            scaling-group instances are not "real" resources, ie defined in the
-            template, which causes problems for event handling since we can't
-            look up the resources via parser.Stack
-            '''
-            def state_set(self, action, status, reason="state changed"):
-                self._store_or_update(action, status, reason)
-
-        conf = self.properties['LaunchConfigurationName']
-        instance_definition = self.stack.t['Resources'][conf]
-
-        # honour the Tags property in the InstanceGroup and AutoScalingGroup
-        instance_definition['Properties']['Tags'] = self._tags()
-        return GroupedInstance(name, instance_definition, self.stack)
+                    self.resize(int(self.properties['Size']))
 
     def _tags(self):
         """
@@ -151,130 +145,47 @@ class InstanceGroup(resource.Resource):
         return tags + [{'Key': 'metering.groupname',
                         'Value': self.FnGetRefId()}]
 
-    def _instances(self):
-        '''
-        Convert the stored instance list into a list of GroupedInstance objects
-        '''
-        gi_list = []
-        if self.resource_id is not None:
-            inst_list = self.resource_id.split(',')
-            for i in inst_list:
-                gi_list.append(self._make_instance(i))
-        return gi_list
-
     def handle_delete(self):
-        for inst in self._instances():
-            logger.debug('handle_delete %s' % inst.name)
-            inst.destroy()
-
-    def handle_suspend(self):
-        cookie_list = []
-        for inst in self._instances():
-            logger.debug('handle_suspend %s' % inst.name)
-            inst_cookie = inst.handle_suspend()
-            cookie_list.append((inst, inst_cookie))
-        return cookie_list
-
-    def check_suspend_complete(self, cookie_list):
-        for inst, inst_cookie in cookie_list:
-            if not inst.check_suspend_complete(inst_cookie):
-                return False
-        return True
-
-    def handle_resume(self):
-        cookie_list = []
-        for inst in self._instances():
-            logger.debug('handle_resume %s' % inst.name)
-            inst_cookie = inst.handle_resume()
-            cookie_list.append((inst, inst_cookie))
-        return cookie_list
-
-    def check_resume_complete(self, cookie_list):
-        for inst, inst_cookie in cookie_list:
-            if not inst.check_resume_complete(inst_cookie):
-                return False
-        return True
-
-    @scheduler.wrappertask
-    def _scale(self, instance_task, indices):
-        group = scheduler.PollingTaskGroup.from_task_with_args(instance_task,
-                                                               indices)
-        yield group()
-
-        # When all instance tasks are complete, reload the LB config
-        self._lb_reload()
+        return self.delete_nested()
 
-    def resize(self, new_capacity, raise_on_error=False):
-        inst_list = []
-        if self.resource_id is not None:
-            inst_list = sorted(self.resource_id.split(','))
+    def _create_template(self, num_instances):
+        """
+        Create a template with a number of instance definitions based on the
+        launch configuration.
+        """
+        conf_name = self.properties['LaunchConfigurationName']
+        instance_definition = self.stack.t['Resources'][conf_name].copy()
+        instance_definition['Type'] = 'AWS::EC2::Instance'
+        instance_definition['Properties']['Tags'] = self._tags()
+        resources = {}
+        for i in range(num_instances):
+            resources["%s-%d" % (self.name, i)] = instance_definition
+        return {"Resources": resources}
 
-        capacity = len(inst_list)
-        if new_capacity == capacity:
-            logger.debug('no change in capacity %d' % capacity)
-            return
-        logger.debug('adjusting capacity from %d to %d' % (capacity,
-                                                           new_capacity))
-
-        @scheduler.wrappertask
-        def create_instance(index):
-            name = '%s-%d' % (self.name, index)
-            inst = self._make_instance(name)
-
-            logger.debug('Creating %s instance %d' % (str(self), index))
-
-            try:
-                yield inst.create()
-            except exception.ResourceFailure as ex:
-                if raise_on_error:
-                    raise
-                # Handle instance creation failure locally by destroying the
-                # failed instance to avoid orphaned instances costing user
-                # extra memory
-                logger.warn('Creating %s instance %d failed %s, destroying'
-                            % (str(self), index, str(ex)))
-                inst.destroy()
-            else:
-                inst_list.append(name)
-                self.resource_id_set(','.join(inst_list))
-
-        if new_capacity > capacity:
-            # grow
-            creator = scheduler.TaskRunner(self._scale,
-                                           create_instance,
-                                           xrange(capacity, new_capacity))
-            creator.start()
-            return creator
-        else:
-            # shrink (kill largest numbered first)
-            del_list = inst_list[new_capacity:]
-            for victim in reversed(del_list):
-                inst = self._make_instance(victim)
-                inst.destroy()
-                inst_list.remove(victim)
-                # If we shrink to zero, set resource_id back to None
-                self.resource_id_set(','.join(inst_list) or None)
+    def resize(self, new_capacity):
+        """
+        Resize the instance group to the new capacity.
 
-            self._lb_reload()
+        When shrinking, the newest instances will be removed.
+        """
+        new_template = self._create_template(new_capacity)
+        result = self.update_with_template(new_template, {})
+        for resource in self.nested():
+            if resource.state == ('CREATE', 'FAILED'):
+                resource.destroy()
+        self._lb_reload()
+        return result
 
     def _lb_reload(self):
         '''
-        Notify the LoadBalancer to reload it's config to include
+        Notify the LoadBalancer to reload its config to include
         the changes in instances we have just made.
 
         This must be done after activation (instance in ACTIVE state),
         otherwise the instances' IP addresses may not be available.
         '''
         if self.properties['LoadBalancerNames']:
-            inst_list = []
-            if self.resource_id is not None:
-                inst_list = sorted(self.resource_id.split(','))
-            # convert the list of instance names into a list of instance id's
-            id_list = []
-            for inst_name in inst_list:
-                inst = self._make_instance(inst_name)
-                id_list.append(inst.FnGetRefId())
-
+            id_list = [inst.FnGetRefId() for inst in self.get_instances()]
             for lb in self.properties['LoadBalancerNames']:
                 self.stack[lb].json_snippet['Properties']['Instances'] = \
                     id_list
@@ -291,14 +202,10 @@ class InstanceGroup(resource.Resource):
         ip addresses.
         '''
         if name == 'InstanceList':
-            if self.resource_id is None:
-                return None
-            name_list = sorted(self.resource_id.split(','))
-            inst_list = []
-            for name in name_list:
-                inst = self._make_instance(name)
-                inst_list.append(inst.FnGetAtt('PublicIp'))
-            return unicode(','.join(inst_list))
+            ips = [inst.FnGetAtt('PublicIp')
+                   for inst in self._nested.resources.values()]
+            if ips:
+                return unicode(','.join(ips))
 
 
 class AutoScalingGroup(InstanceGroup, CooldownMixin):
@@ -338,12 +245,22 @@ class AutoScalingGroup(InstanceGroup, CooldownMixin):
             num_to_create = int(self.properties['DesiredCapacity'])
         else:
             num_to_create = int(self.properties['MinSize'])
+        initial_template = self._create_template(num_to_create)
+        return self.create_with_template(initial_template, {})
 
-        return self._adjust(num_to_create)
+    def check_create_complete(self, task):
+        """Invoke the cooldown after creation succeeds."""
+        done = super(AutoScalingGroup, self).check_create_complete(task)
+        if done:
+            self._cooldown_timestamp(
+                "%s : %s" % ('ExactCapacity', len(self.get_instances())))
+        return done
 
     def handle_update(self, json_snippet, tmpl_diff, prop_diff):
-        # If Properties has changed, update self.properties, so we
-        # get the new values during any subsequent adjustment
+        """
+        If Properties has changed, update self.properties, so we get the new
+        values during any subsequent adjustment.
+        """
         if prop_diff:
             self.properties = Properties(self.properties_schema,
                                          json_snippet.get('Properties', {}),
@@ -352,11 +269,7 @@ class AutoScalingGroup(InstanceGroup, CooldownMixin):
 
             # Get the current capacity, we may need to adjust if
             # MinSize or MaxSize has changed
-            inst_list = []
-            if self.resource_id is not None:
-                inst_list = sorted(self.resource_id.split(','))
-
-            capacity = len(inst_list)
+            capacity = len(self.get_instances())
 
             # Figure out if an adjustment is required
             new_capacity = None
@@ -367,30 +280,22 @@ class AutoScalingGroup(InstanceGroup, CooldownMixin):
                 if capacity > int(self.properties['MaxSize']):
                     new_capacity = int(self.properties['MaxSize'])
             if 'DesiredCapacity' in prop_diff:
-                    if self.properties['DesiredCapacity']:
-                        new_capacity = int(self.properties['DesiredCapacity'])
+                if self.properties['DesiredCapacity']:
+                    new_capacity = int(self.properties['DesiredCapacity'])
 
             if new_capacity is not None:
-                creator = self._adjust(new_capacity)
-                self._wait_for_activation(creator)
+                self.adjust(new_capacity, adjustment_type='ExactCapacity')
 
     def adjust(self, adjustment, adjustment_type='ChangeInCapacity'):
-        creator = self._adjust(adjustment, adjustment_type, False)
-        self._wait_for_activation(creator)
-
-    def _adjust(self, adjustment, adjustment_type='ExactCapacity',
-                raise_on_error=True):
-
+        """
+        Adjust the size of the scaling group if the cooldown permits.
+        """
         if self._cooldown_inprogress():
             logger.info("%s NOT performing scaling adjustment, cooldown %s" %
                         (self.name, self.properties['Cooldown']))
             return
 
-        inst_list = []
-        if self.resource_id is not None:
-            inst_list = sorted(self.resource_id.split(','))
-
-        capacity = len(inst_list)
+        capacity = len(self.get_instances())
         if adjustment_type == 'ChangeInCapacity':
             new_capacity = capacity + adjustment
         elif adjustment_type == 'ExactCapacity':
@@ -410,7 +315,7 @@ class AutoScalingGroup(InstanceGroup, CooldownMixin):
             logger.debug('no change in capacity %d' % capacity)
             return
 
-        result = self.resize(new_capacity, raise_on_error=raise_on_error)
+        result = self.resize(new_capacity)
 
         self._cooldown_timestamp("%s : %s" % (adjustment_type, adjustment))
 
@@ -479,8 +384,10 @@ class ScalingPolicy(signal_responder.SignalResponder, CooldownMixin):
     }
 
     def handle_update(self, json_snippet, tmpl_diff, prop_diff):
-        # If Properties has changed, update self.properties, so we
-        # get the new values during any subsequent adjustment
+        """
+        If Properties has changed, update self.properties, so we get the new
+        values during any subsequent adjustment.
+        """
         if prop_diff:
             self.properties = Properties(self.properties_schema,
                                          json_snippet.get('Properties', {}),
index e322b2401b2319d8f0ed16d16bbc7b9f5b410cfc..5c436361bc8bd0c553cde0edf9869b55ee638e9e 100644 (file)
@@ -93,6 +93,27 @@ class StackResource(resource.Resource):
 
         return done
 
+    def update_with_template(self, child_template, user_params,
+                             timeout_mins=None):
+        """Update the nested stack with the new template."""
+        template = parser.Template(child_template)
+        # Note that there is no call to self._outputs_to_attribs here.
+        # If we have a use case for updating attributes of the resource based
+        # on updated templates we should make sure it's optional because not
+        # all subclasses want that behavior, since they may offer custom
+        # attributes.
+
+        # Note we disable rollback for nested stacks, since they
+        # should be rolled back by the parent stack on failure
+        stack = parser.Stack(self.context,
+                             self.physical_resource_name(),
+                             template,
+                             environment.Environment(user_params),
+                             timeout_mins=timeout_mins,
+                             disable_rollback=True,
+                             parent_resource=self)
+        return self._nested.update(stack)
+
     def delete_nested(self):
         '''
         Delete the nested stack.
index b0ff2d0f2e698eaddbd2620f374fda8d32039eff..a3169b547d8207b9a8ab910b4378e59ea0365cdb 100644 (file)
@@ -178,7 +178,7 @@ class AutoScalingTest(HeatTestCase):
         now = timeutils.utcnow()
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
-        self.assertEqual(None, rsrc.resource_id)
+        self.assertEqual(None, rsrc.FnGetAtt("InstanceList"))
 
         rsrc.delete()
         self.m.VerifyAll()
@@ -195,21 +195,22 @@ class AutoScalingTest(HeatTestCase):
         self._stub_meta_expected(now, 'ExactCapacity : 1')
         self._stub_create(1)
         self.m.ReplayAll()
+
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         # Reduce the min size to 0, should complete without adjusting
         update_snippet = copy.deepcopy(rsrc.parsed_template())
         update_snippet['Properties']['MinSize'] = '0'
         self.assertEqual(None, rsrc.update(update_snippet))
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
-        # trigger adjustment to reduce to 0, resource_id should be None
+        # trigger adjustment to reduce to 0, there should be no more instances
         self._stub_lb_reload(0)
         self._stub_meta_expected(now, 'ChangeInCapacity : -1')
         self.m.ReplayAll()
         rsrc.adjust(-1)
-        self.assertEqual(None, rsrc.resource_id)
+        self.assertEqual([], rsrc.get_instance_names())
 
         rsrc.delete()
         self.m.VerifyAll()
@@ -226,7 +227,7 @@ class AutoScalingTest(HeatTestCase):
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
 
         self.assertEqual('WebServerGroup', rsrc.FnGetRefId())
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
         update_snippet = copy.deepcopy(rsrc.parsed_template())
         update_snippet['Properties']['LaunchConfigurationName'] = 'foo'
         self.assertRaises(resource.UpdateReplace,
@@ -246,7 +247,7 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         self.assertEqual('WebServerGroup', rsrc.FnGetRefId())
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
         self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE))
 
         self.m.VerifyAll()
@@ -277,7 +278,7 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         self.assertEqual('WebServerGroup', rsrc.FnGetRefId())
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
         self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE))
 
         self.m.VerifyAll()
@@ -292,6 +293,8 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
 
         rsrc.state_set(rsrc.SUSPEND, rsrc.COMPLETE)
+        for i in rsrc.nested().resources.values():
+            i.state_set(rsrc.SUSPEND, rsrc.COMPLETE)
 
         scheduler.TaskRunner(rsrc.resume)()
         self.assertEqual(rsrc.state, (rsrc.RESUME, rsrc.COMPLETE))
@@ -312,7 +315,8 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         self.assertEqual('WebServerGroup', rsrc.FnGetRefId())
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
         self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE))
 
         self.m.VerifyAll()
@@ -347,7 +351,8 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         self.assertEqual('WebServerGroup', rsrc.FnGetRefId())
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
         self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE))
 
         self.m.VerifyAll()
@@ -364,6 +369,8 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
 
         rsrc.state_set(rsrc.SUSPEND, rsrc.COMPLETE)
+        for i in rsrc.nested().resources.values():
+            i.state_set(rsrc.SUSPEND, rsrc.COMPLETE)
 
         scheduler.TaskRunner(rsrc.resume)()
         self.assertEqual(rsrc.state, (rsrc.RESUME, rsrc.COMPLETE))
@@ -382,7 +389,7 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         self.assertEqual('WebServerGroup', rsrc.FnGetRefId())
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
         self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE))
 
         self.m.VerifyAll()
@@ -397,7 +404,8 @@ class AutoScalingTest(HeatTestCase):
         sus_task = scheduler.TaskRunner(rsrc.suspend)
         self.assertRaises(exception.ResourceFailure, sus_task, ())
         self.assertEqual(rsrc.state, (rsrc.SUSPEND, rsrc.FAILED))
-        self.assertEqual(rsrc.status_reason, 'Exception: oops')
+        self.assertEqual(rsrc.status_reason,
+                         'Error: Resource suspend failed: Exception: oops')
 
         rsrc.delete()
         self.m.VerifyAll()
@@ -413,7 +421,7 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         self.assertEqual('WebServerGroup', rsrc.FnGetRefId())
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
         self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE))
 
         self.m.VerifyAll()
@@ -426,11 +434,14 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
 
         rsrc.state_set(rsrc.SUSPEND, rsrc.COMPLETE)
+        for i in rsrc.nested().resources.values():
+            i.state_set(rsrc.SUSPEND, rsrc.COMPLETE)
 
         sus_task = scheduler.TaskRunner(rsrc.resume)
         self.assertRaises(exception.ResourceFailure, sus_task, ())
         self.assertEqual(rsrc.state, (rsrc.RESUME, rsrc.FAILED))
-        self.assertEqual(rsrc.status_reason, 'Exception: oops')
+        self.assertEqual(rsrc.status_reason,
+                         'Error: Resource resume failed: Exception: oops')
 
         rsrc.delete()
         self.m.VerifyAll()
@@ -452,7 +463,7 @@ class AutoScalingTest(HeatTestCase):
                           scheduler.TaskRunner(rsrc.create))
         self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
 
-        self.assertEqual(None, rsrc.resource_id)
+        self.assertEqual([], rsrc.get_instance_names())
 
         self.m.VerifyAll()
 
@@ -469,13 +480,13 @@ class AutoScalingTest(HeatTestCase):
         self._stub_create(1)
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         # Reduce the max size to 2, should complete without adjusting
         update_snippet = copy.deepcopy(rsrc.parsed_template())
         update_snippet['Properties']['MaxSize'] = '2'
         self.assertEqual(None, rsrc.update(update_snippet))
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         self.assertEqual('2', rsrc.properties['MaxSize'])
 
@@ -495,7 +506,7 @@ class AutoScalingTest(HeatTestCase):
         self._stub_create(1)
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         # Increase min size to 2, should trigger an ExactCapacity adjust
         self._stub_lb_reload(2)
@@ -506,8 +517,8 @@ class AutoScalingTest(HeatTestCase):
         update_snippet = copy.deepcopy(rsrc.parsed_template())
         update_snippet['Properties']['MinSize'] = '2'
         self.assertEqual(None, rsrc.update(update_snippet))
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
         self.assertEqual('2', rsrc.properties['MinSize'])
 
         rsrc.delete()
@@ -526,7 +537,7 @@ class AutoScalingTest(HeatTestCase):
         self._stub_create(1)
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         # Increase min size to 2 via DesiredCapacity, should adjust
         self._stub_lb_reload(2)
@@ -537,8 +548,8 @@ class AutoScalingTest(HeatTestCase):
         update_snippet = copy.deepcopy(rsrc.parsed_template())
         update_snippet['Properties']['DesiredCapacity'] = '2'
         self.assertEqual(None, rsrc.update(update_snippet))
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         self.assertEqual('2', rsrc.properties['DesiredCapacity'])
 
@@ -557,16 +568,16 @@ class AutoScalingTest(HeatTestCase):
         self._stub_create(2)
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # Remove DesiredCapacity from the updated template, which should
         # have no effect, it's an optional parameter
         update_snippet = copy.deepcopy(rsrc.parsed_template())
         del(update_snippet['Properties']['DesiredCapacity'])
         self.assertEqual(None, rsrc.update(update_snippet))
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         self.assertEqual(None, rsrc.properties['DesiredCapacity'])
 
@@ -587,7 +598,7 @@ class AutoScalingTest(HeatTestCase):
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
 
         self.assertEqual('WebServerGroup', rsrc.FnGetRefId())
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
         update_snippet = copy.deepcopy(rsrc.parsed_template())
         update_snippet['Properties']['Cooldown'] = '61'
         self.assertEqual(None, rsrc.update(update_snippet))
@@ -623,7 +634,7 @@ class AutoScalingTest(HeatTestCase):
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
 
         self.assertEqual('WebServerGroup', rsrc.FnGetRefId())
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
         update_snippet = copy.deepcopy(rsrc.parsed_template())
         update_snippet['Properties']['Cooldown'] = '61'
         self.assertEqual(None, rsrc.update(update_snippet))
@@ -644,15 +655,16 @@ class AutoScalingTest(HeatTestCase):
         self._stub_create(3)
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1,WebServerGroup-2',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1',
+                          'WebServerGroup-2'],
+                         rsrc.get_instance_names())
 
         # reduce to 1
         self._stub_lb_reload(1)
         self._stub_meta_expected(now, 'ChangeInCapacity : -2')
         self.m.ReplayAll()
         rsrc.adjust(-2)
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         # raise to 3
         self._stub_lb_reload(3)
@@ -660,16 +672,17 @@ class AutoScalingTest(HeatTestCase):
         self._stub_create(2)
         self.m.ReplayAll()
         rsrc.adjust(2)
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1,WebServerGroup-2',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1',
+                          'WebServerGroup-2'],
+                         rsrc.get_instance_names())
 
         # set to 2
         self._stub_lb_reload(2)
         self._stub_meta_expected(now, 'ExactCapacity : 2')
         self.m.ReplayAll()
         rsrc.adjust(2, 'ExactCapacity')
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
         self.m.VerifyAll()
 
     def test_scaling_group_scale_up_failure(self):
@@ -683,20 +696,18 @@ class AutoScalingTest(HeatTestCase):
         self._stub_create(1)
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
         self.m.VerifyAll()
         self.m.UnsetStubs()
 
         # Scale up one 1 instance with resource failure
         self.m.StubOutWithMock(instance.Instance, 'handle_create')
         instance.Instance.handle_create().AndRaise(Exception)
-        self.m.StubOutWithMock(instance.Instance, 'destroy')
-        instance.Instance.destroy()
         self._stub_lb_reload(1, unset=False, nochange=True)
         self.m.ReplayAll()
 
         rsrc.adjust(1)
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         self.m.VerifyAll()
 
@@ -714,23 +725,23 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         stack.resources['WebServerGroup'] = rsrc
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # raise above the max
         rsrc.adjust(4)
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # lower below the min
         rsrc.adjust(-2)
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # no change
         rsrc.adjust(0)
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
         rsrc.delete()
         self.m.VerifyAll()
 
@@ -748,16 +759,16 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         stack.resources['WebServerGroup'] = rsrc
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # reduce by 50%
         self._stub_lb_reload(1)
         self._stub_meta_expected(now, 'PercentChangeInCapacity : -50')
         self.m.ReplayAll()
         rsrc.adjust(-50, 'PercentChangeInCapacity')
-        self.assertEqual('WebServerGroup-0',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'],
+                         rsrc.get_instance_names())
 
         # raise by 200%
         self._stub_lb_reload(3)
@@ -765,8 +776,9 @@ class AutoScalingTest(HeatTestCase):
         self._stub_create(2)
         self.m.ReplayAll()
         rsrc.adjust(200, 'PercentChangeInCapacity')
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1,WebServerGroup-2',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1',
+                          'WebServerGroup-2'],
+                         rsrc.get_instance_names())
 
         rsrc.delete()
 
@@ -785,16 +797,16 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         stack.resources['WebServerGroup'] = rsrc
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # reduce by 50%
         self._stub_lb_reload(1)
         self._stub_meta_expected(now, 'PercentChangeInCapacity : -50')
         self.m.ReplayAll()
         rsrc.adjust(-50, 'PercentChangeInCapacity')
-        self.assertEqual('WebServerGroup-0',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'],
+                         rsrc.get_instance_names())
 
         # Now move time on 10 seconds - Cooldown in template is 60
         # so this should not update the policy metadata, and the
@@ -819,7 +831,7 @@ class AutoScalingTest(HeatTestCase):
 
         # raise by 200%, too soon for Cooldown so there should be no change
         rsrc.adjust(200, 'PercentChangeInCapacity')
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         rsrc.delete()
 
@@ -838,16 +850,16 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         stack.resources['WebServerGroup'] = rsrc
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # reduce by 50%
         self._stub_lb_reload(1)
         self._stub_meta_expected(now, 'PercentChangeInCapacity : -50')
         self.m.ReplayAll()
         rsrc.adjust(-50, 'PercentChangeInCapacity')
-        self.assertEqual('WebServerGroup-0',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'],
+                         rsrc.get_instance_names())
 
         # Now move time on 61 seconds - Cooldown in template is 60
         # so this should update the policy metadata, and the
@@ -870,8 +882,9 @@ class AutoScalingTest(HeatTestCase):
         self._stub_meta_expected(now, 'PercentChangeInCapacity : 200')
         self.m.ReplayAll()
         rsrc.adjust(200, 'PercentChangeInCapacity')
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1,WebServerGroup-2',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1',
+                          'WebServerGroup-2'],
+                         rsrc.get_instance_names())
 
         rsrc.delete()
 
@@ -890,16 +903,16 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         stack.resources['WebServerGroup'] = rsrc
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # reduce by 50%
         self._stub_lb_reload(1)
         self._stub_meta_expected(now, 'PercentChangeInCapacity : -50')
         self.m.ReplayAll()
         rsrc.adjust(-50, 'PercentChangeInCapacity')
-        self.assertEqual('WebServerGroup-0',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'],
+                         rsrc.get_instance_names())
 
         # Don't move time, since cooldown is zero, it should work
         previous_meta = {timeutils.strtime(now):
@@ -918,8 +931,9 @@ class AutoScalingTest(HeatTestCase):
         self._stub_create(2)
         self.m.ReplayAll()
         rsrc.adjust(200, 'PercentChangeInCapacity')
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1,WebServerGroup-2',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1',
+                          'WebServerGroup-2'],
+                         rsrc.get_instance_names())
 
         rsrc.delete()
         self.m.VerifyAll()
@@ -937,7 +951,7 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         stack.resources['WebServerGroup'] = rsrc
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         # Scale up one
         self._stub_lb_reload(2)
@@ -955,8 +969,8 @@ class AutoScalingTest(HeatTestCase):
         alarm_url = up_policy.FnGetAtt('AlarmUrl')
         self.assertNotEqual(None, alarm_url)
         up_policy.signal()
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         rsrc.delete()
         self.m.VerifyAll()
@@ -975,8 +989,8 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         stack.resources['WebServerGroup'] = rsrc
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # Scale down one
         self._stub_lb_reload(1)
@@ -990,7 +1004,7 @@ class AutoScalingTest(HeatTestCase):
         down_policy = self.create_scaling_policy(t, stack,
                                                  'WebServerScaleDownPolicy')
         down_policy.signal()
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         rsrc.delete()
         self.m.VerifyAll()
@@ -1007,7 +1021,7 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         stack.resources['WebServerGroup'] = rsrc
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         # Scale up one
         self._stub_lb_reload(2)
@@ -1022,8 +1036,8 @@ class AutoScalingTest(HeatTestCase):
         up_policy = self.create_scaling_policy(t, stack,
                                                'WebServerScaleUpPolicy')
         up_policy.signal()
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # Now move time on 10 seconds - Cooldown in template is 60
         # so this should not update the policy metadata, and the
@@ -1045,8 +1059,8 @@ class AutoScalingTest(HeatTestCase):
 
         self.m.ReplayAll()
         up_policy.signal()
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         rsrc.delete()
         self.m.VerifyAll()
@@ -1063,7 +1077,7 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         stack.resources['WebServerGroup'] = rsrc
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         # Scale up one
         self._stub_lb_reload(2)
@@ -1078,8 +1092,8 @@ class AutoScalingTest(HeatTestCase):
         up_policy = self.create_scaling_policy(t, stack,
                                                'WebServerScaleUpPolicy')
         up_policy.signal()
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # Now move time on 61 seconds - Cooldown in template is 60
         # so this should trigger a scale-up
@@ -1100,8 +1114,9 @@ class AutoScalingTest(HeatTestCase):
 
         self.m.ReplayAll()
         up_policy.signal()
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1,WebServerGroup-2',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1',
+                          'WebServerGroup-2'],
+                         rsrc.get_instance_names())
 
         rsrc.delete()
         self.m.VerifyAll()
@@ -1118,7 +1133,7 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         stack.resources['WebServerGroup'] = rsrc
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         # Create the scaling policy (with Cooldown=0) and scale up one
         properties = t['Resources']['WebServerScaleUpPolicy']['Properties']
@@ -1135,8 +1150,8 @@ class AutoScalingTest(HeatTestCase):
         up_policy = self.create_scaling_policy(t, stack,
                                                'WebServerScaleUpPolicy')
         up_policy.signal()
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # Now trigger another scale-up without changing time, should work
         previous_meta = {timeutils.strtime(now): 'ChangeInCapacity : 1'}
@@ -1155,8 +1170,9 @@ class AutoScalingTest(HeatTestCase):
 
         self.m.ReplayAll()
         up_policy.signal()
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1,WebServerGroup-2',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1',
+                          'WebServerGroup-2'],
+                         rsrc.get_instance_names())
 
         rsrc.delete()
         self.m.VerifyAll()
@@ -1173,7 +1189,7 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         stack.resources['WebServerGroup'] = rsrc
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         # Create the scaling policy no Cooldown property, should behave the
         # same as when Cooldown==0
@@ -1192,8 +1208,8 @@ class AutoScalingTest(HeatTestCase):
         up_policy = self.create_scaling_policy(t, stack,
                                                'WebServerScaleUpPolicy')
         up_policy.signal()
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # Now trigger another scale-up without changing time, should work
         previous_meta = {timeutils.strtime(now): 'ChangeInCapacity : 1'}
@@ -1212,8 +1228,9 @@ class AutoScalingTest(HeatTestCase):
 
         self.m.ReplayAll()
         up_policy.signal()
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1,WebServerGroup-2',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1',
+                          'WebServerGroup-2'],
+                         rsrc.get_instance_names())
 
         rsrc.delete()
         self.m.VerifyAll()
@@ -1235,7 +1252,7 @@ class AutoScalingTest(HeatTestCase):
         self.m.ReplayAll()
         rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
         stack.resources['WebServerGroup'] = rsrc
-        self.assertEqual('WebServerGroup-0', rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0'], rsrc.get_instance_names())
 
         # Create initial scaling policy
         up_policy = self.create_scaling_policy(t, stack,
@@ -1249,8 +1266,8 @@ class AutoScalingTest(HeatTestCase):
 
         # Trigger alarm
         up_policy.signal()
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1'],
+                         rsrc.get_instance_names())
 
         # Update scaling policy
         update_snippet = copy.deepcopy(up_policy.parsed_template())
@@ -1280,9 +1297,9 @@ class AutoScalingTest(HeatTestCase):
 
         # Trigger alarm
         up_policy.signal()
-        self.assertEqual('WebServerGroup-0,WebServerGroup-1,'
-                         'WebServerGroup-2,WebServerGroup-3',
-                         rsrc.resource_id)
+        self.assertEqual(['WebServerGroup-0', 'WebServerGroup-1',
+                          'WebServerGroup-2', 'WebServerGroup-3'],
+                         rsrc.get_instance_names())
 
         rsrc.delete()
         self.m.VerifyAll()
index e387975e208863643a436366d01fb66384f20b7d..23c0cfc60b6242431cafe0a089cfad356acb81e6 100644 (file)
@@ -25,6 +25,7 @@ from heat.tests.common import HeatTestCase
 from heat.tests.utils import setup_dummy_db
 from heat.tests.utils import parse_stack
 
+
 ig_template = '''
 {
   "AWSTemplateFormatVersion" : "2010-09-09",
@@ -101,7 +102,9 @@ class InstanceGroupTest(HeatTestCase):
 
         self.assertEqual('JobServerGroup', rsrc.FnGetRefId())
         self.assertEqual('1.2.3.4', rsrc.FnGetAtt('InstanceList'))
-        self.assertEqual('JobServerGroup-0', rsrc.resource_id)
+
+        nested = rsrc.nested()
+        self.assertEqual(nested.id, rsrc.resource_id)
 
         rsrc.delete()
         self.m.VerifyAll()
@@ -163,8 +166,6 @@ class InstanceGroupTest(HeatTestCase):
         self._stub_create(2)
         self.m.ReplayAll()
         rsrc = self.create_instance_group(t, stack, 'JobServerGroup')
-        self.assertEqual('JobServerGroup-0,JobServerGroup-1',
-                         rsrc.resource_id)
 
         self.m.VerifyAll()
         self.m.UnsetStubs()
@@ -186,9 +187,6 @@ class InstanceGroupTest(HeatTestCase):
         prop_diff = {'Size': '5'}
         self.assertEqual(None, rsrc.handle_update(update_snippet, tmpl_diff,
                          prop_diff))
-        assert_str = ','.join(['JobServerGroup-%s' % x for x in range(5)])
-        self.assertEqual(assert_str,
-                         rsrc.resource_id)
         self.assertEqual('10.0.0.2,10.0.0.3,10.0.0.4,10.0.0.5,10.0.0.6',
                          rsrc.FnGetAtt('InstanceList'))
 
@@ -204,8 +202,6 @@ class InstanceGroupTest(HeatTestCase):
         self._stub_create(2)
         self.m.ReplayAll()
         rsrc = self.create_instance_group(t, stack, 'JobServerGroup')
-        self.assertEqual('JobServerGroup-0,JobServerGroup-1',
-                         rsrc.resource_id)
 
         self.m.ReplayAll()
 
@@ -226,8 +222,6 @@ class InstanceGroupTest(HeatTestCase):
         self._stub_create(2)
         self.m.ReplayAll()
         rsrc = self.create_instance_group(t, stack, 'JobServerGroup')
-        self.assertEqual('JobServerGroup-0,JobServerGroup-1',
-                         rsrc.resource_id)
 
         self.m.ReplayAll()
 
index 0961d8dbe2986bbd2b5a3e29bf45912f6e8a5924..346adbce453a98e62d9a8050ec0f637bcb928880 100644 (file)
@@ -59,6 +59,21 @@ wp_template = '''
 '''
 
 
+generic_template = '''
+{
+  "AWSTemplateFormatVersion" : "2010-09-09",
+  "Description" : "WordPress",
+  "Parameters" : {},
+  "Resources" : {
+    "WebServer": {
+      "Type": "GenericResource",
+      "Properties": {}
+    }
+  }
+}
+'''
+
+
 class MyStackResource(stack_resource.StackResource,
                       generic_rsrc.GenericResource):
     def physical_resource_name(self):
@@ -83,6 +98,8 @@ class StackResourceTest(HeatTestCase):
         setup_dummy_db()
         resource._register_class('some_magic_type',
                                  MyStackResource)
+        resource._register_class('GenericResource',
+                                 generic_rsrc.GenericResource)
         t = parser.Template({template.RESOURCES:
                              {"provider_resource": ws_res_snippet}})
         self.parent_stack = parser.Stack(dummy_context(), 'test_stack', t,
@@ -91,6 +108,7 @@ class StackResourceTest(HeatTestCase):
                                                ws_res_snippet,
                                                self.parent_stack)
         self.templ = template_format.parse(wp_template)
+        self.generic_template = template_format.parse(generic_template)
 
     @stack_delete_after
     def test_create_with_template_ok(self):
@@ -104,6 +122,27 @@ class StackResourceTest(HeatTestCase):
         self.assertEqual(self.templ, self.stack.t.t)
         self.assertEqual(self.stack.id, self.parent_resource.resource_id)
 
+    @stack_delete_after
+    def test_update_with_template_ok(self):
+        """
+        The update_with_template method updates the nested stack with the
+        given template and user parameters.
+        """
+        create_result = self.parent_resource.create_with_template(
+            self.generic_template, {})
+        while not create_result.step():
+            pass
+        self.stack = self.parent_resource.nested()
+
+        new_templ = self.generic_template.copy()
+        inst_snippet = new_templ["Resources"]["WebServer"].copy()
+        new_templ["Resources"]["WebServer2"] = inst_snippet
+        update_result = self.parent_resource.update_with_template(
+            new_templ, {})
+        self.assertEqual(self.stack.state, ('UPDATE', 'COMPLETE'))
+        self.assertEqual(set(self.stack.resources.keys()),
+                         set(["WebServer", "WebServer2"]))
+
     @stack_delete_after
     def test_load_nested_ok(self):
         self.parent_resource.create_with_template(self.templ,