Merge "Add external_ref test for update stack"
diff --git a/.zuul.yaml b/.zuul.yaml
index c9aea1b..88da946 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -13,9 +13,26 @@
     parent: heat-functional-convg-mysql-lbaasv2-py35
     override-checkout: stable/queens
 
+- job:
+    name: heat-functional-convg-rocky
+    parent: heat-functional-convg-mysql-lbaasv2
+    override-checkout: stable/rocky
+
+- job:
+    name: heat-functional-orig-rocky
+    parent: heat-functional-orig-mysql-lbaasv2
+    override-checkout: stable/rocky
+
+- job:
+    name: heat-functional-convg-rocky-py35
+    parent: heat-functional-convg-mysql-lbaasv2-py35
+    override-checkout: stable/rocky
 
 
 - project:
+    templates:
+      - check-requirements
+      - tempest-plugin-jobs
     check:
       jobs:
         - heat-functional-orig-mysql-lbaasv2
@@ -25,9 +42,12 @@
         - heat-functional-convg-queens
         - heat-functional-convg-queens-py35
         - heat-functional-orig-queens
+        - heat-functional-convg-rocky
+        - heat-functional-convg-rocky-py35
+        - heat-functional-orig-rocky
     gate:
+      queue: heat
       jobs:
         - heat-functional-orig-mysql-lbaasv2
         - heat-functional-convg-mysql-lbaasv2
-        - heat-functional-convg-mysql-lbaasv2-non-apache
         - heat-functional-convg-mysql-lbaasv2-py35
diff --git a/heat_tempest_plugin/common/test.py b/heat_tempest_plugin/common/test.py
index 429d4ae..ccbc12a 100644
--- a/heat_tempest_plugin/common/test.py
+++ b/heat_tempest_plugin/common/test.py
@@ -10,7 +10,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import os
 import random
 import re
 import subprocess
@@ -110,6 +109,29 @@
     return decorator
 
 
