Merge "Validate TemplateResource schema when based on other templates"
diff --git a/common/config.py b/common/config.py
index 5a33590..b45a8d8 100644
--- a/common/config.py
+++ b/common/config.py
@@ -94,21 +94,26 @@
     cfg.IntOpt('tenant_network_mask_bits',
                default=28,
                help="The mask bits for tenant ipv4 subnets"),
-    cfg.BoolOpt('skip_software_config_tests',
-                default=True,
-                help="Skip software config deployment tests"),
+    cfg.BoolOpt('skip_scenario_tests',
+                default=False,
+                help="Skip all scenario tests"),
+    cfg.BoolOpt('skip_functional_tests',
+                default=False,
+                help="Skip all functional tests"),
+    cfg.ListOpt('skip_functional_test_list',
+                help="List of functional test class or class.method "
+                     "names to skip ex. AutoscalingGroupTest,"
+                     "InstanceGroupBasicTest.test_size_updates_work"),
+    cfg.ListOpt('skip_scenario_test_list',
+                help="List of scenario test class or class.method "
+                     "names to skip ex. NeutronLoadBalancerTest, "
+                     "CeilometerAlarmTest.test_alarm"),
+    cfg.ListOpt('skip_test_stack_action_list',
+                help="List of stack actions in tests to skip "
+                     "ex. ABANDON, ADOPT, SUSPEND, RESUME"),
     cfg.IntOpt('volume_size',
                default=1,
                help='Default size in GB for volumes created by volumes tests'),
-    cfg.BoolOpt('skip_stack_adopt_tests',
-                default=False,
-                help="Skip Stack Adopt Integration tests"),
-    cfg.BoolOpt('skip_stack_abandon_tests',
-                default=False,
-                help="Skip Stack Abandon Integration tests"),
-    cfg.BoolOpt('skip_notification_tests',
-                default=False,
-                help="Skip Notification Integration tests"),
     cfg.IntOpt('connectivity_timeout',
                default=120,
                help="Timeout in seconds to wait for connectivity to "
diff --git a/common/test.py b/common/test.py
index 584af6d..46911f0 100644
--- a/common/test.py
+++ b/common/test.py
@@ -250,9 +250,9 @@
 
     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':
+            # Handle UPDATE_COMPLETE/FAILED case: Make sure we don't
+            # wait for a stale UPDATE_COMPLETE/FAILED status.
+            if status in ('UPDATE_FAILED', 'UPDATE_COMPLETE'):
                 if self.updated_time.get(
                         stack_identifier) != stack.updated_time:
                     self.updated_time[stack_identifier] = stack.updated_time
@@ -263,8 +263,8 @@
         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':
+            # Handle UPDATE_COMPLETE/UPDATE_FAILED case.
+            if status in ('UPDATE_FAILED', 'UPDATE_COMPLETE'):
                 if self.updated_time.get(
                         stack_identifier) != stack.updated_time:
                     self.updated_time[stack_identifier] = stack.updated_time
@@ -279,7 +279,7 @@
                     stack_status_reason=stack.stack_status_reason)
 
     def _wait_for_stack_status(self, stack_identifier, status,
-                               failure_pattern='^.*_FAILED$',
+                               failure_pattern=None,
                                success_on_not_found=False):
         """
         Waits for a Stack to reach a given status.
@@ -288,7 +288,13 @@
         CREATE_COMPLETE, not just COMPLETE which is exposed
         via the status property of Stack in heatclient
         """
-        fail_regexp = re.compile(failure_pattern)
+        if failure_pattern:
+            fail_regexp = re.compile(failure_pattern)
+        elif 'FAILED' in status:
+            # If we're looking for e.g CREATE_FAILED, COMPLETE is unexpected.
+            fail_regexp = re.compile('^.*_COMPLETE$')
+        else:
+            fail_regexp = re.compile('^.*_FAILED$')
         build_timeout = self.conf.build_timeout
         build_interval = self.conf.build_interval
 
@@ -335,6 +341,8 @@
         build_timeout = self.conf.build_timeout
         build_interval = self.conf.build_interval
         start = timeutils.utcnow()
+        self.updated_time[stack_identifier] = self.client.stacks.get(
+            stack_identifier).updated_time
         while timeutils.delta_seconds(start,
                                       timeutils.utcnow()) < build_timeout:
             try:
@@ -426,7 +434,7 @@
             tags=tags
         )
         if expected_status not in ['ROLLBACK_COMPLETE'] and enable_cleanup:
-            self.addCleanup(self.client.stacks.delete, name)
+            self.addCleanup(self._stack_delete, name)
 
         stack = self.client.stacks.get(name)
         stack_identifier = '%s/%s' % (name, stack.id)
@@ -443,7 +451,8 @@
     def stack_adopt(self, stack_name=None, files=None,
                     parameters=None, environment=None, adopt_data=None,
                     wait_for_status='ADOPT_COMPLETE'):
-        if self.conf.skip_stack_adopt_tests:
+        if (self.conf.skip_test_stack_action_list and
+                'ADOPT' in self.conf.skip_test_stack_action_list):
             self.skipTest('Testing Stack adopt disabled in conf, skipping')
         name = stack_name or self._stack_rand_name()
         templ_files = files or {}
@@ -457,7 +466,7 @@
             environment=env,
             adopt_stack_data=adopt_data,
         )
-        self.addCleanup(self.client.stacks.delete, name)
+        self.addCleanup(self._stack_delete, name)
 
         stack = self.client.stacks.get(name)
         stack_identifier = '%s/%s' % (name, stack.id)
@@ -465,25 +474,32 @@
         return stack_identifier
 
     def stack_abandon(self, stack_id):
-        if self.conf.skip_stack_abandon_tests:
-            self.addCleanup(self.client.stacks.delete, stack_id)
+        if (self.conf.skip_test_stack_action_list and
+                'ABANDON' in self.conf.skip_test_stack_action_list):
+            self.addCleanup(self._stack_delete, stack_id)
             self.skipTest('Testing Stack abandon disabled in conf, skipping')
         info = self.client.stacks.abandon(stack_id=stack_id)
         return info
 
     def stack_suspend(self, stack_identifier):
+        if (self.conf.skip_test_stack_action_list and
+                'SUSPEND' in self.conf.skip_test_stack_action_list):
+            self.addCleanup(self._stack_delete, stack_identifier)
+            self.skipTest('Testing Stack suspend disabled in conf, skipping')
         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):
+        if (self.conf.skip_test_stack_action_list and
+                'RESUME' in self.conf.skip_test_stack_action_list):
+            self.addCleanup(self._stack_delete, stack_identifier)
+            self.skipTest('Testing Stack resume disabled in conf, skipping')
         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')
