Merge tag '2015.1.0'

This is a null-merge of the 2015.1.0 release tag back into the master
branch so that the 2015.1.0 tag will appear in the git commit history of
the master branch. It contains no actual changes to the master branch,
regardless of how our code review system's UI represents it. Please
ask in #openstack-infra if you have any questions, and otherwise try
to merge this as quickly as possible to avoid later conflicts on the
master branch.

Change-Id: I2e88cb9f04864c901a67340620949eb7c54d5543
diff --git a/common/clients.py b/common/clients.py
index 1ba3a21..c7a7f60 100644
--- a/common/clients.py
+++ b/common/clients.py
@@ -12,6 +12,7 @@
 
 import os
 
+import ceilometerclient.client
 import cinderclient.client
 import heatclient.client
 import keystoneclient.exceptions
@@ -30,6 +31,7 @@
     CINDERCLIENT_VERSION = '1'
     HEATCLIENT_VERSION = '1'
     NOVACLIENT_VERSION = '2'
+    CEILOMETER_VERSION = '2'
 
     def __init__(self, conf):
         self.conf = conf
@@ -39,6 +41,7 @@
         self.network_client = self._get_network_client()
         self.volume_client = self._get_volume_client()
         self.object_client = self._get_object_client()
+        self.metering_client = self._get_metering_client()
 
     def _get_orchestration_client(self):
         region = self.conf.region
@@ -136,3 +139,31 @@
             'insecure': dscv,
         }
         return swiftclient.client.Connection(**args)
+
+    def _get_metering_client(self):
+        dscv = self.conf.disable_ssl_certificate_validation
+
+        keystone = self._get_identity_client()
+        try:
+            endpoint = keystone.service_catalog.url_for(
+                attr='region',
+                filter_value=self.conf.region,
+                service_type='metering',
+                endpoint_type='publicURL')
+
+        except keystoneclient.exceptions.EndpointNotFound:
+            return None
+        else:
+            args = {
+                'username': self.conf.username,
+                'password': self.conf.password,
+                'tenant_name': self.conf.tenant_name,
+                'auth_url': self.conf.auth_url,
+                'insecure': dscv,
+                'region_name': self.conf.region,
+                'endpoint_type': 'publicURL',
+                'service_type': 'metering',
+            }
+
+            return ceilometerclient.client.Client(self.CEILOMETER_VERSION,
+                                                  endpoint, **args)
diff --git a/common/config.py b/common/config.py
index 5ced77b..4f6ea3d 100644
--- a/common/config.py
+++ b/common/config.py
@@ -62,10 +62,10 @@
                default=1200,
                help="Timeout in seconds to wait for a stack to build."),
     cfg.StrOpt('network_for_ssh',
-               default='private',
+               default='heat-net',
                help="Network used for SSH connections."),
     cfg.StrOpt('fixed_network_name',
-               default='private',
+               default='heat-net',
                help="Visible fixed network name "),
     cfg.StrOpt('floating_network_name',
                default='public',
@@ -77,7 +77,7 @@
                     "resource type Heat::InstallConfigAgent. Needs to "
                     "be appropriate for the image_ref."),
     cfg.StrOpt('fixed_subnet_name',
-               default='private-subnet',
+               default='heat-subnet',
                help="Visible fixed sub-network name "),
     cfg.IntOpt('ssh_timeout',
                default=300,
diff --git a/common/remote_client.py b/common/remote_client.py
index f0405b8..c8c4f95 100644
--- a/common/remote_client.py
+++ b/common/remote_client.py
@@ -10,7 +10,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import cStringIO
 import re
 import select
 import socket
@@ -34,7 +33,7 @@
         self.password = password
         if isinstance(pkey, six.string_types):
             pkey = paramiko.RSAKey.from_private_key(
-                cStringIO.StringIO(str(pkey)))
+                six.moves.cStringIO(str(pkey)))
         self.pkey = pkey
         self.look_for_keys = look_for_keys
         self.key_filename = key_filename
diff --git a/common/test.py b/common/test.py
index b88c72b..acfe1ad 100644
--- a/common/test.py
+++ b/common/test.py
@@ -15,13 +15,13 @@
 import re
 import subprocess
 import time
-import urllib
 
 import fixtures
 from heatclient import exc as heat_exceptions
 from oslo_log import log as logging
 from oslo_utils import timeutils
 import six
+from six.moves import urllib
 import testscenarios
 import testtools
 
@@ -87,7 +87,9 @@
         self.network_client = self.manager.network_client
         self.volume_client = self.manager.volume_client
         self.object_client = self.manager.object_client
+        self.metering_client = self.manager.metering_client
         self.useFixture(fixtures.FakeLogger(format=_LOG_FORMAT))
+        self.updated_time = {}
 
     def get_remote_client(self, server_or_ip, username, private_key=None):
         if isinstance(server_or_ip, six.string_types):
@@ -111,7 +113,7 @@
     def check_connectivity(self, check_ip):
         def try_connect(ip):
             try:
-                urllib.urlopen('http://%s/' % ip)
+                urllib.request.urlopen('http://%s/' % ip)
                 return True
             except IOError:
                 return False
@@ -191,6 +193,15 @@
         return call_until_true(
             self.conf.build_timeout, 1, ping)
 
+    def _wait_for_all_resource_status(self, stack_identifier,
+                                      status, failure_pattern='^.*_FAILED$',
+                                      success_on_not_found=False):
+        for res in self.client.resources.list(stack_identifier):
+            self._wait_for_resource_status(
+                stack_identifier, res.resource_name,
+                status, failure_pattern=failure_pattern,
+                success_on_not_found=success_on_not_found)
+
     def _wait_for_resource_status(self, stack_identifier, resource_name,
                                   status, failure_pattern='^.*_FAILED$',
                                   success_on_not_found=False):
@@ -213,7 +224,10 @@
             else:
                 if res.resource_status == status:
                     return
-                if fail_regexp.search(res.resource_status):
+                wait_for_action = status.split('_')[0]
+                resource_action = res.resource_status.split('_')[0]
+                if (resource_action == wait_for_action and
+                        fail_regexp.search(res.resource_status)):
                     raise exceptions.StackResourceBuildErrorException(
                         resource_name=res.resource_name,
                         stack_identifier=stack_identifier,
@@ -226,6 +240,36 @@
                    (resource_name, status, build_timeout))
         raise exceptions.TimeoutException(message)
 
+    def _verify_status(self, stack, stack_identifier, status, fail_regexp):
+        if stack.stack_status == status:
+            # Handle UPDATE_COMPLETE case: Make sure we don't
+            # wait for a stale UPDATE_COMPLETE status.
+            if status == 'UPDATE_COMPLETE':
+                if self.updated_time.get(
+                        stack_identifier) != stack.updated_time:
+                    self.updated_time[stack_identifier] = stack.updated_time
+                    return True
+            else:
+                return True
+
+        wait_for_action = status.split('_')[0]
+        if (stack.action == wait_for_action and
+                fail_regexp.search(stack.stack_status)):
+            # Handle UPDATE_FAILED case.
+            if status == 'UPDATE_FAILED':
+                if self.updated_time.get(
+                        stack_identifier) != stack.updated_time:
+                    self.updated_time[stack_identifier] = stack.updated_time
+                    raise exceptions.StackBuildErrorException(
+                        stack_identifier=stack_identifier,
+                        stack_status=stack.stack_status,
+                        stack_status_reason=stack.stack_status_reason)
+            else:
+                raise exceptions.StackBuildErrorException(
+                    stack_identifier=stack_identifier,
+                    stack_status=stack.stack_status,
+                    stack_status_reason=stack.stack_status_reason)
+
     def _wait_for_stack_status(self, stack_identifier, status,
                                failure_pattern='^.*_FAILED$',
                                success_on_not_found=False):
@@ -251,13 +295,10 @@
                 # ignore this, as the resource may not have
                 # been created yet
             else:
-                if stack.stack_status == status:
+                if self._verify_status(stack, stack_identifier, status,
+                                       fail_regexp):
                     return
-                if fail_regexp.search(stack.stack_status):
-                    raise exceptions.StackBuildErrorException(
-                        stack_identifier=stack_identifier,
-                        stack_status=stack.stack_status,
-                        stack_status_reason=stack.stack_status_reason)
+
             time.sleep(build_interval)
 
         message = ('Stack %s failed to reach %s status within '
@@ -275,22 +316,48 @@
             success_on_not_found=True)
 
     def update_stack(self, stack_identifier, template, environment=None,
-                     files=None, parameters=None,
-                     expected_status='UPDATE_COMPLETE'):
+                     files=None, parameters=None, tags=None,
+                     expected_status='UPDATE_COMPLETE',
+                     disable_rollback=True):
         env = environment or {}
         env_files = files or {}
         parameters = parameters or {}
         stack_name = stack_identifier.split('/')[0]
