Merge "Revert "Skip test_server_connectivity_cold_migration_revert until fixed""
diff --git a/.zuul.yaml b/.zuul.yaml
index 462501e..42911a3 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -172,6 +172,41 @@
         c-bak: false
 
 - job:
+    name: tempest-integrated-networking
+    parent: devstack-tempest
+    branches: ^(?!stable/ocata).*$
+    description: |
+      This  job runs integration tests for networking. This is subset of
+      'tempest-full' job and run only Neutron and Nova related tests.
+      This is meant to be run on neutron gate only.
+    vars:
+      tox_envlist: integrated-network
+      devstack_localrc:
+        USE_PYTHON3: true
+        FORCE_CONFIG_DRIVE: true
+      devstack_services:
+        s-account: false
+        s-container: false
+        s-object: false
+        s-proxy: false
+        c-bak: false
+
+- job:
+    name: tempest-integrated-storage
+    parent: devstack-tempest
+    branches: ^(?!stable/ocata).*$
+    description: |
+      This job runs integration tests for image & block storage. This is
+      subset of 'tempest-full' job and run Cinder, Glance, Swift and Nova
+      related tests. This is meant to be run on Cinder and Glance gate only.
+    vars:
+      tox_envlist: integrated-storage
+      devstack_localrc:
+        USE_PYTHON3: true
+        FORCE_CONFIG_DRIVE: true
+        ENABLE_VOLUME_MULTIATTACH: true
+
+- job:
     name: tempest-full-py3-ipv6
     parent: devstack-tempest-ipv6
     # This currently works from stable/pike on.
@@ -273,6 +308,21 @@
       devstack_localrc:
         CINDER_ENABLED_BACKENDS: lvm:lvmdriver-1,lvm:lvmdriver-2
         ENABLE_VOLUME_MULTIATTACH: true
+      devstack_plugins:
+        neutron: https://opendev.org/openstack/neutron
+      devstack_services:
+        neutron-placement: true
+        neutron-qos: true
+      devstack_local_conf:
+        post-config:
+          "/$NEUTRON_CORE_PLUGIN_CONF":
+            ovs:
+              bridge_mappings: public:br-ex
+              resource_provider_bandwidths: br-ex:1000000:1000000
+        test-config:
+          $TEMPEST_CONFIG:
+            network-feature-enabled:
+              qos_placement_physnet: public
       tempest_concurrency: 2
     group-vars:
       # NOTE(mriedem): The ENABLE_VOLUME_MULTIATTACH variable is used on both
@@ -389,7 +439,6 @@
       - opendev.org/openstack/monasca-log-api
       - opendev.org/openstack/monasca-tempest-plugin
       - opendev.org/openstack/murano-tempest-plugin
-      - opendev.org/x/networking-ansible
       - opendev.org/openstack/networking-bgpvpn
       - opendev.org/x/networking-cisco
       - opendev.org/x/networking-fortinet
@@ -418,6 +467,7 @@
       - opendev.org/openstack/senlin-tempest-plugin
       - opendev.org/openstack/solum-tempest-plugin
       - opendev.org/x/tap-as-a-service
+      - opendev.org/x/tap-as-a-service-tempest-plugin
       - opendev.org/openstack/telemetry-tempest-plugin
       - opendev.org/openstack/tempest-horizon
       - opendev.org/x/tobiko
@@ -428,6 +478,7 @@
       - opendev.org/openstack/vitrage-tempest-plugin
       - opendev.org/x/vmware-nsx-tempest-plugin
       - opendev.org/openstack/watcher-tempest-plugin
+      - opendev.org/x/whitebox-tempest-plugin
       - opendev.org/openstack/zaqar-tempest-plugin
       - opendev.org/openstack/zun-tempest-plugin
 
@@ -482,6 +533,37 @@
         ENABLE_FILE_INJECTION: true
         DATABASE_TYPE: postgresql
 
+- project-template:
+    name: integrated-gate-networking
+    description: |
+      Run the python3 Tempest network integration tests (Nova and Neutron related)
+      in check and gate for the neutron integrated gate. This is meant to be
+      run on neutron gate only.
+    check:
+      jobs:
+        - grenade-py3
+        - tempest-integrated-networking
+    gate:
+      jobs:
+        - grenade-py3
+        - tempest-integrated-networking
+
+- project-template:
+    name: integrated-gate-storage
+    description: |
+      Run the python3 Tempest image & block storage integration tests
+      (Cinder, Glance, Swift and Nova related) in check and gate
+      for the neutron integrated gate. This is meant to be
+      run on Cinder and Glance gate only.
+    check:
+      jobs:
+        - grenade-py3
+        - tempest-integrated-storage
+    gate:
+      jobs:
+        - grenade-py3
+        - tempest-integrated-storage
+
 - project:
     templates:
       - check-requirements
diff --git a/README.rst b/README.rst
index e8206ee..841fae6 100644
--- a/README.rst
+++ b/README.rst
@@ -119,6 +119,17 @@
    will run the same set of tests as the default gate jobs. Or you can
    use `unittest`_ compatible test runners such as `testr`_, `pytest`_ etc.
 
+   Tox also contains several existing job configurations. For example::
+
+    $ tox -e full
+
+   which will run the same set of tests as the OpenStack gate. (it's exactly how
+   the gate invokes Tempest) Or::
+
+    $ tox -e smoke
+
+   to run the tests tagged as smoke.
+
 .. _unittest: https://docs.python.org/3/library/unittest.html
 .. _testr: https://testrepository.readthedocs.org/en/latest/MANUAL.html
 .. _stestr: https://stestr.readthedocs.org/en/latest/MANUAL.html
@@ -270,14 +281,3 @@
 To run one single test serially ::
 
     $ testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
-
-Tox also contains several existing job configurations. For example::
-
-    $ tox -e full
-
-which will run the same set of tests as the OpenStack gate. (it's exactly how
-the gate invokes Tempest) Or::
-
-    $ tox -e smoke
-
-to run the tests tagged as smoke.
diff --git a/doc/source/data/tempest-blacklisted-plugins-registry.header b/doc/source/data/tempest-blacklisted-plugins-registry.header
new file mode 100644
index 0000000..6b6af11
--- /dev/null
+++ b/doc/source/data/tempest-blacklisted-plugins-registry.header
@@ -0,0 +1,7 @@
+Blacklisted Plugins
+===================
+
+List of Tempest plugin projects that are stale or unmaintained for a long
+time (6 months or more). They can be moved out of blacklist state once one
+of the relevant patches gets merged:
+https://review.opendev.org/#/q/topic:tempest-sanity-gate+%28status:open%29
diff --git a/releasenotes/notes/QoS-client-for-placement-based-minimum-bw-allocation-8e5854d5754cec68.yaml b/releasenotes/notes/QoS-client-for-placement-based-minimum-bw-allocation-8e5854d5754cec68.yaml
new file mode 100644
index 0000000..b66ea3a
--- /dev/null
+++ b/releasenotes/notes/QoS-client-for-placement-based-minimum-bw-allocation-8e5854d5754cec68.yaml
@@ -0,0 +1,25 @@
+---
+features:
+  - |
+    Add ``qos-policies`` and ``qos-minimum-bandwidth-rule`` clients
+    to Tempest to make possible the testing of the placement based
+    bandwidth allocation feature.
+    The following API calls are available for tempest from now:
+
+    ``QoS policies`` client:
+
+    * GET /qos/policies
+    * POST /qos/policies
+    * GET /qos/policies/{policy_id}
+    * PUT /qos/policies/{policy_id}
+    * DELETE /qos/policies/{policy_id}
+
+
+    ``QoS minimum bandwidth rules`` client:
+
+    * GET qos/policies/{policy_id}/minimum_bandwidth_rules
+    * POST /qos/policies/{policy_id}/minimum_bandwidth_rules
+    * GET qos/policies/{policy_id}/minimum_bandwidth_rules/{rule_id}
+    * PUT qos/policies/{policy_id}/minimum_bandwidth_rules/{rule_id}
+    * DELETE /qos/policies/{policy_id}/minimum_bandwidth_rules/{rule_id}
+
diff --git a/releasenotes/notes/bug-1647999-7aeda50a8d082d4c.yaml b/releasenotes/notes/bug-1647999-7aeda50a8d082d4c.yaml
new file mode 100644
index 0000000..384f916
--- /dev/null
+++ b/releasenotes/notes/bug-1647999-7aeda50a8d082d4c.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    A new parameter, compute/compute_volume_common_az is introduced to
+    specify availability zone where tempest creates instances and volumes
+    for scenario tests, to allow us to run scenario tests in the deployment
+    which has multiple availability zones and cinder/cross_az_attach in
+    nova.conf is set to False.
diff --git a/releasenotes/notes/config_image_certificate_compute_feature-c56efb520d54aff5.yaml b/releasenotes/notes/config_image_certificate_compute_feature-c56efb520d54aff5.yaml
new file mode 100644
index 0000000..8475f50
--- /dev/null
+++ b/releasenotes/notes/config_image_certificate_compute_feature-c56efb520d54aff5.yaml
@@ -0,0 +1,8 @@
+---
+other:
+  - |
+    New configuration options ``[compute]/certified_image_ref`` and
+    ``[compute]/certified_image_trusted_certs`` have been introduced. These
+    are required in order to run the ``ServerShowV263Test`` test and allow a
+    signed image with the required img_signature_* properties set along
+    with a list of trusted certificates to be used during the test.
diff --git a/releasenotes/notes/remove-some-deprecated-auth-and-identity-options-xa1xd9b8fb948g4f.yaml b/releasenotes/notes/remove-some-deprecated-auth-and-identity-options-xa1xd9b8fb948g4f.yaml
new file mode 100644
index 0000000..fa21afd
--- /dev/null
+++ b/releasenotes/notes/remove-some-deprecated-auth-and-identity-options-xa1xd9b8fb948g4f.yaml
@@ -0,0 +1,8 @@
+upgrade:
+  - |
+    Remove deprecated config option ``endpoint_type`` from
+    ``identity`` group. Use ``v2_public_endpoint_type`` from
+    ``identity`` group instead.
+    Remove deprecated config option ``tenant_isolation_domain_name``
+    from ``auth`` group. Use ``default_credentials_domain_name`` from
+    ``auth`` group instead.
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index e71e642..aaf7a5a 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -487,6 +487,9 @@
             kwargs['display_name'] = vol_name
         if image_ref is not None:
             kwargs['imageRef'] = image_ref