+def requires_service_type(service_type):
+    '''Decorator for tests requiring a specific service being available.
+
+    The decorated test will be skipped when a service is not available.
+    '''
+    def decorator(test_method):
+        conf = getattr(config.CONF, 'heat_plugin', None)
+        if not conf or conf.auth_url is None:
+            return test_method
+
+        manager = clients.ClientManager(conf)
+        try:
+            manager.identity_client.get_endpoint_url(
+                service_type, conf.region, conf.endpoint_type)
+        except kc_exceptions.EndpointNotFound:
+            skipper = testtools.skip(
+                "%s service not available, skipping test." % service_type)
+            return skipper(test_method)
+        else:
+            return test_method
+    return decorator
+
+
 def requires_feature(feature):
     '''Decorator for tests requring specific feature.
 
@@ -206,13 +228,6 @@
             LOG.info('Console output for %s', server.id)
             LOG.info(server.get_console_output())
 
-    def _load_template(self, base_file, file_name, sub_dir=None):
-        sub_dir = sub_dir or ''
-        filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
-                                sub_dir, file_name)
-        with open(filepath) as f:
-            return f.read()
-
     def create_keypair(self, client=None, name=None):
         if client is None:
             client = self.compute_client
@@ -254,15 +269,6 @@
             return False
         return True
 
-    def is_service_available(self, service_type):
-        try:
-            self.identity_client.get_endpoint_url(
-                service_type, self.conf.region)
-        except kc_exceptions.EndpointNotFound:
-            return False
-        else:
-            return True
-
     @staticmethod
     def _stack_output(stack, output_key, validate_errors=True):
         """Return a stack output value for a given key."""
diff --git a/heat_tempest_plugin/config.py b/heat_tempest_plugin/config.py
index c77bb51..fae6ebb 100644
--- a/heat_tempest_plugin/config.py
+++ b/heat_tempest_plugin/config.py
@@ -16,9 +16,12 @@
                                        title="Available OpenStack Services")
 
 ServiceAvailableGroup = [
-    cfg.BoolOpt("heat_plugin",
+    cfg.BoolOpt("heat",
                 default=True,
-                help="Whether or not heat is expected to be available"),
+                help="Whether or not heat is expected to be available",
+                deprecated_opts=[cfg.DeprecatedOpt(
+                    'heat_plugin',
+                    group='service_available')]),
 ]
 
 heat_group = cfg.OptGroup(name="heat_plugin",
diff --git a/heat_tempest_plugin/plugin.py b/heat_tempest_plugin/plugin.py
index 6926691..0eeb519 100644
--- a/heat_tempest_plugin/plugin.py
+++ b/heat_tempest_plugin/plugin.py
@@ -22,7 +22,23 @@
 
 
 class HeatTempestPlugin(plugins.TempestPlugin):
+    """A HeatTempestPlugin class
+
+    Provides the basic hooks for an external plugin to provide tempest the
+    necessary information to run the plugin.
+    """
+
     def load_tests(self):
+        """Provide Load tests information
+
+        Method to return the information necessary to load the tests in the
+        plugin.
+
+        :return: a tuple with the first value being the test_dir and the second
+                 being the top_level
+        :return type: tuple
+        """
+
         base_path = os.path.split(os.path.dirname(
             os.path.abspath(__file__)))[0]
         test_dir = "heat_tempest_plugin"
@@ -30,6 +46,15 @@
         return full_test_dir, base_path
 
     def register_opts(self, conf):
+        """Add additional configuration options to tempest.
+
+        This method will be run for the plugin during the register_opts()
+        function in tempest.config
+
+        Parameters:
+        conf (ConfigOpts): The conf object that can be used to register
+        additional options on.
+        """
         config.register_opt_group(conf, heat_config.service_available_group,
                                   heat_config.ServiceAvailableGroup)
         config.register_opt_group(conf, heat_config.heat_group,
@@ -38,6 +63,13 @@
                                   heat_config.HeatFeaturesGroup)
 
     def get_opt_lists(self):
+        """Get a list of options for sample config generation
+
+        Return option_list: A list of tuples with the group name
+                            and options in that group.
+        Return type: list
+        """
+
         return [(heat_config.heat_group.name,
                  heat_config.HeatGroup),
                 (heat_config.heat_features_group.name,
diff --git a/heat_tempest_plugin/services/clients.py b/heat_tempest_plugin/services/clients.py
index 3999cee..d6e1dfb 100644
--- a/heat_tempest_plugin/services/clients.py
+++ b/heat_tempest_plugin/services/clients.py
@@ -111,7 +111,7 @@
             self.HEATCLIENT_VERSION,
             endpoint,
             session=session,
-            endpoint_type='publicURL',
+            endpoint_type=self.conf.endpoint_type,
             service_type='orchestration',
             region_name=self.conf.region,
             username=self._username(),
@@ -174,6 +174,8 @@
         args = {
             'auth_version': self.auth_version,
             'session': self.identity_client.session,
+            'cacert': self.ca_file,
+            'insecure': self.insecure,
             'os_options': {'endpoint_type': self.conf.endpoint_type,
                            'region_name': self.conf.region,
                            'service_type': 'object-store'},
diff --git a/heat_tempest_plugin/tests/api/fixtures.py b/heat_tempest_plugin/tests/api/fixtures.py
new file mode 100644
index 0000000..6c770c8
--- /dev/null
+++ b/heat_tempest_plugin/tests/api/fixtures.py
@@ -0,0 +1,28 @@
+#
+# 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 os
+
+from gabbi import fixture
+from heat_tempest_plugin.services import clients
+from tempest import config
+
+
+class AuthenticationFixture(fixture.GabbiFixture):
+    def start_fixture(self):
+        conf = config.CONF.heat_plugin
+        manager = clients.ClientManager(conf)
+        os.environ['OS_TOKEN'] = manager.identity_client.auth_token
+
+    def stop_fixture(self):
+        pass
diff --git a/heat_tempest_plugin/tests/api/gabbits/environments.yaml b/heat_tempest_plugin/tests/api/gabbits/environments.yaml
index fe1dc04..03849df 100644
--- a/heat_tempest_plugin/tests/api/gabbits/environments.yaml
+++ b/heat_tempest_plugin/tests/api/gabbits/environments.yaml
@@ -1,3 +1,6 @@
+fixtures:
+    - AuthenticationFixture
+
 defaults:
   request_headers:
     X-Auth-Token: $ENVIRON['OS_TOKEN']
diff --git a/heat_tempest_plugin/tests/api/gabbits/resources.yaml b/heat_tempest_plugin/tests/api/gabbits/resources.yaml
index c20333b..c99b6a9 100644
--- a/heat_tempest_plugin/tests/api/gabbits/resources.yaml
+++ b/heat_tempest_plugin/tests/api/gabbits/resources.yaml
@@ -1,3 +1,6 @@
+fixtures:
+    - AuthenticationFixture
+
 defaults:
   request_headers:
     X-Auth-Token: $ENVIRON['OS_TOKEN']
diff --git a/heat_tempest_plugin/tests/api/gabbits/resourcetypes.yaml b/heat_tempest_plugin/tests/api/gabbits/resourcetypes.yaml
index 809feab..954963e 100644
--- a/heat_tempest_plugin/tests/api/gabbits/resourcetypes.yaml
+++ b/heat_tempest_plugin/tests/api/gabbits/resourcetypes.yaml
@@ -1,3 +1,6 @@
+fixtures:
+    - AuthenticationFixture
+
 defaults:
   request_headers:
     X-Auth-Token: $ENVIRON['OS_TOKEN']
diff --git a/heat_tempest_plugin/tests/api/gabbits/softwareconfig.yaml b/heat_tempest_plugin/tests/api/gabbits/softwareconfig.yaml
new file mode 100644
index 0000000..0d0453d
--- /dev/null
+++ b/heat_tempest_plugin/tests/api/gabbits/softwareconfig.yaml
@@ -0,0 +1,86 @@
+fixtures:
+    - AuthenticationFixture
+
+defaults:
+  request_headers:
+    X-Auth-Token: $ENVIRON['OS_TOKEN']
+
+tests:
+- name: create software config
+  desc: d26d11c0-4924-11e8-842f-0ed5f89f718b
+  POST: /software_configs
+  request_headers:
+    content-type: application/json
+  data:
+    group: script
+    config: '#!/bin/sh -x\necho hello'
+  status: 200
+
+- name: show config
+  desc: d26d149a-4924-11e8-842f-0ed5f89f718b
+  GET: $LAST_URL/$RESPONSE['software_config.id']
+  status: 200
+  response_json_paths:
+    $.software_config.group: script
+
+- name: create software deployment
+  desc: d26d15ee-4924-11e8-842f-0ed5f89f718b
+  POST: /software_deployments
+  request_headers:
+    content-type: application/json
+  data:
+    action: ACTION0
+    config_id: $RESPONSE['software_config.id']
+    server_id: dummy_server
+  status: 200
+
+- name: show deployment
+  desc: d26d1968-4924-11e8-842f-0ed5f89f718b
+  GET: $LAST_URL/$RESPONSE['software_deployment.id']
+  status: 200
+  response_json_paths:
+    $.software_deployment.action: ACTION0
+
+- name: show deployment metadata
+  desc: d26d1ae4-4924-11e8-842f-0ed5f89f718b
+  GET: /software_deployments/metadata/$RESPONSE['software_deployment.server_id']
+  status: 200
+  response_json_paths:
+    $.metadata[0].group: script
+
+- name: update deployment
+  desc: d26d1c1a-4924-11e8-842f-0ed5f89f718b
+  PUT: $HISTORY['show deployment'].$URL
+  request_headers:
+    content-type: application/json
+  data:
+    action: ACTION1
+    status: COMPLETE
+    status_reason: completed
+  status: 200
+  response_json_paths:
+    $.software_deployment.action: ACTION1
+
+- name: list software configs
+  desc: d26d1d46-4924-11e8-842f-0ed5f89f718b
+  GET: /software_configs
+  status: 200
+  response_json_paths:
+    $.software_configs[?id = "$RESPONSE['software_deployment.config_id']"].group: script
+
+- name: list software deployments
+  desc: d26d1e90-4924-11e8-842f-0ed5f89f718b
+  GET: /software_deployments
+  status: 200
+  response_json_paths:
+    $.software_deployments[?id= "$HISTORY['update deployment'].$RESPONSE['software_deployment.id']"].server_id: dummy_server
+
+- name: delete deployment
+  desc: d26d1fda-4924-11e8-842f-0ed5f89f718b
+  DELETE: $HISTORY['show deployment'].$URL
+  status: 204
+
+- name: delete config
+  desc: d26d22be-4924-11e8-842f-0ed5f89f718b
+  DELETE: $HISTORY['show config'].$URL
+  status: 204
diff --git a/heat_tempest_plugin/tests/api/gabbits/stacks.yaml b/heat_tempest_plugin/tests/api/gabbits/stacks.yaml
index 9d9328e..4947416 100644
--- a/heat_tempest_plugin/tests/api/gabbits/stacks.yaml
+++ b/heat_tempest_plugin/tests/api/gabbits/stacks.yaml
@@ -1,3 +1,6 @@
+fixtures:
+    - AuthenticationFixture
+
 defaults:
   request_headers:
     X-Auth-Token: $ENVIRON['OS_TOKEN']
diff --git a/heat_tempest_plugin/tests/api/gabbits/templates.yaml b/heat_tempest_plugin/tests/api/gabbits/templates.yaml
index ab0bbab..4319753 100644
--- a/heat_tempest_plugin/tests/api/gabbits/templates.yaml
+++ b/heat_tempest_plugin/tests/api/gabbits/templates.yaml
@@ -1,3 +1,6 @@
+fixtures:
+    - AuthenticationFixture
+
 defaults:
   request_headers:
     X-Auth-Token: $ENVIRON['OS_TOKEN']
diff --git a/heat_tempest_plugin/tests/api/test_heat_api.py b/heat_tempest_plugin/tests/api/test_heat_api.py
index 804f445..7ba3a62 100644
--- a/heat_tempest_plugin/tests/api/test_heat_api.py
+++ b/heat_tempest_plugin/tests/api/test_heat_api.py
@@ -24,6 +24,7 @@
 
 from heat_tempest_plugin.common import test
 from heat_tempest_plugin.services import clients
+from heat_tempest_plugin.tests.api import fixtures
 
 LOG = logging.getLogger(__name__)
 TESTS_DIR = 'gabbits'
@@ -41,7 +42,6 @@
             endpoint = manager.identity_client.get_endpoint_url(
                 'orchestration', region=conf.region,
                 endpoint_type=conf.endpoint_type)
-            os.environ['OS_TOKEN'] = manager.identity_client.auth_token
             os.environ['PREFIX'] = test.rand_name('api')
 
         # Catch the authentication exceptions that can happen if one of the
@@ -93,7 +93,7 @@
                 register_test_case_id(test_case)
 
     api_tests = driver.build_tests(test_dir, loader, url=endpoint, host="",
+                                   fixture_module=fixtures,
                                    test_loader_name=__name__)
-
     register_test_suite_ids(api_tests)
     return api_tests
diff --git a/heat_tempest_plugin/tests/functional/test_event_sinks.py b/heat_tempest_plugin/tests/functional/test_event_sinks.py
index 7cb1d7b..dd1accc 100644
--- a/heat_tempest_plugin/tests/functional/test_event_sinks.py
+++ b/heat_tempest_plugin/tests/functional/test_event_sinks.py
@@ -19,6 +19,7 @@
 from heat_tempest_plugin.tests.functional import functional_base
 
 
+@test.requires_service_type('messaging')
 class ZaqarEventSinkTest(functional_base.FunctionalTestsBase):
     template = '''
 heat_template_version: "2013-05-23"