-        self.client.stacks.update(
-            stack_id=stack_identifier,
-            stack_name=stack_name,
-            template=template,
-            files=env_files,
-            disable_rollback=True,
-            parameters=parameters,
-            environment=env
-        )
-        self._wait_for_stack_status(stack_identifier, expected_status)
+
+        build_timeout = self.conf.build_timeout
+        build_interval = self.conf.build_interval
+        start = timeutils.utcnow()
+        while timeutils.delta_seconds(start,
+                                      timeutils.utcnow()) < build_timeout:
+            try:
+                self.client.stacks.update(
+                    stack_id=stack_identifier,
+                    stack_name=stack_name,
+                    template=template,
+                    files=env_files,
+                    disable_rollback=disable_rollback,
+                    parameters=parameters,
+                    environment=env,
+                    tags=tags
+                )
+            except heat_exceptions.HTTPConflict as ex:
+                # FIXME(sirushtim): Wait a little for the stack lock to be
+                # released and hopefully, the stack should be updatable again.
+                if ex.error['error']['type'] != 'ActionInProgress':
+                    raise ex
+
+                time.sleep(build_interval)
+            else:
+                break
+
+        kwargs = {'stack_identifier': stack_identifier,
+                  'status': expected_status}
+        if expected_status in ['ROLLBACK_COMPLETE']:
+            # To trigger rollback you would intentionally fail the stack
+            # Hence check for rollback failures
+            kwargs['failure_pattern'] = '^ROLLBACK_FAILED$'
+
+        self._wait_for_stack_status(**kwargs)
 
     def assert_resource_is_a_stack(self, stack_identifier, res_name,
                                    wait=False):
@@ -333,8 +400,9 @@
         return dict((r.resource_name, r.resource_type) for r in resources)
 
     def stack_create(self, stack_name=None, template=None, files=None,
-                     parameters=None, environment=None,
-                     expected_status='CREATE_COMPLETE'):
+                     parameters=None, environment=None, tags=None,
+                     expected_status='CREATE_COMPLETE',
+                     disable_rollback=True, enable_cleanup=True):
         name = stack_name or self._stack_rand_name()
         templ = template or self.template
         templ_files = files or {}
@@ -344,16 +412,24 @@
             stack_name=name,
             template=templ,
             files=templ_files,
-            disable_rollback=True,
+            disable_rollback=disable_rollback,
             parameters=params,
-            environment=env
+            environment=env,
+            tags=tags
         )
-        self.addCleanup(self.client.stacks.delete, name)
+        if expected_status not in ['ROLLBACK_COMPLETE'] and enable_cleanup:
+            self.addCleanup(self.client.stacks.delete, name)
 
         stack = self.client.stacks.get(name)
         stack_identifier = '%s/%s' % (name, stack.id)
+        kwargs = {'stack_identifier': stack_identifier,
+                  'status': expected_status}
         if expected_status:
-            self._wait_for_stack_status(stack_identifier, expected_status)
+            if expected_status in ['ROLLBACK_COMPLETE']:
+                # To trigger rollback you would intentionally fail the stack
+                # Hence check for rollback failures
+                kwargs['failure_pattern'] = '^ROLLBACK_FAILED$'
+            self._wait_for_stack_status(**kwargs)
         return stack_identifier
 
     def stack_adopt(self, stack_name=None, files=None,
@@ -390,11 +466,19 @@
     def stack_suspend(self, stack_identifier):
         stack_name = stack_identifier.split('/')[0]
         self.client.actions.suspend(stack_name)
+
+        # improve debugging by first checking the resource's state.
+        self._wait_for_all_resource_status(stack_identifier,
+                                           'SUSPEND_COMPLETE')
         self._wait_for_stack_status(stack_identifier, 'SUSPEND_COMPLETE')
 
     def stack_resume(self, stack_identifier):
         stack_name = stack_identifier.split('/')[0]
         self.client.actions.resume(stack_name)
+
+        # improve debugging by first checking the resource's state.
+        self._wait_for_all_resource_status(stack_identifier,
+                                           'RESUME_COMPLETE')
         self._wait_for_stack_status(stack_identifier, 'RESUME_COMPLETE')
 
     def wait_for_event_with_reason(self, stack_identifier, reason,
diff --git a/common/test_resources/test_resource.py b/common/test_resources/test_resource.py
new file mode 100644
index 0000000..88f1745
--- /dev/null
+++ b/common/test_resources/test_resource.py
@@ -0,0 +1,134 @@
+#
+#    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.
+
+import eventlet
+
+from heat.common.i18n import _
+from heat.engine import attributes
+from heat.engine import properties
+from heat.engine import resource
+from heat.engine import support
+from oslo_log import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+class TestResource(resource.Resource):
+    '''
+    A resource which stores the string value that was provided.
+
+    This resource is to be used only for testing.
+    It has control knobs such as 'update_replace', 'fail', 'wait_secs'
+
+    '''
+
+    support_status = support.SupportStatus(version='2014.1')
+
+    PROPERTIES = (
+        VALUE, UPDATE_REPLACE, FAIL, WAIT_SECS
+    ) = (
+        'value', 'update_replace', 'fail', 'wait_secs'
+    )
+
+    ATTRIBUTES = (
+        OUTPUT,
+    ) = (
+        'output',
+    )
+
+    properties_schema = {
+        VALUE: properties.Schema(
+            properties.Schema.STRING,
+            _('The input string to be stored.'),
+            default='test_string',
+            update_allowed=True
+        ),
+        FAIL: properties.Schema(
+            properties.Schema.BOOLEAN,
+            _('Value which can be set to fail the resource operation '
+              'to test failure scenarios.'),
+            update_allowed=True,
+            default=False
+        ),
+        UPDATE_REPLACE: properties.Schema(
+            properties.Schema.BOOLEAN,
+            _('Value which can be set to trigger update replace for '
+              'the particular resource'),
+            update_allowed=True,
+            default=False
+        ),
+        WAIT_SECS: properties.Schema(
+            properties.Schema.NUMBER,
+            _('Value which can be set for resource to wait after an action '
+              'is performed.'),
+            update_allowed=True,
+            default=0,
+        ),
+    }
+
+    attributes_schema = {
+        OUTPUT: attributes.Schema(
+            _('The string that was stored. This value is '
+              'also available by referencing the resource.'),
+            cache_mode=attributes.Schema.CACHE_NONE
+        ),
+    }
+
+    def handle_create(self):
+        value = self.properties.get(self.VALUE)
+        fail_prop = self.properties.get(self.FAIL)
+        sleep_secs = self.properties.get(self.WAIT_SECS)
+
+        self.data_set('value', value, redact=False)
+        self.resource_id_set(self.physical_resource_name())
+
+        # sleep for specified time
+        if sleep_secs:
+            LOG.debug("Resource %s sleeping for %s seconds",
+                      self.name, sleep_secs)
+            eventlet.sleep(sleep_secs)
+
+        # emulate failure
+        if fail_prop:
+            raise Exception("Test Resource failed %s", self.name)
+
+    def handle_update(self, json_snippet=None, tmpl_diff=None, prop_diff=None):
+        value = prop_diff.get(self.VALUE)
+        new_prop = json_snippet._properties
+        if value:
+            update_replace = new_prop.get(self.UPDATE_REPLACE, False)
+            if update_replace:
+                raise resource.UpdateReplace(self.name)
+            else:
+                fail_prop = new_prop.get(self.FAIL, False)
+                sleep_secs = new_prop.get(self.WAIT_SECS, 0)
+                # emulate failure
+                if fail_prop:
+                    raise Exception("Test Resource failed %s", self.name)
+                # update in place
+                self.data_set('value', value, redact=False)
+
+                if sleep_secs:
+                    LOG.debug("Update of Resource %s sleeping for %s seconds",
+                              self.name, sleep_secs)
+                    eventlet.sleep(sleep_secs)
+
+    def _resolve_attribute(self, name):
+        if name == self.OUTPUT:
+            return self.data().get('value')
+
+
+def resource_mapping():
+    return {
+        'OS::Heat::TestResource': TestResource,
+    }
diff --git a/functional/test_autoscaling.py b/functional/test_autoscaling.py
index 932245d..301981f 100644
--- a/functional/test_autoscaling.py
+++ b/functional/test_autoscaling.py
@@ -183,7 +183,6 @@
                                'flavor': self.conf.instance_type}}
         self.update_stack(stack_identifier, self.template,
                           environment=env2, files=files)
-        self._wait_for_stack_status(stack_identifier, 'UPDATE_COMPLETE')
         stack = self.client.stacks.get(stack_identifier)
         self.assert_instance_count(stack, 5)
 
@@ -318,21 +317,11 @@
         nested_ident = self.assert_resource_is_a_stack(stack_identifier,
                                                        'JobServerGroup')
 
-        self.client.actions.suspend(stack_id=stack_identifier)
-        self._wait_for_resource_status(
-            stack_identifier, 'JobServerGroup', 'SUSPEND_COMPLETE')
-        for res in self.client.resources.list(nested_ident):
-            self._wait_for_resource_status(nested_ident,
-                                           res.resource_name,
-                                           'SUSPEND_COMPLETE')
+        self.stack_suspend(stack_identifier)
+        self._wait_for_all_resource_status(nested_ident, 'SUSPEND_COMPLETE')
 
-        self.client.actions.resume(stack_id=stack_identifier)
-        self._wait_for_resource_status(
-            stack_identifier, 'JobServerGroup', 'RESUME_COMPLETE')
-        for res in self.client.resources.list(nested_ident):
-            self._wait_for_resource_status(nested_ident,
-                                           res.resource_name,
-                                           'RESUME_COMPLETE')
+        self.stack_resume(stack_identifier)
+        self._wait_for_all_resource_status(nested_ident, 'RESUME_COMPLETE')
 
 
 class AutoscalingGroupUpdatePolicyTest(AutoscalingGroupTest):
@@ -384,7 +373,6 @@
         # test stack update
         self.update_stack(stack_identifier, updt_template,
                           environment=env, files=files)
-        self._wait_for_stack_status(stack_identifier, 'UPDATE_COMPLETE')
         updt_stack = self.client.stacks.get(stack_identifier)
 
         # test that the launch configuration is replaced
diff --git a/functional/test_aws_stack.py b/functional/test_aws_stack.py
index 5aabe95..172bc24 100644
--- a/functional/test_aws_stack.py
+++ b/functional/test_aws_stack.py
@@ -13,9 +13,9 @@
 import hashlib
 import json
 import random
-import urlparse
 
 from oslo_log import log as logging
+from six.moves.urllib import parse
 from swiftclient import utils as swiftclient_utils
 import yaml
 
@@ -103,7 +103,7 @@
         timeout = self.conf.build_timeout * 10
         tempurl = swiftclient_utils.generate_temp_url(path, timeout,
                                                       key, 'GET')
-        sw_url = urlparse.urlparse(oc.url)
+        sw_url = parse.urlparse(oc.url)
         return '%s://%s%s' % (sw_url.scheme, sw_url.netloc, tempurl)
 
     def test_nested_stack_create(self):
@@ -203,11 +203,5 @@
         url = self.publish_template(self.nested_name, self.nested_template)
         self.template = self.test_template.replace('the.yaml', url)
         stack_identifier = self.stack_create(template=self.template)