+        if CONF.compute.compute_volume_common_az:
+            kwargs.setdefault('availability_zone',
+                              CONF.compute.compute_volume_common_az)
         volume = cls.volumes_client.create_volume(**kwargs)['volume']
         cls.addClassResourceCleanup(
             cls.volumes_client.wait_for_resource_deletion, volume['id'])
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index 3a474e6..235049a 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -70,9 +70,7 @@
         self.assertEqual(min_img_ram, image['min_ram'])
 
         # Try to create server with flavor of insufficient ram size
-        self.assertRaisesRegex(lib_exc.BadRequest,
-                               "Flavor's memory is too small for "
-                               "requested image",
-                               self.create_test_server,
-                               image_id=image['id'],
-                               flavor=flavor['id'])
+        self.assertRaises(lib_exc.BadRequest,
+                          self.create_test_server,
+                          image_id=image['id'],
+                          flavor=flavor['id'])
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index eeb58d6..3789aa0 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -15,6 +15,7 @@
 
 import time
 
+from oslo_log import log
 import six
 
 from tempest.api.compute import base
@@ -30,6 +31,8 @@
 
 CONF = config.CONF
 
+LOG = log.getLogger(__name__)
+
 
 class AttachInterfacesTestBase(base.BaseV2ComputeTest):
 
@@ -364,10 +367,34 @@
         self.servers_client.add_fixed_ip(server['id'], networkId=network_id)
         # Wait for the ips count to increase by one.
 
+        def _get_server_floating_ips():
+            _floating_ips = []
+            _server = self.os_primary.servers_client.show_server(
+                server['id'])['server']
+            for _ip_set in _server['addresses']:
+                for _ip in _server['addresses'][_ip_set]:
+                    if _ip['OS-EXT-IPS:type'] == 'floating':
+                        _floating_ips.append(_ip['addr'])
+            return _floating_ips
+
         def _wait_for_ip_increase():
             _addresses = self.os_primary.servers_client.list_addresses(
                 server['id'])['addresses']
-            return len(list(_addresses.values())[0]) == original_ip_count + 1
+            _ips = [addr['addr'] for addr in list(_addresses.values())[0]]
+            LOG.debug("Wait for IP increase. All IPs still associated to "
+                      "the server %(id)s: %(ips)s",
+                      {'id': server['id'], 'ips': _ips})
+            if len(_ips) == original_ip_count + 1:
+                return True
+            elif len(_ips) == original_ip_count:
+                return False
+            # If not, lets remove any floating IP from the list and check again
+            _fips = _get_server_floating_ips()
+            _ips = [_ip for _ip in _ips if _ip not in _fips]
+            LOG.debug("Wait for IP increase. Fixed IPs still associated to "
+                      "the server %(id)s: %(ips)s",
+                      {'id': server['id'], 'ips': _ips})
+            return len(_ips) == original_ip_count + 1
 
         if not test_utils.call_until_true(
                 _wait_for_ip_increase, CONF.compute.build_timeout,
@@ -394,7 +421,19 @@
         def _wait_for_ip_decrease():
             _addresses = self.os_primary.servers_client.list_addresses(
                 server['id'])['addresses']
-            return len(list(_addresses.values())[0]) == original_ip_count
+            _ips = [addr['addr'] for addr in list(_addresses.values())[0]]
+            LOG.debug("Wait for IP decrease. All IPs still associated to "
+                      "the server %(id)s: %(ips)s",
+                      {'id': server['id'], 'ips': _ips})
+            if len(_ips) == original_ip_count:
+                return True
+            # If not, lets remove any floating IP from the list and check again
+            _fips = _get_server_floating_ips()
+            _ips = [_ip for _ip in _ips if _ip not in _fips]
+            LOG.debug("Wait for IP decrease. Fixed IPs still associated to "
+                      "the server %(id)s: %(ips)s",
+                      {'id': server['id'], 'ips': _ips})
+            return len(_ips) == original_ip_count
 
         if not test_utils.call_until_true(
                 _wait_for_ip_decrease, CONF.compute.build_timeout,
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index daf6a06..50ffb21 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -16,6 +16,7 @@
 import struct
 
 import six
+import six.moves.urllib.parse as urlparse
 import urllib3
 
 from tempest.api.compute import base
@@ -73,8 +74,9 @@
                          'initial call: ' + six.text_type(resp.status))
         # Do some basic validation to make sure it is an expected HTML document
         resp_data = resp.data.decode()
-        self.assertIn('<html>', resp_data,
-                      'Not a valid html document in the response.')
+        # This is needed in the case of example: <html lang="en">
+        self.assertRegex(resp_data, '<html.*>',
+                         'Not a valid html document in the response.')
         self.assertIn('</html>', resp_data,
                       'Not a valid html document in the response.')
         # Just try to make sure we got JavaScript back for noVNC, since we
@@ -204,7 +206,18 @@
                                                type='novnc')['console']
         self.assertEqual('novnc', body['type'])
         # Do the WebSockify HTTP Request to novncproxy with a bad token
-        url = body['url'].replace('token=', 'token=bad')
+        parts = urlparse.urlparse(body['url'])
+        qparams = urlparse.parse_qs(parts.query)
+        if 'path' in qparams:
+            qparams['path'] = urlparse.unquote(qparams['path'][0]).replace(
+                'token=', 'token=bad')
+        elif 'token' in qparams:
+            qparams['token'] = 'bad' + qparams['token'][0]
+        new_query = urlparse.urlencode(qparams)
+        new_parts = urlparse.ParseResult(parts.scheme, parts.netloc,
+                                         parts.path, parts.params, new_query,
+                                         parts.fragment)
+        url = urlparse.urlunparse(new_parts)
         self._websocket = compute.create_websocket(url)
         # Make sure the novncproxy rejected the connection and closed it
         data = self._websocket.receive_frame()
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index f6c3e73..d47ff51 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -417,10 +417,7 @@
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
         # Make sure everything still looks OK.
         server = self.client.show_server(self.server_id)['server']
-        # The flavor id is not returned in the server response after
-        # microversion 2.46 so handle that gracefully.
-        if server['flavor'].get('id'):
-            self.assertEqual(self.flavor_ref, server['flavor']['id'])
+        self.assert_flavor_equal(self.flavor_ref, server['flavor'])
         attached_volumes = server['os-extended-volumes:volumes_attached']
         self.assertEqual(1, len(attached_volumes))
         self.assertEqual(volume['id'], attached_volumes[0]['id'])
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index e8b1161..76d65dd 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -186,10 +186,17 @@
     min_microversion = '2.63'
     max_microversion = 'latest'
 
+    @testtools.skipUnless(CONF.compute.certified_image_ref,
+                          '``[compute]/certified_image_ref`` required to test '
+                          'image certificate validation.')
+    @testtools.skipUnless(CONF.compute.certified_image_trusted_certs,
+                          '``[compute]/certified_image_trusted_certs`` '
+                          'required to test image certificate validation.')
     @decorators.idempotent_id('71b8e3d5-11d2-494f-b917-b094a4afed3c')
     def test_show_update_rebuild_list_server(self):
-        trusted_certs = ['test-cert-1', 'test-cert-2']
+        trusted_certs = CONF.compute.certified_image_trusted_certs
         server = self.create_test_server(
+            image_id=CONF.compute.certified_image_ref,
             trusted_image_certificates=trusted_certs,
             wait_until='ACTIVE')
 
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 2cd8906..366d6a0 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -44,11 +44,14 @@
             cls.addClassResourceCleanup(
                 cls.services_client.delete_service, service['id'])
 
-            region = data_utils.rand_name('region')
+            region_name = data_utils.rand_name('region')
             url = data_utils.rand_url()
             endpoint = cls.client.create_endpoint(
                 service_id=cls.service_ids[i], interface=interfaces[i],
-                url=url, region=region, enabled=True)['endpoint']
+                url=url, region=region_name, enabled=True)['endpoint']
+            region = cls.regions_client.show_region(region_name)['region']
+            cls.addClassResourceCleanup(
+                cls.regions_client.delete_region, region['id'])
             cls.addClassResourceCleanup(
                 cls.client.delete_endpoint, endpoint['id'])
             cls.setup_endpoint_ids.append(endpoint['id'])
@@ -108,17 +111,19 @@
 
     @decorators.idempotent_id('0e2446d2-c1fd-461b-a729-b9e73e3e3b37')
     def test_create_list_show_delete_endpoint(self):
-        region = data_utils.rand_name('region')
+        region_name = data_utils.rand_name('region')
         url = data_utils.rand_url()
         interface = 'public'
         endpoint = self.client.create_endpoint(service_id=self.service_ids[0],
                                                interface=interface,
-                                               url=url, region=region,
+                                               url=url, region=region_name,
                                                enabled=True)['endpoint']