diff --git a/common/test_resources/test_resource.py b/common/test_resources/test_resource.py
deleted file mode 100644
index 55255a6..0000000
--- a/common/test_resources/test_resource.py
+++ /dev/null
@@ -1,134 +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.
-
-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 ValueError("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/functional_base.py b/functional/functional_base.py
new file mode 100644
index 0000000..3dff3e4
--- /dev/null
+++ b/functional/functional_base.py
@@ -0,0 +1,31 @@
+#    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 FunctionalTestsBase(test.HeatIntegrationTest):
+
+    def setUp(self):
+        super(FunctionalTestsBase, self).setUp()
+        self.check_skip_test()
+        self.client = self.orchestration_client
+
+    def check_skip_test(self):
+        test_cls_name = self.__class__.__name__
+        test_method_name = '.'.join([test_cls_name, self._testMethodName])
+        test_skipped = (self.conf.skip_functional_test_list and (
+            test_cls_name in self.conf.skip_functional_test_list or
+            test_method_name in self.conf.skip_functional_test_list))
+
+        if self.conf.skip_functional_tests or test_skipped:
+            self.skipTest('Test disabled in conf, skipping')
diff --git a/functional/test_autoscaling.py b/functional/test_autoscaling.py
index 301981f..9041405 100644
--- a/functional/test_autoscaling.py
+++ b/functional/test_autoscaling.py
@@ -13,16 +13,19 @@
 import copy
 import json
 
+from heatclient import exc
 from oslo_log import log as logging
+import six
 from testtools import matchers
 
 from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
 LOG = logging.getLogger(__name__)
 
 
-class AutoscalingGroupTest(test.HeatIntegrationTest):
+class AutoscalingGroupTest(functional_base.FunctionalTestsBase):
 
     template = '''
 {
@@ -104,7 +107,7 @@
   waiter:
     type: AWS::CloudFormation::WaitCondition
     properties:
-      Handle: {Ref: ready_poster}
+      Handle: {get_resource: ready_poster}
       Timeout: 1
 outputs:
   PublicIp:
@@ -113,7 +116,6 @@
 
     def setUp(self):
         super(AutoscalingGroupTest, self).setUp()
-        self.client = self.orchestration_client
         if not self.conf.image_ref:
             raise self.skipException("No image configured to test")
         if not self.conf.minimal_image_ref:
@@ -235,7 +237,7 @@
             parameters={},
             environment=env
         )
-        self.addCleanup(self.client.stacks.delete, stack_name)
+        self.addCleanup(self._stack_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_FAILED')
@@ -728,8 +730,13 @@
         self._wait_for_resource_status(
             stack_identifier, 'JobServerGroup', 'SUSPEND_COMPLETE')
 
-        # Send a signal and confirm nothing happened.
-        self.client.resources.signal(stack_identifier, 'ScaleUpPolicy')
+        # Send a signal and a exception will raise
+        ex = self.assertRaises(exc.BadRequest,
+                               self.client.resources.signal,
+                               stack_identifier, 'ScaleUpPolicy')
+
+        error_msg = 'Signal resource during SUSPEND is not supported'
+        self.assertIn(error_msg, six.text_type(ex))
         ev = self.wait_for_event_with_reason(
             stack_identifier,
             reason='Cannot signal resource during SUSPEND',
diff --git a/functional/test_aws_stack.py b/functional/test_aws_stack.py
index 172bc24..de8c6b8 100644
--- a/functional/test_aws_stack.py
+++ b/functional/test_aws_stack.py
@@ -20,12 +20,12 @@
 import yaml
 
 from heat_integrationtests.common import test
-
+from heat_integrationtests.functional import functional_base
 
 LOG = logging.getLogger(__name__)
 
 
-class AwsStackTest(test.HeatIntegrationTest):
+class AwsStackTest(functional_base.FunctionalTestsBase):
     test_template = '''
 HeatTemplateFormatVersion: '2012-12-12'
 Resources:
@@ -75,7 +75,6 @@
 
     def setUp(self):
         super(AwsStackTest, self).setUp()
-        self.client = self.orchestration_client
         self.object_container_name = AwsStackTest.__name__
         self.project_id = self.identity_client.auth_ref.project_id
         self.object_client.put_container(self.object_container_name)
diff --git a/functional/test_conditional_exposure.py b/functional/test_conditional_exposure.py
index c74b297..d037712 100644
--- a/functional/test_conditional_exposure.py
+++ b/functional/test_conditional_exposure.py
@@ -13,23 +13,10 @@
 from heatclient import exc
 import keystoneclient
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
-class ConditionalExposureTestBase(test.HeatIntegrationTest):
-    def setUp(self):
-        super(ConditionalExposureTestBase, self).setUp()
-        self.client = self.orchestration_client
-
-    def _delete(self, stack_name):
-        stacks = self.client.stacks.list()
-        for s in stacks:
-            if s.stack_name == stack_name:
-                self._stack_delete(s.identifier)
-                break
-
-
-class ServiceBasedExposureTest(ConditionalExposureTestBase):
+class ServiceBasedExposureTest(functional_base.FunctionalTestsBase):
     # NOTE(pas-ha) if we ever decide to install Sahara on Heat
     # functional gate, this must be changed to other not-installed
     # but in principle supported service
@@ -73,10 +60,42 @@
 
     def test_unavailable_resources_not_created(self):
         stack_name = self._stack_rand_name()
-        self.addCleanup(self._delete, stack_name)
         ex = self.assertRaises(exc.HTTPBadRequest,
                                self.client.stacks.create,
                                stack_name=stack_name,
                                template=self.unavailable_template)
         self.assertIn('ResourceTypeUnavailable', ex.message)
         self.assertIn('OS::Sahara::NodeGroupTemplate', ex.message)
+
+
+class RoleBasedExposureTest(functional_base.FunctionalTestsBase):
+    forbidden_resource_type = "OS::Nova::Flavor"
+    fl_tmpl = """
+heat_template_version: 2015-10-15
+
+resources:
+  not4everyone:
+    type: OS::Nova::Flavor
+    properties:
+      ram: 20000
+      vcpus: 10
+"""
+
+    def test_non_admin_forbidden_create_flavors(self):
+        """Fail to create Flavor resource w/o admin role
+
+        Integration tests job runs as normal OpenStack user,
+        and OS::Nova:Flavor is configured to require
+        admin role in default policy file of Heat.
+        """
+        stack_name = self._stack_rand_name()
+        ex = self.assertRaises(exc.Forbidden,
+                               self.client.stacks.create,
+                               stack_name=stack_name,
+                               template=self.fl_tmpl)
+        self.assertIn(self.forbidden_resource_type, ex.message)
+
+    def test_forbidden_resource_not_listed(self):
+        resources = self.client.resource_types.list()
+        self.assertNotIn(self.forbidden_resource_type,
+                         (r.resource_type for r in resources))
diff --git a/functional/test_create_update.py b/functional/test_create_update.py
index 6053303..1b273dd 100644
--- a/functional/test_create_update.py
+++ b/functional/test_create_update.py
@@ -12,9 +12,10 @@
 
 
 import copy
-from heat_integrationtests.common import test
 import json
 
+from heat_integrationtests.functional import functional_base
+
 test_template_one_resource = {
     'heat_template_version': '2013-05-23',
     'description': 'Test template to create one instance.',
@@ -25,7 +26,10 @@
                 'value': 'Test1',
                 'fail': False,
                 'update_replace': False,
-                'wait_secs': 0
+                'wait_secs': 0,
+                'action_wait_secs': {'create': 1},
+                'client_name': 'nova',
+                'entity_name': 'servers',
             }
         }
     }
@@ -41,7 +45,8 @@
                 'value': 'Test1',
                 'fail': False,
                 'update_replace': False,
-                'wait_secs': 0
+                'wait_secs': 0,
+                'action_wait_secs': {'update': 1}
             }
         },
         'test2': {
@@ -68,10 +73,9 @@
         return modified_template
 
 
-class CreateStackTest(test.HeatIntegrationTest):
+class CreateStackTest(functional_base.FunctionalTestsBase):
     def setUp(self):
         super(CreateStackTest, self).setUp()
-        self.client = self.orchestration_client
 
     def test_create_rollback(self):
         values = {'fail': True, 'value': 'test_create_rollback'}
@@ -84,7 +88,7 @@
             disable_rollback=False)
 
 
-class UpdateStackTest(test.HeatIntegrationTest):
+class UpdateStackTest(functional_base.FunctionalTestsBase):
 
     provider_template = {
         'heat_template_version': '2013-05-23',
@@ -132,7 +136,6 @@
 
     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,
@@ -240,6 +243,29 @@
         self.assertEqual(updated_resources,
                          self.list_resources(stack_identifier))
 
+    def test_stack_update_from_failed(self):
+        # Prove it's possible to update from an UPDATE_FAILED state
+        template = _change_rsrc_properties(test_template_one_resource,
+                                           ['test1'],
+                                           {'value': 'test_update_failed'})
+        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_one_resource, ['test1'], {'fail': True})
+        # Update with bad template, we should fail
+        self.update_stack(stack_identifier, tmpl_update,
+                          expected_status='UPDATE_FAILED')
+        # but then passing a good template should succeed
+        self.update_stack(stack_identifier, test_template_two_resource)
+        updated_resources = {'test1': 'OS::Heat::TestResource',
+                             'test2': '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'],
diff --git a/functional/test_create_update_neutron_port.py b/functional/test_create_update_neutron_port.py
index 3d82bc4..4b2df59 100644
--- a/functional/test_create_update_neutron_port.py
+++ b/functional/test_create_update_neutron_port.py
@@ -12,7 +12,7 @@
 
 from testtools import testcase
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
 test_template = '''
@@ -44,11 +44,10 @@
 '''
 
 
-class UpdatePortTest(test.HeatIntegrationTest):
+class UpdatePortTest(functional_base.FunctionalTestsBase):
 
     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)
diff --git a/functional/test_default_parameters.py b/functional/test_default_parameters.py
index 00000fd..3e00c35 100644
--- a/functional/test_default_parameters.py
+++ b/functional/test_default_parameters.py
@@ -12,10 +12,10 @@
 
 import yaml
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
-class DefaultParametersTest(test.HeatIntegrationTest):
+class DefaultParametersTest(functional_base.FunctionalTestsBase):
 
     template = '''
 heat_template_version: 2013-05-23
@@ -65,7 +65,6 @@
 
     def setUp(self):
         super(DefaultParametersTest, self).setUp()
-        self.client = self.orchestration_client
 
     def test_defaults(self):
         env = {'parameters': {}, 'parameter_defaults': {}}
diff --git a/functional/test_encrypted_parameter.py b/functional/test_encrypted_parameter.py
index c862f17..21dc925 100644
--- a/functional/test_encrypted_parameter.py
+++ b/functional/test_encrypted_parameter.py
@@ -10,35 +10,55 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
-class EncryptedParametersTest(test.HeatIntegrationTest):
+class EncryptedParametersTest(functional_base.FunctionalTestsBase):
 
     template = '''
-heat_template_version: 2013-05-23
+heat_template_version: 2014-10-16
 parameters:
+  image:
+    type: string
+  flavor:
+    type: string
+  network:
+    type: string
   foo:
     type: string
-    description: Parameter with encryption turned on
+    description: 'parameter with encryption turned on'
     hidden: true
     default: secret