-
-        self.client.actions.suspend(stack_id=stack_identifier)
-        self._wait_for_resource_status(
-            stack_identifier, 'the_nested', 'SUSPEND_COMPLETE')
-
-        self.client.actions.resume(stack_id=stack_identifier)
-        self._wait_for_resource_status(
-            stack_identifier, 'the_nested', 'RESUME_COMPLETE')
+        self.stack_suspend(stack_identifier)
+        self.stack_resume(stack_identifier)
diff --git a/functional/test_create_update.py b/functional/test_create_update.py
new file mode 100644
index 0000000..6053303
--- /dev/null
+++ b/functional/test_create_update.py
@@ -0,0 +1,390 @@
+#    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.
+
+
+import copy
+from heat_integrationtests.common import test
+import json
+
+test_template_one_resource = {
+    'heat_template_version': '2013-05-23',
+    'description': 'Test template to create one instance.',
+    'resources': {
+        'test1': {
+            'type': 'OS::Heat::TestResource',
+            'properties': {
+                'value': 'Test1',
+                'fail': False,
+                'update_replace': False,
+                'wait_secs': 0
+            }
+        }
+    }
+}
+
+test_template_two_resource = {
+    'heat_template_version': '2013-05-23',
+    'description': 'Test template to create two instance.',
+    'resources': {
+        'test1': {
+            'type': 'OS::Heat::TestResource',
+            'properties': {
+                'value': 'Test1',
+                'fail': False,
+                'update_replace': False,
+                'wait_secs': 0
+            }
+        },
+        'test2': {
+            'type': 'OS::Heat::TestResource',
+            'properties': {
+                'value': 'Test1',
+                'fail': False,
+                'update_replace': False,
+                'wait_secs': 0
+            }
+        }
+    }
+}
+
+
+def _change_rsrc_properties(template, rsrcs, values):
+        modified_template = copy.deepcopy(template)
+        for rsrc_name in rsrcs:
+            rsrc_prop = modified_template['resources'][
+                rsrc_name]['properties']
+            for prop in rsrc_prop:
+                if prop in values:
+                    rsrc_prop[prop] = values[prop]
+        return modified_template
+
+
+class CreateStackTest(test.HeatIntegrationTest):
+    def setUp(self):
+        super(CreateStackTest, self).setUp()
+        self.client = self.orchestration_client
+
+    def test_create_rollback(self):
+        values = {'fail': True, 'value': 'test_create_rollback'}
+        template = _change_rsrc_properties(test_template_one_resource,
+                                           ['test1'], values)
+
+        self.stack_create(
+            template=template,
+            expected_status='ROLLBACK_COMPLETE',
+            disable_rollback=False)
+
+
+class UpdateStackTest(test.HeatIntegrationTest):
+
+    provider_template = {
+        'heat_template_version': '2013-05-23',
+        'description': 'foo',
+        'resources': {
+            'test1': {
+                'type': 'My::TestResource'
+            }
+        }
+    }
+
+    provider_group_template = '''
+heat_template_version: 2013-05-23
+resources:
+  test_group:
+    type: OS::Heat::ResourceGroup
+    properties:
+      count: 2
+      resource_def:
+        type: My::TestResource
+'''
+
+    update_userdata_template = '''
+heat_template_version: 2014-10-16
+parameters:
+  flavor:
+    type: string
+  user_data:
+    type: string
+  image:
+    type: string
+  network:
+    type: string
+
+resources:
+  server:
+    type: OS::Nova::Server
+    properties:
+      image: {get_param: image}
+      flavor: {get_param: flavor}
+      networks: [{network: {get_param: network} }]
+      user_data_format: SOFTWARE_CONFIG
+      user_data: {get_param: user_data}
+'''
+
+    def setUp(self):
+        super(UpdateStackTest, self).setUp()
+        self.client = self.orchestration_client
+
+    def test_stack_update_nochange(self):
+        template = _change_rsrc_properties(test_template_one_resource,
+                                           ['test1'],
+                                           {'value': 'test_no_change'})
+        stack_identifier = self.stack_create(
+            template=template)
+        expected_resources = {'test1': 'OS::Heat::TestResource'}
+        self.assertEqual(expected_resources,
+                         self.list_resources(stack_identifier))
+
+        # Update with no changes, resources should be unchanged
+        self.update_stack(stack_identifier, template)
+        self.assertEqual(expected_resources,
+                         self.list_resources(stack_identifier))
+
+    def test_stack_in_place_update(self):
+        template = _change_rsrc_properties(test_template_one_resource,
+                                           ['test1'],
+                                           {'value': 'test_in_place'})
+        stack_identifier = self.stack_create(
+            template=template)
+        expected_resources = {'test1': 'OS::Heat::TestResource'}
+        self.assertEqual(expected_resources,
+                         self.list_resources(stack_identifier))
+        resource = self.client.resources.list(stack_identifier)
+        initial_phy_id = resource[0].physical_resource_id
+
+        tmpl_update = _change_rsrc_properties(
+            test_template_one_resource, ['test1'],
+            {'value': 'test_in_place_update'})
+        # Update the Value
+        self.update_stack(stack_identifier, tmpl_update)
+        resource = self.client.resources.list(stack_identifier)
+        # By default update_in_place
+        self.assertEqual(initial_phy_id,
+                         resource[0].physical_resource_id)
+
+    def test_stack_update_replace(self):
+        template = _change_rsrc_properties(test_template_one_resource,
+                                           ['test1'],
+                                           {'value': 'test_replace'})
+        stack_identifier = self.stack_create(
+            template=template)
+        expected_resources = {'test1': 'OS::Heat::TestResource'}
+        self.assertEqual(expected_resources,
+                         self.list_resources(stack_identifier))
+        resource = self.client.resources.list(stack_identifier)
+        initial_phy_id = resource[0].physical_resource_id
+
+        # Update the value and also set update_replace prop
+        tmpl_update = _change_rsrc_properties(
+            test_template_one_resource, ['test1'],
+            {'value': 'test_in_place_update', 'update_replace': True})
+        self.update_stack(stack_identifier, tmpl_update)
+        resource = self.client.resources.list(stack_identifier)
+        # update Replace
+        self.assertNotEqual(initial_phy_id,
+                            resource[0].physical_resource_id)
+
+    def test_stack_update_add_remove(self):
+        template = _change_rsrc_properties(test_template_one_resource,
+                                           ['test1'],
+                                           {'value': 'test_add_remove'})
+        stack_identifier = self.stack_create(
+            template=template)
+        initial_resources = {'test1': 'OS::Heat::TestResource'}
+        self.assertEqual(initial_resources,
+                         self.list_resources(stack_identifier))
+
+        tmpl_update = _change_rsrc_properties(
+            test_template_two_resource, ['test1', 'test2'],
+            {'value': 'test_add_remove_update'})
+        # Add one resource via a stack update
+        self.update_stack(stack_identifier, tmpl_update)
+        updated_resources = {'test1': 'OS::Heat::TestResource',
+                             'test2': 'OS::Heat::TestResource'}
+        self.assertEqual(updated_resources,
+                         self.list_resources(stack_identifier))
+
+        # Then remove it by updating with the original template
+        self.update_stack(stack_identifier, template)
+        self.assertEqual(initial_resources,
+                         self.list_resources(stack_identifier))
+
+    def test_stack_update_rollback(self):
+        template = _change_rsrc_properties(test_template_one_resource,
+                                           ['test1'],
+                                           {'value': 'test_update_rollback'})
+        stack_identifier = self.stack_create(
+            template=template)
+        initial_resources = {'test1': 'OS::Heat::TestResource'}
+        self.assertEqual(initial_resources,
+                         self.list_resources(stack_identifier))
+
+        tmpl_update = _change_rsrc_properties(
+            test_template_two_resource, ['test1', 'test2'],
+            {'value': 'test_update_rollback', 'fail': True})
+        # stack update, also set failure
+        self.update_stack(stack_identifier, tmpl_update,
+                          expected_status='ROLLBACK_COMPLETE',
+                          disable_rollback=False)
+        # since stack update failed only the original resource is present
+        updated_resources = {'test1': 'OS::Heat::TestResource'}
+        self.assertEqual(updated_resources,
+                         self.list_resources(stack_identifier))
+
+    def test_stack_update_provider(self):
+        template = _change_rsrc_properties(
+            test_template_one_resource, ['test1'],
+            {'value': 'test_provider_template'})
+        files = {'provider.template': json.dumps(template)}
+        env = {'resource_registry':
+               {'My::TestResource': 'provider.template'}}
+        stack_identifier = self.stack_create(
+            template=self.provider_template,
+            files=files,
+            environment=env
+        )
+
+        initial_resources = {'test1': 'My::TestResource'}
+        self.assertEqual(initial_resources,
+                         self.list_resources(stack_identifier))
+
+        # Prove the resource is backed by a nested stack, save the ID
+        nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
+                                                            'test1')
+        nested_id = nested_identifier.split('/')[-1]
+
+        # Then check the expected resources are in the nested stack
+        nested_resources = {'test1': 'OS::Heat::TestResource'}
+        self.assertEqual(nested_resources,
+                         self.list_resources(nested_identifier))
+        tmpl_update = _change_rsrc_properties(
+            test_template_two_resource, ['test1', 'test2'],
+            {'value': 'test_provider_template'})
+        # Add one resource via a stack update by changing the nested stack
+        files['provider.template'] = json.dumps(tmpl_update)
+        self.update_stack(stack_identifier, self.provider_template,
+                          environment=env, files=files)
+
+        # Parent resources should be unchanged and the nested stack
+        # should have been updated in-place without replacement
+        self.assertEqual(initial_resources,
+                         self.list_resources(stack_identifier))
+        rsrc = self.client.resources.get(stack_identifier, 'test1')
+        self.assertEqual(rsrc.physical_resource_id, nested_id)
+
+        # Then check the expected resources are in the nested stack
+        nested_resources = {'test1': 'OS::Heat::TestResource',
+                            'test2': 'OS::Heat::TestResource'}
+        self.assertEqual(nested_resources,
+                         self.list_resources(nested_identifier))
+
+    def test_stack_update_provider_group(self):
+        '''Test two-level nested update.'''
+        # Create a ResourceGroup (which creates a nested stack),
+        # containing provider resources (which create a nested
+        # stack), thus excercising an update which traverses
+        # two levels of nesting.
+        template = _change_rsrc_properties(
+            test_template_one_resource, ['test1'],
+            {'value': 'test_provider_group_template'})
+        files = {'provider.template': json.dumps(template)}
+        env = {'resource_registry':
+               {'My::TestResource': 'provider.template'}}
+
+        stack_identifier = self.stack_create(
+            template=self.provider_group_template,
+            files=files,
+            environment=env
+        )
+
+        initial_resources = {'test_group': 'OS::Heat::ResourceGroup'}
+        self.assertEqual(initial_resources,
+                         self.list_resources(stack_identifier))
+
+        # Prove the resource is backed by a nested stack, save the ID
+        nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
+                                                            'test_group')
+
+        # Then check the expected resources are in the nested stack
+        nested_resources = {'0': 'My::TestResource',
+                            '1': 'My::TestResource'}
+        self.assertEqual(nested_resources,
+                         self.list_resources(nested_identifier))
+
+        for n_rsrc in nested_resources:
+            rsrc = self.client.resources.get(nested_identifier, n_rsrc)
+            provider_stack = self.client.stacks.get(rsrc.physical_resource_id)
+            provider_identifier = '%s/%s' % (provider_stack.stack_name,
+                                             provider_stack.id)
+            provider_resources = {u'test1': u'OS::Heat::TestResource'}
+            self.assertEqual(provider_resources,
+                             self.list_resources(provider_identifier))
+
+        tmpl_update = _change_rsrc_properties(
+            test_template_two_resource, ['test1', 'test2'],
+            {'value': 'test_provider_group_template'})
+        # Add one resource via a stack update by changing the nested stack
+        files['provider.template'] = json.dumps(tmpl_update)
+        self.update_stack(stack_identifier, self.provider_group_template,
+                          environment=env, files=files)
+
+        # Parent resources should be unchanged and the nested stack
+        # should have been updated in-place without replacement
+        self.assertEqual(initial_resources,
+                         self.list_resources(stack_identifier))
+
+        # Resource group stack should also be unchanged (but updated)
+        nested_stack = self.client.stacks.get(nested_identifier)
+        self.assertEqual('UPDATE_COMPLETE', nested_stack.stack_status)
+        self.assertEqual(nested_resources,
+                         self.list_resources(nested_identifier))
+
+        for n_rsrc in nested_resources:
+            rsrc = self.client.resources.get(nested_identifier, n_rsrc)
+            provider_stack = self.client.stacks.get(rsrc.physical_resource_id)
+            provider_identifier = '%s/%s' % (provider_stack.stack_name,
+                                             provider_stack.id)
+            provider_resources = {'test1': 'OS::Heat::TestResource',
+                                  'test2': 'OS::Heat::TestResource'}
+            self.assertEqual(provider_resources,
+                             self.list_resources(provider_identifier))
+
+    def test_stack_update_with_replacing_userdata(self):
+        """Confirm that we can update userdata of instance during updating
+        stack by the user of member role.
+
+        Make sure that a resource that inherites from StackUser can be deleted
+        during updating stack.
+        """
+        if not self.conf.minimal_image_ref:
+            raise self.skipException("No minimal image configured to test")
+        if not self.conf.minimal_instance_type:
+            raise self.skipException("No flavor configured to test")
+
+        parms = {'flavor': self.conf.minimal_instance_type,
+                 'image': self.conf.minimal_image_ref,
+                 'network': self.conf.fixed_network_name,
+                 'user_data': ''}
+        name = self._stack_rand_name()
+
+        stack_identifier = self.stack_create(
+            stack_name=name,
+            template=self.update_userdata_template,
+            parameters=parms
+        )
+
+        parms_updated = parms
+        parms_updated['user_data'] = 'two'
+        self.update_stack(
+            stack_identifier,
+            template=self.update_userdata_template,
+            parameters=parms_updated)
diff --git a/functional/test_create_update_neutron_port.py b/functional/test_create_update_neutron_port.py
new file mode 100644
index 0000000..3d82bc4
--- /dev/null
+++ b/functional/test_create_update_neutron_port.py
@@ -0,0 +1,108 @@
+#    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 testtools import testcase
+
+from heat_integrationtests.common import test
+
+
+test_template = '''
+heat_template_version: 2015-04-30
+description: Test template to create port wit ip_address.
+parameters:
+  mac:
+    type: string
+    default: 00-00-00-00-BB-BB
+resources:
+  net:
+    type: OS::Neutron::Net
+  subnet:
+    type: OS::Neutron::Subnet
+    properties:
+      network: { get_resource: net }
+      cidr: 11.11.11.0/24
+  port:
+    type: OS::Neutron::Port
+    properties:
+      network: {get_resource: net}
+      mac_address: {get_param: mac}
+      fixed_ips:
+        - subnet: {get_resource: subnet}
+          ip_address: 11.11.11.11
+outputs:
+  port_ip:
+    value: {get_attr: [port, fixed_ips, 0, ip_address]}
+'''
+
+
+class UpdatePortTest(test.HeatIntegrationTest):
+
+    def setUp(self):
+        super(UpdatePortTest, self).setUp()
+        self.client = self.orchestration_client
+
+    def get_port_id_and_ip(self, stack_identifier):
+        resources = self.client.resources.list(stack_identifier)
+        port_id = [res.physical_resource_id for res in resources
+                   if res.resource_name == 'port']
+        stack = self.client.stacks.get(stack_identifier)
+        port_ip = self._stack_output(stack, 'port_ip')
+        return port_id[0], port_ip
+
+    def test_stack_update_replace_no_ip(self):
+        templ_no_ip = test_template.replace('ip_address: 11.11.11.11', '')
+        # create with default 'mac' parameter
+        stack_identifier = self.stack_create(template=templ_no_ip)
+        _id, _ip = self.get_port_id_and_ip(stack_identifier)
+
+        # Update with another 'mac' parameter
+        parameters = {'mac': '00-00-00-00-AA-AA'}
+        self.update_stack(stack_identifier, templ_no_ip,
+                          parameters=parameters)
+
+        new_id, new_ip = self.get_port_id_and_ip(stack_identifier)
+        # port id and ip should be different
+        self.assertNotEqual(_ip, new_ip)
+        self.assertNotEqual(_id, new_id)
+
+    @testcase.skip('Skipped until bug #1455100 is fixed.')
+    def test_stack_update_replace_with_ip(self):
+        # create with default 'mac' parameter
+        stack_identifier = self.stack_create(template=test_template)
+
+        _id, _ip = self.get_port_id_and_ip(stack_identifier)
+
+        # Update with another 'mac' parameter
+        parameters = {'mac': '00-00-00-00-AA-AA'}
+
+        # port should be replaced with same ip
+        self.update_stack(stack_identifier, test_template,
+                          parameters=parameters)
+
+        new_id, new_ip = self.get_port_id_and_ip(stack_identifier)
+        # port id should be different, ip should be the same
+        self.assertEqual(_ip, new_ip)
+        self.assertNotEqual(_id, new_id)
+
+    def test_stack_update_in_place_remove_ip(self):
+        # create with default 'mac' parameter and defined ip_address
+        stack_identifier = self.stack_create(template=test_template)
+        _id, _ip = self.get_port_id_and_ip(stack_identifier)
+
+        # remove ip_address property and update stack
+        templ_no_ip = test_template.replace('ip_address: 11.11.11.11', '')
+        self.update_stack(stack_identifier, templ_no_ip)
+
+        new_id, new_ip = self.get_port_id_and_ip(stack_identifier)
+        # port should be updated with the same id, but different ip
+        self.assertNotEqual(_ip, new_ip)
+        self.assertEqual(_id, new_id)
diff --git a/functional/test_default_parameters.py b/functional/test_default_parameters.py
index 138a13e..00000fd 100644
--- a/functional/test_default_parameters.py
+++ b/functional/test_default_parameters.py
@@ -68,8 +68,6 @@
         self.client = self.orchestration_client
 
     def test_defaults(self):
