Merge "Get cidr/mask_bits according to ip_version in resource_setup"
diff --git a/HACKING.rst b/HACKING.rst
index cb9821e..cc1f161 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -380,7 +380,7 @@
 Otherwise the bug fix won't be able to land in the project.
 
 Handily, `Zuul’s cross-repository dependencies
-<https://docs.openstack.org/infra/zuul/gating.html#cross-repository-dependencies>`_.
+<https://docs.openstack.org/infra/zuul/user/gating.html#cross-project-dependencies>`_.
 can be leveraged to do without step 2 and to have steps 3 and 4 happen
 "atomically". To do that, make the patch written in step 1 to depend (refer to
 Zuul's documentation above) on the patch written in step 4. The commit message
diff --git a/README.rst b/README.rst
index 2e13fec..17d4cba 100644
--- a/README.rst
+++ b/README.rst
@@ -3,7 +3,7 @@
 ========================
 
 .. image:: http://governance.openstack.org/badges/tempest.svg
-    :target: http://governance.openstack.org/reference/tags/index.html
+    :target: https://governance.openstack.org/tc/reference/tags/index.html
 
 .. Change things from this point on
 
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 0cfdf34..067eb81 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -158,10 +158,6 @@
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
 
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-html_use_smartypants = False
-
 # Custom sidebar templates, maps document names to template names.
 #html_sidebars = {}
 
diff --git a/doc/source/library/credential_providers.rst b/doc/source/library/credential_providers.rst
index f4eb37d..d96c97a 100644
--- a/doc/source/library/credential_providers.rst
+++ b/doc/source/library/credential_providers.rst
@@ -130,19 +130,18 @@
   # role
   provider.clear_creds()
 
-API Reference
-=============
 
-------------------------------
+API Reference
+-------------
+
 The dynamic credentials module
-------------------------------
+''''''''''''''''''''''''''''''
 
 .. automodule:: tempest.lib.common.dynamic_creds
    :members:
 
---------------------------------------
 The pre-provisioned credentials module
---------------------------------------
+''''''''''''''''''''''''''''''''''''''
 
 .. automodule:: tempest.lib.common.preprov_creds
    :members:
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 573b7d4..307eb07 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -338,4 +338,4 @@
 
  * `3.3`_
 
- .. _3.3:  https://docs.openstack.org/cinder/ocata/devref/api_microversion_history.html#id4
+ .. _3.3:  https://docs.openstack.org/cinder/latest/contributor/api_microversion_history.html#id4
diff --git a/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml b/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml
new file mode 100644
index 0000000..305e756
--- /dev/null
+++ b/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml
@@ -0,0 +1,15 @@
+---
+features:
+  - |
+    Add a new function called ``compare_version_header_to_response`` to
+    ``tempest.lib.common.api_version_utils``, which compares the API
+    micoversion in the response header to another microversion using the
+    comparators defined in
+    ``tempest.lib.common.api_version_request.APIVersionRequest``.
+
+    It is now possible to determine how to retrieve an attribute from a
+    response body of an API call, depending on the returned microversion.
+
+    Add a new exception type called ``InvalidParam`` to
+    ``tempest.lib.exceptions``, allowing the possibility of raising an
+    exception if an invalid parameter is passed to a library function.
diff --git a/releasenotes/notes/identity-tests-domain-drivers-76235f6672221e45.yaml b/releasenotes/notes/identity-tests-domain-drivers-76235f6672221e45.yaml
new file mode 100644
index 0000000..7ed3081
--- /dev/null
+++ b/releasenotes/notes/identity-tests-domain-drivers-76235f6672221e45.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    A new boolean config option ``domain_specific_drivers``
+    is added to the section ``identity-feature-enabled``.
+    This option must be enabled when testing an environment that
+    is configured to use domain-specific identity drivers.
diff --git a/releasenotes/notes/start-of-pike-support-f2a1b7ea8e8b0311.yaml b/releasenotes/notes/start-of-pike-support-f2a1b7ea8e8b0311.yaml
new file mode 100644
index 0000000..0787821
--- /dev/null
+++ b/releasenotes/notes/start-of-pike-support-f2a1b7ea8e8b0311.yaml
@@ -0,0 +1,11 @@
+---
+prelude: >
+    This release marks the start of support for the Pike release in Tempest.
+other:
+    - OpenStack Releases supported after this release are **Pike**, **Ocata**,
+      and **Newton**.
+
+      The release under current development of this tag is Queens, meaning
+      that every Tempest commit is also tested against master during the Queens
+      cycle. However, this does not necessarily mean that using Tempest as of
+      this tag will work against a Queens (or future release) cloud.
diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py
index 3137541..ae3dca1 100644
--- a/releasenotes/source/conf.py
+++ b/releasenotes/source/conf.py
@@ -158,10 +158,6 @@
 # using the given strftime format.
 # html_last_updated_fmt = '%b %d, %Y'
 
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-# html_use_smartypants = True
-
 # Custom sidebar templates, maps document names to template names.
 # html_sidebars = {}
 
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index db01da0..df1de46 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
     :maxdepth: 1
 
     unreleased
+    v17.0.0
     v16.1.0
     v16.0.0
     v15.0.0
diff --git a/releasenotes/source/v17.0.0.rst b/releasenotes/source/v17.0.0.rst
new file mode 100644
index 0000000..3f50f11
--- /dev/null
+++ b/releasenotes/source/v17.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v17.0.0 Release Notes
+=====================
+
+.. release-notes:: 17.0.0 Release Notes
+   :version: 17.0.0
diff --git a/requirements.txt b/requirements.txt
index aaecaaa..36b9efa 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,7 +10,7 @@
 testrepository>=0.0.18 # Apache-2.0/BSD
 oslo.concurrency>=3.8.0 # Apache-2.0
 oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
-oslo.log>=3.22.0 # Apache-2.0
+oslo.log>=3.30.0 # Apache-2.0
 oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0
 oslo.utils>=3.20.0 # Apache-2.0
 six>=1.9.0 # MIT
diff --git a/tempest/api/compute/admin/test_auto_allocate_network.py b/tempest/api/compute/admin/test_auto_allocate_network.py
index ea92563..6f23866 100644
--- a/tempest/api/compute/admin/test_auto_allocate_network.py
+++ b/tempest/api/compute/admin/test_auto_allocate_network.py
@@ -16,7 +16,6 @@
 
 from tempest.api.compute import base
 from tempest.common import compute
-from tempest.common import credentials_factory as credentials
 from tempest.common import utils
 from tempest import config
 from tempest.lib.common.utils import test_utils
@@ -46,11 +45,6 @@
     @classmethod
     def skip_checks(cls):
         super(AutoAllocateNetworkTest, cls).skip_checks()
-        identity_version = cls.get_identity_version()
-        if not credentials.is_admin_available(
-                identity_version=identity_version):
-            msg = "Missing Identity Admin API credentials in configuration."
-            raise cls.skipException(msg)
         if not CONF.service_available.neutron:
             raise cls.skipException('Neutron is required')
         if not utils.is_extension_enabled('auto-allocated-topology',
@@ -181,9 +175,11 @@
         _, servers = compute.create_test_server(
             self.os_primary, networks='auto', wait_until='ACTIVE',
             min_count=3)
-        server_nets = set()
         for server in servers:
             self.addCleanup(self.delete_server, server['id'])
+
+        server_nets = set()
+        for server in servers:
             # get the server ips
             addresses = self.servers_client.list_addresses(
                 server['id'])['addresses']
diff --git a/tempest/api/compute/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index 69c8a82..ebba73c 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -29,6 +29,8 @@
         if CONF.service_available.neutron:
             msg = ("%s skipped as neutron is available" % cls.__name__)
             raise cls.skipException(msg)
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
 
     @classmethod
     def setup_clients(cls):
@@ -49,17 +51,14 @@
                 break
 
     @decorators.idempotent_id('16b7d848-2f7c-4709-85a3-2dfb4576cc52')
-    @utils.services('network')
     def test_list_fixed_ip_details(self):
         fixed_ip = self.client.show_fixed_ip(self.ip)
         self.assertEqual(fixed_ip['fixed_ip']['address'], self.ip)
 
     @decorators.idempotent_id('5485077b-7e46-4cec-b402-91dc3173433b')
-    @utils.services('network')
     def test_set_reserve(self):
         self.client.reserve_fixed_ip(self.ip, reserve="None")
 
     @decorators.idempotent_id('7476e322-b9ff-4710-bf82-49d51bac6e2e')
-    @utils.services('network')
     def test_set_unreserve(self):
         self.client.reserve_fixed_ip(self.ip, unreserve="None")
diff --git a/tempest/api/compute/admin/test_fixed_ips_negative.py b/tempest/api/compute/admin/test_fixed_ips_negative.py
index c54c866..a5deb3c 100644
--- a/tempest/api/compute/admin/test_fixed_ips_negative.py
+++ b/tempest/api/compute/admin/test_fixed_ips_negative.py
@@ -29,6 +29,8 @@
         if CONF.service_available.neutron:
             msg = ("%s skipped as neutron is available" % cls.__name__)
             raise cls.skipException(msg)
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
 
     @classmethod
     def setup_clients(cls):
@@ -51,14 +53,12 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9f17f47d-daad-4adc-986e-12370c93e407')
-    @utils.services('network')
     def test_list_fixed_ip_details_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.show_fixed_ip, self.ip)
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ce60042c-fa60-4836-8d43-1c8e3359dc47')
-    @utils.services('network')
     def test_set_reserve_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.reserve_fixed_ip,
@@ -66,7 +66,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f1f7a35b-0390-48c5-9803-5f27461439db')
-    @utils.services('network')
     def test_set_unreserve_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.reserve_fixed_ip,
@@ -74,7 +73,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f51cf464-7fc5-4352-bc3e-e75cfa2cb717')
-    @utils.services('network')
     def test_set_reserve_with_invalid_ip(self):
         # NOTE(maurosr): since this exercises the same code snippet, we do it
         # only for reserve action
@@ -87,7 +85,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fd26ef50-f135-4232-9d32-281aab3f9176')
-    @utils.services('network')
     def test_fixed_ip_with_invalid_action(self):
         self.assertRaises(lib_exc.BadRequest,
                           self.client.reserve_fixed_ip,
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 746f83a..3340d8c 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -495,12 +495,10 @@
     def get_host_other_than(self, server_id):
         source_host = self.get_host_for_server(server_id)
 
-        list_hosts_resp = self.os_admin.hosts_client.list_hosts()['hosts']
-        hosts = [
-            host_record['host_name']
-            for host_record in list_hosts_resp
-            if host_record['service'] == 'compute'
-        ]
+        hypers = self.os_admin.hypervisor_client.list_hypervisors(
+            )['hypervisors']
+        hosts = [hyper['hypervisor_hostname'] for hyper in hypers
+                 if hyper['state'] == 'up' and hyper['status'] == 'enabled']
 
         for target_host in hosts:
             if source_host != target_host:
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index 4001e26..9ee0ac9 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -32,6 +32,8 @@
     @classmethod
     def skip_checks(cls):
         super(FloatingIPsTestJSON, cls).skip_checks()
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
         if not CONF.network_feature_enabled.floating_ips:
             raise cls.skipException("Floating ips are not available")
 
@@ -62,7 +64,6 @@
         super(FloatingIPsTestJSON, cls).resource_cleanup()
 
     @decorators.idempotent_id('f7bfb946-297e-41b8-9e8c-aba8e9bb5194')
-    @utils.services('network')
     def test_allocate_floating_ip(self):
         # Positive test:Allocation of a new floating IP to a project
         # should be successful
@@ -78,7 +79,6 @@
         self.assertIn(floating_ip_details, body)
 
     @decorators.idempotent_id('de45e989-b5ca-4a9b-916b-04a52e7bbb8b')
-    @utils.services('network')
     def test_delete_floating_ip(self):
         # Positive test:Deletion of valid floating IP from project
         # should be successful
@@ -93,7 +93,6 @@
         self.client.wait_for_resource_deletion(floating_ip_body['id'])
 
     @decorators.idempotent_id('307efa27-dc6f-48a0-8cd2-162ce3ef0b52')
-    @utils.services('network')
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
     def test_associate_disassociate_floating_ip(self):
@@ -116,7 +115,6 @@
             self.server_id)
 
     @decorators.idempotent_id('6edef4b2-aaf1-4abc-bbe3-993e2561e0fe')
-    @utils.services('network')
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
     def test_associate_already_associated_floating_ip(self):
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
index 3455a81..c3d7816 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -30,6 +30,8 @@
     @classmethod
     def skip_checks(cls):
         super(FloatingIPsNegativeTestJSON, cls).skip_checks()
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
         if not CONF.network_feature_enabled.floating_ips:
             raise cls.skipException("Floating ips are not available")
 
@@ -58,7 +60,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6e0f059b-e4dd-48fb-8207-06e3bba5b074')
-    @utils.services('network')
     def test_allocate_floating_ip_from_nonexistent_pool(self):
         # Negative test:Allocation of a new floating IP from a nonexistent_pool
         # to a project should fail
@@ -68,7 +69,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ae1c55a8-552b-44d4-bfb6-2a115a15d0ba')
-    @utils.services('network')
     def test_delete_nonexistent_floating_ip(self):
         # Negative test:Deletion of a nonexistent floating IP
         # from project should fail
@@ -79,7 +79,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('595fa616-1a71-4670-9614-46564ac49a4c')
-    @utils.services('network')
     def test_associate_nonexistent_floating_ip(self):
         # Negative test:Association of a non existent floating IP
         # to specific server should fail
@@ -90,7 +89,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0a081a66-e568-4e6b-aa62-9587a876dca8')
-    @utils.services('network')
     def test_dissociate_nonexistent_floating_ip(self):
         # Negative test:Dissociation of a non existent floating IP should fail
         # Dissociating non existent floating IP
@@ -100,7 +98,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('804b4fcb-bbf5-412f-925d-896672b61eb3')
-    @utils.services('network')
     def test_associate_ip_to_server_without_passing_floating_ip(self):
         # Negative test:Association of empty floating IP to specific server
         # should raise NotFound or BadRequest(In case of Nova V2.1) exception.
@@ -110,7 +107,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('58a80596-ffb2-11e6-9393-fa163e4fa634')
-    @utils.services('network')
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
     def test_associate_ip_to_server_with_floating_ip(self):
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips.py b/tempest/api/compute/floating_ips/test_list_floating_ips.py
index 04d18c0..2314433 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -26,6 +26,8 @@
     @classmethod
     def skip_checks(cls):
         super(FloatingIPDetailsTestJSON, cls).skip_checks()
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
         if not CONF.network_feature_enabled.floating_ips:
             raise cls.skipException("Floating ips are not available")
 
@@ -53,7 +55,6 @@
         super(FloatingIPDetailsTestJSON, cls).resource_cleanup()
 
     @decorators.idempotent_id('16db31c3-fb85-40c9-bbe2-8cf7b67ff99f')
-    @utils.services('network')
     def test_list_floating_ips(self):
         # Positive test:Should return the list of floating IPs
         body = self.client.list_floating_ips()['floating_ips']
@@ -64,7 +65,6 @@
             self.assertIn(self.floating_ip[i], floating_ips)
 
     @decorators.idempotent_id('eef497e0-8ff7-43c8-85ef-558440574f84')
-    @utils.services('network')
     def test_get_floating_ip_details(self):
         # Positive test:Should be able to GET the details of floatingIP
         # Creating a floating IP for which details are to be checked
@@ -86,7 +86,6 @@
         self.assertEqual(floating_ip_id, body['id'])
 
     @decorators.idempotent_id('df389fc8-56f5-43cc-b290-20eda39854d3')
-    @utils.services('network')
     def test_list_floating_ip_pools(self):
         # Positive test:Should return the list of floating IP Pools
         floating_ip_pools = self.pools_client.list_floating_ip_pools()
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
index f0b9fa5..0ade872 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
@@ -28,6 +28,8 @@
     @classmethod
     def skip_checks(cls):
         super(FloatingIPDetailsNegativeTestJSON, cls).skip_checks()
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
         if not CONF.network_feature_enabled.floating_ips:
             raise cls.skipException("Floating ips are not available")
 
@@ -38,7 +40,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7ab18834-4a4b-4f28-a2c5-440579866695')
-    @utils.services('network')
     def test_get_nonexistent_floating_ip_details(self):
         # Negative test:Should not be able to GET the details
         # of non-existent floating IP
diff --git a/tempest/api/compute/security_groups/base.py b/tempest/api/compute/security_groups/base.py
index 5260a99..54a6da8 100644
--- a/tempest/api/compute/security_groups/base.py
+++ b/tempest/api/compute/security_groups/base.py
@@ -24,6 +24,12 @@
 class BaseSecurityGroupsTest(base.BaseV2ComputeTest):
 
     @classmethod
+    def skip_checks(cls):
+        super(BaseSecurityGroupsTest, cls).skip_checks()
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
+
+    @classmethod
     def setup_credentials(cls):
         # A network and a subnet will be created for these tests
         cls.set_network_resources(network=True, subnet=True)
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index 06edc1c..4c99ea6 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from tempest.api.compute.security_groups import base
-from tempest.common import utils
 from tempest.lib import decorators
 
 
@@ -55,7 +54,6 @@
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('850795d7-d4d3-4e55-b527-a774c0123d3a')
-    @utils.services('network')
     def test_security_group_rules_create(self):
         # Positive test: Creation of Security Group rule
         # should be successful
@@ -73,7 +71,6 @@
         self._check_expected_response(rule)
 
     @decorators.idempotent_id('7a01873e-3c38-4f30-80be-31a043cfe2fd')
-    @utils.services('network')
     def test_security_group_rules_create_with_optional_cidr(self):
         # Positive test: Creation of Security Group rule
         # with optional argument cidr
@@ -96,7 +93,6 @@
         self._check_expected_response(rule)
 
     @decorators.idempotent_id('7f5d2899-7705-4d4b-8458-4505188ffab6')
-    @utils.services('network')
     def test_security_group_rules_create_with_optional_group_id(self):
         # Positive test: Creation of Security Group rule
         # with optional argument group_id
@@ -125,7 +121,6 @@
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('a6154130-5a55-4850-8be4-5e9e796dbf17')
-    @utils.services('network')
     def test_security_group_rules_list(self):
         # Positive test: Created Security Group rules should be
         # in the list of all rules
@@ -163,7 +158,6 @@
         self.assertNotEmpty([i for i in rules if i['id'] == rule2_id])
 
     @decorators.idempotent_id('fc5c5acf-2091-43a6-a6ae-e42760e9ffaf')
-    @utils.services('network')
     def test_security_group_rules_delete_when_peer_group_deleted(self):
         # Positive test:rule will delete when peer group deleting
         # Creating a Security Group to add rules to it
diff --git a/tempest/api/compute/security_groups/test_security_group_rules_negative.py b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
index f344d84..8283aae 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules_negative.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from tempest.api.compute.security_groups import base
-from tempest.common import utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
@@ -29,7 +28,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1d507e98-7951-469b-82c3-23f1e6b8c254')
-    @utils.services('network')
     def test_create_security_group_rule_with_non_existent_id(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with non existent Parent group id
@@ -46,7 +44,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('2244d7e4-adb7-4ecb-9930-2d77e123ce4f')
-    @utils.services('network')
     def test_create_security_group_rule_with_invalid_id(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with Parent group id which is not integer
@@ -63,7 +60,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8bd56d02-3ffa-4d67-9933-b6b9a01d6089')
-    @utils.services('network')
     def test_create_security_group_rule_duplicate(self):
         # Negative test: Create Security Group rule duplicate should fail
         # Creating a Security Group to add rule to it
@@ -88,7 +84,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('84c81249-9f6e-439c-9bbf-cbb0d2cddbdf')
-    @utils.services('network')
     def test_create_security_group_rule_with_invalid_ip_protocol(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid ip_protocol
@@ -108,7 +103,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('12bbc875-1045-4f7a-be46-751277baedb9')
-    @utils.services('network')
     def test_create_security_group_rule_with_invalid_from_port(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid from_port
@@ -127,7 +121,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ff88804d-144f-45d1-bf59-dd155838a43a')
-    @utils.services('network')
     def test_create_security_group_rule_with_invalid_to_port(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid to_port
@@ -146,7 +139,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('00296fa9-0576-496a-ae15-fbab843189e0')
-    @utils.services('network')
     def test_create_security_group_rule_with_invalid_port_range(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid port range.
@@ -165,7 +157,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('56fddcca-dbb8-4494-a0db-96e9f869527c')
-    @utils.services('network')
     def test_delete_security_group_rule_with_non_existent_id(self):
         # Negative test: Deletion of Security Group rule should be FAIL
         # with non existent id
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 01b19ca..eeb423e 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from tempest.api.compute.security_groups import base
-from tempest.common import utils
 from tempest.common import waiters
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
@@ -30,7 +29,6 @@
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('eb2b087d-633d-4d0d-a7bd-9e6ba35b32de')
-    @utils.services('network')
     def test_security_groups_create_list_delete(self):
         # Positive test:Should return the list of Security Groups
         # Create 3 Security Groups
@@ -62,7 +60,6 @@
                                             for m_group in deleted_sgs))
 
     @decorators.idempotent_id('ecc0da4a-2117-48af-91af-993cca39a615')
-    @utils.services('network')
     def test_security_group_create_get_delete(self):
         # Security Group should be created, fetched and deleted
         # with char space between name along with
@@ -83,7 +80,6 @@
         self.client.wait_for_resource_deletion(securitygroup['id'])
 
     @decorators.idempotent_id('fe4abc0d-83f5-4c50-ad11-57a1127297a2')
-    @utils.services('network')
     def test_server_security_groups(self):
         # Checks that security groups may be added and linked to a server
         # and not deleted if the server is active.
@@ -125,7 +121,6 @@
         self.client.delete_security_group(sg2['id'])
 
     @decorators.idempotent_id('7d4e1d3c-3209-4d6d-b020-986304ebad1f')
-    @utils.services('network')
     def test_update_security_groups(self):
         # Update security group name and description
         # Create a security group
@@ -144,7 +139,6 @@
         self.assertEqual(s_new_des, fetched_group['description'])
 
     @decorators.idempotent_id('79517d60-535a-438f-af3d-e6feab1cbea7')
-    @utils.services('network')
     def test_list_security_groups_by_server(self):
         # Create a couple security groups that we will use
         # for the server resource this test creates
diff --git a/tempest/api/compute/security_groups/test_security_groups_negative.py b/tempest/api/compute/security_groups/test_security_groups_negative.py
index e727dbb..9c44bb2 100644
--- a/tempest/api/compute/security_groups/test_security_groups_negative.py
+++ b/tempest/api/compute/security_groups/test_security_groups_negative.py
@@ -16,7 +16,6 @@
 import testtools
 
 from tempest.api.compute.security_groups import base
-from tempest.common import utils
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
@@ -34,7 +33,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('673eaec1-9b3e-48ed-bdf1-2786c1b9661c')
-    @utils.services('network')
     def test_security_group_get_nonexistent_group(self):
         # Negative test:Should not be able to GET the details
         # of non-existent Security Group
@@ -46,7 +44,6 @@
                              condition=CONF.service_available.neutron)
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1759c3cb-b0fc-44b7-86ce-c99236be911d')
-    @utils.services('network')
     def test_security_group_create_with_invalid_group_name(self):
         # Negative test: Security Group should not be created with group name
         # as an empty string/with white spaces/chars more than 255
@@ -69,7 +66,6 @@
                              condition=CONF.service_available.neutron)
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('777b6f14-aca9-4758-9e84-38783cfa58bc')
-    @utils.services('network')
     def test_security_group_create_with_invalid_group_description(self):
         # Negative test: Security Group should not be created with description
         # longer than 255 chars. Empty description is allowed by the API
@@ -85,7 +81,6 @@
     @testtools.skipIf(CONF.service_available.neutron,
                       "Neutron allows duplicate names for security groups")
     @decorators.attr(type=['negative'])
-    @utils.services('network')
     def test_security_group_create_with_duplicate_name(self):
         # Negative test:Security Group with duplicate name should not
         # be created
@@ -99,7 +94,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('36a1629f-c6da-4a26-b8b8-55e7e5d5cd58')
-    @utils.services('network')
     def test_delete_the_default_security_group(self):
         # Negative test:Deletion of the "default" Security Group should Fail
         default_security_group_id = None
@@ -115,7 +109,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6727c00b-214c-4f9e-9a52-017ac3e98411')
-    @utils.services('network')
     def test_delete_nonexistent_security_group(self):
         # Negative test:Deletion of a non-existent Security Group should fail
         non_exist_id = self.generate_random_security_group_id()
@@ -124,7 +117,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1438f330-8fa4-4aeb-8a94-37c250106d7f')
-    @utils.services('network')
     def test_delete_security_group_without_passing_id(self):
         # Negative test:Deletion of a Security Group with out passing ID
         # should Fail
@@ -135,7 +127,6 @@
     @testtools.skipIf(CONF.service_available.neutron,
                       "Neutron does not check the security group ID")
     @decorators.attr(type=['negative'])
-    @utils.services('network')
     def test_update_security_group_with_invalid_sg_id(self):
         # Update security_group with invalid sg_id should fail
         s_name = data_utils.rand_name('sg')
@@ -150,7 +141,6 @@
     @testtools.skipIf(CONF.service_available.neutron,
                       "Neutron does not check the security group name")
     @decorators.attr(type=['negative'])
-    @utils.services('network')
     def test_update_security_group_with_invalid_sg_name(self):
         # Update security_group with invalid sg_name should fail
         securitygroup = self.create_security_group()
@@ -165,7 +155,6 @@
     @testtools.skipIf(CONF.service_available.neutron,
                       "Neutron does not check the security group description")
     @decorators.attr(type=['negative'])
-    @utils.services('network')
     def test_update_security_group_with_invalid_sg_des(self):
         # Update security_group with invalid sg_des should fail
         securitygroup = self.create_security_group()
@@ -178,7 +167,6 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('27edee9c-873d-4da6-a68a-3c256efebe8f')
-    @utils.services('network')
     def test_update_non_existent_security_group(self):
         # Update a non-existent Security Group should Fail
         non_exist_id = self.generate_random_security_group_id()
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 69b6e51..e0a1d77 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -245,7 +245,6 @@
                 break
         self.servers_client.remove_fixed_ip(server['id'], address=fixed_ip)
 
-    @decorators.skip_because(bug='1607714')
     @decorators.idempotent_id('2f3a0127-95c7-4977-92d2-bc5aec602fb4')
     def test_reassign_port_between_servers(self):
         """Tests the following:
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 4870a3d..b5fc39c 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -23,6 +23,7 @@
 from tempest.common.utils.linux import remote_client
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common import api_version_utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
@@ -369,7 +370,11 @@
                                 "been successful as it should have been "
                                 "deleted during rotation.", oldest_backup)
 
-        image1_id = data_utils.parse_image_id(resp['location'])
+        if api_version_utils.compare_version_header_to_response(
+                "OpenStack-API-Version", "compute 2.45", resp, "lt"):
+            image1_id = resp['image_id']
+        else:
+            image1_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(_clean_oldest_backup, image1_id)
         waiters.wait_for_image_status(glance_client,
                                       image1_id, 'active')
@@ -380,7 +385,11 @@
                                          backup_type='daily',
                                          rotation=2,
                                          name=backup2).response
-        image2_id = data_utils.parse_image_id(resp['location'])
+        if api_version_utils.compare_version_header_to_response(
+                "OpenStack-API-Version", "compute 2.45", resp, "lt"):
+            image2_id = resp['image_id']
+        else:
+            image2_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(glance_client.delete_image, image2_id)
         waiters.wait_for_image_status(glance_client,
                                       image2_id, 'active')
@@ -419,7 +428,11 @@
                                          backup_type='daily',
                                          rotation=2,
                                          name=backup3).response
-        image3_id = data_utils.parse_image_id(resp['location'])
+        if api_version_utils.compare_version_header_to_response(
+                "OpenStack-API-Version", "compute 2.45", resp, "lt"):
+            image3_id = resp['image_id']
+        else:
+            image3_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(glance_client.delete_image, image3_id)
         # the first back up should be deleted
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
diff --git a/tempest/api/compute/test_extensions.py b/tempest/api/compute/test_extensions.py
index cd5418f..34faf5f 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -26,7 +26,7 @@
 LOG = logging.getLogger(__name__)
 
 
-class ExtensionsTestJSON(base.BaseV2ComputeTest):
+class ExtensionsTest(base.BaseV2ComputeTest):
 
     @decorators.idempotent_id('3bb27738-b759-4e0d-a5fa-37d7a6df07d1')
     def test_list_extensions(self):
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 502bc1b..e0fed58 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
-
 from tempest.api.compute import base
 from tempest.common import compute
 from tempest.common.utils.linux import remote_client
@@ -143,6 +141,10 @@
             self.assertEqual(server['id'], body['serverId'])
             self.assertEqual(attachment['volumeId'], body['volumeId'])
             self.assertEqual(attachment['id'], body['id'])
+            self.servers_client.detach_volume(server['id'],
+                                              attachment['volumeId'])
+            waiters.wait_for_volume_resource_status(
+                self.volumes_client, attachment['volumeId'], 'available')
 
 
 class AttachVolumeShelveTestJSON(AttachVolumeTestJSON):
@@ -155,6 +157,12 @@
     min_microversion = '2.20'
     max_microversion = 'latest'
 
+    @classmethod
+    def skip_checks(cls):
+        super(AttachVolumeShelveTestJSON, cls).skip_checks()
+        if not CONF.compute_feature_enabled.shelve:
+            raise cls.skipException('Shelve is not available.')
+
     def _count_volumes(self, server):
         # Count number of volumes on an instance
         volumes = 0
@@ -202,8 +210,6 @@
             self.assertEqual(number_of_volumes, counted_volumes)
 
     @decorators.idempotent_id('13a940b6-3474-4c3c-b03f-29b89112bfee')
-    @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
-                          'Shelve is not available.')
     def test_attach_volume_shelved_or_offload_server(self):
         # Create server, count number of volumes on it, shelve
         # server and attach pre-created volume to shelved server
@@ -229,8 +235,6 @@
         self.assertIsNotNone(volume_attachment['device'])
 
     @decorators.idempotent_id('b54e86dd-a070-49c4-9c07-59ae6dae15aa')
-    @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
-                          'Shelve is not available.')
     def test_detach_volume_shelved_or_offload_server(self):
         # Count number of volumes on instance, shelve
         # server and attach pre-created volume to shelved server
diff --git a/tempest/api/compute/volumes/test_volume_snapshots.py b/tempest/api/compute/volumes/test_volume_snapshots.py
index 0f436eb..b8ca81d 100644
--- a/tempest/api/compute/volumes/test_volume_snapshots.py
+++ b/tempest/api/compute/volumes/test_volume_snapshots.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
-
 from tempest.api.compute import base
 from tempest.common import waiters
 from tempest import config
@@ -38,6 +36,9 @@
         if not CONF.service_available.cinder:
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
+        if not CONF.volume_feature_enabled.snapshot:
+            skip_msg = ("Cinder volume snapshots are disabled")
+            raise cls.skipException(skip_msg)
 
     @classmethod
     def setup_clients(cls):
@@ -46,8 +47,6 @@
         cls.snapshots_client = cls.snapshots_extensions_client
 
     @decorators.idempotent_id('cd4ec87d-7825-450d-8040-6e2068f2da8f')
-    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
-                          'Cinder volume snapshots are disabled')
     def test_volume_snapshot_create_get_list_delete(self):
         volume = self.create_volume()
         self.addCleanup(self.delete_volume, volume['id'])
diff --git a/tempest/api/identity/admin/v3/test_groups.py b/tempest/api/identity/admin/v3/test_groups.py
index 4bc987f..17db3ea 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -14,9 +14,12 @@
 #    under the License.
 
 from tempest.api.identity import base
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
+CONF = config.CONF
+
 
 class GroupsV3TestJSON(base.BaseIdentityV3AdminTest):
 
@@ -130,7 +133,14 @@
             self.addCleanup(self.groups_client.delete_group, group['id'])
             group_ids.append(group['id'])
         # List and Verify Groups
-        body = self.groups_client.list_groups()['groups']
+        # When domain specific drivers are enabled the operations
+        # of listing all users and listing all groups are not supported,
+        # they need a domain filter to be specified
+        if CONF.identity_feature_enabled.domain_specific_drivers:
+            body = self.groups_client.list_groups(
+                domain_id=self.domain['id'])['groups']
+        else:
+            body = self.groups_client.list_groups()['groups']
         for g in body:
             fetched_ids.append(g['id'])
         missing_groups = [g for g in group_ids if g not in fetched_ids]
diff --git a/tempest/api/identity/admin/v3/test_list_users.py b/tempest/api/identity/admin/v3/test_list_users.py
index 47a3580..506c729 100644
--- a/tempest/api/identity/admin/v3/test_list_users.py
+++ b/tempest/api/identity/admin/v3/test_list_users.py
@@ -14,9 +14,12 @@
 #    under the License.
 
 from tempest.api.identity import base
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
+CONF = config.CONF
+
 
 class UsersV3TestJSON(base.BaseIdentityV3AdminTest):
 
@@ -82,6 +85,11 @@
     def test_list_users_with_name(self):
         # List users with name
         params = {'name': self.domain_enabled_user['name']}
+        # When domain specific drivers are enabled the operations
+        # of listing all users and listing all groups are not supported,
+        # they need a domain filter to be specified
+        if CONF.identity_feature_enabled.domain_specific_drivers:
+            params['domain_id'] = self.domain_enabled_user['domain_id']
         self._list_users_with_params(params, 'name',
                                      self.domain_enabled_user,
                                      self.non_domain_enabled_user)
@@ -89,7 +97,18 @@
     @decorators.idempotent_id('b30d4651-a2ea-4666-8551-0c0e49692635')
     def test_list_users(self):
         # List users
-        body = self.users_client.list_users()['users']
+        # When domain specific drivers are enabled the operations
+        # of listing all users and listing all groups are not supported,
+        # they need a domain filter to be specified
+        if CONF.identity_feature_enabled.domain_specific_drivers:
+            body_enabled_user = self.users_client.list_users(
+                domain_id=self.domain_enabled_user['domain_id'])['users']
+            body_non_enabled_user = self.users_client.list_users(
+                domain_id=self.non_domain_enabled_user['domain_id'])['users']
+            body = (body_enabled_user + body_non_enabled_user)
+        else:
+            body = self.users_client.list_users()['users']
+
         fetched_ids = [u['id'] for u in body]
         missing_users = [u['id'] for u in self.users
                          if u['id'] not in fetched_ids]
diff --git a/tempest/api/network/test_allowed_address_pair.py b/tempest/api/network/test_allowed_address_pair.py
index 37f5ec2..a471bd6 100644
--- a/tempest/api/network/test_allowed_address_pair.py
+++ b/tempest/api/network/test_allowed_address_pair.py
@@ -41,6 +41,8 @@
         api_extensions
     """
 
+    _project_network_cidr = CONF.network.project_network_cidr
+
     @classmethod
     def skip_checks(cls):
         super(AllowedAddressPairTestJSON, cls).skip_checks()
@@ -103,7 +105,7 @@
     @decorators.idempotent_id('4d6d178f-34f6-4bff-a01c-0a2f8fe909e4')
     def test_update_port_with_cidr_address_pair(self):
         # Update allowed address pair with cidr
-        cidr = str(netaddr.IPNetwork(CONF.network.project_network_cidr))
+        cidr = str(netaddr.IPNetwork(self._project_network_cidr))
         self._update_port_with_address(cidr)
 
     @decorators.idempotent_id('b3f20091-6cd5-472b-8487-3516137df933')
@@ -133,3 +135,4 @@
 
 class AllowedAddressPairIpV6TestJSON(AllowedAddressPairTestJSON):
     _ip_version = 6
+    _project_network_cidr = CONF.network.project_network_v6_cidr
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 26199fc..1c59556 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -366,22 +366,36 @@
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
     def test_external_network_visibility(self):
-        """Verifies user can see external networks but not subnets."""
+        public_network_id = CONF.network.public_network_id
+
+        # find external network matching public_network_id
         body = self.networks_client.list_networks(**{'router:external': True})
-        networks = [network['id'] for network in body['networks']]
-        self.assertNotEmpty(networks, "No external networks found")
+        external_network = next((network for network in body['networks']
+                                 if network['id'] == public_network_id), None)
+        self.assertIsNotNone(external_network, "Public network %s not found "
+                                               "in external network list"
+                             % public_network_id)
 
         nonexternal = [net for net in body['networks'] if
                        not net['router:external']]
         self.assertEmpty(nonexternal, "Found non-external networks"
                                       " in filtered list (%s)." % nonexternal)
-        self.assertIn(CONF.network.public_network_id, networks)
+
         # only check the public network ID because the other networks may
         # belong to other tests and their state may have changed during this
         # test
-        body = self.subnets_client.list_subnets(
-            network_id=CONF.network.public_network_id)
-        self.assertEmpty(body['subnets'], "Public subnets visible")
+        body = self.subnets_client.list_subnets(network_id=public_network_id)
+
+        # check subnet visibility of external_network
+        if external_network['shared']:
+            self.assertNotEmpty(body['subnets'], "Subnets should be visible "
+                                                 "for shared public network %s"
+                                % public_network_id)
+        else:
+            self.assertEmpty(body['subnets'], "Subnets should not be visible "
+                                              "for non-shared public "
+                                              "network %s"
+                             % public_network_id)
 
     @decorators.idempotent_id('c72c1c0c-2193-4aca-ccc4-b1442640bbbb')
     @utils.requires_ext(extension="standard-attr-description",
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 2db8010..c0891e4 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -66,8 +66,7 @@
 
         params = {'name': vol_name, 'volume_type': type_name,
                   'size': CONF.volume.volume_size}
-        cls.volume = cls.admin_volume_client.create_volume(
-            **params)['volume']
+        cls.volume = cls.create_volume(**params)
         if with_prefix:
             cls.volume_id_list_with_prefix.append(cls.volume['id'])
         else:
@@ -76,21 +75,6 @@
         waiters.wait_for_volume_resource_status(cls.admin_volume_client,
                                                 cls.volume['id'], 'available')
 
-    @classmethod
-    def resource_cleanup(cls):
-        # volumes deletion
-        vid_prefix = getattr(cls, 'volume_id_list_with_prefix', [])
-        for volume_id in vid_prefix:
-            cls.admin_volume_client.delete_volume(volume_id)
-            cls.admin_volume_client.wait_for_resource_deletion(volume_id)
-
-        vid_no_pre = getattr(cls, 'volume_id_list_without_prefix', [])
-        for volume_id in vid_no_pre:
-            cls.admin_volume_client.delete_volume(volume_id)
-            cls.admin_volume_client.wait_for_resource_deletion(volume_id)
-
-        super(VolumeMultiBackendTest, cls).resource_cleanup()
-
     @decorators.idempotent_id('c1a41f3f-9dad-493e-9f09-3ff197d477cc')
     def test_backend_name_reporting(self):
         # get volume id which created by type without prefix
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 471f39a..41849bc 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from tempest.api.volume import base
+from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
 
@@ -43,6 +44,8 @@
         snapshot_id = self.snapshot['id']
         self.admin_snapshots_client.reset_snapshot_status(snapshot_id,
                                                           status)
+        waiters.wait_for_volume_resource_status(self.snapshots_client,
+                                                snapshot_id, status)
         super(SnapshotsActionsTest, self).tearDown()
 
     def _create_reset_and_force_delete_temp_snapshot(self, status=None):
@@ -50,10 +53,11 @@
         # and force delete temp snapshot
         temp_snapshot = self.create_snapshot(volume_id=self.volume['id'])
         if status:
-            self.admin_snapshots_client.\
-                reset_snapshot_status(temp_snapshot['id'], status)
-        self.admin_snapshots_client.\
-            force_delete_snapshot(temp_snapshot['id'])
+            self.admin_snapshots_client.reset_snapshot_status(
+                temp_snapshot['id'], status)
+            waiters.wait_for_volume_resource_status(
+                self.snapshots_client, temp_snapshot['id'], status)
+        self.admin_snapshots_client.force_delete_snapshot(temp_snapshot['id'])
         self.snapshots_client.wait_for_resource_deletion(temp_snapshot['id'])
 
     def _get_progress_alias(self):
@@ -63,18 +67,19 @@
     def test_reset_snapshot_status(self):
         # Reset snapshot status to creating
         status = 'creating'
-        self.admin_snapshots_client.\
-            reset_snapshot_status(self.snapshot['id'], status)
-        snapshot_get = self.admin_snapshots_client.show_snapshot(
-            self.snapshot['id'])['snapshot']
-        self.assertEqual(status, snapshot_get['status'])
+        self.admin_snapshots_client.reset_snapshot_status(
+            self.snapshot['id'], status)
+        waiters.wait_for_volume_resource_status(self.snapshots_client,
+                                                self.snapshot['id'], status)
 
     @decorators.idempotent_id('41288afd-d463-485e-8f6e-4eea159413eb')
     def test_update_snapshot_status(self):
         # Reset snapshot status to creating
         status = 'creating'
-        self.admin_snapshots_client.\
-            reset_snapshot_status(self.snapshot['id'], status)
+        self.admin_snapshots_client.reset_snapshot_status(
+            self.snapshot['id'], status)
+        waiters.wait_for_volume_resource_status(self.snapshots_client,
+                                                self.snapshot['id'], status)
 
         # Update snapshot status to error
         progress = '80%'
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index e4ec442..ce0cbd2 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import random
-
 from tempest.api.volume import base
 from tempest.lib import decorators
 
@@ -42,20 +40,25 @@
                                 "The count of volume hosts is < 2, "
                                 "response of list hosts is: %s" % hosts)
 
-        # Note(jeremyZ): Host in volume is always presented in two formats:
-        # <host-name> or <host-name>@<driver-name>. Since Mitaka is EOL,
-        # both formats can be chosen for test.
-        host_names = [host['host_name'] for host in hosts]
-        self.assertNotEmpty(host_names, "No available volume host is found, "
-                                        "all hosts that found are: %s" % hosts)
+        # Note(jeremyZ): The show host API is to show volume usage info on the
+        # specified cinder-volume host. If the host does not run cinder-volume
+        # service, or the cinder-volume service is disabled on the host, the
+        # show host API should fail (return code: 404). The cinder-volume host
+        # is presented in format: <host-name>@driver-name.
+        c_vol_hosts = [host['host_name'] for host in hosts
+                       if (host['service'] == 'cinder-volume'
+                           and host['service-state'] == 'enabled')]
+        self.assertNotEmpty(c_vol_hosts,
+                            "No available cinder-volume host is found, "
+                            "all hosts that found are: %s" % hosts)
 
-        # Choose a random host to get and check its elements
-        host_details = self.admin_hosts_client.show_host(
-            random.choice(host_names))['host']
-        self.assertNotEmpty(host_details)
+        # Check each cinder-volume host.
         host_detail_keys = ['project', 'volume_count', 'snapshot_count',
                             'host', 'total_volume_gb', 'total_snapshot_gb']
-        for detail in host_details:
-            self.assertIn('resource', detail)
-            for key in host_detail_keys:
-                self.assertIn(key, detail['resource'])
+        for host in c_vol_hosts:
+            host_details = self.admin_hosts_client.show_host(host)['host']
+            self.assertNotEmpty(host_details)
+            for detail in host_details:
+                self.assertIn('resource', detail)
+                for key in host_detail_keys:
+                    self.assertIn(key, detail['resource'])
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index afc3281..375aacb 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -99,8 +99,7 @@
                                                 'available')
 
         # Verify Import Backup
-        backups = self.admin_backups_client.list_backups(
-            detail=True)['backups']
+        backups = self.admin_backups_client.list_backups()['backups']
         self.assertIn(new_id, [b['id'] for b in backups])
 
         # Restore backup
diff --git a/tempest/api/volume/admin/test_volumes_list.py b/tempest/api/volume/admin/test_volumes_list.py
index 9d98b7a..6ce4a85 100644
--- a/tempest/api/volume/admin/test_volumes_list.py
+++ b/tempest/api/volume/admin/test_volumes_list.py
@@ -45,9 +45,9 @@
         # Create a volume in admin tenant
         adm_vol = self.admin_volume_client.create_volume(
             size=CONF.volume.volume_size)['volume']
+        self.addCleanup(self.admin_volume_client.delete_volume, adm_vol['id'])
         waiters.wait_for_volume_resource_status(self.admin_volume_client,
                                                 adm_vol['id'], 'available')
-        self.addCleanup(self.admin_volume_client.delete_volume, adm_vol['id'])
         params = {'all_tenants': 1,
                   'project_id': self.volumes_client.tenant_id}
         # Getting volume list from primary tenant using admin credentials
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 9142dc3..63ef85b 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -72,6 +72,11 @@
         if cls._api_version == 3:
             cls.backups_client = cls.os_primary.backups_v3_client
             cls.volumes_client = cls.os_primary.volumes_v3_client
+            cls.messages_client = cls.os_primary.volume_v3_messages_client
+            cls.versions_client = cls.os_primary.volume_v3_versions_client
+            cls.groups_client = cls.os_primary.groups_v3_client
+            cls.group_snapshots_client = (
+                cls.os_primary.group_snapshots_v3_client)
         else:
             cls.backups_client = cls.os_primary.backups_v2_client
             cls.volumes_client = cls.os_primary.volumes_v2_client
@@ -82,10 +87,6 @@
         cls.availability_zone_client = (
             cls.os_primary.volume_v2_availability_zone_client)
         cls.volume_limits_client = cls.os_primary.volume_v2_limits_client
-        cls.messages_client = cls.os_primary.volume_v3_messages_client
-        cls.versions_client = cls.os_primary.volume_v3_versions_client
-        cls.groups_client = cls.os_primary.groups_v3_client
-        cls.group_snapshots_client = cls.os_primary.group_snapshots_v3_client
 
     def setUp(self):
         super(BaseVolumeTest, self).setUp()
@@ -259,6 +260,11 @@
         cls.admin_volume_client = cls.os_admin.volumes_v2_client
         if cls._api_version == 3:
             cls.admin_volume_client = cls.os_admin.volumes_v3_client
+            cls.admin_groups_client = cls.os_admin.groups_v3_client
+            cls.admin_messages_client = cls.os_admin.volume_v3_messages_client
+            cls.admin_group_snapshots_client = \
+                cls.os_admin.group_snapshots_v3_client
+            cls.admin_group_types_client = cls.os_admin.group_types_v3_client
         cls.admin_hosts_client = cls.os_admin.volume_hosts_v2_client
         cls.admin_snapshot_manage_client = \
             cls.os_admin.snapshot_manage_v2_client
@@ -274,11 +280,6 @@
             cls.os_admin.volume_capabilities_v2_client
         cls.admin_scheduler_stats_client = \
             cls.os_admin.volume_scheduler_stats_v2_client
-        cls.admin_messages_client = cls.os_admin.volume_v3_messages_client
-        cls.admin_groups_client = cls.os_admin.groups_v3_client
-        cls.admin_group_snapshots_client = \
-            cls.os_admin.group_snapshots_v3_client
-        cls.admin_group_types_client = cls.os_admin.group_types_v3_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index eea65f1..be5638e 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -112,6 +112,10 @@
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 self.volume['id'], 'available')
 
+        image_info = self.images_client.show_image(image_id)
+        self.assertEqual(image_name, image_info['name'])
+        self.assertEqual(CONF.volume.disk_format, image_info['disk_format'])
+
     @decorators.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
     def test_reserve_unreserve_volume(self):
         # Mark volume as reserved.
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index f2d8801..1e240b8 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -83,6 +83,9 @@
         # Get all backups with detail
         backups = self.backups_client.list_backups(
             detail=True)['backups']
+        for backup_info in backups:
+            self.assertIn('created_at', backup_info)
+            self.assertIn('links', backup_info)
         self.assertIn((backup['name'], backup['id']),
                       [(m['name'], m['id']) for m in backups])
 
diff --git a/tempest/common/validation_resources.py b/tempest/common/validation_resources.py
index ae9d584..3a04b9a 100644
--- a/tempest/common/validation_resources.py
+++ b/tempest/common/validation_resources.py
@@ -1,4 +1,5 @@
 # Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
+# Copyright (c) 2017 IBM Corp.
 #    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
@@ -12,6 +13,7 @@
 #    limitations under the License.
 
 from oslo_log import log as logging
+from oslo_utils import excutils
 
 from tempest.lib.common.utils import data_utils
 from tempest.lib import exceptions as lib_exc
@@ -72,26 +74,48 @@
     # Security Group Rules clients require different parameters depending on
     # the network service in use
     if add_rule:
-        if use_neutron:
-            security_group_rules_client.create_security_group_rule(
-                security_group_id=security_group['id'],
-                protocol='tcp',
-                ethertype=ethertype,
-                port_range_min=22,
-                port_range_max=22,
-                direction='ingress')
-            security_group_rules_client.create_security_group_rule(
-                security_group_id=security_group['id'],
-                protocol='icmp',
-                ethertype=ethertype,
-                direction='ingress')
-        else:
-            security_group_rules_client.create_security_group_rule(
-                parent_group_id=security_group['id'], ip_protocol='tcp',
-                from_port=22, to_port=22)
-            security_group_rules_client.create_security_group_rule(
-                parent_group_id=security_group['id'], ip_protocol='icmp',
-                from_port=-1, to_port=-1)
+        try:
+            if use_neutron:
+                security_group_rules_client.create_security_group_rule(
+                    security_group_id=security_group['id'],
+                    protocol='tcp',
+                    ethertype=ethertype,
+                    port_range_min=22,
+                    port_range_max=22,
+                    direction='ingress')
+                security_group_rules_client.create_security_group_rule(
+                    security_group_id=security_group['id'],
+                    protocol='icmp',
+                    ethertype=ethertype,
+                    direction='ingress')
+            else:
+                security_group_rules_client.create_security_group_rule(
+                    parent_group_id=security_group['id'], ip_protocol='tcp',
+                    from_port=22, to_port=22)
+                security_group_rules_client.create_security_group_rule(
+                    parent_group_id=security_group['id'], ip_protocol='icmp',
+                    from_port=-1, to_port=-1)
+        except Exception as sgc_exc:
+            # If adding security group rules fails, we cleanup the SG before
+            # re-raising the failure up
+            with excutils.save_and_reraise_exception():
+                try:
+                    msg = ('Error while provisioning security group rules in '
+                           'security group %s. Trying to cleanup.')
+                    # The exceptions logging is already handled, so using
+                    # debug here just to provide more context
+                    LOG.debug(msg, sgc_exc)
+                    clear_validation_resources(
+                        clients, keypair=None, floating_ip=None,
+                        security_group=security_group,
+                        use_neutron=use_neutron)
+                except Exception as cleanup_exc:
+                    msg = ('Error during cleanup of a security group. '
+                           'The cleanup was triggered by an exception during '
+                           'the provisioning of security group rules.\n'
+                           'Provisioning exception: %s\n'
+                           'First cleanup exception: %s')
+                    LOG.exception(msg, sgc_exc, cleanup_exc)
     LOG.debug("SSH Validation resource security group with tcp and icmp "
               "rules %s created", sg_name)
     return security_group
@@ -130,9 +154,9 @@
     :param floating_network_name: The name of the floating IP pool used to
         provision the floating IP. Only used if a floating IP is requested and
         with nova-net.
-    :returns: A dictionary with the same keys as the input
-        `validation_resources` and the resources for values in the format
-         they are returned by the API.
+    :returns: A dictionary with the resources in the format they are returned
+        by the API. Valid keys are 'keypair', 'floating_ip' and
+        'security_group'.
 
     Examples::
 
@@ -157,35 +181,64 @@
     """
     # Create and Return the validation resources required to validate a VM
     validation_data = {}
-    if keypair:
-        keypair_name = data_utils.rand_name('keypair')
-        validation_data.update(
-            clients.compute.KeyPairsClient().create_keypair(
-                name=keypair_name))
-        LOG.debug("Validation resource key %s created", keypair_name)
-    if security_group:
-        validation_data['security_group'] = create_ssh_security_group(
-            clients, add_rule=security_group_rules,
-            use_neutron=use_neutron, ethertype=ethertype)
-    if floating_ip:
-        floating_ip_client = _network_service(
-            clients, use_neutron).FloatingIPsClient()
-        if use_neutron:
-            floatingip = floating_ip_client.create_floatingip(
-                floating_network_id=floating_network_id)
-            # validation_resources['floating_ip'] has historically looked
-            # like a compute API POST /os-floating-ips response, so we need
-            # to mangle it a bit for a Neutron response with different
-            # fields.
-            validation_data['floating_ip'] = floatingip['floatingip']
-            validation_data['floating_ip']['ip'] = (
-                floatingip['floatingip']['floating_ip_address'])
-        else:
-            # NOTE(mriedem): The os-floating-ips compute API was deprecated
-            # in the 2.36 microversion. Any tests for CRUD operations on
-            # floating IPs using the compute API should be capped at 2.35.
-            validation_data.update(floating_ip_client.create_floating_ip(
-                pool=floating_network_name))
+    try:
+        if keypair:
+            keypair_name = data_utils.rand_name('keypair')
+            validation_data.update(
+                clients.compute.KeyPairsClient().create_keypair(
+                    name=keypair_name))
+            LOG.debug("Validation resource key %s created", keypair_name)
+        if security_group:
+            validation_data['security_group'] = create_ssh_security_group(
+                clients, add_rule=security_group_rules,
+                use_neutron=use_neutron, ethertype=ethertype)
+        if floating_ip:
+            floating_ip_client = _network_service(
+                clients, use_neutron).FloatingIPsClient()
+            if use_neutron:
+                floatingip = floating_ip_client.create_floatingip(
+                    floating_network_id=floating_network_id)
+                # validation_resources['floating_ip'] has historically looked
+                # like a compute API POST /os-floating-ips response, so we need
+                # to mangle it a bit for a Neutron response with different
+                # fields.
+                validation_data['floating_ip'] = floatingip['floatingip']
+                validation_data['floating_ip']['ip'] = (
+                    floatingip['floatingip']['floating_ip_address'])
+            else:
+                # NOTE(mriedem): The os-floating-ips compute API was deprecated
+                # in the 2.36 microversion. Any tests for CRUD operations on
+                # floating IPs using the compute API should be capped at 2.35.
+                validation_data.update(floating_ip_client.create_floating_ip(
+                    pool=floating_network_name))
+            LOG.debug("Validation resource floating IP %s created",
+                      validation_data['floating_ip'])
+    except Exception as prov_exc:
+        # If something goes wrong, cleanup as much as possible before we
+        # re-raise the exception
+        with excutils.save_and_reraise_exception():
+            if validation_data:
+                # Cleanup may fail as well
+                try:
+                    msg = ('Error while provisioning validation resources %s. '
+                           'Trying to cleanup what we provisioned so far: %s')
+                    # The exceptions logging is already handled, so using
+                    # debug here just to provide more context
+                    LOG.debug(msg, prov_exc, str(validation_data))
+                    clear_validation_resources(
+                        clients,
+                        keypair=validation_data.get('keypair', None),
+                        floating_ip=validation_data.get('floating_ip', None),
+                        security_group=validation_data.get('security_group',
+                                                           None),
+                        use_neutron=use_neutron)
+                except Exception as cleanup_exc:
+                    msg = ('Error during cleanup of validation resources. '
+                           'The cleanup was triggered by an exception during '
+                           'the provisioning step.\n'
+                           'Provisioning exception: %s\n'
+                           'First cleanup exception: %s')
+                    LOG.exception(msg, prov_exc, cleanup_exc)
     return validation_data
 
 
@@ -209,9 +262,6 @@
         Defaults to None.
     :param use_neutron: When True resources are provisioned via neutron, when
         False resources are provisioned via nova.
-    :returns: A dictionary with the same keys as the input
-        `validation_resources` and the resources for values in the format
-         they are returned by the API.
 
     Examples::
 
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index f4c2866..10afee0 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -211,6 +211,8 @@
                        (resource_name, resource_id, statuses, resource_status,
                         client.build_timeout))
             raise lib_exc.TimeoutException(message)
+    LOG.info('%s %s reached %s after waiting for %f seconds',
+             resource_name, resource_id, statuses, time.time() - start)
 
 
 def wait_for_volume_retype(client, volume_id, new_volume_type):
diff --git a/tempest/config.py b/tempest/config.py
index af9eefc..e78a07f 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -234,6 +234,12 @@
                 deprecated_reason="This feature flag was introduced to "
                                   "support testing of old OpenStack versions, "
                                   "which are not supported anymore"),
+    cfg.BoolOpt('domain_specific_drivers',
+                default=False,
+                help='Are domain specific drivers enabled? '
+                     'This configuration value should be same as '
+                     '[identity]->domain_specific_drivers_enabled '
+                     'in keystone.conf.'),
     cfg.BoolOpt('security_compliance',
                 default=False,
                 help='Does the environment have the security compliance '
diff --git a/tempest/lib/common/api_version_utils.py b/tempest/lib/common/api_version_utils.py
index 1371b3c..98f174d 100644
--- a/tempest/lib/common/api_version_utils.py
+++ b/tempest/lib/common/api_version_utils.py
@@ -12,6 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log as logging
 import testtools
 
 from tempest.lib.common import api_version_request
@@ -19,6 +20,7 @@
 
 
 LATEST_MICROVERSION = 'latest'
+LOG = logging.getLogger(__name__)
 
 
 class BaseMicroversionTest(object):
@@ -120,3 +122,60 @@
                                     api_microversion,
                                     response_header))
         raise exceptions.InvalidHTTPResponseHeader(msg)
+
+
+def compare_version_header_to_response(api_microversion_header_name,
+                                       api_microversion,
+                                       response_header,
+                                       operation='eq'):
+    """Compares API microversion in response header to ``api_microversion``.
+
+    Compare the ``api_microversion`` value in response header if microversion
+    header is present in response, otherwise return false.
+
+    To make this function work for APIs which do not return microversion
+    header in response (example compute v2.0), this function does *not* raise
+    InvalidHTTPResponseHeader.
+
+    :param api_microversion_header_name: Microversion header name. Example:
+        'Openstack-Api-Version'.
+    :param api_microversion: Microversion number. Example:
+
+        * '2.10' for the old-style header name, 'X-OpenStack-Nova-API-Version'
+        * 'Compute 2.10' for the new-style header name, 'Openstack-Api-Version'
+
+    :param response_header: Response header where microversion is
+        expected to be present.
+    :param operation: The boolean operation to use to compare the
+        ``api_microversion`` to the microversion in ``response_header``.
+        Can be 'lt', 'eq', 'gt', 'le', 'ne', 'ge'. Default is 'eq'. The
+        operation type should be based on the order of the arguments:
+        ``api_microversion`` <operation> ``response_header`` microversion.
+    :returns: True if the comparison is logically true, else False if the
+        comparison is logically false or if ``api_microversion_header_name`` is
+        missing in the ``response_header``.
+    :raises InvalidParam: If the operation is not lt, eq, gt, le, ne or ge.
+    """
+    api_microversion_header_name = api_microversion_header_name.lower()
+    if api_microversion_header_name not in response_header:
+        return False
+
+    op = getattr(api_version_request.APIVersionRequest,
+                 '__%s__' % operation, None)
+
+    if op is None:
+        msg = ("Operation %s is invalid. Valid options include: lt, eq, gt, "
+               "le, ne, ge." % operation)
+        LOG.debug(msg)
+        raise exceptions.InvalidParam(invalid_param=msg)
+
+    # Remove "volume" from "volume <microversion>", for example, so that the
+    # microversion can be converted to `APIVersionRequest`.
+    api_version = api_microversion.split(' ')[-1]
+    resp_version = response_header[api_microversion_header_name].split(' ')[-1]
+    if not op(
+        api_version_request.APIVersionRequest(api_version),
+        api_version_request.APIVersionRequest(resp_version)):
+        return False
+
+    return True
diff --git a/tempest/lib/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
index 9a6c8f5..4f1a883 100644
--- a/tempest/lib/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -28,6 +28,43 @@
 
 
 class DynamicCredentialProvider(cred_provider.CredentialProvider):
+    """Creates credentials dynamically for tests
+
+    A credential provider that, based on an initial set of
+    admin credentials, creates new credentials on the fly for
+    tests to use and then discard.
+
+    :param str identity_version: identity API version to use `v2` or `v3`
+    :param str admin_role: name of the admin role added to admin users
+    :param str name: names of dynamic resources include this parameter
+                     when specified
+    :param str credentials_domain: name of the domain where the users
+                                   are created. If not defined, the project
+                                   domain from admin_credentials is used
+    :param dict network_resources: network resources to be created for
+                                   the created credentials
+    :param Credentials admin_creds: initial admin credentials
+    :param bool identity_admin_domain_scope: Set to true if admin should be
+                                             scoped to the domain. By
+                                             default this is False and the
+                                             admin role is scoped to the
+                                             project.
+    :param str identity_admin_role: The role name to use for admin
+    :param list extra_roles: A list of strings for extra roles that should
+                             be assigned to all created users
+    :param bool neutron_available: Whether we are running in an environemnt
+                                   with neutron
+    :param bool create_networks: Whether dynamic project networks should be
+                                 created or not
+    :param project_network_cidr: The CIDR to use for created project
+                                 networks
+    :param project_network_mask_bits: The network mask bits to use for
+                                      created project networks
+    :param public_network_id: The id for the public network to use
+    :param identity_admin_endpoint_type: The endpoint type for identity
+                                         admin clients. Defaults to public.
+    :param identity_uri: Identity URI of the target cloud
+    """
 
     def __init__(self, identity_version, name=None, network_resources=None,
                  credentials_domain=None, admin_role=None, admin_creds=None,
@@ -37,43 +74,6 @@
                  project_network_cidr=None, project_network_mask_bits=None,
                  public_network_id=None, resource_prefix=None,
                  identity_admin_endpoint_type='public', identity_uri=None):
-        """Creates credentials dynamically for tests
-
-        A credential provider that, based on an initial set of
-        admin credentials, creates new credentials on the fly for
-        tests to use and then discard.
-
-        :param str identity_version: identity API version to use `v2` or `v3`
-        :param str admin_role: name of the admin role added to admin users
-        :param str name: names of dynamic resources include this parameter
-                         when specified
-        :param str credentials_domain: name of the domain where the users
-                                       are created. If not defined, the project
-                                       domain from admin_credentials is used
-        :param dict network_resources: network resources to be created for
-                                       the created credentials
-        :param Credentials admin_creds: initial admin credentials
-        :param bool identity_admin_domain_scope: Set to true if admin should be
-                                                 scoped to the domain. By
-                                                 default this is False and the
-                                                 admin role is scoped to the
-                                                 project.
-        :param str identity_admin_role: The role name to use for admin
-        :param list extra_roles: A list of strings for extra roles that should
-                                 be assigned to all created users
-        :param bool neutron_available: Whether we are running in an environemnt
-                                       with neutron
-        :param bool create_networks: Whether dynamic project networks should be
-                                     created or not
-        :param project_network_cidr: The CIDR to use for created project
-                                     networks
-        :param project_network_mask_bits: The network mask bits to use for
-                                          created project networks
-        :param public_network_id: The id for the public network to use
-        :param identity_admin_endpoint_type: The endpoint type for identity
-                                             admin clients. Defaults to public.
-        :param identity_uri: Identity URI of the target cloud
-        """
         super(DynamicCredentialProvider, self).__init__(
             identity_version=identity_version, identity_uri=identity_uri,
             admin_role=admin_role, name=name,
diff --git a/tempest/lib/common/preprov_creds.py b/tempest/lib/common/preprov_creds.py
index cd3a10e..83db513 100644
--- a/tempest/lib/common/preprov_creds.py
+++ b/tempest/lib/common/preprov_creds.py
@@ -41,6 +41,35 @@
 
 
 class PreProvisionedCredentialProvider(cred_provider.CredentialProvider):
+    """Credentials provider using pre-provisioned accounts
+
+    This credentials provider loads the details of pre-provisioned
+    accounts from a YAML file, in the format specified by
+    ``etc/accounts.yaml.sample``. It locks accounts while in use, using the
+    external locking mechanism, allowing for multiple python processes
+    to share a single account file, and thus running tests in parallel.
+
+    The accounts_lock_dir must be generated using `lockutils.get_lock_path`
+    from the oslo.concurrency library. For instance::
+
+        accounts_lock_dir = os.path.join(lockutils.get_lock_path(CONF),
+                                         'test_accounts')
+
+    Role names for object storage are optional as long as the
+    `operator` and `reseller_admin` credential types are not used in the
+    accounts file.
+
+    :param identity_version: identity version of the credentials
+    :param admin_role: name of the admin role
+    :param test_accounts_file: path to the accounts YAML file
+    :param accounts_lock_dir: the directory for external locking
+    :param name: name of the hash file (optional)
+    :param credentials_domain: name of the domain credentials belong to
+                               (if no domain is configured)
+    :param object_storage_operator_role: name of the role
+    :param object_storage_reseller_admin_role: name of the role
+    :param identity_uri: Identity URI of the target cloud
+    """
 
     # Exclude from the hash fields specific to v2 or v3 identity API
     # i.e. only include user*, project*, tenant* and password
@@ -51,35 +80,6 @@
                  accounts_lock_dir, name=None, credentials_domain=None,
                  admin_role=None, object_storage_operator_role=None,
                  object_storage_reseller_admin_role=None, identity_uri=None):
-        """Credentials provider using pre-provisioned accounts
-
-        This credentials provider loads the details of pre-provisioned
-        accounts from a YAML file, in the format specified by
-        `etc/accounts.yaml.sample`. It locks accounts while in use, using the
-        external locking mechanism, allowing for multiple python processes
-        to share a single account file, and thus running tests in parallel.
-
-        The accounts_lock_dir must be generated using `lockutils.get_lock_path`
-        from the oslo.concurrency library. For instance:
-
-            accounts_lock_dir = os.path.join(lockutils.get_lock_path(CONF),
-                                             'test_accounts')
-
-        Role names for object storage are optional as long as the
-        `operator` and `reseller_admin` credential types are not used in the
-        accounts file.
-
-        :param identity_version: identity version of the credentials
-        :param admin_role: name of the admin role
-        :param test_accounts_file: path to the accounts YAML file
-        :param accounts_lock_dir: the directory for external locking
-        :param name: name of the hash file (optional)
-        :param credentials_domain: name of the domain credentials belong to
-                                   (if no domain is configured)
-        :param object_storage_operator_role: name of the role
-        :param object_storage_reseller_admin_role: name of the role
-        :param identity_uri: Identity URI of the target cloud
-        """
         super(PreProvisionedCredentialProvider, self).__init__(
             identity_version=identity_version, name=name,
             admin_role=admin_role, credentials_domain=credentials_domain,
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index c538c72..9b2e87e 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -276,3 +276,7 @@
 
 class InvalidTestResource(TempestException):
     message = "%(name)s is not a valid %(type)s, or the name is ambiguous"
+
+
+class InvalidParam(TempestException):
+    message = ("Invalid Parameter passed: %(invalid_param)s")
diff --git a/tempest/test.py b/tempest/test.py
index f390ae4..a4cc2cc 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -220,10 +220,13 @@
         resource_setup or at test level.
         """
         identity_version = cls.get_identity_version()
-        if 'admin' in cls.credentials and not credentials.is_admin_available(
-                identity_version=identity_version):
-            msg = "Missing Identity Admin API credentials in configuration."
-            raise cls.skipException(msg)
+        # setting force_tenant_isolation to True also needs admin credentials.
+        if ('admin' in cls.credentials or
+                getattr(cls, 'force_tenant_isolation', False)):
+            if not credentials.is_admin_available(
+                    identity_version=identity_version):
+                raise cls.skipException(
+                    "Missing Identity Admin API credentials in configuration.")
         if 'alt' in cls.credentials and not credentials.is_alt_available(
                 identity_version=identity_version):
             msg = "Missing a 2nd set of API credentials in configuration."
diff --git a/tempest/tests/lib/common/test_api_version_utils.py b/tempest/tests/lib/common/test_api_version_utils.py
index c063556..b99e8d4 100644
--- a/tempest/tests/lib/common/test_api_version_utils.py
+++ b/tempest/tests/lib/common/test_api_version_utils.py
@@ -92,24 +92,106 @@
     def test_header_matches(self):
         microversion_header_name = 'x-openstack-xyz-api-version'
         request_microversion = '2.1'
-        test_respose = {microversion_header_name: request_microversion}
+        test_response = {microversion_header_name: request_microversion}
         api_version_utils.assert_version_header_matches_request(
-            microversion_header_name, request_microversion, test_respose)
+            microversion_header_name, request_microversion, test_response)
 
     def test_header_does_not_match(self):
         microversion_header_name = 'x-openstack-xyz-api-version'
         request_microversion = '2.1'