diff --git a/heat_tempest_plugin/tests/functional/test_remote_stack.py b/heat_tempest_plugin/tests/functional/test_remote_stack.py
index 5e778e7..1a467f4 100644
--- a/heat_tempest_plugin/tests/functional/test_remote_stack.py
+++ b/heat_tempest_plugin/tests/functional/test_remote_stack.py
@@ -94,9 +94,10 @@
         ex = self.assertRaises(exc.HTTPBadRequest, self.stack_create, **kwargs)
 
         error_msg = ('ERROR: Cannot establish connection to Heat endpoint '
-                     'at region "DARKHOLE" due to "publicURL endpoint for '
+                     'at region "DARKHOLE" due to '
+                     '"(?:public|internal|admin)(?:URL)? endpoint for '
                      'orchestration service in DARKHOLE region not found"')
-        self.assertEqual(error_msg, six.text_type(ex))
+        self.assertRegex(six.text_type(ex), error_msg)
 
     @decorators.idempotent_id('b2190dfc-d223-4595-b168-6c42b0f3a3e5')
     def test_stack_resource_validation_fail(self):
diff --git a/heat_tempest_plugin/tests/functional/test_software_config.py b/heat_tempest_plugin/tests/functional/test_software_config.py
index f034096..ada67f2 100644
--- a/heat_tempest_plugin/tests/functional/test_software_config.py
+++ b/heat_tempest_plugin/tests/functional/test_software_config.py
@@ -183,6 +183,7 @@
                           verify=self.verify_cert)
 
 