-        stack_name = self._stack_rand_name()
-
         env = {'parameters': {}, 'parameter_defaults': {}}
         if self.param:
             env['parameters'] = {'length': self.param}
@@ -84,22 +82,13 @@
         else:
             nested_template = self.nested_template
 
-        self.client.stacks.create(
-            stack_name=stack_name,
+        stack_identifier = self.stack_create(
             template=self.template,
             files={'nested_random.yaml': nested_template},
-            disable_rollback=True,
-            parameters={},
             environment=env
         )
-        self.addCleanup(self.client.stacks.delete, stack_name)
 
-        stack = self.client.stacks.get(stack_name)
-        stack_identifier = '%s/%s' % (stack_name, stack.id)
-
-        self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
-
-        stack = self.client.stacks.get(stack_name)
+        stack = self.client.stacks.get(stack_identifier)
         for out in stack.outputs:
             if out['output_key'] == 'random1':
                 self.assertEqual(self.expect1, len(out['output_value']))
diff --git a/functional/test_heat_autoscaling.py b/functional/test_heat_autoscaling.py
index 0e6e0cb..dbe3e8f 100644
--- a/functional/test_heat_autoscaling.py
+++ b/functional/test_heat_autoscaling.py
@@ -132,4 +132,3 @@
             'scaling_adjustment: 2')
 
         self.update_stack(stack_identifier, template=new_template)
-        self._wait_for_stack_status(stack_identifier, 'UPDATE_COMPLETE')
diff --git a/functional/test_instance_group.py b/functional/test_instance_group.py
index 5c88bed..1455d0b 100644
--- a/functional/test_instance_group.py
+++ b/functional/test_instance_group.py
@@ -174,7 +174,6 @@
                                'flavor': self.conf.instance_type}}
         self.update_stack(stack_identifier, self.template,
                           environment=env2, files=files)
-        self._wait_for_stack_status(stack_identifier, 'UPDATE_COMPLETE')
         stack = self.client.stacks.get(stack_identifier)
         self.assert_instance_count(stack, 5)
 
@@ -345,7 +344,6 @@
         # test stack update
         self.update_stack(stack_identifier, updt_template,
                           environment=env, files=files)
-        self._wait_for_stack_status(stack_identifier, 'UPDATE_COMPLETE')
         updt_stack = self.client.stacks.get(stack_identifier)
 
         # test that the launch configuration is replaced
diff --git a/functional/test_notifications.py b/functional/test_notifications.py
index a9b6cf1..a4c419c 100644
--- a/functional/test_notifications.py
+++ b/functional/test_notifications.py
@@ -141,17 +141,9 @@
         return len(handler.notifications) == count
 
     def test_basic_notifications(self):
-        stack_identifier = self._stack_rand_name()
-        # do this manually so we can call _stack_delete() directly.
-        self.client.stacks.create(
-            stack_name=stack_identifier,
-            template=self.basic_template,
-            files={},
-            disable_rollback=True,
-            parameters={},
-            environment={}
-        )
-        self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
+        # disable cleanup so we can call _stack_delete() directly.
+        stack_identifier = self.stack_create(template=self.basic_template,
+                                             enable_cleanup=False)
         self.update_stack(stack_identifier,
                           template=self.update_basic_template)
         self.stack_suspend(stack_identifier)
diff --git a/functional/test_remote_stack.py b/functional/test_remote_stack.py
index 7579eb0..868f24e 100644
--- a/functional/test_remote_stack.py
+++ b/functional/test_remote_stack.py
@@ -140,17 +140,5 @@
     def test_stack_suspend_resume(self):
         files = {'remote_stack.yaml': self.remote_template}
         stack_id = self.stack_create(files=files)
-        rsrc = self.client.resources.get(stack_id, 'my_stack')
-        remote_id = rsrc.physical_resource_id
-
-        # suspend stack
-        self.client.actions.suspend(stack_id)
-        self._wait_for_stack_status(stack_id, 'SUSPEND_COMPLETE')
-        rsrc = self.client.stacks.get(remote_id)
-        self.assertEqual('SUSPEND_COMPLETE', rsrc.stack_status)
-
-        # resume stack
-        self.client.actions.resume(stack_id)
-        self._wait_for_stack_status(stack_id, 'RESUME_COMPLETE')
-        rsrc = self.client.stacks.get(remote_id)
-        self.assertEqual('RESUME_COMPLETE', rsrc.stack_status)
+        self.stack_suspend(stack_id)
+        self.stack_resume(stack_id)
diff --git a/functional/test_resource_group.py b/functional/test_resource_group.py
index 322c99c..a41f841 100644
--- a/functional/test_resource_group.py
+++ b/functional/test_resource_group.py
@@ -98,7 +98,7 @@
 
         # Prove validation works for non-zero create/update
         template_two_nested = self.template.replace("count: 0", "count: 2")
-        expected_err = "length Value 'BAD' is not an integer"
+        expected_err = "Value 'BAD' is not an integer"
         ex = self.assertRaises(exc.HTTPBadRequest, self.update_stack,
                                stack_identifier, template_two_nested,
                                environment=env, files=files)