+resources:
+  server_with_encrypted_property:
+    type: OS::Nova::Server
+    properties:
+      name: { get_param: foo }
+      image: { get_param: image }
+      flavor: { get_param: flavor }
+      networks: [{network: {get_param: network} }]
 outputs:
   encrypted_foo_param:
-    description: ''
-    value: {get_param: foo}
+    description: 'encrypted param'
+    value: { get_param: foo }
 '''
 
     def setUp(self):
         super(EncryptedParametersTest, self).setUp()
-        self.client = self.orchestration_client
 
     def test_db_encryption(self):
-        # Create a stack with a non-default value for 'foo' to be encrypted
+        # Create a stack with the value of 'foo' to be encrypted
         foo_param = 'my_encrypted_foo'
+        parameters = {
+            "image": self.conf.minimal_image_ref,
+            "flavor": self.conf.minimal_instance_type,
+            'network': self.conf.fixed_network_name,
+            "foo": foo_param
+        }
+
         stack_identifier = self.stack_create(
             template=self.template,
-            parameters={'foo': foo_param}
+            parameters=parameters
         )
         stack = self.client.stacks.get(stack_identifier)
 
diff --git a/functional/test_encryption_vol_type.py b/functional/test_encryption_vol_type.py
new file mode 100644
index 0000000..e1a3e76
--- /dev/null
+++ b/functional/test_encryption_vol_type.py
@@ -0,0 +1,91 @@
+#    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 clients
+from heat_integrationtests.common import config
+from heat_integrationtests.functional import functional_base
+
+test_encryption_vol_type = {
+    'heat_template_version': '2015-04-30',
+    'description': 'Test template to create encryption volume type.',
+    'resources': {
+        'my_volume_type': {
+            'type': 'OS::Cinder::VolumeType',
+            'properties': {
+                'name': 'LUKS'
+            }
+        },
+        'my_encrypted_vol_type': {
+            'type': 'OS::Cinder::EncryptedVolumeType',
+            'properties': {
+                'provider': 'nova.volume.encryptors.luks.LuksEncryptor',
+                'control_location': 'front-end',
+                'cipher': 'aes-xts-plain64',
+                'key_size': 512,
+                'volume_type': {'get_resource': 'my_volume_type'}
+            }
+        }
+    }
+}
+
+
+class EncryptionVolTypeTest(functional_base.FunctionalTestsBase):
+    def setUp(self):
+        super(EncryptionVolTypeTest, self).setUp()
+        self.conf = config.init_conf()
+        # cinder security policy usage of volume type is limited
+        # to being used by administrators only.
+        # Temporarily set username as admin for this test case.
+        self.conf.username = 'admin'
+        self.manager = clients.ClientManager(self.conf)
+        self.client = self.manager.orchestration_client
+        self.volume_client = self.manager.volume_client
+
+    def check_stack(self, sid):
+        vt = 'my_volume_type'
+        e_vt = 'my_encrypted_vol_type'
+
+        # check if only two resources are present.
+        expected_resources = {vt: 'OS::Cinder::VolumeType',
+                              e_vt: 'OS::Cinder::EncryptedVolumeType'}
+        self.assertEqual(expected_resources,
+                         self.list_resources(sid))
+
+        e_vt_obj = self.client.resources.get(sid, e_vt)
+        my_encrypted_vol_type_tmpl_prop = test_encryption_vol_type[
+            'resources']['my_encrypted_vol_type']['properties']
+
+        # check if the phy rsrc specs was created in accordance with template.
+        phy_rsrc_specs = self.volume_client.volume_encryption_types.get(
+            e_vt_obj.physical_resource_id)
+        self.assertEqual(my_encrypted_vol_type_tmpl_prop['key_size'],
+                         phy_rsrc_specs.key_size)
+        self.assertEqual(my_encrypted_vol_type_tmpl_prop['provider'],
+                         phy_rsrc_specs.provider)
+        self.assertEqual(my_encrypted_vol_type_tmpl_prop['cipher'],
+                         phy_rsrc_specs.cipher)
+        self.assertEqual(my_encrypted_vol_type_tmpl_prop['control_location'],
+                         phy_rsrc_specs.control_location)
+
+    def test_create_update(self):
+        stack_identifier = self.stack_create(
+            template=test_encryption_vol_type)
+        self.check_stack(stack_identifier)
+
+        # Change some properties and trigger update.
+        my_encrypted_vol_type_tmpl_prop = test_encryption_vol_type[
+            'resources']['my_encrypted_vol_type']['properties']
+        my_encrypted_vol_type_tmpl_prop['key_size'] = 256
+        my_encrypted_vol_type_tmpl_prop['cipher'] = 'aes-cbc-essiv'
+        self.update_stack(stack_identifier, test_encryption_vol_type)
+        self.check_stack(stack_identifier)
diff --git a/functional/test_heat_autoscaling.py b/functional/test_heat_autoscaling.py
index dbe3e8f..e87badb 100644
--- a/functional/test_heat_autoscaling.py
+++ b/functional/test_heat_autoscaling.py
@@ -10,10 +10,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
-class HeatAutoscalingTest(test.HeatIntegrationTest):
+class HeatAutoscalingTest(functional_base.FunctionalTestsBase):
     template = '''
 heat_template_version: 2014-10-16
 
@@ -74,7 +74,6 @@
 
     def setUp(self):
         super(HeatAutoscalingTest, self).setUp()
-        self.client = self.orchestration_client
 
     def _assert_output_values(self, stack_id):
         stack = self.client.stacks.get(stack_id)
@@ -99,7 +98,7 @@
         self._assert_output_values(stack_id)
 
 
-class AutoScalingGroupUpdateWithNoChanges(test.HeatIntegrationTest):
+class AutoScalingGroupUpdateWithNoChanges(functional_base.FunctionalTestsBase):
 
     template = '''
 heat_template_version: 2013-05-23
@@ -123,7 +122,6 @@
 
     def setUp(self):
         super(AutoScalingGroupUpdateWithNoChanges, self).setUp()
-        self.client = self.orchestration_client
 
     def test_as_group_update_without_resource_changes(self):
         stack_identifier = self.stack_create(template=self.template)
diff --git a/functional/test_hooks.py b/functional/test_hooks.py
index 5c6f6bd..ce1e7aa 100644
--- a/functional/test_hooks.py
+++ b/functional/test_hooks.py
@@ -14,17 +14,16 @@
 
 import yaml
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
 LOG = logging.getLogger(__name__)
 
 
-class HooksTest(test.HeatIntegrationTest):
+class HooksTest(functional_base.FunctionalTestsBase):
 
     def setUp(self):
         super(HooksTest, self).setUp()
-        self.client = self.orchestration_client
         self.template = {'heat_template_version': '2014-10-16',
                          'resources': {
                              'foo_step1': {'type': 'OS::Heat::RandomString'},
diff --git a/functional/test_instance_group.py b/functional/test_instance_group.py
index 1455d0b..b8bcc3d 100644
--- a/functional/test_instance_group.py
+++ b/functional/test_instance_group.py
@@ -15,10 +15,10 @@
 
 from testtools import matchers
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
-class InstanceGroupTest(test.HeatIntegrationTest):
+class InstanceGroupTest(functional_base.FunctionalTestsBase):
 
     template = '''
 {
@@ -105,7 +105,6 @@
 
     def setUp(self):
         super(InstanceGroupTest, self).setUp()
-        self.client = self.orchestration_client
         if not self.conf.image_ref:
             raise self.skipException("No image configured to test")
         if not self.conf.minimal_image_ref:
@@ -226,7 +225,7 @@
             parameters={},
             environment=env
         )