+@test.requires_service_type('messaging')
 class ZaqarSignalTransportTest(functional_base.FunctionalTestsBase):
     server_template = '''
 heat_template_version: "2013-05-23"
diff --git a/heat_tempest_plugin/tests/functional/test_waitcondition.py b/heat_tempest_plugin/tests/functional/test_waitcondition.py
index b50c0a2..3bffd76 100644
--- a/heat_tempest_plugin/tests/functional/test_waitcondition.py
+++ b/heat_tempest_plugin/tests/functional/test_waitcondition.py
@@ -16,9 +16,11 @@
 from tempest.lib import decorators
 from zaqarclient.queues.v2 import client as zaqarclient
 
+from heat_tempest_plugin.common import test
 from heat_tempest_plugin.tests.functional import functional_base
 
 
+@test.requires_service_type('messaging')
 class ZaqarWaitConditionTest(functional_base.FunctionalTestsBase):
     template = '''
 heat_template_version: "2013-05-23"
@@ -54,7 +56,7 @@
             password=signal['password'],
             project_id=signal['project_id'])
         endpoint = ks.service_catalog.url_for(
-            service_type='messaging', endpoint_type='publicURL')
+            service_type='messaging', endpoint_type=self.conf.endpoint_type)
         conf = {
             'auth_opts': {
                 'backend': 'keystone',
diff --git a/heat_tempest_plugin/tests/scenario/scenario_base.py b/heat_tempest_plugin/tests/scenario/scenario_base.py
index 5cfb5d1..db38c4e 100644
--- a/heat_tempest_plugin/tests/scenario/scenario_base.py
+++ b/heat_tempest_plugin/tests/scenario/scenario_base.py
@@ -10,6 +10,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import os
+import yaml
+
+from heatclient.common import template_utils
 from oslo_utils import reflection
 
 from heat_tempest_plugin.common import test
@@ -33,9 +37,19 @@
         if not self.conf.minimal_instance_type:
             raise self.skipException("No minimal flavor configured to test")
 
+    def _load_template(self, base_file, file_name, sub_dir=None, files=None):
+        sub_dir = sub_dir or ''
+        filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
+                                sub_dir, file_name)
+        _files, template = template_utils.get_template_contents(filepath,
+                                                                files=files)
+        return yaml.safe_dump(template)
+
     def launch_stack(self, template_name, expected_status='CREATE_COMPLETE',
                      parameters=None, **kwargs):
-        template = self._load_template(__file__, template_name, self.sub_dir)
+        files = kwargs.get('files', {})
+        template = self._load_template(__file__, template_name, self.sub_dir,
+                                       files)
 
         parameters = parameters or {}
 
@@ -45,7 +59,7 @@
         stack_id = self.stack_create(
             stack_name=kwargs.get('stack_name'),
             template=template,
-            files=kwargs.get('files'),
+            files=files,
             parameters=parameters,
             environment=kwargs.get('environment'),
             expected_status=expected_status
diff --git a/heat_tempest_plugin/tests/functional/templates/lb_member.yaml b/heat_tempest_plugin/tests/scenario/templates/lb_member.yaml
similarity index 100%
rename from heat_tempest_plugin/tests/functional/templates/lb_member.yaml
rename to heat_tempest_plugin/tests/scenario/templates/lb_member.yaml
diff --git a/heat_tempest_plugin/tests/functional/templates/octavia_lbaas.yaml b/heat_tempest_plugin/tests/scenario/templates/octavia_lbaas.yaml
similarity index 100%
rename from heat_tempest_plugin/tests/functional/templates/octavia_lbaas.yaml
rename to heat_tempest_plugin/tests/scenario/templates/octavia_lbaas.yaml
diff --git a/heat_tempest_plugin/tests/scenario/templates/remote_nested_base.yaml b/heat_tempest_plugin/tests/scenario/templates/remote_nested_base.yaml
new file mode 100644
index 0000000..fc1441f
--- /dev/null
+++ b/heat_tempest_plugin/tests/scenario/templates/remote_nested_base.yaml
@@ -0,0 +1,29 @@
+heat_template_version: 2015-10-15
+description: |
+  The base stack (containing an actual resource) for the remote deeply-nested
+  stack test.
+
+parameters:
+  name:
+    type: string
+    description: Name of the router
+    constraints:
+      - allowed_pattern: "[a-z][a-z0-9-]{1,}"
+  network_name:
+    type: string
+    description: The network to connect to
+    constraints:
+      - custom_constraint: neutron.network
+
+resources:
+  router:
+    type: OS::Neutron::Router
+    properties:
+      name:
+        list_join: ['-', [{ get_param: name }, 'router']]
+      external_gateway_info:
+        network: {get_param: network_name}
+
+outputs:
+  router:
+    value: {get_resource: router}
diff --git a/heat_tempest_plugin/tests/scenario/templates/remote_nested_intermediate.yaml b/heat_tempest_plugin/tests/scenario/templates/remote_nested_intermediate.yaml
new file mode 100644
index 0000000..6fc7082
--- /dev/null
+++ b/heat_tempest_plugin/tests/scenario/templates/remote_nested_intermediate.yaml
@@ -0,0 +1,27 @@
+heat_template_version: 2015-10-15
+description: |
+  The intermediate stack (containing a local nested stack) to be instantiated
+  remotely in the remote deeply-nested stack test.
+
+parameters:
+  name:
+    type: string
+    description: Name of the router
+    constraints:
+      - allowed_pattern: "[a-z][a-z0-9-]{1,}"
+  network_name:
+    type: string
+    description: The public network to connect to
+    constraints:
+      - custom_constraint: neutron.network
+
+resources:
+  network_stack_as_custom_type:
+    type: remote_nested_base.yaml
+    properties:
+      name: {get_param: name}
+      network_name: {get_param: network_name}
+
+outputs:
+  router:
+    value: {get_attr: [network_stack_as_custom_type, router]}
diff --git a/heat_tempest_plugin/tests/scenario/templates/remote_nested_root.yaml b/heat_tempest_plugin/tests/scenario/templates/remote_nested_root.yaml
new file mode 100644
index 0000000..39bb8cb
--- /dev/null
+++ b/heat_tempest_plugin/tests/scenario/templates/remote_nested_root.yaml
@@ -0,0 +1,35 @@
+heat_template_version: 2015-10-15
+description: |
+  The root stack (containing a remote stack) for the deeply-nested remote
+  stack test.
+
+parameters:
+  name:
+    type: string
+    description: Name of the router
+    constraints:
+      - allowed_pattern: "[a-z][a-z0-9-]{1,}"
+  network_name:
+    type: string
+    description: The public network to connect to
+    constraints:
+      - custom_constraint: neutron.network
+  region:
+    type: string
+    description: The region in which to create the remote stack
+    default: RegionOne
+
+resources:
+  network_stack:
+    type: OS::Heat::Stack
+    properties:
+      template: {get_file: remote_nested_intermediate.yaml}
+      context:
+        region_name: {get_param: region}
+      parameters:
+        name: {get_param: name}
+        network_name: {get_param: network_name}
+
+outputs:
+  router:
+    value: {get_attr: [network_stack, outputs, router]}
diff --git a/heat_tempest_plugin/tests/scenario/test_aodh_alarm.py b/heat_tempest_plugin/tests/scenario/test_aodh_alarm.py
index 4e25158..dafca12 100644
--- a/heat_tempest_plugin/tests/scenario/test_aodh_alarm.py
+++ b/heat_tempest_plugin/tests/scenario/test_aodh_alarm.py
@@ -51,9 +51,9 @@
         stack_identifier = self.stack_create(template=self.template,
                                              parameters=parameters)
 
-        measures = [{'timestamp': test.isotime(datetime.datetime.now()),
+        measures = [{'timestamp': test.isotime(datetime.datetime.utcnow()),
                      'value': 100}, {'timestamp': test.isotime(
-                         datetime.datetime.now() + datetime.timedelta(
+                         datetime.datetime.utcnow() + datetime.timedelta(
                              minutes=1)), 'value': 100}]
         # send measures(should cause the alarm to fire)
         self.metric_client.metric.add_measures(metric['id'], measures)
diff --git a/heat_tempest_plugin/tests/functional/test_octavia_lbaas.py b/heat_tempest_plugin/tests/scenario/test_octavia_lbaas.py
similarity index 75%
rename from heat_tempest_plugin/tests/functional/test_octavia_lbaas.py
rename to heat_tempest_plugin/tests/scenario/test_octavia_lbaas.py
index b1e500c..ce07262 100644
--- a/heat_tempest_plugin/tests/functional/test_octavia_lbaas.py
+++ b/heat_tempest_plugin/tests/scenario/test_octavia_lbaas.py
@@ -13,11 +13,11 @@
 from tempest.lib import decorators
 
 from heat_tempest_plugin.common import test
-from heat_tempest_plugin.tests.functional import functional_base
+from heat_tempest_plugin.tests.scenario import scenario_base
 
 
 @test.requires_resource_type('OS::Octavia::LoadBalancer')
-class LoadBalancerTest(functional_base.FunctionalTestsBase):
+class LoadBalancerTest(scenario_base.ScenarioTestsBase):
     def setUp(self):
         super(LoadBalancerTest, self).setUp()
         self.template_name = 'octavia_lbaas.yaml'
@@ -36,66 +36,60 @@
         )
         self.files = {'lb_member.yaml': member_template}
         self.env = {'resource_registry': {
-            'OS::Test::PoolMember': 'lb_member.yaml'}}
+            'OS::Test::PoolMember': self.member_template_name}}
 
-        self.template = self._load_template(__file__, self.template_name,
-                                            self.sub_dir)
-        return self.stack_create(template=self.template,
+        return self.launch_stack(self.template_name,
                                  parameters=self.parameters,
                                  files=self.files,
                                  environment=self.env)
 
     @decorators.idempotent_id('5d2c4452-4433-4438-899c-7711c01d3c50')
     def test_create_update_loadbalancer(self):
+        statuses = ['PENDING_UPDATE', 'ACTIVE']
         stack_identifier = self._create_stack()
         stack = self.client.stacks.get(stack_identifier)
         output = self._stack_output(stack, 'loadbalancer')
-        self.assertEqual('ONLINE', output['operating_status'])
+        self.assertIn(output['provisioning_status'], statuses)
         self.parameters['lb_algorithm'] = 'SOURCE_IP'
 
         self.update_stack(stack_identifier,
-                          template=self.template,
                           parameters=self.parameters,
-                          files=self.files,
-                          environment=self.env)
+                          existing=True)
         stack = self.client.stacks.get(stack_identifier)
 
         output = self._stack_output(stack, 'loadbalancer')
-        self.assertEqual('ONLINE', output['operating_status'])
+        self.assertIn(output['provisioning_status'], statuses)
         output = self._stack_output(stack, 'pool')
         self.assertEqual('SOURCE_IP', output['lb_algorithm'])
 
     @decorators.idempotent_id('970e91af-1be8-4990-837b-66f9b5aff2b9')
     def test_add_delete_poolmember(self):
+        statuses = ['PENDING_UPDATE', 'ACTIVE']
         stack_identifier = self._create_stack()
         stack = self.client.stacks.get(stack_identifier)
         output = self._stack_output(stack, 'loadbalancer')
-        self.assertEqual('ONLINE', output['operating_status'])
+        self.assertIn(output['provisioning_status'], statuses)
         output = self._stack_output(stack, 'pool')
         self.assertEqual(1, len(output['members']))
         # add pool member
         self.parameters['member_count'] = 2
         self.update_stack(stack_identifier,
-                          template=self.template,
                           parameters=self.parameters,
-                          files=self.files,
-                          environment=self.env)
+                          existing=True)
         stack = self.client.stacks.get(stack_identifier)
 
         output = self._stack_output(stack, 'loadbalancer')
-        self.assertEqual('ONLINE', output['operating_status'])
+        self.assertIn(output['provisioning_status'], statuses)
         output = self._stack_output(stack, 'pool')
         self.assertEqual(2, len(output['members']))
         # delete pool member
         self.parameters['member_count'] = 1
         self.update_stack(stack_identifier,
-                          template=self.template,
                           parameters=self.parameters,
-                          files=self.files,
-                          environment=self.env)
+                          existing=True)
         stack = self.client.stacks.get(stack_identifier)
 
         output = self._stack_output(stack, 'loadbalancer')