diff --git a/functional/test_stack_tags.py b/functional/test_stack_tags.py
new file mode 100644
index 0000000..a183d25
--- /dev/null
+++ b/functional/test_stack_tags.py
@@ -0,0 +1,69 @@
+#    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 heat_integrationtests.common import test
+
+
+class StackTagTest(test.HeatIntegrationTest):
+
+    template = '''
+heat_template_version: 2014-10-16
+description:
+  foo
+'''
+
+    def setUp(self):
+        super(StackTagTest, self).setUp()
+        self.client = self.orchestration_client
+
+    def test_stack_tag(self):
+        # Stack create with stack tags
+        tags = ['foo', 'bar']
+        stack_identifier = self.stack_create(
+            template=self.template,
+            tags=tags
+        )
+
+        # Ensure property tag is populated and matches given tags
+        stack = self.client.stacks.get(stack_identifier)
+        self.assertEqual(tags, stack.tags)
+
+        # Update tags
+        updated_tags = ['tag1', 'tag2']
+        self.update_stack(
+            stack_identifier,
+            template=self.template,
+            tags=updated_tags)
+
+        # Ensure property tag is populated and matches updated tags
+        updated_stack = self.client.stacks.get(stack_identifier)
+        self.assertEqual(updated_tags, updated_stack.tags)
+
+        # Delete tags
+        self.update_stack(
+            stack_identifier,
+            template=self.template
+        )
+
+        # Ensure property tag is not populated
+        empty_tags_stack = self.client.stacks.get(stack_identifier)
+        self.assertIsNone(empty_tags_stack.tags)
+
+    def test_hidden_stack(self):
+        # Stack create with hidden stack tag
+        tags = ['foo', 'hidden']
+        self.stack_create(
+            template=self.template,
+            tags=tags)
+        # Ensure stack does not exist when we do a stack list
+        for stack in self.client.stacks.list():
+            self.assertNotIn('hidden', stack.tags, "Hidden stack can be seen")
diff --git a/functional/test_template_resource.py b/functional/test_template_resource.py
index 392bddd..5b189ac 100644
--- a/functional/test_template_resource.py
+++ b/functional/test_template_resource.py
@@ -102,17 +102,12 @@
         This tests that if you manually delete a nested
         stack, the parent stack is still deletable.
         """
-        name = self._stack_rand_name()
-        # do this manually so we can call _stack_delete() directly.
-        self.client.stacks.create(
-            stack_name=name,
+        # disable cleanup so we can call _stack_delete() directly.
+        stack_identifier = self.stack_create(
             template=self.template,
             files={'nested.yaml': self.nested_templ},
             environment=self.env_templ,
-            disable_rollback=True)
-        stack = self.client.stacks.get(name)
-        stack_identifier = '%s/%s' % (name, stack.id)
-        self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
+            enable_cleanup=False)
 
         nested_ident = self.assert_resource_is_a_stack(stack_identifier,
                                                        'secret1')
@@ -120,6 +115,36 @@
         self._stack_delete(nested_ident)
         self._stack_delete(stack_identifier)
 
+    def test_change_in_file_path(self):
+        stack_identifier = self.stack_create(
+            template=self.template,
+            files={'nested.yaml': self.nested_templ},
+            environment=self.env_templ)
+        stack = self.client.stacks.get(stack_identifier)
+        secret_out1 = self._stack_output(stack, 'secret-out')
+
+        nested_templ_2 = '''
+heat_template_version: 2013-05-23
+resources:
+  secret2:
+    type: OS::Heat::RandomString
+outputs:
+  value:
+    value: freddy
+'''
+        env_templ_2 = '''
+resource_registry:
+  "OS::Heat::RandomString": new/nested.yaml
+'''
+        self.update_stack(stack_identifier,
+                          template=self.template,
+                          files={'new/nested.yaml': nested_templ_2},
+                          environment=env_templ_2)
+        stack = self.client.stacks.get(stack_identifier)
+        secret_out2 = self._stack_output(stack, 'secret-out')
+        self.assertNotEqual(secret_out1, secret_out2)
+        self.assertEqual('freddy', secret_out2)
+
 
 class NestedAttributesTest(test.HeatIntegrationTest):
     """Prove that we can use the template resource references."""
@@ -226,6 +251,22 @@
     Type: the.yaml
     Properties:
       one: my_name
+      two: your_name
+Outputs:
+  identifier:
+    Value: {Ref: the_nested}
+  value:
+    Value: {'Fn::GetAtt': [the_nested, the_str]}
+'''
+
+    main_template_change_prop = '''
+HeatTemplateFormatVersion: '2012-12-12'
+Resources:
+  the_nested:
+    Type: the.yaml
+    Properties:
+      one: updated_name
+      two: your_name
 
 Outputs:
   identifier:
@@ -234,13 +275,30 @@
     Value: {'Fn::GetAtt': [the_nested, the_str]}
 '''
 
-    main_template_2 = '''
+    main_template_add_prop = '''
 HeatTemplateFormatVersion: '2012-12-12'
 Resources:
   the_nested:
     Type: the.yaml
     Properties:
-      one: updated_name
+      one: my_name
+      two: your_name
+      three: third_name
+
+Outputs:
+  identifier:
+    Value: {Ref: the_nested}
+  value:
+    Value: {'Fn::GetAtt': [the_nested, the_str]}
+'''
+
+    main_template_remove_prop = '''
+HeatTemplateFormatVersion: '2012-12-12'
+Resources:
+  the_nested:
+    Type: the.yaml
+    Properties:
+      one: my_name
 
 Outputs:
   identifier:
@@ -255,6 +313,10 @@
   one:
     Default: foo
     Type: String
+  two:
+    Default: bar
+    Type: String
+
 Resources:
   NestedResource:
     Type: OS::Heat::RandomString
@@ -264,6 +326,7 @@
   the_str:
     Value: {'Fn::GetAtt': [NestedResource, value]}
 '''
+
     prop_change_tmpl = '''
 HeatTemplateFormatVersion: '2012-12-12'
 Parameters:
@@ -277,17 +340,62 @@
   NestedResource:
     Type: OS::Heat::RandomString
     Properties:
+      salt: {Ref: two}
+Outputs:
+  the_str:
+    Value: {'Fn::GetAtt': [NestedResource, value]}
+'''
+
+    prop_add_tmpl = '''
+HeatTemplateFormatVersion: '2012-12-12'
+Parameters:
+  one:
+    Default: yikes
+    Type: String
+  two:
+    Default: foo
+    Type: String
+  three:
+    Default: bar
+    Type: String
+
+Resources:
+  NestedResource:
+    Type: OS::Heat::RandomString
+    Properties:
+      salt: {Ref: three}
+Outputs:
+  the_str:
+    Value: {'Fn::GetAtt': [NestedResource, value]}
+'''
+
+    prop_remove_tmpl = '''
+HeatTemplateFormatVersion: '2012-12-12'
+Parameters:
+  one:
+    Default: yikes
+    Type: String
+
+Resources:
+  NestedResource:
+    Type: OS::Heat::RandomString
+    Properties:
       salt: {Ref: one}
 Outputs:
   the_str:
     Value: {'Fn::GetAtt': [NestedResource, value]}
 '''
+
     attr_change_tmpl = '''
 HeatTemplateFormatVersion: '2012-12-12'
 Parameters:
   one:
     Default: foo
     Type: String
+  two:
+    Default: bar
+    Type: String
+
 Resources:
   NestedResource:
     Type: OS::Heat::RandomString
@@ -299,12 +407,17 @@
   something_else:
     Value: just_a_string
 '''
+
     content_change_tmpl = '''
 HeatTemplateFormatVersion: '2012-12-12'
 Parameters:
   one:
     Default: foo
     Type: String
+  two:
+    Default: bar
+    Type: String
+
 Resources:
   NestedResource:
     Type: OS::Heat::RandomString
@@ -320,7 +433,7 @@
         ('no_changes', dict(template=main_template,
                             provider=initial_tmpl,
                             expect=NOCHANGE)),
-        ('main_tmpl_change', dict(template=main_template_2,
+        ('main_tmpl_change', dict(template=main_template_change_prop,
                                   provider=initial_tmpl,
                                   expect=UPDATE)),
         ('provider_change', dict(template=main_template,
@@ -328,6 +441,12 @@
                                  expect=UPDATE)),
         ('provider_props_change', dict(template=main_template,
                                        provider=prop_change_tmpl,
+                                       expect=UPDATE)),
+        ('provider_props_add', dict(template=main_template_add_prop,
+                                    provider=prop_add_tmpl,
+                                    expect=UPDATE)),
+        ('provider_props_remove', dict(template=main_template_remove_prop,
+                                       provider=prop_remove_tmpl,
                                        expect=NOCHANGE)),
         ('provider_attr_change', dict(template=main_template,
                                       provider=attr_change_tmpl,
@@ -445,16 +564,11 @@
         return yaml.load(yaml_templ)
 
     def test_abandon(self):
-        stack_name = self._stack_rand_name()
-        self.client.stacks.create(
-            stack_name=stack_name,
+        stack_identifier = self.stack_create(
             template=self.main_template,
             files={'the.yaml': self.nested_templ},
-            disable_rollback=True,
+            enable_cleanup=False
         )
-        stack = self.client.stacks.get(stack_name)
-        stack_identifier = '%s/%s' % (stack_name, stack.id)
-        self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
 
         info = self.stack_abandon(stack_id=stack_identifier)
         self.assertEqual(self._yaml_to_json(self.main_template),
@@ -527,16 +641,10 @@
         self.client = self.orchestration_client
 
     def test_check(self):
-        stack_name = self._stack_rand_name()
-        self.client.stacks.create(
-            stack_name=stack_name,
+        stack_identifier = self.stack_create(
             template=self.main_template,
-            files={'the.yaml': self.nested_templ},
-            disable_rollback=True,
+            files={'the.yaml': self.nested_templ}
         )
-        stack = self.client.stacks.get(stack_name)
-        stack_identifier = '%s/%s' % (stack_name, stack.id)
-        self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
 
         self.client.actions.check(stack_id=stack_identifier)
         self._wait_for_stack_status(stack_identifier, 'CHECK_COMPLETE')
diff --git a/functional/test_update.py b/functional/test_update.py
deleted file mode 100644
index b329ddc..0000000
--- a/functional/test_update.py
+++ /dev/null
@@ -1,240 +0,0 @@
-#    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 heat_integrationtests.common import test
-
-
-class UpdateStackTest(test.HeatIntegrationTest):
-
-    template = '''
-heat_template_version: 2013-05-23
-resources:
-  random1:
-    type: OS::Heat::RandomString
-'''
-    update_template = '''
-heat_template_version: 2013-05-23
-resources:
-  random1:
-    type: OS::Heat::RandomString
-  random2:
-    type: OS::Heat::RandomString
-'''
-
-    provider_template = '''
-heat_template_version: 2013-05-23
-resources:
-  random1:
-    type: My::RandomString
-'''
-
-    provider_group_template = '''
-heat_template_version: 2013-05-23
-resources:
-  random_group:
-    type: OS::Heat::ResourceGroup
-    properties:
-      count: 2
-      resource_def:
-        type: My::RandomString
-'''
-
-    update_userdata_template = '''
-heat_template_version: 2014-10-16
-parameters:
-  flavor:
-    type: string
-  user_data:
-    type: string
-  image:
-    type: string
-
-resources:
-  server:
-    type: OS::Nova::Server
-    properties:
-      image: {get_param: image}
-      flavor: {get_param: flavor}
-      user_data_format: SOFTWARE_CONFIG
-      user_data: {get_param: user_data}
-'''
-
-    def setUp(self):
-        super(UpdateStackTest, self).setUp()
-        self.client = self.orchestration_client
-
-    def test_stack_update_nochange(self):
-        stack_identifier = self.stack_create()
-        expected_resources = {'random1': 'OS::Heat::RandomString'}
-        self.assertEqual(expected_resources,
-                         self.list_resources(stack_identifier))
-
-        # Update with no changes, resources should be unchanged
-        self.update_stack(stack_identifier, self.template)
-        self.assertEqual(expected_resources,
-                         self.list_resources(stack_identifier))
-
-    def test_stack_update_add_remove(self):
-        stack_identifier = self.stack_create()
-        initial_resources = {'random1': 'OS::Heat::RandomString'}
-        self.assertEqual(initial_resources,
-                         self.list_resources(stack_identifier))
-
-        # Add one resource via a stack update
-        self.update_stack(stack_identifier, self.update_template)
-        updated_resources = {'random1': 'OS::Heat::RandomString',
-                             'random2': 'OS::Heat::RandomString'}
-        self.assertEqual(updated_resources,
-                         self.list_resources(stack_identifier))
-
-        # Then remove it by updating with the original template
-        self.update_stack(stack_identifier, self.template)
-        self.assertEqual(initial_resources,
-                         self.list_resources(stack_identifier))
-
-    def test_stack_update_provider(self):
-        files = {'provider.yaml': self.template}
-        env = {'resource_registry':
-               {'My::RandomString': 'provider.yaml'}}
-        stack_identifier = self.stack_create(
-            template=self.provider_template,
-            files=files,
-            environment=env
-        )
-
-        initial_resources = {'random1': 'My::RandomString'}
-        self.assertEqual(initial_resources,
-                         self.list_resources(stack_identifier))
-
-        # Prove the resource is backed by a nested stack, save the ID
-        nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
-                                                            'random1')
-        nested_id = nested_identifier.split('/')[-1]
-
-        # Then check the expected resources are in the nested stack
-        nested_resources = {'random1': 'OS::Heat::RandomString'}
-        self.assertEqual(nested_resources,
-                         self.list_resources(nested_identifier))
-
-        # Add one resource via a stack update by changing the nested stack
-        files['provider.yaml'] = self.update_template
-        self.update_stack(stack_identifier, self.provider_template,
-                          environment=env, files=files)
-
-        # Parent resources should be unchanged and the nested stack
-        # should have been updated in-place without replacement
-        self.assertEqual(initial_resources,
-                         self.list_resources(stack_identifier))
-        rsrc = self.client.resources.get(stack_identifier, 'random1')
-        self.assertEqual(rsrc.physical_resource_id, nested_id)
-
-        # Then check the expected resources are in the nested stack
-        nested_resources = {'random1': 'OS::Heat::RandomString',
-                            'random2': 'OS::Heat::RandomString'}
-        self.assertEqual(nested_resources,
-                         self.list_resources(nested_identifier))
-
-    def test_stack_update_provider_group(self):
-        '''Test two-level nested update.'''
-        # Create a ResourceGroup (which creates a nested stack),
-        # containing provider resources (which create a nested
-        # stack), thus excercising an update which traverses
-        # two levels of nesting.
-        files = {'provider.yaml': self.template}
-        env = {'resource_registry':
-               {'My::RandomString': 'provider.yaml'}}
-
-        stack_identifier = self.stack_create(
-            template=self.provider_group_template,
-            files=files,
-            environment=env
-        )
-
-        initial_resources = {'random_group': 'OS::Heat::ResourceGroup'}
-        self.assertEqual(initial_resources,
-                         self.list_resources(stack_identifier))
-
-        # Prove the resource is backed by a nested stack, save the ID
-        nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
-                                                            'random_group')
-
-        # Then check the expected resources are in the nested stack
-        nested_resources = {'0': 'My::RandomString',
-                            '1': 'My::RandomString'}
-        self.assertEqual(nested_resources,
-                         self.list_resources(nested_identifier))
-
-        for n_rsrc in nested_resources:
-            rsrc = self.client.resources.get(nested_identifier, n_rsrc)
-            provider_stack = self.client.stacks.get(rsrc.physical_resource_id)
-            provider_identifier = '%s/%s' % (provider_stack.stack_name,
-                                             provider_stack.id)
-            provider_resources = {u'random1': u'OS::Heat::RandomString'}
-            self.assertEqual(provider_resources,
-                             self.list_resources(provider_identifier))
-
-        # Add one resource via a stack update by changing the nested stack
-        files['provider.yaml'] = self.update_template
-        self.update_stack(stack_identifier, self.provider_group_template,
-                          environment=env, files=files)
-
-        # Parent resources should be unchanged and the nested stack
-        # should have been updated in-place without replacement
-        self.assertEqual(initial_resources,
-                         self.list_resources(stack_identifier))
-
-        # Resource group stack should also be unchanged (but updated)
-        nested_stack = self.client.stacks.get(nested_identifier)
-        self.assertEqual('UPDATE_COMPLETE', nested_stack.stack_status)
-        self.assertEqual(nested_resources,
-                         self.list_resources(nested_identifier))
-
-        for n_rsrc in nested_resources:
-            rsrc = self.client.resources.get(nested_identifier, n_rsrc)
-            provider_stack = self.client.stacks.get(rsrc.physical_resource_id)
-            provider_identifier = '%s/%s' % (provider_stack.stack_name,
-                                             provider_stack.id)
-            provider_resources = {'random1': 'OS::Heat::RandomString',
-                                  'random2': 'OS::Heat::RandomString'}
-            self.assertEqual(provider_resources,
-                             self.list_resources(provider_identifier))
-
-    def test_stack_update_with_replacing_userdata(self):
-        """Confirm that we can update userdata of instance during updating
-        stack by the user of member role.
-
-        Make sure that a resource that inherites from StackUser can be deleted
-        during updating stack.
-        """
-        if not self.conf.minimal_image_ref:
-            raise self.skipException("No minimal image configured to test")
-        if not self.conf.minimal_instance_type:
-            raise self.skipException("No flavor configured to test")
-
-        parms = {'flavor': self.conf.minimal_instance_type,
-                 'image': self.conf.minimal_image_ref,
-                 'user_data': ''}
-        name = self._stack_rand_name()
-
-        stack_identifier = self.stack_create(
-            stack_name=name,
-            template=self.update_userdata_template,
-            parameters=parms
-        )
-
-        parms_updated = parms
-        parms_updated['user_data'] = 'two'
-        self.update_stack(
-            stack_identifier,
-            template=self.update_userdata_template,
-            parameters=parms_updated)
diff --git a/scenario/templates/test_ceilometer_alarm.yaml b/scenario/templates/test_ceilometer_alarm.yaml
new file mode 100644
index 0000000..01bc790
--- /dev/null
+++ b/scenario/templates/test_ceilometer_alarm.yaml
@@ -0,0 +1,33 @@
+heat_template_version: 2013-05-23
+resources:
+  asg:
+    type: OS::Heat::AutoScalingGroup
+    properties:
+      max_size: 5
+      min_size: 1
+      resource:
+        type: OS::Heat::RandomString
+  scaleup_policy:
+    type: OS::Heat::ScalingPolicy
+    properties:
+      adjustment_type: change_in_capacity
+      auto_scaling_group_id: {get_resource: asg}
+      cooldown: 0
+      scaling_adjustment: 1
+  alarm:
+    type: OS::Ceilometer::Alarm
+    properties:
+      description: Scale-up if the average CPU > 50% for 1 minute
+      meter_name: test_meter
+      statistic: count
+      comparison_operator: ge
+      threshold: 1
+      period: 60
+      evaluation_periods: 1
+      alarm_actions:
+        - {get_attr: [scaleup_policy, alarm_url]}
+      matching_metadata:
+        metadata.metering.stack_id: {get_param: "OS::stack_id"}
+outputs:
+  asg_size:
+    value: {get_attr: [asg, current_size]}
diff --git a/scenario/templates/test_neutron_autoscaling.yaml b/scenario/templates/test_neutron_autoscaling.yaml
index 59aad2c..a34ec43 100644
--- a/scenario/templates/test_neutron_autoscaling.yaml
+++ b/scenario/templates/test_neutron_autoscaling.yaml
@@ -11,7 +11,7 @@
     type: string
     label: Capacity
     description: Auto-scaling group desired capacity
-  fixed_subnet_name:
+  fixed_subnet:
     type: string
     label: fixed subnetwork ID
     description: subnetwork ID used for autoscaling
@@ -28,7 +28,7 @@
       lb_method: ROUND_ROBIN
       name: test_pool
       protocol: HTTP
-      subnet: { get_param: fixed_subnet_name }
+      subnet: { get_param: fixed_subnet }
       vip: {
         "description": "Test VIP",
         "protocol_port": 80,
@@ -49,6 +49,7 @@
     properties:
       AvailabilityZones : ["nova"]
       LaunchConfigurationName : { get_resource : launch_config }
+      VPCZoneIdentifier: [{ get_param: fixed_subnet }]
       MinSize : 1
       MaxSize : 5
       DesiredCapacity: { get_param: capacity }
diff --git a/scenario/templates/test_neutron_loadbalancer.yaml b/scenario/templates/test_neutron_loadbalancer.yaml
index a7e9f9c..dd659d0 100644
--- a/scenario/templates/test_neutron_loadbalancer.yaml
+++ b/scenario/templates/test_neutron_loadbalancer.yaml
@@ -10,6 +10,8 @@
     type: string
   image:
     type: string
+  network:
+    type: string
   private_subnet_id:
     type: string
   external_network_id:
@@ -17,6 +19,8 @@
   port:
     type: string
     default: '80'
+  timeout:
+    type: number
 
 resources:
   sec_group:
@@ -32,6 +36,31 @@
         - remote_ip_prefix: 0.0.0.0/0
           protocol: icmp
 
+  wait_condition:
+    type: OS::Heat::WaitCondition
+    properties:
+      handle: { get_resource: wait_condition_handle }
+      count: 2
+      timeout: { get_param: timeout }
+
+  wait_condition_handle:
+    type: OS::Heat::WaitConditionHandle
+
+  config:
+    type: OS::Heat::SoftwareConfig
+    properties:
+      group: ungrouped
+      config:
+        str_replace:
+          template: |
+            #!/bin/bash -v
+            echo  $(hostname) > index.html
+            python -m SimpleHTTPServer port &
+            wc_notify --data-binary '{"status": "SUCCESS"}'
+          params:
+            wc_notify: { get_attr: ['wait_condition_handle', 'curl_cli'] }
+            port: { get_param: port }
+
   server1:
     type: OS::Nova::Server
     properties:
@@ -39,18 +68,10 @@
       image: { get_param: image }
       flavor: { get_param: flavor }
       key_name: { get_param: key_name }
+      networks: [{network: {get_param: network} }]
       security_groups: [{ get_resource: sec_group }]
-      user_data:
-        list_join:
-        - ''
-        - - '#!/bin/bash -v
-
-            '
-          - 'echo  $(hostname) > index.html
-
-            '
-          - 'python -m SimpleHTTPServer '
-          - { get_param: port }
+      user_data_format: SOFTWARE_CONFIG
+      user_data: { get_resource: config }
 
   server2:
     type: OS::Nova::Server
@@ -59,18 +80,10 @@
       image: { get_param: image }
       flavor: { get_param: flavor }
       key_name: { get_param: key_name }
+      networks: [{network: {get_param: network} }]
       security_groups: [{ get_resource: sec_group }]
-      user_data:
-        list_join:
-        - ''
-        - - '#!/bin/bash -v
-
-            '
-          - 'echo  $(hostname) > index.html
-
-            '
-          - 'python -m SimpleHTTPServer '
-          - { get_param: port }
+      user_data_format: SOFTWARE_CONFIG
+      user_data: { get_resource: config }
 
   health_monitor:
     type: OS::Neutron::HealthMonitor
@@ -97,9 +110,12 @@
        floating_network: { get_param: external_network_id }
        port_id:
          { get_attr: [test_pool, vip, 'port_id'] }
+       fixed_ip_address:
+         { get_attr: [test_pool, vip, 'address'] }
 
   LBaaS:
     type: OS::Neutron::LoadBalancer
+    depends_on: wait_condition
     properties:
       pool_id: { get_resource: test_pool }
       protocol_port: { get_param: port }
@@ -108,9 +124,9 @@
 
 outputs:
   serv1_ip:
-    value: {get_attr: [server1, networks, private, 0]}
+    value: {get_attr: [server1, networks, { get_param: network }, 0]}
   serv2_ip:
-    value: {get_attr: [server2, networks, private, 0]}
+    value: {get_attr: [server2, networks, { get_param: network }, 0]}
   vip:
     value: {get_attr: [test_pool, vip, address]}
   fip:
diff --git a/scenario/test_ceilometer_alarm.py b/scenario/test_ceilometer_alarm.py
new file mode 100644
index 0000000..d8f21fd
--- /dev/null
+++ b/scenario/test_ceilometer_alarm.py
@@ -0,0 +1,57 @@
+#    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 oslo_log import log as logging
+
+from heat_integrationtests.common import test
+
+LOG = logging.getLogger(__name__)
+
+
+class CeilometerAlarmTest(test.HeatIntegrationTest):
+    """Class is responsible for testing of ceilometer usage."""
+    def setUp(self):
+        super(CeilometerAlarmTest, self).setUp()
+        self.client = self.orchestration_client
+        self.template = self._load_template(__file__,
+                                            'test_ceilometer_alarm.yaml',
+                                            'templates')
+
+    def check_instance_count(self, stack_identifier, expected):
+        stack = self.client.stacks.get(stack_identifier)
+        actual = self._stack_output(stack, 'asg_size')
+        if actual != expected:
+            LOG.warn('check_instance_count exp:%d, act:%s' % (expected,
+                                                              actual))
+        return actual == expected
+
+    def test_alarm(self):
+        """Confirm we can create an alarm and trigger it."""
+
+        # 1. create the stack
+        stack_identifier = self.stack_create(template=self.template)
+
+        # 2. send ceilometer a metric (should cause the alarm to fire)
+        sample = {}
+        sample['counter_type'] = 'gauge'
+        sample['counter_name'] = 'test_meter'
+        sample['counter_volume'] = 1
+        sample['counter_unit'] = 'count'
+        sample['resource_metadata'] = {'metering.stack_id':
+                                       stack_identifier.split('/')[-1]}
+        sample['resource_id'] = 'shouldnt_matter'
+        self.metering_client.samples.create(**sample)
+
+        # 3. confirm we get a scaleup.
+        # Note: there is little point waiting more than 60s+time to scale up.
+        self.assertTrue(test.call_until_true(
+            120, 2, self.check_instance_count, stack_identifier, 2))
diff --git a/scenario/test_neutron_autoscaling.py b/scenario/test_neutron_autoscaling.py
index 9bbfbab..e7aae19 100644
--- a/scenario/test_neutron_autoscaling.py
+++ b/scenario/test_neutron_autoscaling.py
@@ -40,7 +40,7 @@
             "image_id": self.conf.minimal_image_ref,
             "capacity": "1",
             "instance_type": self.conf.minimal_instance_type,
-            "fixed_subnet_name": self.conf.fixed_subnet_name,
+            "fixed_subnet": self.net['subnets'][0],
         }
 
         # Launch stack
@@ -50,8 +50,10 @@
         )
 
         # Check number of members
-        members = self.network_client.list_members()
-        self.assertEqual(1, len(members["members"]))
+        pool_resource = self.client.resources.get(stack_id, 'test_pool')
+        pool_members = self.network_client.list_members(
+            pool_id=pool_resource.physical_resource_id)['members']
+        self.assertEqual(1, len(pool_members))
 
         # Increase desired capacity and update the stack
         template = self._load_template(
@@ -65,5 +67,6 @@
         )
 
         # Check number of members
-        upd_members = self.network_client.list_members()
-        self.assertEqual(2, len(upd_members["members"]))
+        pool_members = self.network_client.list_members(
+            pool_id=pool_resource.physical_resource_id)['members']
+        self.assertEqual(2, len(pool_members))
diff --git a/scenario/test_neutron_loadbalancer.py b/scenario/test_neutron_loadbalancer.py
index bbe85bc..d8e0197 100644
--- a/scenario/test_neutron_loadbalancer.py
+++ b/scenario/test_neutron_loadbalancer.py
@@ -12,7 +12,8 @@
 #    under the License.
 
 import time
-import urllib
+
+from six.moves import urllib
 
 from heat_integrationtests.scenario import scenario_base
 
@@ -31,7 +32,7 @@
         resp = set()
         for count in range(10):
             time.sleep(1)
-            resp.add(urllib.urlopen('http://%s/' % ip).read())
+            resp.add(urllib.request.urlopen('http://%s/' % ip).read())
 
         self.assertEqual(expected_resp, resp)
 
@@ -54,8 +55,10 @@
             'key_name': self.keypair_name,
             'flavor': self.conf.minimal_instance_type,
             'image': self.conf.image_ref,
+            'network': self.net['name'],
             'private_subnet_id': self.net['subnets'][0],
-            'external_network_id': self.public_net['id']
+            'external_network_id': self.public_net['id'],
+            'timeout': self.conf.build_timeout
         }
 
         # Launch stack
@@ -69,8 +72,7 @@
         vip = self._stack_output(stack, 'vip')
         server1_ip = self._stack_output(stack, 'serv1_ip')
         server2_ip = self._stack_output(stack, 'serv2_ip')
-
-        # Check connection and info about received responces
+        # Check connection and info about received responses
         self.check_connectivity(server1_ip)
         self.collect_responses(server1_ip, {'server1\n'})
 
diff --git a/scenario/test_server_cfn_init.py b/scenario/test_server_cfn_init.py
index a49606c..b5b1e67 100644
--- a/scenario/test_server_cfn_init.py
+++ b/scenario/test_server_cfn_init.py
@@ -47,9 +47,6 @@
             # logs to be compared
             self._log_console_output(servers=[server])
 
-        # Check stack status
-        self._wait_for_stack_status(sid, 'CREATE_COMPLETE')
-
         stack = self.client.stacks.get(sid)
 
         # This is an assert of great significance, as it means the following
@@ -94,11 +91,11 @@
                via generated keypair.
         """
         parameters = {
-            "key_name": self.keypair_name,
-            "flavor": self.conf.instance_type,
-            "image": self.conf.image_ref,
-            "timeout": self.conf.build_timeout,
-            "subnet": self.net["subnets"][0],
+            'key_name': self.keypair_name,
+            'flavor': self.conf.instance_type,
+            'image': self.conf.image_ref,
+            'timeout': self.conf.build_timeout,
+            'subnet': self.net['subnets'][0],
         }
 
         # Launch stack
