Merge "Add gabbi api tests for software config/deployment"
diff --git a/.zuul.yaml b/.zuul.yaml
index 61c94c3..c9aea1b 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -1,3 +1,20 @@
+- job:
+    name: heat-functional-convg-queens
+    parent: heat-functional-convg-mysql-lbaasv2
+    override-checkout: stable/queens
+
+- job:
+    name: heat-functional-orig-queens
+    parent: heat-functional-orig-mysql-lbaasv2
+    override-checkout: stable/queens
+
+- job:
+    name: heat-functional-convg-queens-py35
+    parent: heat-functional-convg-mysql-lbaasv2-py35
+    override-checkout: stable/queens
+
+
+
 - project:
     check:
       jobs:
@@ -5,6 +22,9 @@
         - heat-functional-convg-mysql-lbaasv2
         - heat-functional-convg-mysql-lbaasv2-non-apache
         - heat-functional-convg-mysql-lbaasv2-py35
+        - heat-functional-convg-queens
+        - heat-functional-convg-queens-py35
+        - heat-functional-orig-queens
     gate:
       jobs:
         - heat-functional-orig-mysql-lbaasv2
diff --git a/heat_tempest_plugin/common/test.py b/heat_tempest_plugin/common/test.py
index 4fdfb8e..429d4ae 100644
--- a/heat_tempest_plugin/common/test.py
+++ b/heat_tempest_plugin/common/test.py
@@ -34,6 +34,7 @@
 
 LOG = logging.getLogger(__name__)
 _LOG_FORMAT = "%(levelname)8s [%(name)s] %(message)s"
+_resource_types = None
 
 
 def call_until_true(duration, sleep_for, func, *args, **kwargs):
@@ -86,7 +87,47 @@
     return skipper(test_method)
 
 
-class HeatIntegrationTest(testscenarios.WithScenarios,
+def requires_resource_type(resource_type):
+    '''Decorator for tests requiring a resource type.
+
+    The decorated test will be skipped when the resource type 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
+
+        global _resource_types
+        if not _resource_types:
+            manager = clients.ClientManager(conf)
+            obj_rtypes = manager.orchestration_client.resource_types.list()
+            _resource_types = list(t.resource_type for t in obj_rtypes)
+        rtype_available = resource_type and resource_type in _resource_types
+        skipper = testtools.skipUnless(
+            rtype_available,
+            "%s resource type not available, skipping test." % resource_type)
+        return skipper(test_method)
+    return decorator
+
+
+def requires_feature(feature):
+    '''Decorator for tests requring specific feature.
+
+    The decorated test will be skipped when a specific feature is disabled.
+    '''
+    def decorator(test_method):
+        features_group = getattr(config.CONF, 'heat_features_enabled', None)
+        if not features_group:
+            return test_method
+        feature_enabled = config.CONF.heat_features_enabled.get(feature, False)
+        skipper = testtools.skipUnless(feature_enabled,
+                                       "%s - Feature not enabled." % feature)
+        return skipper(test_method)
+    return decorator
+
+
+class HeatIntegrationTest(testtools.testcase.WithAttributes,
+                          testscenarios.WithScenarios,
                           testtools.TestCase):
 
     def setUp(self):
@@ -712,13 +753,13 @@
             time.sleep(build_interval)
 
     def check_autoscale_complete(self, stack_id, expected_num, parent_stack,
-                                 policy):
+                                 group_name):
         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
         if all_res and all_res_complete:
-            metadata = self.client.resources.metadata(parent_stack, policy)
+            metadata = self.client.resources.metadata(parent_stack, group_name)
             return not metadata.get('scaling_in_progress')
         return False
diff --git a/heat_tempest_plugin/config.py b/heat_tempest_plugin/config.py
index d658a98..c77bb51 100644
--- a/heat_tempest_plugin/config.py
+++ b/heat_tempest_plugin/config.py
@@ -153,10 +153,23 @@
     cfg.StrOpt('heat_config_notify_script',
                default=('heat-config-notify'),
                help="Path to the script heat-config-notify"),
+    cfg.StrOpt('hidden_stack_tag',
+               default='data-processing-cluster',
+               help="Tag to be considered as hidden for stack tags tests"),
+]
 
+heat_features_group = cfg.OptGroup(
+    name='heat_features_enabled',
+    title="Enabled Orchestration Service Features")
+
+HeatFeaturesGroup = [
+    cfg.BoolOpt('stack_cancel',
+                default=False,
+                help="If false, skip stack cancel tests")
 ]
 
 
 def list_opts():
     yield heat_group.name, HeatGroup
+    yield heat_features_group.name, HeatFeaturesGroup
     yield service_available_group.name, ServiceAvailableGroup
diff --git a/heat_tempest_plugin/plugin.py b/heat_tempest_plugin/plugin.py
index acf4fc7..6926691 100644
--- a/heat_tempest_plugin/plugin.py
+++ b/heat_tempest_plugin/plugin.py
@@ -34,7 +34,11 @@
                                   heat_config.ServiceAvailableGroup)
         config.register_opt_group(conf, heat_config.heat_group,
                                   heat_config.HeatGroup)
+        config.register_opt_group(conf, heat_config.heat_features_group,
+                                  heat_config.HeatFeaturesGroup)
 
     def get_opt_lists(self):
         return [(heat_config.heat_group.name,
-                 heat_config.HeatGroup)]
+                 heat_config.HeatGroup),
+                (heat_config.heat_features_group.name,
+                 heat_config.HeatFeaturesGroup)]
diff --git a/heat_tempest_plugin/services/clients.py b/heat_tempest_plugin/services/clients.py
index 4567968..3999cee 100644
--- a/heat_tempest_plugin/services/clients.py
+++ b/heat_tempest_plugin/services/clients.py
@@ -15,7 +15,6 @@
 from cinderclient import client as cinder_client
 from gnocchiclient import client as gnocchi_client
 from heatclient import client as heat_client
-from keystoneauth1 import exceptions as kc_exceptions
 from keystoneauth1.identity.generic import password
 from keystoneauth1 import session
 from neutronclient.v2_0 import client as neutron_client
@@ -104,23 +103,19 @@
     def _get_orchestration_client(self):
         endpoint = os.environ.get('HEAT_URL')
         if os.environ.get('OS_NO_CLIENT_AUTH') == 'True':
-            token = None
+            session = None
         else:
-            token = self.identity_client.auth_token
-        try:
-            if endpoint is None:
-                endpoint = self.identity_client.get_endpoint_url(
-                    'orchestration', region=self.conf.region,
-                    endpoint_type=self.conf.endpoint_type)
-        except kc_exceptions.EndpointNotFound:
-            return None
-        else:
-            return heat_client.Client(
-                self.HEATCLIENT_VERSION,
-                endpoint,
-                token=token,
-                username=self._username(),
-                password=self._password())
+            session = self.identity_client.session
+
+        return heat_client.Client(
+            self.HEATCLIENT_VERSION,
+            endpoint,
+            session=session,
+            endpoint_type='publicURL',
+            service_type='orchestration',
+            region_name=self.conf.region,
+            username=self._username(),
+            password=self._password())
 
     def _get_identity_client(self):
         user_domain_id = self.conf.user_domain_id
diff --git a/heat_tempest_plugin/tests/api/gabbits/environments.yaml b/heat_tempest_plugin/tests/api/gabbits/environments.yaml
index 17ac476..fe1dc04 100644
--- a/heat_tempest_plugin/tests/api/gabbits/environments.yaml
+++ b/heat_tempest_plugin/tests/api/gabbits/environments.yaml
@@ -4,6 +4,7 @@
 
 tests:
 - name: environment with parameter
+  desc: 8281d088-0c80-4071-a13d-333b309be6ca
   POST: /stacks
   request_headers:
     content-type: application/json
@@ -34,15 +35,17 @@
     location: //stacks/$ENVIRON['PREFIX']-envstack/[a-f0-9-]+/
 
 - name: poll for envstack CREATE_COMPLETE
+  desc: 29899c70-9c94-4e24-8988-df76f7eaaa70
   GET: $LOCATION
   redirects: True
   poll:
-    count: 5
+    count: 10
     delay: 1.0
   response_json_paths:
     $.stack.stack_status: CREATE_COMPLETE
 
 - name: get stack output
+  desc: f60dd8df-82d1-4228-8926-54d65ebd12e1
   GET: $LAST_URL/outputs/output_value
   redirects: True
   status: 200
@@ -50,6 +53,7 @@
     $.output.output_value: test
 
 - name: delete envstack
+  desc: 0efde180-cc0e-4f2c-bb18-fa345e8d21ad
   DELETE: /stacks/$ENVIRON['PREFIX']-envstack
   redirects: True
   status: 204
diff --git a/heat_tempest_plugin/tests/api/gabbits/resources.yaml b/heat_tempest_plugin/tests/api/gabbits/resources.yaml
index 41da444..c20333b 100644
--- a/heat_tempest_plugin/tests/api/gabbits/resources.yaml
+++ b/heat_tempest_plugin/tests/api/gabbits/resources.yaml
@@ -4,6 +4,7 @@
 
 tests:
 - name: create stack with resources
+  desc: 947be7b2-503d-41f5-9843-61be50954f13
   POST: /stacks
   request_headers:
     content-type: application/json
@@ -29,15 +30,17 @@
     location: //stacks/$ENVIRON['PREFIX']-rsrcstack/[a-f0-9-]+/
 
 - name: poll for rsrcstack CREATE_COMPLETE
+  desc: e9eac22f-c3e7-450f-a087-08a8655a6e8e
   GET: $LOCATION
   redirects: True
   poll:
-    count: 5
+    count: 10
     delay: 1.0
   response_json_paths:
     $.stack.stack_status: CREATE_COMPLETE
 
 - name: list resources
+  desc: ec53f10d-a89a-4243-8706-629a01ea890f
   GET: $LAST_URL/resources
   request_headers:
     content-type: application/json
@@ -47,6 +50,7 @@
     $.resources[0].resource_status: CREATE_COMPLETE
 
 - name: list filtered resources
+  desc: da07d3d2-9ccc-4fa1-9b1b-9cb3074fe9b9
   GET: $LAST_URL
   request_headers:
     content-type: application/json
@@ -57,6 +61,7 @@
     $.resources: []
 
 - name: show resource
+  desc: 2cbcedc5-0aa7-454e-bf89-a3dd5d379dc1
   GET: $LAST_URL/test
   request_headers:
     content-type: application/json
@@ -65,6 +70,7 @@
     $.resource.attributes.output: test
 
 - name: mark resource unhealthy
+  desc: 6031516b-3a8f-4d1b-8990-81a571b5f956
   PATCH: $LAST_URL
   request_headers:
     content-type: application/json
@@ -74,6 +80,7 @@
   status: 200
 
 - name: show unhealthy resource
+  desc: 9e784490-2e88-49af-8ee7-c4c6aba2be64
   GET: $LAST_URL
   status: 200
   response_json_paths:
@@ -81,10 +88,12 @@
     $.resource.resource_status_reason: 'resource deleted'
 
 - name: signal resource
+  desc: c65a047c-8c7b-4b44-9f5f-bf1069751c5c
   POST: $LAST_URL/signal
   status: 400
 
 - name: delete stack with resources
+  desc: 0edc4fdc-811d-4d27-a0dd-6ec4db2bda6e
   DELETE: /stacks/$ENVIRON['PREFIX']-rsrcstack
   redirects: True
   status: 204
diff --git a/heat_tempest_plugin/tests/api/gabbits/resourcetypes.yaml b/heat_tempest_plugin/tests/api/gabbits/resourcetypes.yaml
index 0730cc8..809feab 100644
--- a/heat_tempest_plugin/tests/api/gabbits/resourcetypes.yaml
+++ b/heat_tempest_plugin/tests/api/gabbits/resourcetypes.yaml
@@ -4,10 +4,12 @@
 
 tests:
 - name: list resource types
+  desc: 5b4db88b-d171-4400-b7a7-a7dc8f597d31
   GET: /resource_types
   status: 200
 
 - name: show resource type
+  desc: cc05d1ef-17f1-430e-bea1-0f6766f7d0b4
   GET: /resource_types/OS::Heat::TestResource
   status: 200
   response_json_paths:
@@ -15,10 +17,11 @@
     $.properties.wait_secs.default: 0
 
 - name: resource type template
+  desc: 5a2164eb-645a-4245-acd7-b222a715fc09
   GET: /resource_types/OS::Heat::TestResource/template
   query_parameters:
     template_type: hot
   status: 200
   response_json_paths:
     $.resources.TestResource.type: OS::Heat::TestResource
-    $.heat_template_version: '2016-10-14'
+    $.heat_template_version: /^20[1-9][0-9]-[0-1][0-9]-[0-3][0-9]$/
diff --git a/heat_tempest_plugin/tests/api/gabbits/stacks.yaml b/heat_tempest_plugin/tests/api/gabbits/stacks.yaml
index cb67e71..9d9328e 100644
--- a/heat_tempest_plugin/tests/api/gabbits/stacks.yaml
+++ b/heat_tempest_plugin/tests/api/gabbits/stacks.yaml
@@ -4,12 +4,14 @@
 
 tests:
 - name: stack list
+  desc: 39c0245e-6055-41cf-9f0e-15adfe55ded6
   GET: /stacks
   status: 200
   response_headers:
     content-type: application/json
 
 - name: create empty stack
+  desc: bde1b827-65fb-47ea-909f-82537e6260d3
   POST: /stacks
   request_headers:
     content-type: application/json
@@ -28,25 +30,29 @@
 
 
 - name: poll for empty CREATE_COMPLETE
+  desc: f575e5c4-2aed-4381-9f0d-2dfcb0640c4b
   GET: $LOCATION
   redirects: True
   poll:
-    count: 5
+    count: 10
     delay: 1.0
   response_json_paths:
     $.stack.stack_status: CREATE_COMPLETE
 
 - name: show empty stack
+  desc: 89b233fe-0d55-4959-9289-0b5dabe4e4c9
   GET: $LAST_URL
   redirects: True
   status: 200
 
 - name: delete empty stack
+  desc: 7eca55fe-8300-43b6-a6b8-fb2d99b51911
   DELETE: $LAST_URL
   redirects: True
   status: 204
 
 - name: create stack
+  desc: 56ac2173-97c5-4347-bd32-529a260cfac3
   POST: /stacks
   request_headers:
     content-type: application/json
@@ -74,20 +80,23 @@
     location: //stacks/$ENVIRON['PREFIX']-stack/[a-f0-9-]+/
 
 - name: poll for stack CREATE_COMPLETE
+  desc: 6a0fe2dc-2822-4af3-b606-321ff7ad3de9
   GET: $LOCATION
   redirects: True
   poll:
-    count: 5
+    count: 10
     delay: 1.0
   response_json_paths:
     $.stack.stack_status: CREATE_COMPLETE
 
 - name: show stack
+  desc: 9b268607-0335-4667-a613-bccf81e66f8f
   GET: $LAST_URL
   redirects: True
   status: 200
 
 - name: update stack
+  desc: 6bb1ec02-dd19-4b2c-9a6d-866ce666650f
   PUT: $LAST_URL
   request_headers:
     content-type: application/json
@@ -115,15 +124,17 @@
   status: 202
 
 - name: poll for stack UPDATE_COMPLETE
+  desc: 3e280fb3-02b6-44fb-84dd-e04921d47733
   GET: $LAST_URL
   redirects: True
   poll:
-    count: 5
+    count: 10
     delay: 1.0
   response_json_paths:
     $.stack.stack_status: UPDATE_COMPLETE
 
 - name: patch update stack
+  desc: 927cea42-a35b-4664-b209-ab2cb34e6ef4
   PATCH: $LAST_URL
   request_headers:
     content-type: application/json
@@ -133,16 +144,18 @@
   status: 202
 
 - name: poll for stack patch UPDATE_COMPLETE
+  desc: a1cfd3b4-2536-4c54-94f4-12093f2ccf3b
   GET: $LAST_URL
   redirects: True
   poll:
-    count: 5
+    count: 10
     delay: 1.0
   response_json_paths:
     $.stack.stack_status: UPDATE_COMPLETE
     $.stack.updated_time: /^(?!$HISTORY['poll for stack UPDATE_COMPLETE'].$RESPONSE['$.stack.updated_time'])/
 
 - name: list stack outputs
+  desc: bbd98b50-b75b-44a1-b7e8-0a68fd7c6d33
   GET: $LAST_URL/outputs
   redirects: True
   status: 200
@@ -150,6 +163,7 @@
     $.outputs[0].output_key: output_value
 
 - name: get stack output
+  desc: e761f5d7-70f6-4d95-a11b-e5fa0ecb43d2
   GET: $LAST_URL/output_value
   redirects: True
   status: 200
@@ -157,6 +171,7 @@
     $.output.output_value: new_patched_value
 
 - name: delete stack
+  desc: bcf4c359-0a64-4652-b465-df3f688a9d4d
   DELETE: /stacks/$ENVIRON['PREFIX']-stack
   redirects: True
   status: 204
diff --git a/heat_tempest_plugin/tests/api/gabbits/templates.yaml b/heat_tempest_plugin/tests/api/gabbits/templates.yaml
index 7b67054..ab0bbab 100644
--- a/heat_tempest_plugin/tests/api/gabbits/templates.yaml
+++ b/heat_tempest_plugin/tests/api/gabbits/templates.yaml
@@ -4,19 +4,22 @@
 
 tests:
 - name: list template versions
+  desc: cbc28c20-e740-43ef-a01b-b1a39f4a0db3
   GET: /template_versions
   status: 200
   response_json_paths:
-    $.template_versions[?(@.version='heat_template_version.2017-02-24')].type: hot
+    $.template_versions[?(@.version='heat_template_version.2016-04-08')].type: hot
 
 - name: list template functions
-  GET: /template_versions/heat_template_version.2016-10-14/functions
+  desc: 721ff23c-8527-480f-a090-1c915b4f8430
+  GET: /template_versions/heat_template_version.2016-04-08/functions
   status: 200
   response_json_paths:
     $.template_functions[?(@.functions='get_file')].description:
       A function for including a file inline.
 
 - name: template validate
+  desc: f307139b-03d0-4006-92b7-81c86c949727
   POST: /validate
   request_headers:
     content-type: application/json
diff --git a/heat_tempest_plugin/tests/api/test_heat_api.py b/heat_tempest_plugin/tests/api/test_heat_api.py
index bf86839..804f445 100644
--- a/heat_tempest_plugin/tests/api/test_heat_api.py
+++ b/heat_tempest_plugin/tests/api/test_heat_api.py
@@ -13,15 +13,19 @@
 
 """A test module to exercise the Heat API with gabbi.  """
 
+import keystoneauth1
 import os
+import sys
+import unittest
 
 from gabbi import driver
-from six.moves.urllib import parse as urlparse
+from oslo_log import log as logging
+from tempest import config
 
 from heat_tempest_plugin.common import test
 from heat_tempest_plugin.services import clients
-from tempest import config
 
+LOG = logging.getLogger(__name__)
 TESTS_DIR = 'gabbits'
 
 
@@ -29,16 +33,67 @@
     """Provide a TestSuite to the discovery process."""
     test_dir = os.path.join(os.path.dirname(__file__), TESTS_DIR)
 
+    endpoint = None
     conf = config.CONF.heat_plugin
-    if conf.auth_url is None:
-        # It's not configured, let's not load tests
-        return
-    manager = clients.ClientManager(conf)
-    endpoint = manager.identity_client.get_endpoint_url(
-        'orchestration', region=conf.region, endpoint_type=conf.endpoint_type)
-    host = urlparse.urlparse(endpoint).hostname
-    os.environ['OS_TOKEN'] = manager.identity_client.auth_token
-    os.environ['PREFIX'] = test.rand_name('api')
+    if conf.auth_url:
+        try:
+            manager = clients.ClientManager(conf)
+            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')
 
-    return driver.build_tests(test_dir, loader, host=host,
-                              url=endpoint, test_loader_name=__name__)
+        # Catch the authentication exceptions that can happen if one of the
+        # following conditions occur:
+        #   1. conf.auth_url IP/port is incorrect or keystone not available
+        #      (ConnectFailure)
+        #   2. conf.auth_url is malformed (BadRequest, UnknownConnectionError,
+        #      EndpointNotFound, NotFound, or DiscoveryFailure)
+        #   3. conf.username/password is incorrect (Unauthorized)
+        #   4. conf.project_name is missing/incorrect (EmptyCatalog)
+        # These exceptions should not prevent a test list from being returned,
+        # so just issue a warning log and move forward with test listing.
+        except (keystoneauth1.exceptions.http.BadRequest,
+                keystoneauth1.exceptions.http.Unauthorized,
+                keystoneauth1.exceptions.http.NotFound,
+                keystoneauth1.exceptions.catalog.EmptyCatalog,
+                keystoneauth1.exceptions.catalog.EndpointNotFound,
+                keystoneauth1.exceptions.discovery.DiscoveryFailure,
+                keystoneauth1.exceptions.connection.UnknownConnectionError,
+                keystoneauth1.exceptions.connection.ConnectFailure):
+            LOG.warn("Keystone auth exception: %s: %s" % (sys.exc_info()[0],
+                                                          sys.exc_info()[1]))
+            # Clear the auth_url, as there is no point in tempest trying
+            # to authenticate later with mis-configured or unreachable endpoint
+            conf.auth_url = None
+
+        except Exception:
+            LOG.error("Fatal exception: %s: %s" % (sys.exc_info()[0],
+                                                   sys.exc_info()[1]))
+            raise
+
+    def register_test_case_id(test_case):
+        tempest_id = test_case.test_data.get('desc')
+        test_name = test_case.id()
+        if not tempest_id:
+            raise AssertionError(
+                "No Tempest ID registered for API test %s" % test_name)
+
+        def test_id():
+            return test_name + '[id-%s]' % tempest_id
+
+        test_case.id = test_id
+
+    def register_test_suite_ids(test_suite):
+        for test_case in test_suite:
+            if isinstance(test_case, unittest.TestSuite):
+                register_test_suite_ids(test_case)
+            else:
+                register_test_case_id(test_case)
+
+    api_tests = driver.build_tests(test_dir, loader, url=endpoint, host="",
+                                   test_loader_name=__name__)
+
+    register_test_suite_ids(api_tests)
+    return api_tests
diff --git a/heat_tempest_plugin/tests/functional/templates/lb_member.yaml b/heat_tempest_plugin/tests/functional/templates/lb_member.yaml
new file mode 100644
index 0000000..0afa754
--- /dev/null
+++ b/heat_tempest_plugin/tests/functional/templates/lb_member.yaml
@@ -0,0 +1,61 @@
+heat_template_version: pike
+parameters:
+  image:
+    type: string
+  flavor:
+    type: string
+  network:
+    type: string
+  sec_group:
+    type: string
+  pool:
+    type: string
+  app_port:
+    type: number
+  timeout:
+    type: number
+    default: 120
+  subnet:
+    type: string
+
+resources:
+  server:
+    type: OS::Nova::Server
+    properties:
+      image: {get_param: image}
+      flavor: {get_param: flavor}
+      networks:
+        - network: {get_param: network}
+      security_groups:
+        - {get_param: sec_group}
+      user_data_format: RAW
+      user_data:
+        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_attr: [handle, curl_cli]}
+
+  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::Octavia::PoolMember
+    depends_on: waiter
+    properties:
+      address: {get_attr: [server, networks, {get_param: network}, 0]}
+      pool: {get_param: pool}
+      protocol_port: {get_param: app_port}
+      subnet: {get_param: subnet}
diff --git a/heat_tempest_plugin/tests/functional/templates/octavia_lbaas.yaml b/heat_tempest_plugin/tests/functional/templates/octavia_lbaas.yaml
new file mode 100644
index 0000000..d20ae60
--- /dev/null
+++ b/heat_tempest_plugin/tests/functional/templates/octavia_lbaas.yaml
@@ -0,0 +1,86 @@
+heat_template_version: pike
+parameters:
+  app_port:
+    type: number
+    default: 8080
+  flavor:
+    type: string
+    default: m1.nano
+  image:
+    type: string
+    default: cirros-0.3.5-x86_64-disk
+  lb_port:
+    type: number
+    default: 80
+  network:
+    type: string
+    default: heat-net
+  subnet:
+    type: string
+    default: heat-subnet
+  member_count:
+    type: number
+    default: 1
+  lb_algorithm:
+    type: string
+    default: ROUND_ROBIN
+
+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}
+
+  pool_members:
+    type: OS::Heat::ResourceGroup
+    properties:
+      count: {get_param: member_count}
+      resource_def:
+        type: OS::Test::PoolMember
+        properties:
+          image: {get_param: image}
+          flavor: {get_param: flavor}
+          pool: {get_resource: pool}
+          app_port: {get_param: app_port}
+          network: {get_param: network}
+          sec_group: {get_resource: sec_group}
+          subnet: {get_param: subnet}
+
+  monitor:
+    type: OS::Octavia::HealthMonitor
+    properties:
+      delay: 3
+      type: HTTP
+      timeout: 3
+      max_retries: 3
+      pool: {get_resource: pool}
+
+  pool:
+    type: OS::Octavia::Pool
+    properties:
+      lb_algorithm: {get_param: lb_algorithm}
+      protocol: HTTP
+      listener: {get_resource: listener}
+
+  listener:
+    type: OS::Octavia::Listener
+    properties:
+      loadbalancer: {get_resource: loadbalancer}
+      protocol: HTTP
+      protocol_port: {get_param: lb_port}
+
+  loadbalancer:
+    type: OS::Octavia::LoadBalancer
+    properties:
+      vip_subnet: {get_param: subnet}
+outputs:
+  loadbalancer:
+    value: {get_attr: [loadbalancer, show]}
+  pool:
+    value: {get_attr: [pool, show]}
+  listener:
+    value: {get_attr: [listener, show]}
diff --git a/heat_tempest_plugin/tests/functional/test_create_update_neutron_trunk.py b/heat_tempest_plugin/tests/functional/test_create_update_neutron_trunk.py
index ff5bcaf..bdcb58e 100644
--- a/heat_tempest_plugin/tests/functional/test_create_update_neutron_trunk.py
+++ b/heat_tempest_plugin/tests/functional/test_create_update_neutron_trunk.py
@@ -17,6 +17,7 @@
 
 from tempest.lib import decorators
 
+from heat_tempest_plugin.common import test
 from heat_tempest_plugin.tests.functional import functional_base
 
 
@@ -72,6 +73,7 @@
 '''
 
 
+@test.requires_resource_type('OS::Neutron::Trunk')
 class UpdateTrunkTest(functional_base.FunctionalTestsBase):
 
     @staticmethod
diff --git a/heat_tempest_plugin/tests/functional/test_octavia_lbaas.py b/heat_tempest_plugin/tests/functional/test_octavia_lbaas.py
new file mode 100644
index 0000000..b1e500c
--- /dev/null
+++ b/heat_tempest_plugin/tests/functional/test_octavia_lbaas.py
@@ -0,0 +1,101 @@
+#    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 tempest.lib import decorators
+
+from heat_tempest_plugin.common import test
+from heat_tempest_plugin.tests.functional import functional_base
+
+
+@test.requires_resource_type('OS::Octavia::LoadBalancer')
+class LoadBalancerTest(functional_base.FunctionalTestsBase):
+    def setUp(self):
+        super(LoadBalancerTest, self).setUp()
+        self.template_name = 'octavia_lbaas.yaml'
+        self.member_template_name = 'lb_member.yaml'
+        self.sub_dir = 'templates'
+
+    def _create_stack(self):
+        self.parameters = {
+            'flavor': self.conf.minimal_instance_type,
+            'image': self.conf.minimal_image_ref,
+            'network': self.conf.fixed_network_name,
+            'subnet': self.conf.fixed_subnet_name
+        }
+        member_template = self._load_template(
+            __file__, self.member_template_name, self.sub_dir
+        )
+        self.files = {'lb_member.yaml': member_template}
+        self.env = {'resource_registry': {
+            'OS::Test::PoolMember': 'lb_member.yaml'}}
+
+        self.template = self._load_template(__file__, self.template_name,
+                                            self.sub_dir)
+        return self.stack_create(template=self.template,
+                                 parameters=self.parameters,
+                                 files=self.files,
+                                 environment=self.env)
+
+    @decorators.idempotent_id('5d2c4452-4433-4438-899c-7711c01d3c50')
+    def test_create_update_loadbalancer(self):
+        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.parameters['lb_algorithm'] = 'SOURCE_IP'
+
+        self.update_stack(stack_identifier,
+                          template=self.template,
+                          parameters=self.parameters,
+                          files=self.files,
+                          environment=self.env)
+        stack = self.client.stacks.get(stack_identifier)
+
+        output = self._stack_output(stack, 'loadbalancer')
+        self.assertEqual('ONLINE', output['operating_status'])
+        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):
+        stack_identifier = self._create_stack()
+        stack = self.client.stacks.get(stack_identifier)
+        output = self._stack_output(stack, 'loadbalancer')
+        self.assertEqual('ONLINE', output['operating_status'])
+        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)
+        stack = self.client.stacks.get(stack_identifier)
+
+        output = self._stack_output(stack, 'loadbalancer')
+        self.assertEqual('ONLINE', output['operating_status'])
+        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)
+        stack = self.client.stacks.get(stack_identifier)
+
+        output = self._stack_output(stack, 'loadbalancer')
+        self.assertEqual('ONLINE', output['operating_status'])
+        output = self._stack_output(stack, 'pool')
+        self.assertEqual(1, len(output['members']))
diff --git a/heat_tempest_plugin/tests/functional/test_os_wait_condition.py b/heat_tempest_plugin/tests/functional/test_os_wait_condition.py
index dc78341..603b5e5 100644
--- a/heat_tempest_plugin/tests/functional/test_os_wait_condition.py
+++ b/heat_tempest_plugin/tests/functional/test_os_wait_condition.py
@@ -57,11 +57,11 @@
             wc_notify --data-binary ''{"status": "SUCCESS", "reason":
             "signal4", "data": "data4"}''
 
-            # check signals with the same number
+            # check signals with the same ID
 
-            wc_notify --data-binary ''{"status": "SUCCESS", "id": "5"}''
+            wc_notify --data-binary ''{"status": "SUCCESS", "id": "test5"}''
 
-            wc_notify --data-binary ''{"status": "SUCCESS", "id": "5"}''
+            wc_notify --data-binary ''{"status": "SUCCESS", "id": "test5"}''
 
             # loop for 20 signals without reasons and data
 
diff --git a/heat_tempest_plugin/tests/functional/test_stack_tags.py b/heat_tempest_plugin/tests/functional/test_stack_tags.py
index 7d3d52a..55c262d 100644
--- a/heat_tempest_plugin/tests/functional/test_stack_tags.py
+++ b/heat_tempest_plugin/tests/functional/test_stack_tags.py
@@ -72,7 +72,7 @@
     @decorators.idempotent_id('5ed79584-0684-4f9c-ae8e-44a8f874ec79')
     def test_hidden_stack(self):
         # Stack create with hidden stack tag
-        tags = 'foo,hidden'
+        tags = 'foo,%s' % self.conf.hidden_stack_tag
         self.stack_create(
             template=self.template,
             tags=tags)
diff --git a/heat_tempest_plugin/tests/scenario/test_autoscaling_lb.py b/heat_tempest_plugin/tests/scenario/test_autoscaling_lb.py
index b8ffa1f..23e27c7 100644
--- a/heat_tempest_plugin/tests/scenario/test_autoscaling_lb.py
+++ b/heat_tempest_plugin/tests/scenario/test_autoscaling_lb.py
@@ -106,7 +106,7 @@
         test.call_until_true(self.conf.build_timeout,
                              self.conf.build_interval,
                              self.check_autoscale_complete,
-                             asg.physical_resource_id, 2, sid, 'scale_up')
+                             asg.physical_resource_id, 2, sid, 'asg')
 
         # Check number of distinctive responses, must now be 2
         self.check_num_responses(lb_url, 2)