+        region = self.regions_client.show_region(region_name)['region']
+        self.addCleanup(self.regions_client.delete_region, region['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.client.delete_endpoint, endpoint['id'])
         # Asserting Create Endpoint response body
-        self.assertEqual(region, endpoint['region'])
+        self.assertEqual(region_name, endpoint['region'])
         self.assertEqual(url, endpoint['url'])
 
         # Checking if created endpoint is present in the list of endpoints
@@ -133,7 +138,7 @@
         self.assertEqual(self.service_ids[0], fetched_endpoint['service_id'])
         self.assertEqual(interface, fetched_endpoint['interface'])
         self.assertEqual(url, fetched_endpoint['url'])
-        self.assertEqual(region, fetched_endpoint['region'])
+        self.assertEqual(region_name, fetched_endpoint['region'])
         self.assertEqual(True, fetched_endpoint['enabled'])
 
         # Deleting the endpoint created in this method
@@ -161,28 +166,33 @@
         self.addCleanup(self.services_client.delete_service, service2['id'])
 
         # Creating an endpoint so as to check update endpoint with new values
-        region1 = data_utils.rand_name('region')
+        region1_name = data_utils.rand_name('region')
         url1 = data_utils.rand_url()
         interface1 = 'public'
         endpoint_for_update = (
             self.client.create_endpoint(service_id=self.service_ids[0],
                                         interface=interface1,
-                                        url=url1, region=region1,
+                                        url=url1, region=region1_name,
                                         enabled=True)['endpoint'])
-        self.addCleanup(self.client.delete_endpoint, endpoint_for_update['id'])
+        region1 = self.regions_client.show_region(region1_name)['region']
+        self.addCleanup(self.regions_client.delete_region, region1['id'])
 
         # Updating endpoint with new values
-        region2 = data_utils.rand_name('region')
+        region2_name = data_utils.rand_name('region')
         url2 = data_utils.rand_url()
         interface2 = 'internal'
         endpoint = self.client.update_endpoint(endpoint_for_update['id'],
                                                service_id=service2['id'],
                                                interface=interface2,
-                                               url=url2, region=region2,
+                                               url=url2, region=region2_name,
                                                enabled=False)['endpoint']
+        region2 = self.regions_client.show_region(region2_name)['region']
+        self.addCleanup(self.regions_client.delete_region, region2['id'])
+        self.addCleanup(self.client.delete_endpoint, endpoint_for_update['id'])
+
         # Asserting if the attributes of endpoint are updated
         self.assertEqual(service2['id'], endpoint['service_id'])
         self.assertEqual(interface2, endpoint['interface'])
         self.assertEqual(url2, endpoint['url'])
-        self.assertEqual(region2, endpoint['region'])
+        self.assertEqual(region2_name, endpoint['region'])
         self.assertEqual(False, endpoint['enabled'])
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index 4c3eb1c..164b577 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -70,14 +70,16 @@
     def _assert_update_raises_bad_request(self, enabled):
 
         # Create an endpoint
-        region1 = data_utils.rand_name('region')
+        region1_name = data_utils.rand_name('region')
         url1 = data_utils.rand_url()
         interface1 = 'public'
         endpoint_for_update = (
             self.client.create_endpoint(service_id=self.service_id,
                                         interface=interface1,
-                                        url=url1, region=region1,
+                                        url=url1, region=region1_name,
                                         enabled=True)['endpoint'])
+        region1 = self.regions_client.show_region(region1_name)['region']
+        self.addCleanup(self.regions_client.delete_region, region1['id'])
         self.addCleanup(self.client.delete_endpoint, endpoint_for_update['id'])
 
         self.assertRaises(lib_exc.BadRequest, self.client.update_endpoint,
diff --git a/tempest/api/identity/v2/test_users.py b/tempest/api/identity/v2/test_users.py
index 158dfb3..2eea860 100644
--- a/tempest/api/identity/v2/test_users.py
+++ b/tempest/api/identity/v2/test_users.py
@@ -15,6 +15,8 @@
 
 import time
 
+import testtools
+
 from tempest.api.identity import base
 from tempest import config
 from tempest.lib.common.utils import data_utils
@@ -78,6 +80,10 @@
         self.non_admin_users_client.auth_provider.set_auth()
 
     @decorators.idempotent_id('165859c9-277f-4124-9479-a7d1627b0ca7')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an '
+                      'immutable user source and solely '
+                      'provides read-only access to users.')
     def test_user_update_own_password(self):
         old_pass = self.creds.password
         old_token = self.non_admin_users_client.token
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index 13b5161..d4e7612 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -77,6 +77,10 @@
         self.non_admin_users_client.auth_provider.set_auth()
 
     @decorators.idempotent_id('ad71bd23-12ad-426b-bb8b-195d2b635f27')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an '
+                      'immutable user source and solely '
+                      'provides read-only access to users.')
     def test_user_update_own_password(self):
         old_pass = self.creds.password
         old_token = self.non_admin_client.token
@@ -102,6 +106,10 @@
     @testtools.skipUnless(CONF.identity_feature_enabled.security_compliance,
                           'Security compliance not available.')
     @decorators.idempotent_id('941784ee-5342-4571-959b-b80dd2cea516')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an '
+                      'immutable user source and solely '
+                      'provides read-only access to users.')
     def test_password_history_check_self_service_api(self):
         old_pass = self.creds.password
         new_pass1 = data_utils.rand_password()
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 64fe29a..1bfd075 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -124,6 +124,10 @@
             name = data_utils.rand_name(cls.__name__ + '-Volume')
             kwargs['name'] = name
 
+        if CONF.compute.compute_volume_common_az:
+            kwargs.setdefault('availability_zone',
+                              CONF.compute.compute_volume_common_az)
+
         volume = cls.volumes_client.create_volume(**kwargs)['volume']
         cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
                                     cls.delete_volume, cls.volumes_client,
diff --git a/tempest/clients.py b/tempest/clients.py
index 0506646..f7a83be 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -69,6 +69,8 @@
         self.network_versions_client = self.network.NetworkVersionsClient()
         self.service_providers_client = self.network.ServiceProvidersClient()
         self.tags_client = self.network.TagsClient()
+        self.qos_client = self.network.QosClient()
+        self.qos_min_bw_client = self.network.QosMinimumBandwidthRulesClient()
 
     def _set_image_clients(self):
         if CONF.service_available.glance:
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index e6db2e9..f0d7264 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -94,6 +94,8 @@
 
 class TempestCleanup(command.Command):
 
+    GOT_EXCEPTIONS = []
+
     def take_action(self, parsed_args):
         try:
             self.init(parsed_args)
@@ -103,6 +105,8 @@
             LOG.exception("Failure during cleanup")
             traceback.print_exc()
             raise
+        if self.GOT_EXCEPTIONS:
+            raise Exception(self.GOT_EXCEPTIONS)
 
     def init(self, parsed_args):
         cleanup_service.init_conf()
@@ -159,7 +163,8 @@
                   'is_dry_run': is_dry_run,
                   'saved_state_json': self.json_data,
                   'is_preserve': is_preserve,
-                  'is_save_state': is_save_state}
+                  'is_save_state': is_save_state,
+                  'got_exceptions': self.GOT_EXCEPTIONS}
         for service in self.global_services:
             svc = service(admin_mgr, **kwargs)
             svc.run()
@@ -200,7 +205,8 @@
                   'saved_state_json': self.json_data,
                   'is_preserve': is_preserve,
                   'is_save_state': False,
-                  'project_id': project_id}
+                  'project_id': project_id,
+                  'got_exceptions': self.GOT_EXCEPTIONS}
         for service in self.project_services:
             svc = service(mgr, **kwargs)
             svc.run()
@@ -300,7 +306,8 @@
                   'is_dry_run': False,
                   'saved_state_json': data,
                   'is_preserve': False,
-                  'is_save_state': True}
+                  'is_save_state': True,
+                  'got_exceptions': self.GOT_EXCEPTIONS}
         for service in self.global_services:
             svc = service(admin_mgr, **kwargs)
             svc.run()
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 104958a..ccceb34 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -22,6 +22,7 @@
 from tempest.common import utils
 from tempest.common.utils import net_info
 from tempest import config
+from tempest.lib import exceptions
 
 LOG = logging.getLogger(__name__)
 CONF = config.CONF
@@ -127,12 +128,23 @@
         pass
 
     def run(self):
-        if self.is_dry_run:
-            self.dry_run()
-        elif self.is_save_state:
-            self.save_state()
-        else:
-            self.delete()
+        try:
+            if self.is_dry_run:
+                self.dry_run()
+            elif self.is_save_state:
+                self.save_state()
+            else:
+                self.delete()
+        except exceptions.NotImplemented as exc:
+            # Many OpenStack services use extensions logic to implement the
+            # features or resources. Tempest cleanup tries to clean up the test
+            # resources without having much logic of extensions checks etc.
+            # If any of the extension is missing then, service will return
+            # NotImplemented error.
+            msg = ("Got NotImplemented error in %s, full exception: %s" %
+                   (str(self.__class__), str(exc)))
+            LOG.exception(msg)
+            self.got_exceptions.append(msg)
 
 
 class SnapshotService(BaseService):
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index 77d4496..f9ca2c7 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -19,11 +19,11 @@
 ==============
 Tempest run has several options:
 
- * **--regex/-r**: This is a selection regex like what stestr uses. It will run
-                   any tests that match on re.match() with the regex
- * **--smoke/-s**: Run all the tests tagged as smoke
- * **--black-regex**: It allows to do simple test exclusion via passing a
-                      rejection/black regexp
+* ``--regex/-r``: This is a selection regex like what stestr uses. It will run
+  any tests that match on re.match() with the regex
+* ``--smoke/-s``: Run all the tests tagged as smoke
+* ``--black-regex``: It allows to do simple test exclusion via passing a
+  rejection/black regexp
 
 There are also the ``--blacklist-file`` and ``--whitelist-file`` options that
 let you pass a filepath to tempest run with the file format being a line
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 1489e60..cd85ede 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -167,6 +167,9 @@
         params = {'name': volume_name,
                   'imageRef': image_id,
                   'size': CONF.volume.volume_size}
+        if CONF.compute.compute_volume_common_az:
+            params.setdefault('availability_zone',
+                              CONF.compute.compute_volume_common_az)
         volume = volumes_client.create_volume(**params)
         try:
             waiters.wait_for_volume_resource_status(volumes_client,
@@ -193,6 +196,9 @@
         # to be specified.
         image_id = ''
 
+    if CONF.compute.compute_volume_common_az:
+        kwargs.setdefault('availability_zone',
+                          CONF.compute.compute_volume_common_az)
     body = clients.servers_client.create_server(name=name, imageRef=image_id,
                                                 flavorRef=flavor,
                                                 **kwargs)
@@ -392,8 +398,11 @@
 
     def _upgrade(self, url):
         """Upgrade the HTTP connection to a WebSocket and verify."""
-        # The real request goes to the /websockify URI always
-        reqdata = 'GET /websockify HTTP/1.1\r\n'
+        # It is possible to pass the path as a query parameter in the request,
+        # so use it if present
+        qparams = urlparse.parse_qs(url.query)
+        path = qparams['path'][0] if 'path' in qparams else '/websockify'
+        reqdata = 'GET %s HTTP/1.1\r\n' % path
         reqdata += 'Host: %s' % url.hostname
         # Add port only if we have one specified
         if url.port:
@@ -402,7 +411,7 @@
         reqdata += '\r\n'
         # Tell the HTTP Server to Upgrade the connection to a WebSocket
         reqdata += 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
-        # The token=xxx is sent as a Cookie not in the URI
+        # The token=xxx is sent as a Cookie not in the URI for noVNC < v1.1.0
         reqdata += 'Cookie: %s\r\n' % url.query
         # Use a hard-coded WebSocket key since a test program
         reqdata += 'Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n'
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 77ec0f8..11f3bf9 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -121,7 +121,9 @@
                      '/'.join((server_status, str(task_state))),
                      time.time() - start_time)
         if server_status == 'ERROR' and not ignore_error:
-            raise lib_exc.DeleteErrorException(resource_id=server_id)
+            raise lib_exc.DeleteErrorException(
+                "Server %s failed to delete and is in ERROR status" %
+                server_id)
 
         if int(time.time()) - start_time >= client.build_timeout:
             raise lib_exc.TimeoutException
@@ -202,6 +204,8 @@
                 resource_name=resource_name, resource_id=resource_id)
         if resource_name == 'volume' and resource_status == 'error_restoring':
             raise exceptions.VolumeRestoreErrorException(volume_id=resource_id)