-        self.assertEqual('ONLINE', output['operating_status'])
+        self.assertIn(output['provisioning_status'], statuses)
         output = self._stack_output(stack, 'pool')
         self.assertEqual(1, len(output['members']))
diff --git a/heat_tempest_plugin/tests/scenario/test_remote_deeply_nested.py b/heat_tempest_plugin/tests/scenario/test_remote_deeply_nested.py
new file mode 100644
index 0000000..d020e63
--- /dev/null
+++ b/heat_tempest_plugin/tests/scenario/test_remote_deeply_nested.py
@@ -0,0 +1,39 @@
+# 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 six
+import uuid
+
+from heat_tempest_plugin.tests.scenario import scenario_base
+from tempest.lib import decorators
+
+
+class RemoteDeeplyNestedStackTest(scenario_base.ScenarioTestsBase):
+    @decorators.idempotent_id('2ed94cae-da14-4060-a6b3-526e7a8cbbe4')
+    def test_remote_nested(self):
+        parameters = {
+            'name': 'remote-nested',
+            'network_name': 'public',
+        }
+
+        stack_id = self.launch_stack(
+            template_name='remote_nested_root.yaml',
+            parameters={'region': self.conf.region},
+            environment={'parameters': parameters}
+        )
+
+        stack = self.client.stacks.get(stack_id)
+        router_id = self._stack_output(stack, 'router')
+        self.assertIsInstance(router_id, six.string_types)
+        uuid.UUID(router_id)
+
+        self._stack_delete(stack_id)
diff --git a/tox.ini b/tox.ini
index 7b8aeec..549457a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -12,6 +12,7 @@
            testr run {posargs}
 
 [testenv:pep8]
+basepython = python3
 setenv =
     PYTHONPATH = .
 commands =
@@ -25,12 +26,14 @@
     check-uuid --fix --package heat_tempest_plugin
 
 [testenv:docs]
+basepython = python3
 deps = -r{toxinidir}/requirements.txt
        -r{toxinidir}/test-requirements.txt
        sphinxcontrib-httpdomain
 commands = python setup.py build_sphinx
 
 [testenv:venv]
+basepython = python3
 commands = {posargs}
 
 [flake8]