-        self.addCleanup(self.client.stacks.delete, stack_name)
+        self.addCleanup(self._stack_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_FAILED')
diff --git a/functional/test_notifications.py b/functional/test_notifications.py
index c729a67..3b8e003 100644
--- a/functional/test_notifications.py
+++ b/functional/test_notifications.py
@@ -17,7 +17,7 @@
 import requests
 
 from heat_integrationtests.common import test
-
+from heat_integrationtests.functional import functional_base
 
 BASIC_NOTIFICATIONS = [
     'orchestration.stack.create.start',
@@ -70,7 +70,7 @@
         return self._notifications
 
 
-class NotificationTest(test.HeatIntegrationTest):
+class NotificationTest(functional_base.FunctionalTestsBase):
 
     basic_template = '''
 heat_template_version: 2013-05-23
@@ -124,10 +124,6 @@
 
     def setUp(self):
         super(NotificationTest, self).setUp()
-        if self.conf.skip_notification_tests:
-            self.skipTest('Testing Notifications disabled in conf, skipping')
-
-        self.client = self.orchestration_client
         self.exchange = kombu.Exchange('heat', 'topic', durable=False)
         queue = kombu.Queue(exchange=self.exchange,
                             routing_key='notifications.info',
diff --git a/functional/test_preview.py b/functional/test_preview.py
new file mode 100644
index 0000000..19da01b
--- /dev/null
+++ b/functional/test_preview.py
@@ -0,0 +1,188 @@
+#    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
+from heatclient import exc
+import six
+
+
+class StackPreviewTest(test.HeatIntegrationTest):
+    template = '''
+heat_template_version: 2015-04-30
+parameters:
+  incomming:
+    type: string
+resources:
+  one:
+    type: OS::Heat::TestResource
+    properties:
+      value: fred
+  two:
+    type: OS::Heat::TestResource
+    properties:
+      value: {get_param: incomming}
+    depends_on: one
+outputs:
+  main_out:
+    value: {get_attr: [two, output]}
+    '''
+    env = '''
+parameters:
+  incomming: abc
+    '''
+
+    def setUp(self):
+        super(StackPreviewTest, self).setUp()
+        self.client = self.orchestration_client
+        self.project_id = self.identity_client.auth_ref.project_id
+
+    def _assert_resource(self, res, stack_name):
+        self.assertEqual(stack_name, res['stack_name'])
+        self.assertEqual('INIT', res['resource_action'])
+        self.assertEqual('COMPLETE', res['resource_status'])
+        for field in ('resource_status_reason', 'physical_resource_id',
+                      'description'):
+            self.assertIn(field, res)
+            self.assertEqual('', res[field])
+        # 'creation_time' and 'updated_time' are None when preview
+        for field in ('creation_time', 'updated_time'):
+            self.assertIn(field, res)
+            self.assertIsNone(res[field])
+        self.assertIn('output', res['attributes'])
+
+        # resource_identity
+        self.assertEqual(stack_name,
+                         res['resource_identity']['stack_name'])
+        self.assertEqual('None', res['resource_identity']['stack_id'])
+        self.assertEqual(self.project_id,
+                         res['resource_identity']['tenant'])
+        self.assertEqual('/resources/%s' % res['resource_name'],
+                         res['resource_identity']['path'])
+        # stack_identity
+        self.assertEqual(stack_name,
+                         res['stack_identity']['stack_name'])
+        self.assertEqual('None', res['stack_identity']['stack_id'])
+        self.assertEqual(self.project_id,
+                         res['stack_identity']['tenant'])
+        self.assertEqual('', res['stack_identity']['path'])
+
+    def _assert_results(self, result, stack_name):
+        # global stuff.
+        self.assertEqual(stack_name, result['stack_name'])
+        self.assertTrue(result['disable_rollback'])
+        self.assertEqual('None', result['id'])
+        self.assertIsNone(result['parent'])
+        self.assertEqual('No description', result['template_description'])
+
+        # parameters
+        self.assertEqual('None', result['parameters']['OS::stack_id'])
+        self.assertEqual(stack_name, result['parameters']['OS::stack_name'])
+        self.assertEqual('abc', result['parameters']['incomming'])
+
+    def test_basic_pass(self):
+        stack_name = self._stack_rand_name()
+        result = self.client.stacks.preview(
+            template=self.template,
+            stack_name=stack_name,
+            disable_rollback=True,
+            environment=self.env).to_dict()
+
+        self._assert_results(result, stack_name)
+        for res in result['resources']:
+            self._assert_resource(res, stack_name)
+            self.assertEqual('OS::Heat::TestResource',
+                             res['resource_type'])
+
+            # common properties
+            self.assertEqual(False, res['properties']['fail'])
+            self.assertEqual(0, res['properties']['wait_secs'])
+            self.assertEqual(False, res['properties']['update_replace'])
+
+            if res['resource_name'] == 'one':
+                self.assertEqual('fred', res['properties']['value'])
+                self.assertEqual(['two'], res['required_by'])
+            if res['resource_name'] == 'two':
+                self.assertEqual('abc', res['properties']['value'])
+                self.assertEqual([], res['required_by'])
+
+    def test_basic_fail(self):
+        stack_name = self._stack_rand_name()
+
+        # break the template so it fails validation.
+        wont_work = self.template.replace('get_param: incomming',
+                                          'get_param: missing')
+        excp = self.assertRaises(exc.HTTPBadRequest,
+                                 self.client.stacks.preview,
+                                 template=wont_work,
+                                 stack_name=stack_name,
+                                 disable_rollback=True,
+                                 environment=self.env)
+
+        self.assertIn('Property error: : resources.two.properties.value: '
+                      ': The Parameter (missing) was not provided.',
+                      six.text_type(excp))
+
+    def test_nested_pass(self):
+        """Nested stacks need to recurse down the stacks."""
+        main_template = '''
+heat_template_version: 2015-04-30
+parameters:
+  incomming:
+    type: string
+resources:
+  main:
+    type: nested.yaml
+    properties:
+      value: {get_param: incomming}
+outputs:
+  main_out:
+    value: {get_attr: [main, output]}
+    '''
+        nested_template = '''
+heat_template_version: 2015-04-30
+parameters:
+  value:
+    type: string
+resources:
+  nested:
+    type: OS::Heat::TestResource
+    properties:
+      value: {get_param: value}
+outputs:
+  output:
+    value: {get_attr: [nested, output]}
+'''
+        stack_name = self._stack_rand_name()
+        result = self.client.stacks.preview(
+            disable_rollback=True,
+            stack_name=stack_name,
+            template=main_template,
+            files={'nested.yaml': nested_template},
+            environment=self.env).to_dict()
+
+        self._assert_results(result, stack_name)
+
+        # nested resources return a list of their resources.
+        res = result['resources'][0][0]
+        nested_stack_name = '%s-%s' % (stack_name,
+                                       res['parent_resource'])
+
+        self._assert_resource(res, nested_stack_name)
+        self.assertEqual('OS::Heat::TestResource',
+                         res['resource_type'])
+
+        self.assertEqual(False, res['properties']['fail'])
+        self.assertEqual(0, res['properties']['wait_secs'])
+        self.assertEqual(False, res['properties']['update_replace'])
+
+        self.assertEqual('abc', res['properties']['value'])
+        self.assertEqual([], res['required_by'])
diff --git a/functional/test_reload_on_sighup.py b/functional/test_reload_on_sighup.py
index f149a58..cba5386 100644
--- a/functional/test_reload_on_sighup.py
+++ b/functional/test_reload_on_sighup.py
@@ -17,10 +17,10 @@
 from oslo_concurrency import processutils
 from six.moves import configparser
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
-class ReloadOnSighupTest(test.HeatIntegrationTest):
+class ReloadOnSighupTest(functional_base.FunctionalTestsBase):
 
     def setUp(self):
         self.config_file = "/etc/heat/heat.conf"
diff --git a/functional/test_remote_stack.py b/functional/test_remote_stack.py
index 868f24e..91a165c 100644
--- a/functional/test_remote_stack.py
+++ b/functional/test_remote_stack.py
@@ -14,10 +14,10 @@
 from heatclient import exc
 import six
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
-class RemoteStackTest(test.HeatIntegrationTest):
+class RemoteStackTest(functional_base.FunctionalTestsBase):
     template = '''
 heat_template_version: 2013-05-23
 resources:
@@ -45,7 +45,6 @@
 
     def setUp(self):
         super(RemoteStackTest, self).setUp()
-        self.client = self.orchestration_client
 
     def test_remote_stack_alone(self):
         stack_id = self.stack_create(template=self.remote_template)
diff --git a/functional/test_resource_group.py b/functional/test_resource_group.py
index 8bc9950..5335245 100644
--- a/functional/test_resource_group.py
+++ b/functional/test_resource_group.py
@@ -17,10 +17,10 @@
 import six
 import yaml
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
-class ResourceGroupTest(test.HeatIntegrationTest):
+class ResourceGroupTest(functional_base.FunctionalTestsBase):
     template = '''
 heat_template_version: 2013-05-23
 resources:
@@ -44,7 +44,6 @@
 
     def setUp(self):
         super(ResourceGroupTest, self).setUp()
-        self.client = self.orchestration_client
 
     def _group_nested_identifier(self, stack_identifier,
                                  group_name='random_group'):
@@ -328,7 +327,7 @@
         self.assertNotEqual(initial_rand, updated_rand)
 
 
-class ResourceGroupTestNullParams(test.HeatIntegrationTest):
+class ResourceGroupTestNullParams(functional_base.FunctionalTestsBase):
     template = '''
 heat_template_version: 2013-05-23
 parameters:
@@ -383,7 +382,6 @@
 
     def setUp(self):
         super(ResourceGroupTestNullParams, self).setUp()
-        self.client = self.orchestration_client
 
     def test_create_pass_zero_parameter(self):
         templ = self.template.replace('type: empty',
@@ -403,7 +401,7 @@
         self.assertEqual(self.param, self._stack_output(stack, 'val')[0])
 
 
-class ResourceGroupAdoptTest(test.HeatIntegrationTest):
+class ResourceGroupAdoptTest(functional_base.FunctionalTestsBase):
     """Prove that we can do resource group adopt."""
 
     main_template = '''
@@ -424,7 +422,6 @@
 
     def setUp(self):
         super(ResourceGroupAdoptTest, self).setUp()
-        self.client = self.orchestration_client
 
     def _yaml_to_json(self, yaml_templ):
         return yaml.load(yaml_templ)
@@ -474,7 +471,7 @@
         self.assertEqual('different', self._stack_output(stack, 'test1'))
 
 
-class ResourceGroupErrorResourceTest(test.HeatIntegrationTest):
+class ResourceGroupErrorResourceTest(functional_base.FunctionalTestsBase):
     template = '''
 heat_template_version: "2013-05-23"
 resources:
@@ -497,7 +494,6 @@
 
     def setUp(self):
         super(ResourceGroupErrorResourceTest, self).setUp()
-        self.client = self.orchestration_client
 
     def test_fail(self):
         stack_identifier = self.stack_create(
diff --git a/functional/test_software_config.py b/functional/test_software_config.py
index 2e0ed76..af7d671 100644
--- a/functional/test_software_config.py
+++ b/functional/test_software_config.py
@@ -10,11 +10,17 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from heat_integrationtests.common import test
+from oslo_utils import timeutils
+import requests
+import time
+import yaml
+
+from heat_integrationtests.common import exceptions
+from heat_integrationtests.functional import functional_base
 
 
-class ParallelDeploymentsTest(test.HeatIntegrationTest):
-    template = '''
+class ParallelDeploymentsTest(functional_base.FunctionalTestsBase):
+    server_template = '''
 heat_template_version: "2013-05-23"
 parameters:
   flavor:
@@ -30,48 +36,112 @@
       image: {get_param: image}
       flavor: {get_param: flavor}
       user_data_format: SOFTWARE_CONFIG
-      networks: [{network: {get_param: network} }]
+      networks: [{network: {get_param: network}}]
+outputs:
+  server:
+    value: {get_resource: server}
+'''
+
+    config_template = '''
+heat_template_version: "2013-05-23"
+parameters:
+  server:
+    type: string
+resources:
   config:
     type: OS::Heat::SoftwareConfig
     properties:
-      config: hi!
-  dep1:
-    type: OS::Heat::SoftwareDeployment
-    properties:
-      config: {get_resource: config}
-      server: {get_resource: server}
-      signal_transport: NO_SIGNAL
-  dep2:
-    type: OS::Heat::SoftwareDeployment
-    properties:
-      config: {get_resource: config}
-      server: {get_resource: server}
-      signal_transport: NO_SIGNAL
-  dep3:
-    type: OS::Heat::SoftwareDeployment
-    properties:
-      config: {get_resource: config}
-      server: {get_resource: server}
-      signal_transport: NO_SIGNAL
-  dep4:
-    type: OS::Heat::SoftwareDeployment
-    properties:
-      config: {get_resource: config}
-      server: {get_resource: server}
-      signal_transport: NO_SIGNAL
 '''
 
-    def setUp(self):
-        super(ParallelDeploymentsTest, self).setUp()
-        self.client = self.orchestration_client
+    deployment_snippet = '''
+type: OS::Heat::SoftwareDeployments
+properties:
+  config: {get_resource: config}
+  servers: {'0': {get_param: server}}
+'''
 
-    def test_fail(self):
+    enable_cleanup = True
+
+    def test_deployments_metadata(self):
         parms = {'flavor': self.conf.minimal_instance_type,
                  'network': self.conf.fixed_network_name,
                  'image': self.conf.minimal_image_ref}
         stack_identifier = self.stack_create(
             parameters=parms,
-            template=self.template)
-        stack = self.client.stacks.get(stack_identifier)
-        server_metadata = self.client.resources.metadata(stack.id, 'server')
-        self.assertEqual(4, len(server_metadata['deployments']))
+            template=self.server_template,
+            enable_cleanup=self.enable_cleanup)
+        server_stack = self.client.stacks.get(stack_identifier)
+        server = server_stack.outputs[0]['output_value']
+
+        config_stacks = []
+        # add up to 3 stacks each with up to 3 deployments
+        deploy_count = 0
+        deploy_count = self.deploy_many_configs(
+            stack_identifier,
+            server,
+            config_stacks,
+            2,
+            5,
+            deploy_count)
+        self.deploy_many_configs(
+            stack_identifier,
+            server,
+            config_stacks,
+            3,
+            3,
+            deploy_count)
+
+        self.signal_deployments(stack_identifier)
+        for config_stack in config_stacks:
+            self._wait_for_stack_status(config_stack, 'CREATE_COMPLETE')
+
+    def deploy_many_configs(self, stack, server, config_stacks,
+                            stack_count, deploys_per_stack,
+                            deploy_count_start):
+        for a in range(stack_count):
+            config_stacks.append(
+                self.deploy_config(server, deploys_per_stack))
+
+        new_count = deploy_count_start + stack_count * deploys_per_stack
+        self.wait_for_deploy_metadata_set(stack, new_count)
+        return new_count
+
+    def deploy_config(self, server, deploy_count):
+        parms = {'server': server}
+        template = yaml.safe_load(self.config_template)
+        resources = template['resources']
+        resources['config']['properties'] = {'config': 'x' * 10000}
+        for a in range(deploy_count):
+            resources['dep_%s' % a] = yaml.safe_load(self.deployment_snippet)
+        return self.stack_create(
+            parameters=parms,
+            template=template,
+            enable_cleanup=self.enable_cleanup,
+            expected_status=None)
+
+    def wait_for_deploy_metadata_set(self, stack, deploy_count):
+        build_timeout = self.conf.build_timeout
+        build_interval = self.conf.build_interval
+
+        start = timeutils.utcnow()
+        while timeutils.delta_seconds(start,
+                                      timeutils.utcnow()) < build_timeout:
+            server_metadata = self.client.resources.metadata(
+                stack, 'server')
+            if len(server_metadata['deployments']) == deploy_count:
+                return
+            time.sleep(build_interval)
+
+        message = ('Deployment resources failed to be created within '
+                   'the required time (%s s).' %
+                   (build_timeout))
+        raise exceptions.TimeoutException(message)
+
+    def signal_deployments(self, stack_identifier):
+        server_metadata = self.client.resources.metadata(
+            stack_identifier, 'server')
+        for dep in server_metadata['deployments']:
+            iv = dict((i['name'], i['value']) for i in dep['inputs'])
+            sigurl = iv.get('deploy_signal_id')
+            requests.post(sigurl, data='{}',
+                          headers={'content-type': None})
diff --git a/functional/test_stack_tags.py b/functional/test_stack_tags.py
index cdcdcd3..4a97798 100644
--- a/functional/test_stack_tags.py
+++ b/functional/test_stack_tags.py
@@ -10,21 +10,27 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
-class StackTagTest(test.HeatIntegrationTest):
+class StackTagTest(functional_base.FunctionalTestsBase):
 
     template = '''
 heat_template_version: 2014-10-16
 description:
   foo
+parameters:
+  input:
+    type: string
+    default: test
+resources:
+  not-used:
+    type: OS::Heat::TestResource
+    properties:
+      wait_secs: 1
+      value: {get_param: input}
 '''
 
-    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'
@@ -42,7 +48,8 @@
         self.update_stack(
             stack_identifier,
             template=self.template,
-            tags=updated_tags)
+            tags=updated_tags,
+            parameters={'input': 'next'})
 
         # Ensure property tag is populated and matches updated tags
         updated_stack = self.client.stacks.get(stack_identifier)
@@ -51,7 +58,8 @@
         # Delete tags
         self.update_stack(
             stack_identifier,
-            template=self.template
+            template=self.template,
+            parameters={'input': 'none'}
         )
 
         # Ensure property tag is not populated
diff --git a/functional/test_swiftsignal_update.py b/functional/test_swiftsignal_update.py
index 0f1c43c..5321014 100644
--- a/functional/test_swiftsignal_update.py
+++ b/functional/test_swiftsignal_update.py
@@ -11,7 +11,7 @@
 #    under the License.
 
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 test_template = '''
 heat_template_version: 2014-10-16
@@ -31,11 +31,10 @@
 '''
 
 
-class SwiftSignalHandleUpdateTest(test.HeatIntegrationTest):
+class SwiftSignalHandleUpdateTest(functional_base.FunctionalTestsBase):
 
     def setUp(self):
         super(SwiftSignalHandleUpdateTest, self).setUp()
-        self.client = self.orchestration_client
 
     def test_stack_update_same_template_replace_no_url(self):
         stack_identifier = self.stack_create(template=test_template)
diff --git a/functional/test_template_resource.py b/functional/test_template_resource.py
index e047da1..1ec05d9 100644
--- a/functional/test_template_resource.py
+++ b/functional/test_template_resource.py
@@ -17,9 +17,10 @@
 import yaml
 
 from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
-class TemplateResourceTest(test.HeatIntegrationTest):
+class TemplateResourceTest(functional_base.FunctionalTestsBase):
     """Prove that we can use the registry in a nested provider."""
 
     template = '''
@@ -48,7 +49,6 @@
 
     def setUp(self):
         super(TemplateResourceTest, self).setUp()
-        self.client = self.orchestration_client
 
     def test_nested_env(self):
         main_templ = '''
@@ -148,7 +148,7 @@
         self.assertEqual('freddy', secret_out2)
 
 
-class NestedAttributesTest(test.HeatIntegrationTest):
+class NestedAttributesTest(functional_base.FunctionalTestsBase):
     """Prove that we can use the template resource references."""
 
     main_templ = '''
@@ -174,7 +174,6 @@
 
     def setUp(self):
         super(NestedAttributesTest, self).setUp()
-        self.client = self.orchestration_client
 
     def test_stack_ref(self):
         nested_templ = '''
@@ -246,7 +245,7 @@
         self.assertEqual(old_way, test_attr2)
 
 
-class TemplateResourceUpdateTest(test.HeatIntegrationTest):
+class TemplateResourceUpdateTest(functional_base.FunctionalTestsBase):
     """Prove that we can do template resource updates."""
 
     main_template = '''
@@ -460,7 +459,6 @@
 
     def setUp(self):
         super(TemplateResourceUpdateTest, self).setUp()
-        self.client = self.orchestration_client
 
     def test_template_resource_update_template_schema(self):
         stack_identifier = self.stack_create(
@@ -484,7 +482,7 @@
                                 self._stack_output(stack, 'value'))
 
 
-class TemplateResourceUpdateFailedTest(test.HeatIntegrationTest):
+class TemplateResourceUpdateFailedTest(functional_base.FunctionalTestsBase):
     """Prove that we can do updates on a nested stack to fix a stack."""
     main_template = '''
 HeatTemplateFormatVersion: '2012-12-12'
@@ -507,7 +505,6 @@
 
     def setUp(self):
         super(TemplateResourceUpdateFailedTest, self).setUp()
-        self.client = self.orchestration_client
         self.assign_keypair()
 
     def test_update_on_failed_create(self):
@@ -528,7 +525,7 @@
                           files={'server_fail.yaml': self.nested_templ})
 
 
-class TemplateResourceAdoptTest(test.HeatIntegrationTest):
+class TemplateResourceAdoptTest(functional_base.FunctionalTestsBase):
     """Prove that we can do template resource adopt/abandon."""
 
     main_template = '''
@@ -563,7 +560,6 @@
 
     def setUp(self):
         super(TemplateResourceAdoptTest, self).setUp()
-        self.client = self.orchestration_client
 
     def _yaml_to_json(self, yaml_templ):
         return yaml.load(yaml_templ)
@@ -608,7 +604,7 @@
         self.assertEqual('goopie', self._stack_output(stack, 'value'))
 
 
-class TemplateResourceCheckTest(test.HeatIntegrationTest):
+class TemplateResourceCheckTest(functional_base.FunctionalTestsBase):
     """Prove that we can do template resource check."""
 
     main_template = '''
@@ -643,7 +639,6 @@
 
     def setUp(self):
         super(TemplateResourceCheckTest, self).setUp()
-        self.client = self.orchestration_client
 
     def test_check(self):
         stack_identifier = self.stack_create(
@@ -655,7 +650,7 @@
         self._wait_for_stack_status(stack_identifier, 'CHECK_COMPLETE')
 
 
-class TemplateResourceErrorMessageTest(test.HeatIntegrationTest):
+class TemplateResourceErrorMessageTest(functional_base.FunctionalTestsBase):
     """Prove that nested stack errors don't suck."""
     template = '''
 HeatTemplateFormatVersion: '2012-12-12'
@@ -675,7 +670,6 @@
 
     def setUp(self):
         super(TemplateResourceErrorMessageTest, self).setUp()
-        self.client = self.orchestration_client
 
     def test_fail(self):
         stack_identifier = self.stack_create(
@@ -691,7 +685,7 @@
         self.assertEqual(exp, stack.stack_status_reason)
 
 
-class TemplateResourceSuspendResumeTest(test.HeatIntegrationTest):
+class TemplateResourceSuspendResumeTest(functional_base.FunctionalTestsBase):
     """Prove that we can do template resource suspend/resume."""
 
     main_template = '''
@@ -711,7 +705,6 @@
 
     def setUp(self):
         super(TemplateResourceSuspendResumeTest, self).setUp()
-        self.client = self.orchestration_client
 
     def test_suspend_resume(self):
         """Basic test for template resource suspend resume"""
diff --git a/functional/test_validation.py b/functional/test_validation.py
index 1111ea8..da5cd68 100644
--- a/functional/test_validation.py
+++ b/functional/test_validation.py
@@ -11,14 +11,13 @@
 #    under the License.
 
 
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
 
 
-class StackValidationTest(test.HeatIntegrationTest):
+class StackValidationTest(functional_base.FunctionalTestsBase):
 
     def setUp(self):
         super(StackValidationTest, self).setUp()
-        self.client = self.orchestration_client
         if not self.conf.minimal_image_ref:
             raise self.skipException("No image configured to test")
 
diff --git a/scenario/scenario_base.py b/scenario/scenario_base.py
index dd25b89..84e14a9 100644
--- a/scenario/scenario_base.py
+++ b/scenario/scenario_base.py
@@ -18,6 +18,8 @@
 
     def setUp(self):
         super(ScenarioTestsBase, self).setUp()
+        self.check_skip_test()
+
         self.client = self.orchestration_client
         self.sub_dir = 'templates'
         self.assign_keypair()
@@ -55,3 +57,12 @@
         )
 
         return stack_id
+
+    def check_skip_test(self):
+        test_cls_name = self.__class__.__name__
+        test_method_name = '.'.join([test_cls_name, self._testMethodName])
+        test_skipped = (self.conf.skip_scenario_test_list and (
+            test_cls_name in self.conf.skip_scenario_test_list or
+            test_method_name in self.conf.skip_scenario_test_list))
+        if self.conf.skip_scenario_tests or test_skipped:
+            self.skipTest('Test disabled in conf, skipping')
diff --git a/scenario/templates/app_server_neutron.yaml b/scenario/templates/app_server_neutron.yaml
new file mode 100644
index 0000000..9cbf82a
--- /dev/null
+++ b/scenario/templates/app_server_neutron.yaml
@@ -0,0 +1,65 @@
+heat_template_version: 2015-10-15
+
+description: |
+  App server that is a member of Neutron Pool.
+
+parameters:
+
+  image:
+    type: string
+
+  flavor:
+    type: string
+
+  net:
+    type: string
+
+  sec_group:
+    type: string
+
+  pool_id:
+    type: string
+
+  app_port:
+    type: number
+
+  timeout:
+    type: number
+
+resources:
+
+  config:
+    type: OS::Test::WebAppConfig
+    properties:
+      app_port: { get_param: app_port }
+      wc_curl_cli: { get_attr: [ handle, curl_cli ] }
+
+  server:
+    type: OS::Nova::Server
+    properties:
+      image: { get_param: image }
+      flavor: { get_param: flavor }
+      networks:
+        - network: { get_param: net }
+      security_groups:
+        - { get_param: sec_group }
+      user_data_format: RAW
+      user_data: { get_resource: config }
+
+  handle:
+    type: OS::Heat::WaitConditionHandle
+
+  waiter:
+    type: OS::Heat::WaitCondition
+    depends_on: server
+    properties:
+      timeout: { get_param: timeout }
+      handle: { get_resource: handle }
+
+  pool_member:
+    type: OS::Neutron::PoolMember
+    depends_on: waiter
+    properties:
+      address: { get_attr: [ server, networks, { get_param: net }, 0 ] }
+      pool_id: { get_param: pool_id }
+      protocol_port: { get_param: app_port }
diff --git a/scenario/templates/netcat-webapp.yaml b/scenario/templates/netcat-webapp.yaml
new file mode 100644
index 0000000..fdb0335
--- /dev/null
+++ b/scenario/templates/netcat-webapp.yaml
@@ -0,0 +1,35 @@
+heat_template_version: 2015-10-15
+
+description: |
+  Simplest web-app using netcat reporting only hostname.
+  Specifically tailored for minimal Cirros image.
+
+parameters:
+
+  app_port:
+    type: number
+
+  wc_curl_cli:
+    type: string
+
+resources:
+
+  webapp_nc:
+    type: OS::Heat::SoftwareConfig
+    properties:
+      group: ungrouped
+      config:
+        str_replace:
+          template: |
+            #! /bin/sh -v
+            Body=$(hostname)
+            Response="HTTP/1.1 200 OK\r\nContent-Length: ${#Body}\r\n\r\n$Body"
+            wc_notify --data-binary '{"status": "SUCCESS"}'
+            while true ; do echo -e $Response | nc -llp PORT; done
+          params:
+            PORT: { get_param: app_port }
+            wc_notify: { get_param: wc_curl_cli }
+
+outputs:
+  OS::stack_id:
+    value: { get_resource: webapp_nc }
diff --git a/scenario/templates/test_autoscaling_lb_neutron.yaml b/scenario/templates/test_autoscaling_lb_neutron.yaml
new file mode 100644
index 0000000..d47e787
--- /dev/null
+++ b/scenario/templates/test_autoscaling_lb_neutron.yaml
@@ -0,0 +1,113 @@
+heat_template_version: 2015-04-30
+
+description: |
+  Template which tests Neutron load balancing requests to members of
+  Heat AutoScalingGroup.
+  Instances must be running some webserver on a given app_port
+  producing HTTP response that is different between servers
+  but stable over time for given server.
+
+parameters:
+  flavor:
+    type: string
+  image:
+    type: string
+  net:
+    type: string
+  subnet:
+    type: string
+  public_net:
+    type: string
+  app_port:
+    type: number
+    default: 8080
+  lb_port:
+    type: number
+    default: 80
+  timeout:
+    type: number
+    default: 600
+
+resources:
+
+  sec_group:
+    type: OS::Neutron::SecurityGroup
+    properties:
+      rules:
+      - remote_ip_prefix: 0.0.0.0/0
+        protocol: tcp
+        port_range_min: { get_param: app_port }
+        port_range_max: { get_param: app_port }
+
+  asg:
+    type: OS::Heat::AutoScalingGroup
+    properties:
+      desired_capacity: 1
+      max_size: 2
+      min_size: 1
+      resource:
+        type: OS::Test::NeutronAppServer
+        properties:
+          image: { get_param: image }
+          flavor: { get_param: flavor }
+          net: { get_param: net}
+          sec_group: { get_resource: sec_group }
+          app_port: { get_param: app_port }
+          pool_id: { get_resource: pool }
+          timeout: { get_param: timeout }
+
+  scale_up:
+    type: OS::Heat::ScalingPolicy
+    properties:
+      adjustment_type: change_in_capacity
+      auto_scaling_group_id: { get_resource: asg }
+      scaling_adjustment: 1
+
+  scale_down:
+    type: OS::Heat::ScalingPolicy
+    properties:
+      adjustment_type: change_in_capacity
+      auto_scaling_group_id: { get_resource: asg }
+      scaling_adjustment: -1
+
+  health_monitor:
+    type: OS::Neutron::HealthMonitor
+    properties:
+      delay: 3
+      type: HTTP
+      timeout: 3
+      max_retries: 3
+
+  pool:
+    type: OS::Neutron::Pool
+    properties:
+      lb_method: ROUND_ROBIN
+      protocol: HTTP
+      subnet: { get_param: subnet }
+      monitors:
+      - { get_resource: health_monitor }
+      vip:
+        protocol_port: { get_param: lb_port }
+
+  floating_ip:
+     type: OS::Neutron::FloatingIP
+     properties:
+       floating_network: { get_param: public_net }
+       port_id:
+         { get_attr: [pool, vip, 'port_id'] }
+
+  loadbalancer:
+    type: OS::Neutron::LoadBalancer
+    properties:
+      pool_id: { get_resource: pool }
+      protocol_port: { get_param: app_port }
+
+outputs:
+  lburl:
+    description: URL of the loadbalanced app
+    value:
+      str_replace:
+        template: http://IP_ADDRESS:PORT
+        params:
+          IP_ADDRESS: { get_attr: [ floating_ip, floating_ip_address ] }
+          PORT: { get_param: lb_port }
diff --git a/scenario/templates/test_neutron_autoscaling.yaml b/scenario/templates/test_neutron_autoscaling.yaml
deleted file mode 100644
index a34ec43..0000000
--- a/scenario/templates/test_neutron_autoscaling.yaml
+++ /dev/null
@@ -1,56 +0,0 @@
-heat_template_version: 2014-10-16
-
-description: Auto-scaling Test
-
-parameters:
-  image_id:
-    type: string
-    label: Image ID
-    description: Image ID from configurations
-  capacity:
-    type: string
-    label: Capacity
-    description: Auto-scaling group desired capacity
-  fixed_subnet:
-    type: string
-    label: fixed subnetwork ID
-    description: subnetwork ID used for autoscaling
-  instance_type:
-    type: string
-    label: instance_type
-    description: type of instance to launch
-
-resources:
-  test_pool:
-    type: OS::Neutron::Pool
-    properties:
-      description: Test Pool
-      lb_method: ROUND_ROBIN
-      name: test_pool
-      protocol: HTTP
-      subnet: { get_param: fixed_subnet }
-      vip: {
-        "description": "Test VIP",
-        "protocol_port": 80,
-        "name": "test_vip"
-      }
-  load_balancer:
-    type: OS::Neutron::LoadBalancer
-    properties:
-      protocol_port: 80
-      pool_id: { get_resource: test_pool }
-  launch_config:
-    type: AWS::AutoScaling::LaunchConfiguration
-    properties:
-      ImageId: { get_param: image_id }
-      InstanceType: { get_param: instance_type }
-  server_group:
-    type: AWS::AutoScaling::AutoScalingGroup
-    properties:
-      AvailabilityZones : ["nova"]
-      LaunchConfigurationName : { get_resource : launch_config }
-      VPCZoneIdentifier: [{ get_param: fixed_subnet }]
-      MinSize : 1
-      MaxSize : 5
-      DesiredCapacity: { get_param: capacity }
-      LoadBalancerNames : [ { get_resource : load_balancer } ]
diff --git a/scenario/templates/test_neutron_loadbalancer.yaml b/scenario/templates/test_neutron_loadbalancer.yaml
deleted file mode 100644
index dd659d0..0000000
--- a/scenario/templates/test_neutron_loadbalancer.yaml
+++ /dev/null
@@ -1,133 +0,0 @@
-heat_template_version: 2014-10-16
-
-description: |
-  Template which tests neutron load balancing resources
-
-parameters:
-  key_name:
-    type: string
-  flavor:
-    type: string
-  image:
-    type: string
-  network:
-    type: string
-  private_subnet_id:
-    type: string
-  external_network_id:
-    type: string
-  port:
-    type: string
-    default: '80'
-  timeout:
-    type: number
-
-resources:
-  sec_group:
-    type: OS::Neutron::SecurityGroup
-    properties:
-      description: Add security group rules for servers
-      name: security-group
-      rules:
-        - remote_ip_prefix: 0.0.0.0/0
-          protocol: tcp
-          port_range_min: { get_param: port }
-          port_range_max: { get_param: port }
-        - 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:
-      name: Server1
-      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_format: SOFTWARE_CONFIG
-      user_data: { get_resource: config }
-
-  server2:
-    type: OS::Nova::Server
-    properties:
-      name: Server2
-      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_format: SOFTWARE_CONFIG
-      user_data: { get_resource: config }
-
-  health_monitor:
-    type: OS::Neutron::HealthMonitor
-    properties:
-      delay: 3
-      type: HTTP
-      timeout: 3
-      max_retries: 3
-
-  test_pool:
-    type: OS::Neutron::Pool
-    properties:
-      lb_method: ROUND_ROBIN
-      protocol: HTTP
-      subnet: { get_param: private_subnet_id }
-      monitors:
-      - { get_resource: health_monitor }
-      vip:
-        protocol_port: { get_param: port }
-
-  floating_ip:
-     type: OS::Neutron::FloatingIP
-     properties:
-       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 }
-      members:
-      - { get_resource: server1 }
-
-outputs:
-  serv1_ip:
-    value: {get_attr: [server1, networks, { get_param: network }, 0]}
-  serv2_ip:
-    value: {get_attr: [server2, networks, { get_param: network }, 0]}
-  vip:
-    value: {get_attr: [test_pool, vip, address]}
-  fip:
-    value: {get_attr: [floating_ip, floating_ip_address]}
diff --git a/scenario/test_autoscaling_lb.py b/scenario/test_autoscaling_lb.py
new file mode 100644
index 0000000..21b27dd
--- /dev/null
+++ b/scenario/test_autoscaling_lb.py
@@ -0,0 +1,114 @@
+#
+# 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 time
+
+import requests
+
+from heat_integrationtests.common import test
+from heat_integrationtests.scenario import scenario_base
+
+
+class AutoscalingLoadBalancerTest(scenario_base.ScenarioTestsBase):
+    """
+    The class is responsible for testing ASG + LB scenario.
+
+    The very common use case tested is an autoscaling group
+    of some web application servers behind a loadbalancer.
+    """
+
+    def setUp(self):
+        super(AutoscalingLoadBalancerTest, self).setUp()
+        self.template_name = 'test_autoscaling_lb_neutron.yaml'
+        self.app_server_template_name = 'app_server_neutron.yaml'
+        self.webapp_template_name = 'netcat-webapp.yaml'
+
+    def check_num_responses(self, url, expected_num, retries=10):
+        resp = set()
+        for count in range(retries):
+            time.sleep(1)
+            r = requests.get(url)
+            # skip unsuccessfull requests
+            if r.status_code == 200:
+                resp.add(r.text)
+        self.assertEqual(expected_num, len(resp))
+
+    def autoscale_complete(self, stack_id, expected_num):
+        res_list = self.client.resources.list(stack_id)
+        all_res_complete = all(res.resource_status in ('UPDATE_COMPLETE',
+                                                       'CREATE_COMPLETE')
+                               for res in res_list)
+        all_res = len(res_list) == expected_num
+        return all_res and all_res_complete
+
+    def test_autoscaling_loadbalancer_neutron(self):
+        """
+        Check work of AutoScaing and Neutron LBaaS resource in Heat.
+
+        The scenario is the following:
+            1. Launch a stack with a load balancer and autoscaling group
+               of one server, wait until stack create is complete.
+            2. Check that there is only one distinctive response from
+               loadbalanced IP.
+            3. Signal the scale_up policy, wait until all resources in
+               autoscaling group are complete.
+            4. Check that now there are two distinctive responses from
+               loadbalanced IP.
+        """
+
+        parameters = {
+            'flavor': self.conf.minimal_instance_type,
+            'image': self.conf.minimal_image_ref,
+            'net': self.conf.fixed_network_name,
+            'subnet': self.conf.fixed_subnet_name,
+            'public_net': self.conf.floating_network_name,
+            'app_port': 8080,
+            'lb_port': 80,
+            'timeout': 600
+        }
+
+        app_server_template = self._load_template(
+            __file__, self.app_server_template_name, self.sub_dir
+        )
+        webapp_template = self._load_template(
+            __file__, self.webapp_template_name, self.sub_dir
+        )
+        files = {'appserver.yaml': app_server_template,
+                 'webapp.yaml': webapp_template}
+        env = {'resource_registry':
+               {'OS::Test::NeutronAppServer': 'appserver.yaml',
+                'OS::Test::WebAppConfig': 'webapp.yaml'}}
+        # Launch stack
+        sid = self.launch_stack(
+            template_name=self.template_name,
+            parameters=parameters,
+            files=files,
+            environment=env
+        )
+        stack = self.client.stacks.get(sid)
+        lb_url = self._stack_output(stack, 'lburl')
+        # Check number of distinctive responces, must be 1
+        self.check_num_responses(lb_url, 1)
+
+        # Signal the scaling hook
+        self.client.resources.signal(sid, 'scale_up')
+
+        # Wait for AutoScalingGroup update to finish
+        asg = self.client.resources.get(sid, 'asg')
+        test.call_until_true(self.conf.build_timeout,
+                             self.conf.build_interval,
+                             self.autoscale_complete,
+                             asg.physical_resource_id, 2)
+
+        # Check number of distinctive responses, must now be 2
+        self.check_num_responses(lb_url, 2)
diff --git a/scenario/test_ceilometer_alarm.py b/scenario/test_ceilometer_alarm.py
index d8f21fd..e0523aa 100644
--- a/scenario/test_ceilometer_alarm.py
+++ b/scenario/test_ceilometer_alarm.py
@@ -13,15 +13,15 @@
 from oslo_log import log as logging
 
 from heat_integrationtests.common import test
+from heat_integrationtests.scenario import scenario_base
 
 LOG = logging.getLogger(__name__)
 
 
-class CeilometerAlarmTest(test.HeatIntegrationTest):
+class CeilometerAlarmTest(scenario_base.ScenarioTestsBase):
     """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')
diff --git a/scenario/test_neutron_autoscaling.py b/scenario/test_neutron_autoscaling.py
deleted file mode 100644
index c81e22d..0000000
--- a/scenario/test_neutron_autoscaling.py
+++ /dev/null
@@ -1,73 +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.scenario import scenario_base
-
-
-class NeutronAutoscalingTest(scenario_base.ScenarioTestsBase):
-    """
-    The class is responsible for testing of neutron resources autoscaling.
-    """
-
-    def setUp(self):
-        super(NeutronAutoscalingTest, self).setUp()
-        raise self.skipException("Skipping until bug #1479869 is fixed.")
-        if not self.conf.fixed_subnet_name:
-            raise self.skipException("No sub-network configured to test")
-        self.template_name = 'test_neutron_autoscaling.yaml'
-
-    def test_neutron_autoscaling(self):
-        """
-        Check autoscaling of load balancer members in Heat.
-
-        The alternative scenario is the following:
-            1. Launch a stack with a load balancer.
-            2. Check that the load balancer created
-            one load balancer member for stack.
-            3. Update stack definition: increase desired capacity of stack.
-            4. Check that number of members in load balancer was increased.
-        """
-
-        parameters = {
-            "image_id": self.conf.minimal_image_ref,
-            "capacity": "1",
-            "instance_type": self.conf.minimal_instance_type,
-            "fixed_subnet": self.net['subnets'][0],
-        }
-
-        # Launch stack
-        stack_id = self.launch_stack(
-            template_name=self.template_name,
-            parameters=parameters
-        )
-
-        # Check number of 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(
-            __file__, self.template_name, self.sub_dir
-        )
-        parameters["capacity"] = "2"
-        self.update_stack(
-            stack_id,
-            template=template,
-            parameters=parameters
-        )
-
-        # Check number of 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
deleted file mode 100644
index a890257..0000000
--- a/scenario/test_neutron_loadbalancer.py
+++ /dev/null
@@ -1,109 +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.
-
-import time
-
-from six.moves import urllib
-
-from heat_integrationtests.scenario import scenario_base
-
-
-class NeutronLoadBalancerTest(scenario_base.ScenarioTestsBase):
-    """
-    The class is responsible for testing of neutron resources balancer.
-    """
-
-    def setUp(self):
-        super(NeutronLoadBalancerTest, self).setUp()
-        raise self.skipException("Skipping until bug #1479869 is fixed.")
-        self.public_net = self._get_network(self.conf.floating_network_name)
-        self.template_name = 'test_neutron_loadbalancer.yaml'
-
-    def collect_responses(self, ip, expected_resp):
-        resp = set()
-        for count in range(10):
-            time.sleep(1)
-            resp.add(urllib.request.urlopen('http://%s/' % ip).read())
-
-        self.assertEqual(expected_resp, resp)
-
-    def test_neutron_loadbalancer(self):
-        """
-        Check work of Neutron LBaaS resource in Heat.
-
-        The alternative scenario is the following:
-            1. Launch a stack with a load balancer, two servers,
-               but use only one as a LB member.
-            2. Check connection to the servers and LB.
-            3. Collect info about responces, which were received by LB from
-               its members (responces have to be received only from 'server1').
-            4. Update stack definition: include 'server2' into LBaaS.
-            5. Check that number of members in LB was increased and
-               responces were received from 'server1' and 'server2'.
-        """
-
-        parameters = {
-            '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'],
-            'timeout': self.conf.build_timeout
-        }
-
-        # Launch stack
-        sid = self.launch_stack(
-            template_name=self.template_name,
-            parameters=parameters
-        )
-
-        stack = self.client.stacks.get(sid)
-        floating_ip = self._stack_output(stack, 'fip')
-        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 responses
-        self.check_connectivity(server1_ip)
-        self.collect_responses(server1_ip, {'server1\n'})
-
-        self.check_connectivity(server2_ip)
-        self.collect_responses(server2_ip, {'server2\n'})
-
-        self.check_connectivity(vip)
-        self.collect_responses(vip, {'server1\n'})
-
-        self.check_connectivity(floating_ip)
-        self.collect_responses(floating_ip, {'server1\n'})
-
-        # Include 'server2' to LB and update the stack
-        template = self._load_template(
-            __file__, self.template_name, self.sub_dir
-        )
-
-        template = template.replace(
-            '- { get_resource: server1 }',
-            '- { get_resource: server1 }\n      - { get_resource: server2 }\n'
-        )
-
-        self.update_stack(
-            sid,
-            template=template,
-            parameters=parameters
-        )
-
-        self.check_connectivity(vip)
-        self.collect_responses(vip, {'server1\n', 'server2\n'})
-
-        self.check_connectivity(floating_ip)
-        self.collect_responses(floating_ip, {'server1\n', 'server2\n'})
diff --git a/scenario/test_server_cfn_init.py b/scenario/test_server_cfn_init.py
index 197e0c0..267b44b 100644
--- a/scenario/test_server_cfn_init.py
+++ b/scenario/test_server_cfn_init.py
@@ -23,7 +23,6 @@
 
     def setUp(self):
         super(CfnInitIntegrationTest, self).setUp()
-        raise self.skipException("Skipping until bug #1479869 is fixed.")
 
     def check_stack(self, sid):
         # Check status of all resources
diff --git a/scenario/test_server_software_config.py b/scenario/test_server_software_config.py
index 19fd1a8..8739a99 100644
--- a/scenario/test_server_software_config.py
+++ b/scenario/test_server_software_config.py
@@ -44,9 +44,6 @@
 
     def setUp(self):
         super(SoftwareConfigIntegrationTest, self).setUp()
-        if self.conf.skip_software_config_tests:
-            self.skipTest('Testing software config disabled in conf, '
-                          'skipping')
         self.stack_name = self._stack_rand_name()
 
     def check_stack(self):
diff --git a/scenario/test_volumes.py b/scenario/test_volumes.py
index 7562304..d4b1aa8 100644
--- a/scenario/test_volumes.py
+++ b/scenario/test_volumes.py
@@ -14,7 +14,6 @@
 from cinderclient import exceptions as cinder_exceptions
 from oslo_log import log as logging
 import six
-from testtools import testcase
 
 from heat_integrationtests.common import exceptions
 from heat_integrationtests.scenario import scenario_base
@@ -101,8 +100,6 @@
                           self.volume_client.volumes.get,
                           volume_id2)
 
-    @testcase.skip('Skipped until failure rate '
-                   'can be reduced ref bug #1382300')
     def test_cinder_volume_create_backup_restore(self):
         """
         Ensure the 'Snapshot' deletion policy works.