diff --git a/scenario/test_server_software_config.py b/scenario/test_server_software_config.py
index 8b614c1..19fd1a8 100644
--- a/scenario/test_server_software_config.py
+++ b/scenario/test_server_software_config.py
@@ -84,9 +84,6 @@
             # logs to be compared
             self._log_console_output(servers=[server])
 
-        # Check that stack was fully created
-        self._wait_for_stack_status(sid, 'CREATE_COMPLETE')
-
         complete_server_metadata = self.client.resources.metadata(
             sid, 'server')
 
diff --git a/scenario/test_volumes.py b/scenario/test_volumes.py
index 79d4931..7562304 100644
--- a/scenario/test_volumes.py
+++ b/scenario/test_volumes.py
@@ -61,8 +61,7 @@
 
         # Delete the stack and ensure a backup is created for volume_id
         # but the volume itself is gone
-        self.client.stacks.delete(stack_id)
-        self._wait_for_stack_status(stack_id, 'DELETE_COMPLETE')
+        self._stack_delete(stack_id)
         self.assertRaises(cinder_exceptions.NotFound,
                           self.volume_client.volumes.get,
                           volume_id)
@@ -97,8 +96,7 @@
                          testfile_data)
 
         # Delete the stack and ensure the volume is gone
-        self.client.stacks.delete(stack_identifier2)
-        self._wait_for_stack_status(stack_identifier2, 'DELETE_COMPLETE')
+        self._stack_delete(stack_identifier2)
         self.assertRaises(cinder_exceptions.NotFound,
                           self.volume_client.volumes.get,
                           volume_id2)