-        test_respose = {microversion_header_name: '2.2'}
+        test_response = {microversion_header_name: '2.2'}
         self.assertRaises(
             exceptions.InvalidHTTPResponseHeader,
             api_version_utils.assert_version_header_matches_request,
-            microversion_header_name, request_microversion, test_respose)
+            microversion_header_name, request_microversion, test_response)
 
     def test_header_not_present(self):
         microversion_header_name = 'x-openstack-xyz-api-version'
         request_microversion = '2.1'
-        test_respose = {}
+        test_response = {}
         self.assertRaises(
             exceptions.InvalidHTTPResponseHeader,
             api_version_utils.assert_version_header_matches_request,
-            microversion_header_name, request_microversion, test_respose)
+            microversion_header_name, request_microversion, test_response)
+
+    def test_compare_versions_less_than(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.2'
+        test_response = {microversion_header_name: '2.1'}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "lt"))
+
+    def test_compare_versions_less_than_equal(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.2'
+        test_response = {microversion_header_name: '2.1'}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "le"))
+
+    def test_compare_versions_greater_than_equal(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_response = {microversion_header_name: '2.2'}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "ge"))
+
+    def test_compare_versions_greater_than(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_response = {microversion_header_name: '2.2'}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "gt"))
+
+    def test_compare_versions_equal(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.11'
+        test_response = {microversion_header_name: '2.1'}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "eq"))
+
+    def test_compare_versions_not_equal(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_response = {microversion_header_name: '2.1'}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "ne"))
+
+    def test_compare_versions_with_name_in_microversion(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = 'volume 3.1'
+        test_response = {microversion_header_name: 'volume 3.1'}
+        self.assertTrue(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "eq"))
+
+    def test_compare_versions_invalid_operation(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_response = {microversion_header_name: '2.1'}
+        self.assertRaises(
+            exceptions.InvalidParam,
+            api_version_utils.compare_version_header_to_response,
+            microversion_header_name, request_microversion, test_response,
+            "foo")
+
+    def test_compare_versions_header_not_present(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_response = {}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "eq"))