Merge tag '6.0.0'

This is a null-merge of the 6.0.0 release tag back into the master
branch so that the 6.0.0 tag will appear in the git commit history of
the master branch. It contains no actual changes to the master branch,
regardless of how our code review system's UI represents it. Please
ask in #openstack-infra if you have any questions, and otherwise try
to merge this as quickly as possible to avoid later conflicts on the
master branch.

Change-Id: Id8bbf0421c4d74d92df22cf485b4c29bb05d685d
diff --git a/common/clients.py b/common/clients.py
index acbf239..8913595 100644
--- a/common/clients.py
+++ b/common/clients.py
@@ -110,7 +110,8 @@
                 password=self.conf.password)
 
     def _get_identity_client(self):
-        domain = self.conf.domain_name
+        user_domain_name = self.conf.user_domain_name
+        project_domain_name = self.conf.project_domain_name
         kwargs = {
             'username': self.conf.username,
             'password': self.conf.password,
@@ -120,8 +121,8 @@
         # keystone v2 can't ignore domain details
         if self.auth_version == '3':
             kwargs.update({
-                'project_domain_name': domain,
-                'user_domain_name': domain})
+                'user_domain_name': user_domain_name,
+                'project_domain_name': project_domain_name})
         auth = password.Password(**kwargs)
         if self.insecure:
             verify_cert = False
@@ -196,7 +197,8 @@
         return swift_client.Connection(**args)
 
     def _get_metering_client(self):
-        domain = self.conf.domain_name
+        user_domain_name = self.conf.user_domain_name
+        project_domain_name = self.conf.project_domain_name
         try:
             endpoint = self.identity_client.get_endpoint_url('metering',
                                                              self.conf.region)
@@ -218,8 +220,8 @@
             # v2 auth_url
             if self.auth_version == '3':
                 args.update(
-                    {'user_domain_name': domain,
-                     'project_domain_name': domain})
+                    {'user_domain_name': user_domain_name,
+                     'project_domain_name': project_domain_name})
 
             return ceilometer_client.Client(self.CEILOMETER_VERSION,
                                             endpoint, **args)
diff --git a/common/config.py b/common/config.py
index 3aee48f..e99d034 100644
--- a/common/config.py
+++ b/common/config.py
@@ -38,9 +38,13 @@
     cfg.StrOpt('auth_url',
                default=os.environ.get('OS_AUTH_URL'),
                help="Full URI of the OpenStack Identity API (Keystone)"),
-    cfg.StrOpt('domain_name',
-               default='default',
-               help="User/project domain name, if keystone v3 auth_url"
+    cfg.StrOpt('user_domain_name',
+               default=os.environ.get('OS_USER_DOMAIN_NAME'),
+               help="User domain name, if keystone v3 auth_url"
+                    "is used"),
+    cfg.StrOpt('project_domain_name',
+               default=os.environ.get('OS_PROJECT_DOMAIN_NAME'),
+               help="Project domain name, if keystone v3 auth_url"
                     "is used"),
     cfg.StrOpt('region',
                default=os.environ.get('OS_REGION_NAME'),
diff --git a/functional/test_remote_stack.py b/functional/test_remote_stack.py
index 96306ac..b82958c 100644
--- a/functional/test_remote_stack.py
+++ b/functional/test_remote_stack.py
@@ -45,6 +45,9 @@
 
     def setUp(self):
         super(RemoteStackTest, self).setUp()
+        # replacing the template region with the one from the config
+        self.template = self.template.replace('RegionOne',
+                                              self.conf.region)
 
     def test_remote_stack_alone(self):
         stack_id = self.stack_create(template=self.remote_template)
@@ -78,7 +81,7 @@
         self.assertEqual(remote_resources, self.list_resources(remote_id))
 
     def test_stack_create_bad_region(self):
-        tmpl_bad_region = self.template.replace('RegionOne', 'DARKHOLE')
+        tmpl_bad_region = self.template.replace(self.conf.region, 'DARKHOLE')
         files = {'remote_stack.yaml': self.remote_template}
         kwargs = {
             'template': tmpl_bad_region,
@@ -98,8 +101,9 @@
         ex = self.assertRaises(exc.HTTPBadRequest, self.stack_create, **kwargs)
 
         error_msg = ('ERROR: Failed validating stack template using Heat '
-                     'endpoint at region "RegionOne" due to '
-                     '"ERROR: The template section is invalid: resource"')
+                     'endpoint at region "%s" due to '
+                     '"ERROR: The template section is '
+                     'invalid: resource"') % self.conf.region
         self.assertEqual(error_msg, six.text_type(ex))
 
     def test_stack_update(self):
diff --git a/functional/test_templates.py b/functional/test_templates.py
index dfc9c4f..82a1af4 100644
--- a/functional/test_templates.py
+++ b/functional/test_templates.py
@@ -54,7 +54,7 @@
         supported_template_versions = ["2013-05-23", "2014-10-16",
                                        "2015-04-30", "2015-10-15",
                                        "2012-12-12", "2010-09-09",
-                                       "2016-04-08"]
+                                       "2016-04-08", "2016-10-14"]
         for template in template_versions:
             self.assertIn(template.version.split(".")[1],
                           supported_template_versions)
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)