+        if resource_status == 'error_extending' and resource_status != status:
+            raise exceptions.VolumeExtendErrorException(volume_id=resource_id)
 
         if int(time.time()) - start >= client.build_timeout:
             message = ('%s %s failed to reach %s status (current %s) '
diff --git a/tempest/config.py b/tempest/config.py
index 6830148..c50ebbe 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -69,10 +69,7 @@
     cfg.StrOpt('default_credentials_domain_name',
                default='Default',
                help="Default domain used when getting v3 credentials. "
-                    "This is the name keystone uses for v2 compatibility.",
-               deprecated_opts=[cfg.DeprecatedOpt(
-                                'tenant_isolation_domain_name',
-                                group='auth')]),
+                    "This is the name keystone uses for v2 compatibility."),
     cfg.BoolOpt('create_isolated_networks',
                 default=True,
                 help="If use_dynamic_credentials is set to True and Neutron "
@@ -139,9 +136,7 @@
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
                help="The public endpoint type to use for OpenStack Identity "
-                    "(Keystone) API v2",
-               deprecated_opts=[cfg.DeprecatedOpt('endpoint_type',
-                                                  group='identity')]),
+                    "(Keystone) API v2"),
     cfg.StrOpt('v3_endpoint_type',
                default='adminURL',
                choices=['public', 'admin', 'internal',
@@ -276,6 +271,17 @@
                help="Valid secondary image reference to be used in tests. "
                     "This is a required option, but if only one image is "
                     "available duplicate the value of image_ref above"),
+    cfg.StrOpt('certified_image_ref',
+               help="Valid image reference to be used in image certificate "
+                    "validation tests when enabled. This image must also "
+                    "have the required img_signature_* properties set. "
+                    "Additional details available within the following Nova "
+                    "documentation: https://docs.openstack.org/nova/latest/"
+                    "user/certificate-validation.html"),
+    cfg.ListOpt('certified_image_trusted_certs',
+                help="A list of trusted certificates to be used when the "
+                     "image certificate validation compute feature is "
+                     "enabled."),
     cfg.StrOpt('flavor_ref',
                default="1",
                help="Valid primary flavor to use in tests."),
@@ -357,6 +363,19 @@
                     "If both values are not specified, Tempest avoids tests "
                     "which require a microversion. Valid values are string "
                     "with format 'X.Y' or string 'latest'"),
+    cfg.StrOpt('compute_volume_common_az',
+               default=None,
+               help='AZ to be used for Cinder and Nova. Set this parameter '
+                    'when the cloud has nova.conf: cinder.cross_az_attach '
+                    'set to false. Which means volumes attached to an '
+                    'instance must be in the same availability zone in Cinder '
+                    'as the instance availability zone in Nova. Set the '
+                    'common availability zone in this config which will be '
+                    'used to boot an instance as well as creating a volume. '
+                    'NOTE: If that AZ is not in Cinder (or '
+                    'allow_availability_zone_fallback=False in cinder.conf), '
+                    'the volume create request will fail and the instance '
+                    'will fail the build request.'),
 ]
 
 placement_group = cfg.OptGroup(name='placement',
@@ -516,9 +535,8 @@
                 default=True,
                 help='Enable special configuration drive with metadata.'),
     cfg.ListOpt('scheduler_enabled_filters',
-                default=["RetryFilter", "AvailabilityZoneFilter",
-                         "ComputeFilter", "ComputeCapabilitiesFilter",
-                         "ImagePropertiesFilter",
+                default=["AvailabilityZoneFilter", "ComputeFilter",
+                         "ComputeCapabilitiesFilter", "ImagePropertiesFilter",
                          "ServerGroupAntiAffinityFilter",
                          "ServerGroupAffinityFilter"],
                 help="A list of enabled filters that Nova will accept as "
@@ -731,7 +749,13 @@
                 help="Does the test environment support port security?"),
     cfg.BoolOpt('floating_ips',
                 default=True,
-                help='Does the test environment support floating_ips')
+                help='Does the test environment support floating_ips'),
+    cfg.StrOpt('qos_placement_physnet', default=None,
+               help='Name of the physnet for placement based minimum '
+                    'bandwidth allocation.'),
+    cfg.StrOpt('provider_net_base_segmentation_id', default=3000,
+               help='Base segmentation ID to create provider networks. '
+                    'This value will be increased in case of conflict.')
 ]
 
 validation_group = cfg.OptGroup(name='validation',
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
old mode 100644
new mode 100755
index a430d5d..c05e7a6
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -42,6 +42,11 @@
     message = "Volume %(volume_id)s failed to restore and is in ERROR status"
 
 
+class VolumeExtendErrorException(exceptions.TempestException):
+    message = ("Volume %(volume_id)s failed to extend and "
+               "is in error_extending status")
+
+
 class StackBuildErrorException(exceptions.TempestException):
     message = ("Stack %(stack_identifier)s is in %(stack_status)s status "
                "due to '%(stack_status_reason)s'")
diff --git a/tempest/lib/services/network/__init__.py b/tempest/lib/services/network/__init__.py
index 419e593..69f178e 100644
--- a/tempest/lib/services/network/__init__.py
+++ b/tempest/lib/services/network/__init__.py
@@ -21,6 +21,9 @@
     MeteringLabelsClient
 from tempest.lib.services.network.networks_client import NetworksClient
 from tempest.lib.services.network.ports_client import PortsClient
+from tempest.lib.services.network.qos_client import QosClient
+from tempest.lib.services.network.qos_minimum_bandwidth_rules_client import \
+    QosMinimumBandwidthRulesClient
 from tempest.lib.services.network.quotas_client import QuotasClient
 from tempest.lib.services.network.routers_client import RoutersClient
 from tempest.lib.services.network.security_group_rules_client import \
@@ -37,6 +40,7 @@
 __all__ = ['AgentsClient', 'ExtensionsClient', 'FloatingIPsClient',
            'MeteringLabelRulesClient', 'MeteringLabelsClient',
            'NetworksClient', 'NetworkVersionsClient', 'PortsClient',
-           'QuotasClient', 'RoutersClient', 'SecurityGroupRulesClient',
-           'SecurityGroupsClient', 'ServiceProvidersClient',
-           'SubnetpoolsClient', 'SubnetsClient', 'TagsClient']
+           'QosClient', 'QosMinimumBandwidthRulesClient', 'QuotasClient',
+           'RoutersClient', 'SecurityGroupRulesClient', 'SecurityGroupsClient',
+           'ServiceProvidersClient', 'SubnetpoolsClient', 'SubnetsClient',
+           'TagsClient']
diff --git a/tempest/lib/services/network/qos_client.py b/tempest/lib/services/network/qos_client.py
new file mode 100644
index 0000000..bcd1066
--- /dev/null
+++ b/tempest/lib/services/network/qos_client.py
@@ -0,0 +1,70 @@
+# Copyright (c) 2019 Ericsson
+#
+#    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.services.network import base
+
+
+class QosClient(base.BaseNetworkClient):
+
+    def create_qos_policy(self, **kwargs):
+        """Creates a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#create-qos-policy
+        """
+        uri = '/qos/policies'
+        post_data = {'policy': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_qos_policy(self, qos_policy_id, **kwargs):
+        """Updates a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#update-qos-policy
+        """
+        uri = '/qos/policies/%s' % qos_policy_id
+        post_data = {'policy': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_qos_policy(self, qos_policy_id, **fields):
+        """Show details of a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#show-qos-policy-details
+        """
+        uri = '/qos/policies/%s' % qos_policy_id
+        return self.show_resource(uri, **fields)
+
+    def delete_qos_policy(self, qos_policy_id):
+        """Deletes a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#delete-qos-policy
+        """
+        uri = '/qos/policies/%s' % qos_policy_id
+        return self.delete_resource(uri)
+
+    def list_qos_policies(self, **filters):
+        """Lists QoS policies.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#list-qos-policies
+        """
+        uri = '/qos/policies'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/qos_minimum_bandwidth_rules_client.py b/tempest/lib/services/network/qos_minimum_bandwidth_rules_client.py
new file mode 100644
index 0000000..4f4ee3f
--- /dev/null
+++ b/tempest/lib/services/network/qos_minimum_bandwidth_rules_client.py
@@ -0,0 +1,73 @@
+# Copyright (c) 2019 Ericsson
+#
+#    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.services.network import base
+
+
+class QosMinimumBandwidthRulesClient(base.BaseNetworkClient):
+
+    def create_minimum_bandwidth_rule(self, qos_policy_id, **kwargs):
+        """Creates a minimum bandwidth rule for a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#create-minimum-bandwidth-rule
+        """
+        uri = '/qos/policies/%s/minimum_bandwidth_rules' % qos_policy_id
+        post_data = {'minimum_bandwidth_rule': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_minimum_bandwidth_rule(self, qos_policy_id, rule_id, **kwargs):
+        """Updates a minimum bandwidth rule.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#update-minimum-bandwidth-rule
+        """
+        uri = '/qos/policies/%s/minimum_bandwidth_rules/%s' % (
+            qos_policy_id, rule_id)
+        post_data = {'minimum_bandwidth_rule': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_minimum_bandwidth_rule(self, qos_policy_id, rule_id, **fields):
+        """Show details of a minimum bandwidth rule.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#show-minimum-bandwidth-rule-details
+        """
+        uri = '/qos/policies/%s/minimum_bandwidth_rules/%s' % (
+            qos_policy_id, rule_id)
+        return self.show_resource(uri, **fields)
+
+    def delete_minimum_bandwidth_rule(self, qos_policy_id, rule_id):
+        """Deletes a minimum bandwidth rule for a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#delete-minimum-bandwidth-rule
+        """
+        uri = '/qos/policies/%s/minimum_bandwidth_rules/%s' % (
+            qos_policy_id, rule_id)
+        return self.delete_resource(uri)
+
+    def list_minimum_bandwidth_rules(self, qos_policy_id, **filters):
+        """Lists all minimum bandwidth rules for a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#list-minimum-bandwidth-rules-for-qos-policy
+        """
+        uri = '/qos/policies/%s/minimum_bandwidth_rules' % qos_policy_id
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/volume/v3/volumes_client.py b/tempest/lib/services/volume/v3/volumes_client.py
index 2dbdd11..a93c76e 100644
--- a/tempest/lib/services/volume/v3/volumes_client.py
+++ b/tempest/lib/services/volume/v3/volumes_client.py
@@ -212,7 +212,9 @@
         except lib_exc.NotFound:
             return True
         if volume["volume"]["status"] == "error_deleting":
-            raise lib_exc.DeleteErrorException(resource_id=id)
+            raise lib_exc.DeleteErrorException(
+                "Volume %s failed to delete and is in error_deleting status" %
+                volume['id'])
         return False
 
     @property
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 87d7e76..1252f09 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -274,6 +274,10 @@
 
         tenant_network = self.get_tenant_network()
 
+        if CONF.compute.compute_volume_common_az:
+            kwargs.setdefault('availability_zone',
+                              CONF.compute.compute_volume_common_az)
+
         body, _ = compute.create_test_server(
             clients,
             tenant_network=tenant_network,
@@ -307,6 +311,11 @@
                   'imageRef': imageRef,
                   'volume_type': volume_type,
                   'size': size}
+
+        if CONF.compute.compute_volume_common_az:
+            kwargs.setdefault('availability_zone',
+                              CONF.compute.compute_volume_common_az)
+
         volume = self.volumes_client.create_volume(**kwargs)['volume']
 
         self.addCleanup(self.volumes_client.wait_for_resource_deletion,
@@ -826,13 +835,15 @@
     def _create_network(self, networks_client=None,
                         tenant_id=None,
                         namestart='network-smoke-',
-                        port_security_enabled=True):
+                        port_security_enabled=True, **net_dict):
         if not networks_client:
             networks_client = self.networks_client
         if not tenant_id:
             tenant_id = networks_client.tenant_id
         name = data_utils.rand_name(namestart)
         network_kwargs = dict(name=name, tenant_id=tenant_id)
+        if net_dict:
+            network_kwargs.update(net_dict)
         # Neutron disables port security by default so we have to check the
         # config before trying to create the network with port_security_enabled
         if CONF.network_feature_enabled.port_security:
@@ -1257,7 +1268,7 @@
     def create_networks(self, networks_client=None,
                         routers_client=None, subnets_client=None,
                         tenant_id=None, dns_nameservers=None,
-                        port_security_enabled=True):
+                        port_security_enabled=True, **net_dict):
         """Create a network with a subnet connected to a router.
 
         The baremetal driver is a special case since all nodes are
@@ -1265,6 +1276,11 @@
 
         :param tenant_id: id of tenant to create resources in.
         :param dns_nameservers: list of dns servers to send to subnet.
+        :param port_security_enabled: whether or not port_security is enabled
+        :param net_dict: a dict containing experimental network information in
+                a form like this: {'provider:network_type': 'vlan',
+                                   'provider:physical_network': 'foo',
+                                   'provider:segmentation_id': '42'}
         :returns: network, subnet, router
         """
         if CONF.network.shared_physical_network:
@@ -1284,7 +1300,8 @@
             network = self._create_network(
                 networks_client=networks_client,
                 tenant_id=tenant_id,
-                port_security_enabled=port_security_enabled)
+                port_security_enabled=port_security_enabled,
+                **net_dict)
             router = self._get_router(client=routers_client,
                                       tenant_id=tenant_id)
             subnet_kwargs = dict(network=network,
diff --git a/tempest/scenario/test_minbw_allocation_placement.py b/tempest/scenario/test_minbw_allocation_placement.py
new file mode 100644
index 0000000..e7085f6
--- /dev/null
+++ b/tempest/scenario/test_minbw_allocation_placement.py
@@ -0,0 +1,195 @@
+# Copyright (c) 2019 Ericsson
+#
+#    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 oslo_log import log as logging
+
+from tempest.common import utils
+from tempest.common import waiters
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+from tempest.scenario import manager
+
+
+LOG = logging.getLogger(__name__)
+CONF = config.CONF
+
+
+class MinBwAllocationPlacementTest(manager.NetworkScenarioTest):
+    credentials = ['primary', 'admin']
+    required_extensions = ['port-resource-request',
+                           'qos',
+                           'qos-bw-minimum-ingress']
+    # The feature QoS minimum bandwidth allocation in Placement API depends on
+    # Granular resource requests to GET /allocation_candidates and Support
+    # allocation candidates with nested resource providers features in
+    # Placement (see: https://specs.openstack.org/openstack/nova-specs/specs/
+    # stein/approved/bandwidth-resource-provider.html#rest-api-impact) and this
+    # means that the minimum placement microversion is 1.29
+    placement_min_microversion = '1.29'
+    placement_max_microversion = 'latest'
+
+    # Nova rejects to boot VM with port which has resource_request field, below
+    # microversion 2.72
+    compute_min_microversion = '2.72'
+    compute_max_microversion = 'latest'
+
+    INGRESS_RESOURCE_CLASS = "NET_BW_IGR_KILOBIT_PER_SEC"
+    INGRESS_DIRECTION = 'ingress'
+
+    SMALLEST_POSSIBLE_BW = 1
+    # For any realistic inventory value (that is inventory != MAX_INT) an
+    # allocation candidate request of MAX_INT is expected to be rejected, see:
+    # https://github.com/openstack/placement/blob/master/placement/
+    # db/constants.py#L16
+    PLACEMENT_MAX_INT = 0x7FFFFFFF
+
+    @classmethod
+    def setup_clients(cls):
+        super(MinBwAllocationPlacementTest, cls).setup_clients()
+        cls.placement_client = cls.os_admin.placement_client
+        cls.networks_client = cls.os_admin.networks_client
+        cls.subnets_client = cls.os_admin.subnets_client
+        cls.routers_client = cls.os_adm.routers_client
+        cls.qos_client = cls.os_admin.qos_client
+        cls.qos_min_bw_client = cls.os_admin.qos_min_bw_client
+
+    @classmethod
+    def skip_checks(cls):
+        super(MinBwAllocationPlacementTest, cls).skip_checks()
+        if not CONF.network_feature_enabled.qos_placement_physnet:
+            msg = "Skipped as no physnet is available in config for " \
+                  "placement based QoS allocation."
+            raise cls.skipException(msg)
+
+    def _create_policy_and_min_bw_rule(self, name_prefix, min_kbps):
+        policy = self.qos_client.create_qos_policy(
+            name=data_utils.rand_name(name_prefix),
+            shared=True)['policy']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.qos_client.delete_qos_policy, policy['id'])
+        rule = self.qos_min_bw_client.create_minimum_bandwidth_rule(
+            policy['id'],
+            **{
+                'min_kbps': min_kbps,
+                'direction': self.INGRESS_DIRECTION
+            })['minimum_bandwidth_rule']
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.qos_min_bw_client.delete_minimum_bandwidth_rule, policy['id'],
+            rule['id'])
+
+        return policy
+
+    def _create_qos_policies(self):
+        self.qos_policy_valid = self._create_policy_and_min_bw_rule(
+            name_prefix='test_policy_valid',
+            min_kbps=self.SMALLEST_POSSIBLE_BW)
+        self.qos_policy_not_valid = self._create_policy_and_min_bw_rule(
+            name_prefix='test_policy_not_valid',
+            min_kbps=self.PLACEMENT_MAX_INT)
+
+    def _create_network_and_qos_policies(self):
+        physnet_name = CONF.network_feature_enabled.qos_placement_physnet
+        base_segm = \
+            CONF.network_feature_enabled.provider_net_base_segmentation_id
+
+        self.prov_network, _, _ = self.create_networks(
+            networks_client=self.networks_client,
+            routers_client=self.routers_client,
+            subnets_client=self.subnets_client,
+            **{
+                'shared': True,
+                'provider:network_type': 'vlan',
+                'provider:physical_network': physnet_name,
+                'provider:segmentation_id': base_segm
+            })
+
+        self._create_qos_policies()
+
+    def _check_if_allocation_is_possible(self):
+        alloc_candidates = self.placement_client.list_allocation_candidates(
+            resources1='%s:%s' % (self.INGRESS_RESOURCE_CLASS,
+                                  self.SMALLEST_POSSIBLE_BW))
+        if len(alloc_candidates['provider_summaries']) == 0:
+            self.fail('No allocation candidates are available for %s:%s' %
+                      (self.INGRESS_RESOURCE_CLASS, self.SMALLEST_POSSIBLE_BW))
+
+        # Just to be sure check with impossible high (placement max_int),
+        # allocation
+        alloc_candidates = self.placement_client.list_allocation_candidates(
+            resources1='%s:%s' % (self.INGRESS_RESOURCE_CLASS,
+                                  self.PLACEMENT_MAX_INT))
+        if len(alloc_candidates['provider_summaries']) != 0:
+            self.fail('For %s:%s there should be no available candidate!' %
+                      (self.INGRESS_RESOURCE_CLASS, self.PLACEMENT_MAX_INT))
+
+    @decorators.idempotent_id('78625d92-212c-400e-8695-dd51706858b8')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
+    def test_qos_min_bw_allocation_basic(self):
+        """"Basic scenario with QoS min bw allocation in placement.
+
+        Steps:
+        * Create prerequisites:
+        ** VLAN type provider network with subnet.
+        ** valid QoS policy with minimum bandwidth rule with min_kbps=1
+        (This is a simplification to skip the checks in placement for
+        detecting the resource provider tree and inventories, as if
+        bandwidth resource is available 1 kbs will be available).
+        ** invalid QoS policy with minimum bandwidth rule with
+        min_kbs=max integer from placement (this is a simplification again
+        to avoid detection of RP tress and inventories, as placement will
+        reject such big allocation).
+        * Create port with valid QoS policy, and boot VM with that, it should
+        pass.
+        * Create port with invalid QoS policy, and try to boot VM with that,
+        it should fail.
+        """
+
+        self._check_if_allocation_is_possible()
+
+        self._create_network_and_qos_policies()
+
+        valid_port = self.create_port(
+            self.prov_network['id'], qos_policy_id=self.qos_policy_valid['id'])
+
+        server1 = self.create_server(
+            networks=[{'port': valid_port['id']}])
+        allocations = self.placement_client.list_allocations(server1['id'])
+
+        self.assertGreater(len(allocations['allocations']), 0)
+        bw_resource_in_alloc = False
+        for rp, resources in allocations['allocations'].items():
+            if self.INGRESS_RESOURCE_CLASS in resources['resources']:
+                bw_resource_in_alloc = True
+        self.assertTrue(bw_resource_in_alloc)
+
+        # boot another vm with max int bandwidth
+        not_valid_port = self.create_port(
+            self.prov_network['id'],
+            qos_policy_id=self.qos_policy_not_valid['id'])
+        server2 = self.create_server(
+            wait_until=None,
+            networks=[{'port': not_valid_port['id']}])
+        waiters.wait_for_server_status(
+            client=self.os_primary.servers_client, server_id=server2['id'],
+            status='ERROR', ready_wait=False, raise_on_error=False)
+        allocations = self.placement_client.list_allocations(server2['id'])
+
+        self.assertEqual(0, len(allocations['allocations']))
+        server2 = self.servers_client.show_server(server2['id'])
+        self.assertIn('fault', server2['server'])
+        self.assertIn('No valid host', server2['server']['fault']['message'])
diff --git a/tempest/tests/cmd/test_cleanup.py b/tempest/tests/cmd/test_cleanup.py
index b47da0b..1618df9 100644
--- a/tempest/tests/cmd/test_cleanup.py
+++ b/tempest/tests/cmd/test_cleanup.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import mock
+
 from tempest.cmd import cleanup
 from tempest.tests import base
 
@@ -24,3 +26,17 @@
         test_saved_json = 'tempest/tests/cmd/test_saved_state_json.json'
         # test if the file is loaded without any issues/exceptions
         c._load_json(test_saved_json)
+
+    @mock.patch('tempest.cmd.cleanup.TempestCleanup.init')
+    @mock.patch('tempest.cmd.cleanup.TempestCleanup._cleanup')
+    def test_take_action_got_exception(self, mock_cleanup, mock_init):
+        c = cleanup.TempestCleanup(None, None, 'test')
+        c.GOT_EXCEPTIONS.append('exception')
+        mock_cleanup.return_value = True
+        mock_init.return_value = True
+        try:
+            c.take_action(mock.Mock())
+        except Exception as exc:
+            self.assertEqual(str(exc), '[\'exception\']')
+            return
+        assert False
diff --git a/tempest/tests/cmd/test_cleanup_services.py b/tempest/tests/cmd/test_cleanup_services.py
index 3262b1c..de0dbec 100644
--- a/tempest/tests/cmd/test_cleanup_services.py
+++ b/tempest/tests/cmd/test_cleanup_services.py
@@ -19,6 +19,7 @@
 from tempest import clients
 from tempest.cmd import cleanup_service
 from tempest import config
+from tempest.lib import exceptions
 from tempest.tests import base
 from tempest.tests import fake_config
 from tempest.tests.lib import fake_credentials
@@ -27,13 +28,24 @@
 
 class TestBaseService(base.TestCase):
 
+    class TestException(cleanup_service.BaseService):
+        def delete(self):
+            raise exceptions.NotImplemented
+
+        def dry_run(self):
+            raise exceptions.NotImplemented
+
+        def save_state(self):
+            raise exceptions.NotImplemented
+
     def test_base_service_init(self):
         kwargs = {'data': {'data': 'test'},
                   'is_dry_run': False,
                   'saved_state_json': {'saved': 'data'},
                   'is_preserve': False,
                   'is_save_state': True,
-                  'tenant_id': 'project_id'}
+                  'tenant_id': 'project_id',
+                  'got_exceptions': []}
         base = cleanup_service.BaseService(kwargs)
         self.assertEqual(base.data, kwargs['data'])
         self.assertFalse(base.is_dry_run)
@@ -41,6 +53,28 @@
         self.assertFalse(base.is_preserve)
         self.assertTrue(base.is_save_state)
         self.assertEqual(base.tenant_filter['project_id'], kwargs['tenant_id'])
+        self.assertEqual(base.got_exceptions, kwargs['got_exceptions'])
+
+    def test_not_implemented_ex(self):
+        kwargs = {'data': {'data': 'test'},
+                  'is_dry_run': False,
+                  'saved_state_json': {'saved': 'data'},
+                  'is_preserve': False,
+                  'is_save_state': False,
+                  'tenant_id': 'project_id',
+                  'got_exceptions': []}
+        base = self.TestException(kwargs)
+        # delete
+        base.run()
+        self.assertEqual(len(base.got_exceptions), 1)
+        # save_state
+        base.save_state = True
+        base.run()
+        self.assertEqual(len(base.got_exceptions), 2)
+        # dry_run
+        base.is_dry_run = True
+        base.run()
+        self.assertEqual(len(base.got_exceptions), 3)
 
 
 class MockFunctionsBase(base.TestCase):
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
index 0e00d94..8997a4c 100644
--- a/tempest/tests/cmd/test_run.py
+++ b/tempest/tests/cmd/test_run.py
@@ -49,7 +49,7 @@
         args = mock.Mock(spec=argparse.Namespace)
         setattr(args, 'smoke', False)
         setattr(args, 'regex', '')
-        self.assertIsNone(None, self.run_cmd._build_regex(args))
+        self.assertIsNone(self.run_cmd._build_regex(args))
 
     def test__build_regex_smoke(self):
         args = mock.Mock(spec=argparse.Namespace)
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
old mode 100644
new mode 100755
index d56e8a4..02e1c99
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -73,6 +73,25 @@
                                     mock.call(volume_id)])
         mock_sleep.assert_called_once_with(1)
 
+    @mock.patch.object(time, 'sleep')
+    def test_wait_for_volume_status_error_extending(self, mock_sleep):
+        # Tests that the wait method raises VolumeExtendErrorException if
+        # the volume status is 'error_extending'.
+        client = mock.Mock(spec=volumes_client.VolumesClient,
+                           resource_type="volume",
+                           build_interval=1)
+        volume1 = {'volume': {'status': 'extending'}}
+        volume2 = {'volume': {'status': 'error_extending'}}
+        mock_show = mock.Mock(side_effect=(volume1, volume2))
+        client.show_volume = mock_show
+        volume_id = '7532b91e-aa0a-4e06-b3e5-20c0c5ee1caa'
+        self.assertRaises(exceptions.VolumeExtendErrorException,
+                          waiters.wait_for_volume_resource_status,
+                          client, volume_id, 'available')
+        mock_show.assert_has_calls([mock.call(volume_id),
+                                    mock.call(volume_id)])
+        mock_sleep.assert_called_once_with(1)
+
 
 class TestInterfaceWaiters(base.TestCase):
 
diff --git a/tempest/tests/lib/services/network/test_qos_client.py b/tempest/tests/lib/services/network/test_qos_client.py
new file mode 100644
index 0000000..b04b847
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_qos_client.py
@@ -0,0 +1,139 @@
+# Copyright (c) 2019 Ericsson
+#
+#    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 copy
+
+from tempest.lib.services.network import qos_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQosClient(base.BaseServiceTest):
+
+    FAKE_QOS_POLICY_ID = "f1011b08-1297-11e9-a1e7-c7e6825a2616"
+
+    FAKE_QOS_POLICY_REQUEST = {
+        'name': 'foo',
+        'shared': True
+    }
+
+    FAKE_QOS_POLICY_RESPONSE = {
+        'policy': {
+            "name": "10Mbit",
+            "description": "This policy limits the ports to 10Mbit max.",
+            "rules": [],
+            "id": FAKE_QOS_POLICY_ID,
+            "is_default": False,
+            "project_id": "8d4c70a21fed4aeba121a1a429ba0d04",
+            "revision_number": 1,
+            "tenant_id": "8d4c70a21fed4aeba121a1a429ba0d04",
+            "created_at": "2018-04-03T21:26:39Z",
+            "updated_at": "2018-04-03T21:26:39Z",
+            "shared": False,
+            "tags": ["tag1,tag2"]
+        }
+    }
+
+    FAKE_QOS_POLICIES = {
+        'policies': [
+            FAKE_QOS_POLICY_RESPONSE['policy']
+        ]
+    }
+
+    def setUp(self):
+        super(TestQosClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.qos_client = qos_client.QosClient(
+            fake_auth, "network", "regionOne")
+
+    def _test_create_qos_policy(self, bytes_body=False):
+        self.check_service_client_function(
+            self.qos_client.create_qos_policy,
+            "tempest.lib.common.rest_client.RestClient.post",
+            self.FAKE_QOS_POLICY_RESPONSE,
+            bytes_body,
+            201,
+            **self.FAKE_QOS_POLICY_REQUEST)
+
+    def _test_list_qos_policies(self, bytes_body=False):
+        self.check_service_client_function(
+            self.qos_client.list_qos_policies,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_QOS_POLICIES,
+            bytes_body,
+            200)
+
+    def _test_show_qos_policy(self, bytes_body=False):
+        self.check_service_client_function(
+            self.qos_client.show_qos_policy,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_QOS_POLICY_RESPONSE,
+            bytes_body,
+            200,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID)
+
+    def _test_update_qos_polcy(self, bytes_body=False):
+        update_kwargs = {
+            "name": "100Mbit",
+            "description": "This policy limits the ports to 100Mbit max.",
+            "shared": True
+        }
+
+        resp_body = {
+            "policy": copy.deepcopy(
+                self.FAKE_QOS_POLICY_RESPONSE['policy']
+            )
+        }
+        resp_body["policy"].update(update_kwargs)
+
+        self.check_service_client_function(
+            self.qos_client.update_qos_policy,
+            "tempest.lib.common.rest_client.RestClient.put",
+            resp_body,
+            bytes_body,
+            200,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID,
+            **update_kwargs)
+
+    def test_create_qos_policy_with_str_body(self):
+        self._test_create_qos_policy()
+
+    def test_create_qos_policy_with_bytes_body(self):
+        self._test_create_qos_policy(bytes_body=True)
+
+    def test_update_qos_policy_with_str_body(self):
+        self._test_update_qos_polcy()
+
+    def test_update_qos_policy_with_bytes_body(self):
+        self._test_update_qos_polcy(bytes_body=True)
+
+    def test_show_qos_policy_with_str_body(self):
+        self._test_show_qos_policy()
+
+    def test_show_qos_policy_with_bytes_body(self):
+        self._test_show_qos_policy(bytes_body=True)
+
+    def test_delete_qos_policy(self):
+        self.check_service_client_function(
+            self.qos_client.delete_qos_policy,
+            "tempest.lib.common.rest_client.RestClient.delete",
+            {},
+            status=204,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID)
+
+    def test_list_qos_policies_with_str_body(self):
+        self._test_list_qos_policies()
+
+    def test_list_qos_policies_with_bytes_body(self):
+        self._test_list_qos_policies(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_qos_minimum_bandwidth_rules_client.py b/tempest/tests/lib/services/network/test_qos_minimum_bandwidth_rules_client.py
new file mode 100644
index 0000000..8234dda
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_qos_minimum_bandwidth_rules_client.py
@@ -0,0 +1,137 @@
+# Copyright (c) 2019 Ericsson
+#
+#    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 copy
+
+from tempest.lib.services.network import qos_minimum_bandwidth_rules_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQosMinimumBandwidthRulesClient(base.BaseServiceTest):
+
+    FAKE_QOS_POLICY_ID = "f1011b08-1297-11e9-a1e7-c7e6825a2616"
+    FAKE_MIN_BW_RULE_ID = "e758c89e-1297-11e9-a6cf-cf46a71e6699"
+
+    FAKE_MIN_BW_RULE_REQUEST = {
+        'qos_policy_id': FAKE_QOS_POLICY_ID,
+        'min_kbps': 1000,
+        'direction': 'ingress'
+    }
+
+    FAKE_MIN_BW_RULE_RESPONSE = {
+        'minimum_bandwidth_rule': {
+            'id': FAKE_MIN_BW_RULE_ID,
+            'min_kbps': 10000,
+            'direction': 'egress'
+        }
+    }
+
+    FAKE_MIN_BW_RULES = {
+        'bandwidth_limit_rules': [
+            FAKE_MIN_BW_RULE_RESPONSE['minimum_bandwidth_rule']
+        ]
+    }
+
+    def setUp(self):
+        super(TestQosMinimumBandwidthRulesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.qos_min_bw_client = qos_minimum_bandwidth_rules_client.\
+            QosMinimumBandwidthRulesClient(fake_auth, "network", "regionOne")
+
+    def _test_create_minimum_bandwidth_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.qos_min_bw_client.create_minimum_bandwidth_rule,
+            "tempest.lib.common.rest_client.RestClient.post",
+            self.FAKE_MIN_BW_RULE_RESPONSE,
+            bytes_body,
+            201,
+            **self.FAKE_MIN_BW_RULE_REQUEST
+        )
+
+    def _test_list_minimum_bandwidth_rules(self, bytes_body=False):
+        self.check_service_client_function(
+            self.qos_min_bw_client.list_minimum_bandwidth_rules,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_MIN_BW_RULES,
+            bytes_body,
+            200,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID
+        )
+
+    def _test_show_minimum_bandwidth_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.qos_min_bw_client.show_minimum_bandwidth_rule,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_MIN_BW_RULE_RESPONSE,
+            bytes_body,
+            200,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID,
+            rule_id=self.FAKE_MIN_BW_RULE_ID
+        )
+
+    def _test_update_qos_polcy(self, bytes_body=False):
+        update_kwargs = {
+            "min_kbps": "20000"
+        }
+
+        resp_body = {
+            "minimum_bandwidth_rule": copy.deepcopy(
+                self.FAKE_MIN_BW_RULE_RESPONSE['minimum_bandwidth_rule']
+            )
+        }
+        resp_body["minimum_bandwidth_rule"].update(update_kwargs)
+
+        self.check_service_client_function(
+            self.qos_min_bw_client.update_minimum_bandwidth_rule,
+            "tempest.lib.common.rest_client.RestClient.put",
+            resp_body,
+            bytes_body,
+            200,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID,
+            rule_id=self.FAKE_MIN_BW_RULE_ID,
+            **update_kwargs)
+
+    def test_create_minimum_bandwidth_rule_with_str_body(self):
+        self._test_create_minimum_bandwidth_rule()
+
+    def test_create_minimum_bandwidth_rule_with_bytes_body(self):
+        self._test_create_minimum_bandwidth_rule(bytes_body=True)
+
+    def test_update_minimum_bandwidth_rule_with_str_body(self):
+        self._test_update_qos_polcy()
+
+    def test_update_minimum_bandwidth_rule_with_bytes_body(self):
+        self._test_update_qos_polcy(bytes_body=True)
+
+    def test_show_minimum_bandwidth_rule_with_str_body(self):
+        self._test_show_minimum_bandwidth_rule()
+
+    def test_show_minimum_bandwidth_rule_with_bytes_body(self):
+        self._test_show_minimum_bandwidth_rule(bytes_body=True)
+
+    def test_delete_minimum_bandwidth_rule(self):
+        self.check_service_client_function(
+            self.qos_min_bw_client.delete_minimum_bandwidth_rule,
+            "tempest.lib.common.rest_client.RestClient.delete",
+            {},
+            status=204,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID,
+            rule_id=self.FAKE_MIN_BW_RULE_ID)
+
+    def test_list_minimum_bandwidth_rule_with_str_body(self):
+        self._test_list_minimum_bandwidth_rules()
+
+    def test_list_minimum_bandwidth_rule_with_bytes_body(self):
+        self._test_list_minimum_bandwidth_rules(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py b/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
index c788181..87bd379 100644
--- a/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
@@ -30,6 +30,15 @@
         }
     }
 