diff --git a/heat_tempest_plugin/tests/scenario/test_autoscaling_lbv2.py b/heat_tempest_plugin/tests/scenario/test_autoscaling_lbv2.py
index 52957f5..c3bda78 100644
--- a/heat_tempest_plugin/tests/scenario/test_autoscaling_lbv2.py
+++ b/heat_tempest_plugin/tests/scenario/test_autoscaling_lbv2.py
@@ -106,7 +106,7 @@
         test.call_until_true(self.conf.build_timeout,
                              self.conf.build_interval,
                              self.check_autoscale_complete,
-                             asg.physical_resource_id, 2, sid, 'scale_up')
+                             asg.physical_resource_id, 2, sid, 'asg')
 
         # Check number of distinctive responses, must now be 2
         self.check_num_responses(lb_url, 2)
diff --git a/heat_tempest_plugin/tests/scenario/test_server_signal.py b/heat_tempest_plugin/tests/scenario/test_server_signal.py
index a43c74a..1823087 100644
--- a/heat_tempest_plugin/tests/scenario/test_server_signal.py
+++ b/heat_tempest_plugin/tests/scenario/test_server_signal.py
@@ -29,6 +29,7 @@
             'key_name': self.keypair_name,
             'flavor': flavor,
             'image': image,
+            'public_net': self.conf.floating_network_name,
             'timeout': self.conf.build_timeout,
             'user_data_format': user_data_format
         }
diff --git a/requirements.txt b/requirements.txt
index 34fa1fd..43abf78 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,9 +1,8 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 # MIT
-keystoneauth1>=3.3.0 # Apache-2.0
-oslo.config>=5.1.0 # Apache-2.0
+keystoneauth1>=3.4.0 # Apache-2.0
+oslo.config>=5.2.0 # Apache-2.0
 oslo.log>=3.36.0 # Apache-2.0
 oslo.messaging>=5.29.0 # Apache-2.0
 os-collect-config>=5.0.0 # Apache-2.0
@@ -11,12 +10,12 @@
 python-cinderclient>=3.3.0 # Apache-2.0
 gnocchiclient>=3.3.1 # Apache-2.0
 python-heatclient>=1.10.0 # Apache-2.0
-python-neutronclient>=6.3.0 # Apache-2.0
+python-neutronclient>=6.7.0 # Apache-2.0
 python-novaclient>=9.1.0 # Apache-2.0
 python-swiftclient>=3.2.0 # Apache-2.0
 python-zaqarclient>=1.0.0 # Apache-2.0
 testtools>=2.2.0 # MIT
 testscenarios>=0.4 # Apache-2.0/BSD
 tempest>=17.1.0 # Apache-2.0
-gabbi>=1.35.0 # Apache-2.0
+gabbi>=1.42.1 # Apache-2.0
 kombu!=4.0.2,>=4.0.0 # BSD
diff --git a/test-requirements.txt b/test-requirements.txt
index 9f37a05..f6c0a00 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -4,5 +4,5 @@
 
 # Hacking already pins down pep8, pyflakes and flake8
 hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
-openstackdocstheme>=1.17.0 # Apache-2.0
-sphinx!=1.6.6,>=1.6.2 # BSD
+openstackdocstheme>=1.18.1 # Apache-2.0
+sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD