Add lbaas v2 scenario test

Make use of the new lbaas v2 resources.
This is an experimental gate job.

Will use the lbaas v2 namespace-haproxy driver instead of octavia.  Octavia
requires nested vms which is very slow and causes timeouts in devstack gates.

Change-Id: I7ea6e50a1da46622bdddcfccaf82203f473bfacc
diff --git a/scenario/templates/app_server_lbv2_neutron.yaml b/scenario/templates/app_server_lbv2_neutron.yaml
new file mode 100644
index 0000000..f750a98
--- /dev/null
+++ b/scenario/templates/app_server_lbv2_neutron.yaml
@@ -0,0 +1,69 @@
+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:
+    type: string
+
+  app_port:
+    type: number
+
+  timeout:
+    type: number
+
+  subnet:
+    type: string
+
+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::LBaaS::PoolMember
+    depends_on: waiter
+    properties:
+      address: { get_attr: [ server, networks, { get_param: net }, 0 ] }
+      pool: { get_param: pool }
+      protocol_port: { get_param: app_port }
+      subnet: { get_param: subnet }
diff --git a/scenario/templates/test_autoscaling_lbv2_neutron.yaml b/scenario/templates/test_autoscaling_lbv2_neutron.yaml
new file mode 100644
index 0000000..4702366
--- /dev/null
+++ b/scenario/templates/test_autoscaling_lbv2_neutron.yaml
@@ -0,0 +1,116 @@
+heat_template_version: 2015-04-30
+
+description: |
+  Template which tests Neutron load balancing requests to members of
+  Heat AutoScalingGroup. This uses LBaas V2.
+  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: { get_resource: pool }
+          subnet: { get_param: subnet }
+          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::LBaaS::HealthMonitor
+    properties:
+      delay: 3
+      type: HTTP
+      timeout: 3
+      max_retries: 3
+      pool: { get_resource: pool }
+
+  pool:
+    type: OS::Neutron::LBaaS::Pool
+    properties:
+      lb_algorithm: ROUND_ROBIN
+      protocol: HTTP
+      listener: { get_resource: listener }
+
+  listener:
+    type: OS::Neutron::LBaaS::Listener
+    properties:
+      loadbalancer: { get_resource: loadbalancer }
+      protocol: HTTP
+      protocol_port: { get_param: lb_port }
+
+  loadbalancer:
+    type: OS::Neutron::LBaaS::LoadBalancer
+    properties:
+      vip_subnet: { get_param: subnet }
+
+  floating_ip:
+    type: OS::Neutron::FloatingIP
+    properties:
+      floating_network: { get_param: public_net }
+      port_id: { get_attr: [loadbalancer, vip_port_id] }
+
+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/test_autoscaling_lbv2.py b/scenario/test_autoscaling_lbv2.py
index 861943a..89c4877 100644
--- a/scenario/test_autoscaling_lbv2.py
+++ b/scenario/test_autoscaling_lbv2.py
@@ -11,6 +11,11 @@
 #    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
 
 
@@ -29,6 +34,28 @@
         if not self.is_network_extension_supported('lbaasv2'):
             self.skipTest('LBaasv2 extension not available, skipping')
 
+    def check_num_responses(self, url, expected_num, retries=20):
+        resp = set()
+        for count in range(retries):
+            time.sleep(2)
+            try:
+                r = requests.get(url, verify=self.verify_cert)
+            except requests.exceptions.ConnectionError:
+                # The LB may not be up yet, let's retry
+                continue
+            # skip unsuccessful 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 v2 resource in Heat.
 
@@ -43,5 +70,47 @@
                loadbalanced IP.
         """
 
-        # TODO(MRV): Place holder for AutoScaing and Neutron LBaaS v2 test
-        pass
+        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_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)