+    UPDATE_ENCRYPTION_TYPE = {
+        "encryption": {
+            "key_size": 64,
+            "provider": "LuksEncryptor",
+            "control_location": "front-end",
+            "cipher": "aes-xts-plain64"
+        }
+    }
+
     FAKE_INFO_ENCRYPTION_TYPE = {
         "encryption": {
             "name": "FakeEncryptionType",
@@ -50,10 +59,8 @@
     def setUp(self):
         super(TestEncryptionTypesClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
-        self.client = encryption_types_client.EncryptionTypesClient(fake_auth,
-                                                                    'volume',
-                                                                    'regionOne'
-                                                                    )
+        self.client = encryption_types_client.EncryptionTypesClient(
+            fake_auth, 'volume', 'regionOne')
 
     def _test_create_encryption(self, bytes_body=False):
         self.check_service_client_function(
@@ -101,3 +108,16 @@
             {},
             volume_type_id="cbc36478b0bd8e67e89",
             status=202)
+
+    def test_update_encryption_type_with_str_body(self):
+        self._test_update_encryption_type()
+
+    def test_update_encryption_type_with_bytes_body(self):
+        self._test_update_encryption_type(bytes_body=True)
+
+    def _test_update_encryption_type(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_encryption_type,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.UPDATE_ENCRYPTION_TYPE,
+            bytes_body, volume_type_id="cbc36478b0bd8e67e89")
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
index 746cb34..c18f109 100644
--- a/tools/generate-tempest-plugins-list.py
+++ b/tools/generate-tempest-plugins-list.py
@@ -25,6 +25,7 @@
 
 import json
 import re
+import sys
 
 try:
     # For Python 3.0 and later
@@ -35,6 +36,27 @@
     import urllib2 as urllib
     from urllib2 import HTTPError
 
+# List of projects having tempest plugin stale or unmaintained for a long time
+# (6 months or more)
+# TODO(masayukig): Some of these can be removed from BLACKLIST in the future
+# when the patches are merged.
+BLACKLIST = [
+    'openstack/barbican-tempest-plugin',
+    # https://review.opendev.org/#/c/634631/
+    'x/gce-api',  # It looks gce-api doesn't support python3 yet.
+    'x/intel-nfv-ci-tests',  # https://review.opendev.org/#/c/634640/
+    'openstack/networking-generic-switch',
+    # https://review.opendev.org/#/c/634846/
+    'openstack/networking-l2gw-tempest-plugin',
+    # https://review.opendev.org/#/c/635093/
+    'openstack/networking-midonet',  # https://review.opendev.org/#/c/635096/
+    'x/networking-plumgrid',  # https://review.opendev.org/#/c/635096/
+    'x/networking-spp',  # https://review.opendev.org/#/c/635098/
+    'openstack/neutron-dynamic-routing',
+    # https://review.opendev.org/#/c/637718/
+    'openstack/neutron-vpnaas',  # https://review.opendev.org/#/c/637719/
+    'x/valet',  # https://review.opendev.org/#/c/638339/
+]
 
 url = 'https://review.opendev.org/projects/'
 
@@ -47,15 +69,10 @@
 '''
 
 
-def is_in_openstack_namespace(proj):
-    return proj.startswith('openstack/')
-
 # Rather than returning a 404 for a nonexistent file, cgit delivers a
 # 0-byte response to a GET request.  It also does not provide a
 # Content-Length in a HEAD response, so the way we tell if a file exists
 # is to check the length of the entire GET response body.
-
-
 def has_tempest_plugin(proj):
     try:
         r = urllib.urlopen(
@@ -64,6 +81,8 @@
     except HTTPError as err:
         if err.code == 404:
             return False
+        # We should not ignore non 404 errors.
+        raise err
     p = re.compile(r'^tempest\.test_plugins', re.M)
     if p.findall(r.read().decode('utf-8')):
         return True
@@ -71,24 +90,45 @@
         False
 
 
+if len(sys.argv) > 1 and sys.argv[1] == 'blacklist':
+    for black_plugin in BLACKLIST:
+        print(black_plugin)
+    # We just need BLACKLIST when we use this `blacklist` option.
+    # So, this exits here.
+    sys.exit()
+
 r = urllib.urlopen(url)
 # Gerrit prepends 4 garbage octets to the JSON, in order to counter
 # cross-site scripting attacks.  Therefore we must discard it so the
 # json library won't choke.
 content = r.read().decode('utf-8')[4:]
-projects = sorted(filter(is_in_openstack_namespace, json.loads(content)))
+projects = sorted(json.loads(content))
 
-# Retrieve projects having no deb, puppet, ui or spec namespace as those
+# Retrieve projects having no deployment tool repo (such as deb,
+# puppet, ansible, etc.), infra repos, ui or spec namespace as those
 # namespaces do not contains tempest plugins.
 projects_list = [i for i in projects if not (
+    i.startswith('openstack-dev/') or
+    i.startswith('openstack-infra/') or
+    i.startswith('openstack/ansible-') or
+    i.startswith('openstack/charm-') or
+    i.startswith('openstack/cookbook-openstack-') or
+    i.startswith('openstack/devstack-') or
+    i.startswith('openstack/fuel-') or
     i.startswith('openstack/deb-') or
     i.startswith('openstack/puppet-') or
+    i.startswith('openstack/openstack-ansible-') or
+    i.startswith('x/deb-') or
+    i.startswith('x/fuel-') or
+    i.startswith('x/python-') or
+    i.startswith('zuul/') or
     i.endswith('-ui') or
     i.endswith('-specs'))]
 
 found_plugins = list(filter(has_tempest_plugin, projects_list))
 
-# Every element of the found_plugins list begins with "openstack/".
-# We drop those initial 10 octets when printing the list.
+# We have tempest plugins not only in 'openstack/' namespace but also the
+# other name spaces such as 'airship/', 'x/', etc.
+# So, we print all of them here.
 for project in found_plugins:
-    print(project[10:])
+    print(project)
diff --git a/tools/generate-tempest-plugins-list.sh b/tools/generate-tempest-plugins-list.sh
index b4e5430..6e473b7 100755
--- a/tools/generate-tempest-plugins-list.sh
+++ b/tools/generate-tempest-plugins-list.sh
@@ -61,20 +61,37 @@
     printf " ===\n"
 }
 
+function print_plugin_table() {
+    title_underline ${name_col_len}
+    printf "%-3s %-${name_col_len}s %s\n" "SR" "Plugin Name" "URL"
+    title_underline ${name_col_len}
+
+    i=0
+    for plugin in $1; do
+        i=$((i+1))
+        giturl="https://opendev.org/openstack/${plugin}"
+        printf "%-3s %-${name_col_len}s %s\n" "$i" "${plugin}" "${giturl}"
+    done
+
+    title_underline ${name_col_len}
+}
+
 printf "\n\n"
-title_underline ${name_col_len}
-printf "%-3s %-${name_col_len}s %s\n" "SR" "Plugin Name" "URL"
-title_underline ${name_col_len}
+print_plugin_table "${sorted_plugins}"
 
-i=0
-for plugin in ${sorted_plugins}; do
-    i=$((i+1))
-    giturl="https://opendev.org/openstack/${plugin}"
-    gitlink="https://opendev.org/openstack/${plugin}"
-    printf "%-3s %-${name_col_len}s %s\n" "$i" "${plugin}" "\`${giturl} <${gitlink}>\`__"
-done
+printf "\n\n"
 
-title_underline ${name_col_len}
+# Print BLACKLIST
+if [[ -r doc/source/data/tempest-blacklisted-plugins-registry.header ]]; then
+    cat doc/source/data/tempest-blacklisted-plugins-registry.header
+fi
+
+blacklist=$(python tools/generate-tempest-plugins-list.py blacklist)
+name_col_len=$(echo "${blacklist}" | wc -L)
+name_col_len=$(( name_col_len + 20 ))
+
+printf "\n\n"
+print_plugin_table "${blacklist}"
 
 printf "\n\n"
 
diff --git a/tools/tempest-integrated-gate-networking-blacklist.txt b/tools/tempest-integrated-gate-networking-blacklist.txt
new file mode 100644
index 0000000..9566f69
--- /dev/null
+++ b/tools/tempest-integrated-gate-networking-blacklist.txt
@@ -0,0 +1,17 @@
+# This file includes the backlist of tests which need to be
+# skipped for Integrated-gate-networking template.
+
+# Skip Cinder, Glance, keystone and Swift API tests.
+tempest.api.volume
+tempest.api.image
+tempest.api.object_storage
+tempest.api.identity
+
+# Skip Cinder, Glance and Swift only scenario tests.
+tempest.scenario.test_encrypted_cinder_volumes.TestEncryptedCinderVolumes.test_encrypted_cinder_volumes_luks
+tempest.scenario.test_encrypted_cinder_volumes.TestEncryptedCinderVolumes.test_encrypted_cinder_volumes_cryptsetup
+tempest.scenario.test_object_storage_basic_ops.TestObjectStorageBasicOps.test_swift_basic_ops
+tempest.scenario.test_object_storage_basic_ops.TestObjectStorageBasicOps.test_swift_acl_anonymous_download
+tempest.scenario.test_volume_boot_pattern.TestVolumeBootPattern.test_boot_server_from_encrypted_volume_luks
+tempest.scenario.test_volume_boot_pattern.TestVolumeBootPattern.test_image_defined_boot_from_volume
+tempest.scenario.test_volume_boot_pattern.TestVolumeBootPattern.test_create_server_from_volume_snapshot
diff --git a/tools/tempest-integrated-gate-storage-blacklist.txt b/tools/tempest-integrated-gate-storage-blacklist.txt
new file mode 100644
index 0000000..3900f96
--- /dev/null
+++ b/tools/tempest-integrated-gate-storage-blacklist.txt
@@ -0,0 +1,13 @@
+# This file includes the backlist of tests which need to be
+# skipped for Integrated-gate-storage template. Integrated-gate-storage template
+# needs to run only Cinder, Glance, Swift and Nova related tests and rest all
+# tests will be skipped by below list.
+
+# Skip network, keystone API tests.
+tempest.api.network
+tempest.api.identity
+
+# Skip network only scenario tests.
+tempest.scenario.test_network_advanced_server_ops.TestNetworkAdvancedServerOps.test_network_advanced_server_ops
+tempest.scenario.test_network_basic_ops.TestNetworkBasicOps.test_network_basic_ops
+tempest.scenario.test_network_v6.TestGettingAddress.test_security_groups_basic_ops
diff --git a/tools/tempest-plugin-sanity.sh b/tools/tempest-plugin-sanity.sh
index b291fcc..b652369 100644
--- a/tools/tempest-plugin-sanity.sh
+++ b/tools/tempest-plugin-sanity.sh
@@ -43,49 +43,19 @@
 
 # retrieve a list of projects having tempest plugins
 PROJECT_LIST="$(python tools/generate-tempest-plugins-list.py)"
-# List of projects having tempest plugin stale or unmaintained for a long time
-# (6 months or more)
-# TODO(masayukig): Some of these can be removed from BLACKLIST in the future.
-# barbican-tempest-plugin: https://review.opendev.org/#/c/634631/
-# cyborg-tempest-plugin: https://review.opendev.org/659687
-# intel-nfv-ci-tests: https://review.opendev.org/#/c/634640/
-# networking-ansible: https://review.opendev.org/#/c/634647/
-# networking-generic-switch: https://review.opendev.org/#/c/634846/
-# networking-l2gw-tempest-plugin: https://review.opendev.org/#/c/635093/
-# networking-midonet: https://review.opendev.org/#/c/635096/
-# networking-plumgrid: https://review.opendev.org/#/c/635096/
-# networking-spp: https://review.opendev.org/#/c/635098/
-# neutron-dynamic-routing: https://review.opendev.org/#/c/637718/
-# neutron-vpnaas: https://review.opendev.org/#/c/637719/
-# nova-lxd: https://review.opendev.org/#/c/638334/
-# valet: https://review.opendev.org/#/c/638339/
 
-BLACKLIST="
-barbican-tempest-plugin
-cyborg-tempest-plugin
-intel-nfv-ci-tests
-networking-ansible
-networking-generic-switch
-networking-l2gw-tempest-plugin
-networking-midonet
-networking-plumgrid
-networking-spp
-neutron-dynamic-routing
-neutron-vpnaas
-nova-lxd
-valet
-"
+BLACKLIST="$(python tools/generate-tempest-plugins-list.py blacklist)"
 
 # Function to clone project using zuul-cloner or from git
 function clone_project() {
     if [ -e /usr/zuul-env/bin/zuul-cloner ]; then
         /usr/zuul-env/bin/zuul-cloner --cache-dir /opt/git \
         https://opendev.org \
-        openstack/"$1"
+        "$1"
 
     elif [ -e /usr/bin/git ]; then
-        /usr/bin/git clone https://opendev.org/openstack/"$1" \
-        openstack/"$1"
+        /usr/bin/git clone https://opendev.org/"$1" \
+        "$1"
 
     fi
 }
@@ -103,10 +73,10 @@
 
 # Function to install project
 function install_project() {
-    "$TVENV" pip install "$SANITY_DIR"/openstack/"$1"
+    "$TVENV" pip install "$SANITY_DIR"/"$1"
     # Check for test-requirements.txt file in a project then install it.
-    if [ -e "$SANITY_DIR"/openstack/"$1"/test-requirements.txt ]; then
-        "$TVENV" pip install -r "$SANITY_DIR"/openstack/"$1"/test-requirements.txt
+    if [ -e "$SANITY_DIR"/"$1"/test-requirements.txt ]; then
+        "$TVENV" pip install -r "$SANITY_DIR"/"$1"/test-requirements.txt
     fi
 }
 
@@ -124,7 +94,7 @@
     # Remove the sanity workspace in case of remaining
     rm -fr "$SANITY_DIR"/tempest_sanity
     # Remove the project directory after sanity run
-    rm -fr "$SANITY_DIR"/openstack/"$1"
+    rm -fr "$SANITY_DIR"/"$1"
 
     return $retval
 }
@@ -151,8 +121,10 @@
     fi
 done
 
+echo "Passed Plugins: $passed_plugin"
+echo "Failed Plugins: $failed_plugin"
+
 # Check for failed status
 if [[ -n $failed_plugin ]]; then
-    echo "Failed Plugins: $failed_plugin"
     exit 1
 fi
diff --git a/tox.ini b/tox.ini
index 48a2baa..087a298 100644
--- a/tox.ini
+++ b/tox.ini
@@ -62,7 +62,7 @@
 deps = {[tempestenv]deps}
 commands =
     find . -type f -name "*.pyc" -delete
-    tempest run --regex {posargs}
+    tempest run --regex {posargs:''}
 
 [testenv:all-plugin]
 # DEPRECATED
@@ -82,7 +82,7 @@
     echo "WARNING: The all-plugin env is deprecated and will be removed"
     echo "WARNING  Please use the 'all' environment for Tempest plugins."
     find . -type f -name "*.pyc" -delete
-    tempest run --regex {posargs}
+    tempest run --regex {posargs:''}
 
 [testenv:all-site-packages]
 sitepackages = True
@@ -93,7 +93,7 @@
 deps = {[tempestenv]deps}
 commands =
     find . -type f -name "*.pyc" -delete
-    tempest run --regex {posargs}
+    tempest run --regex {posargs:''}
 
 [testenv:full]
 envdir = .tox/tempest
@@ -118,6 +118,30 @@
     find . -type f -name "*.pyc" -delete
     tempest run --regex '(^tempest\.scenario.*)|(?!.*\[.*\bslow\b.*\])(^tempest\.api)' {posargs}
 
+[testenv:integrated-network]
+envdir = .tox/tempest
+sitepackages = {[tempestenv]sitepackages}
+setenv = {[tempestenv]setenv}
+deps = {[tempestenv]deps}
+# The regex below is used to select which tests to run and exclude the slow tag and
+# tests listed in blacklist file:
+commands =
+    find . -type f -name "*.pyc" -delete
+    tempest run --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.api)' --blacklist_file ./tools/tempest-integrated-gate-networking-blacklist.txt {posargs}
+    tempest run --combine --serial --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.scenario)' --blacklist_file ./tools/tempest-integrated-gate-networking-blacklist.txt {posargs}
+
+[testenv:integrated-storage]
+envdir = .tox/tempest
+sitepackages = {[tempestenv]sitepackages}
+setenv = {[tempestenv]setenv}
+deps = {[tempestenv]deps}
+# The regex below is used to select which tests to run and exclude the slow tag and
+# tests listed in blacklist file:
+commands =
+    find . -type f -name "*.pyc" -delete
+    tempest run --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.api)' --blacklist_file ./tools/tempest-integrated-gate-storage-blacklist.txt {posargs}
+    tempest run --combine --serial --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.scenario)' --blacklist_file ./tools/tempest-integrated-gate-storage-blacklist.txt {posargs}
+
 [testenv:full-serial]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}