Merge "Force API test listing even if auth fails"
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 f75385f..a4c0edf 100644
--- a/heat_tempest_plugin/common/test.py
+++ b/heat_tempest_plugin/common/test.py
@@ -110,6 +110,22 @@
     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):
diff --git a/heat_tempest_plugin/config.py b/heat_tempest_plugin/config.py
index d658a98..e4c7b47 100644
--- a/heat_tempest_plugin/config.py
+++ b/heat_tempest_plugin/config.py
@@ -156,7 +156,18 @@
 
 ]
 
+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/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_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/requirements.txt b/requirements.txt
index 985e536..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.4.0 # Apache-2.0
-oslo.config>=5.1.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
diff --git a/test-requirements.txt b/test-requirements.txt
index 90882be..f6c0a00 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -5,4 +5,4 @@
 # Hacking already pins down pep8, pyflakes and flake8
 hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
 openstackdocstheme>=1.18.1 # Apache-2.0
-sphinx!=1.6.6,>=1.6.2 # BSD
+sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD