Merge "glance_http: add IPv6 support"
diff --git a/HACKING.rst b/HACKING.rst
index 0962f80..efabaf6 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -18,6 +18,7 @@
 - [T109] Cannot use testtools.skip decorator; instead use
          decorators.skip_because from tempest-lib
 - [T110] Check that service client names of GET should be consistent
+- [T111] Check that service client names of DELETE should be consistent
 - [N322] Method's default argument shouldn't be mutable
 
 Test Data/Configuration
diff --git a/README.rst b/README.rst
index c859ddd..7da83cd 100644
--- a/README.rst
+++ b/README.rst
@@ -1,6 +1,14 @@
 Tempest - The OpenStack Integration Test Suite
 ==============================================
 
+.. image:: https://img.shields.io/pypi/v/tempest.svg
+    :target: https://pypi.python.org/pypi/tempest/
+    :alt: Latest Version
+
+.. image:: https://img.shields.io/pypi/dm/tempest.svg
+    :target: https://pypi.python.org/pypi/tempest/
+    :alt: Downloads
+
 This is a set of integration tests to be run against a live OpenStack
 cluster. Tempest has batteries of tests for OpenStack API validation,
 Scenarios, and other specific tests useful in validating an OpenStack
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 524c0fa..367be41 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -246,6 +246,21 @@
 run. This section covers the different methods of configuring Tempest to provide
 a network when creating servers.
 
+The ``validation`` group gathers all the connection options to remotely access the
+created servers.
+
+To enable remote access to servers, at least the three following options need to be
+set:
+
+* The ``run_validation`` option needs be set to ``true``.
+
+* The ``connect_method`` option. Two connect methods are available: ``fixed`` and
+  ``floating``, the later being set by default.
+
+* The ``auth_method`` option. Currently, only authentication by keypair is
+  available.
+
+
 Fixed Network Name
 """"""""""""""""""
 This is the simplest method of specifying how networks should be used. You can
@@ -296,7 +311,7 @@
 With Dynamic Credentials
 """"""""""""""""""""""""
 With dynamic credentials enabled and using nova-network, your only option for
-configuration is to either set a fixed network name or not.  However, in most
+configuration is to either set a fixed network name or not. However, in most
 cases it shouldn't matter because nova-network should have no problem booting a
 server with multiple networks. If this is not the case for your cloud then using
 an accounts file is recommended because it provides the necessary flexibility to
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 17def1c..5f357b2 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -12,6 +12,7 @@
    REVIEWING
    plugin
    library
+   microversion_testing
 
 ------------
 Field Guides
diff --git a/doc/source/library/api_microversion_testing.rst b/doc/source/library/api_microversion_testing.rst
index 0d0b595..b7a4ba8 100644
--- a/doc/source/library/api_microversion_testing.rst
+++ b/doc/source/library/api_microversion_testing.rst
@@ -8,7 +8,7 @@
 ---------------------------------------------
 
 Many of the OpenStack components have implemented API microversions.
-It is important to tests those microversion in Tempest or external plugins.
+It is important to test those microversions in Tempest or external plugins.
 Tempest now provides stable interfaces to support to test the API microversions.
 Based on the microversion range coming from the combination of both configuration
 and each test case, APIs request will be made with selected microversion.
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
new file mode 100644
index 0000000..63ec04b
--- /dev/null
+++ b/doc/source/microversion_testing.rst
@@ -0,0 +1,207 @@
+===================================
+How To Implement Microversion Tests
+===================================
+
+Tempest provides stable interfaces to test API Microversion.
+For Details, see: `API Microversion testing Framework`_
+This document explains how to implement Microversion tests using those
+interfaces.
+
+.. _API Microversion testing Framework: http://docs.openstack.org/developer/tempest/library/api_microversion_testing.html
+
+
+Configuration options for Microversion
+""""""""""""""""""""""""""""""""""""""
+
+* Add configuration options for specifying test target Microversions.
+  We need to specify test target Microversions because the supported
+  Microversions may be different between OpenStack clouds. For operating
+  multiple Microversion tests in a single Tempest operation, configuration
+  options should represent the range of test target Microversions.
+  New configuration options are:
+  * min_microversion
+  * max_microversion
+
+  Those should be defined under respective section of each service.
+  For example::
+      [compute]
+      min_microversion = None
+      max_microversion = latest
+
+
+How To Implement Microversion Tests
+"""""""""""""""""""""""""""""""""""
+
+Step1: Add skip logic based on configured Microversion range
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Add logic to skip the tests based on Tests class and configured Microversion
+range.
+api_version_utils.check_skip_with_microversion function can be used
+to automatically skip the tests which do not fall under configured
+Microversion range.
+For example::
+
+    class BaseTestCase1(api_version_utils.BaseMicroversionTest):
+
+        [..]
+    @classmethod
+    def skip_checks(cls):
+        super(BaseTestCase1, cls).skip_checks()
+        api_version_utils.check_skip_with_microversion(cls.min_microversion,
+                                                       cls.max_microversion,
+                                                       CONF.compute.min_microversion,
+                                                       CONF.compute.max_microversion)
+
+Skip logic can be added in tests base class or any specific test class depends on
+tests class structure.
+
+Step2: Selected API request microversion
+''''''''''''''''''''''''''''''''''''''''
+
+Select appropriate Microversion which needs to be used
+to send with API request.
+api_version_utils.select_request_microversion function can be used
+to select the appropriate Microversion which will be used for API request.
+For example::
+
+    @classmethod
+    def resource_setup(cls):
+        super(BaseTestCase1, cls).resource_setup()
+        cls.request_microversion = (
+            api_version_utils.select_request_microversion(
+                cls.min_microversion,
+                CONF.compute.min_microversion))
+
+
+Step3: Set Microversion on Service Clients
+''''''''''''''''''''''''''''''''''''''''''
+
+Microversion selected by Test Class in previous step needs to be set on
+service clients so that APIs can be requested with selected Microversion.
+
+Microversion can be defined as global variable on service clients which
+can be set using fixture.
+Also Microversion header name needs to be defined on service clients which
+should be constant because it is not supposed to be changed by project
+as per API contract.
+For example::
+
+      COMPUTE_MICROVERSION = None
+
+      class BaseClient1(rest_client.RestClient):
+          api_microversion_header_name = 'X-OpenStack-Nova-API-Version'
+
+Now test class can set the selected Microversion on required service clients
+using fixture which can take care of resetting the same once tests is completed.
+For example::
+
+    def setUp(self):
+        super(BaseTestCase1, self).setUp()
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            self.request_microversion))
+
+Service clients needs to add set Microversion in API request header which
+can be done by overriding the get_headers() method of rest_client.
+For example::
+
+      COMPUTE_MICROVERSION = None
+
+      class BaseClient1(rest_client.RestClient):
+          api_microversion_header_name = 'X-OpenStack-Nova-API-Version'
+
+          def get_headers(self):
+              headers = super(BaseClient1, self).get_headers()
+              if COMPUTE_MICROVERSION:
+                  headers[self.api_microversion_header_name] = COMPUTE_MICROVERSION
+              return headers
+
+
+Step4: Separate Test classes for each Microversion
+''''''''''''''''''''''''''''''''''''''''''''''''''
+
+This is last step to implement Microversion test class.
+
+For any Microversion tests, basically we need to implement a
+separate test class. In addition, each test class defines its
+Microversion range with class variable like min_microversion
+and max_microversion. Tests will be valid for that defined range.
+If that range is out of configured Microversion range then, test
+will be skipped.
+
+*NOTE: Microversion testing is supported at test class level not at individual
+test case level.*
+For example:
+
+Below test is applicable for Microversion from 2.2 till 2.9::
+
+    class BaseTestCase1(api_version_utils.BaseMicroversionTest,
+                        tempest.test.BaseTestCase):
+
+        [..]
+
+
+    class Test1(BaseTestCase1):
+        min_microversion = '2.2'
+        max_microversion = '2.9'
+
+        [..]
+
+Below test is applicable for Microversion from 2.10 till latest::
+
+    class Test2(BaseTestCase1):
+        min_microversion = '2.10'
+        max_microversion = 'latest'
+
+        [..]
+
+
+
+
+Notes about Compute Microversion Tests
+"""""""""""""""""""""""""""""""""""
+Some of the compute Microversion tests have been already implemented
+with the Microversion testing framework. So for further tests only
+step 4 is needed.
+
+Along with that JSON response schema might need versioning if needed.
+
+Compute service clients strictly validate the response against defined JSON
+schema and does not allow additional elements in response.
+So if that Microversion changed the API response then schema needs to be versioned.
+New JSON schema file needs to be defined with new response attributes and service
+client methods will select the schema based on requested microversion.
+
+If Microversion tests are implemented randomly meaning not
+in sequence order(v2.20 tests added and previous Microversion tests are not yet added)
+then, still schema might need to be version for older Microversion if they changed
+the response.
+This is because Nova Microversion includes all the previous Microversions behavior.
+
+For Example:
+    Implementing the v2.20 Microversion tests before v2.9 and 2.19-
+    v2.20 API request will respond as latest behavior of Nova till v2.20,
+    and in v2.9 and 2.19, server response has been changed so response schema needs
+    to be versioned accordingly.
+
+That can be done by using the get_schema method in below module:
+
+The base_compute_client module
+''''''''''''''''''''''''''''''
+
+.. automodule:: tempest.lib.services.compute.base_compute_client
+   :members:
+
+
+Microversion tests implemented in Tempest
+"""""""""""""""""""""""""""""""""""""""""
+
+* Compute
+
+ * `2.1`_
+
+ .. _2.1:  http://docs.openstack.org/developer/nova/api_microversion_history.html#id1
+
+ * `2.2`_
+
+ .. _2.2: http://docs.openstack.org/developer/nova/api_microversion_history.html#id2
diff --git a/releasenotes/notes/compute-microversion-support-e0b23f960f894b9b.yaml b/releasenotes/notes/compute-microversion-support-e0b23f960f894b9b.yaml
new file mode 100644
index 0000000..de1b35e
--- /dev/null
+++ b/releasenotes/notes/compute-microversion-support-e0b23f960f894b9b.yaml
@@ -0,0 +1,3 @@
+---
+features:
+  - Compute Microversion testing support in Service Clients.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 584540b..5bd9176 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -5,6 +5,7 @@
  .. toctree::
     :maxdepth: 1
 
+    v10.0.0
     unreleased
 
 Indices and tables
diff --git a/releasenotes/source/v10.0.0.rst b/releasenotes/source/v10.0.0.rst
new file mode 100644
index 0000000..38ed2ef
--- /dev/null
+++ b/releasenotes/source/v10.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v10.0.0 Release Notes
+=====================
+
+.. release-notes:: 10.0.0 Release Notes
+   :version: 10.0.0
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index ead6db3..94635ff 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -81,7 +81,8 @@
         body = self.volumes_client.show_volume(volume_id)['volume']
         if body['status'] == 'in-use':
             self.servers_client.detach_volume(server_id, volume_id)
-            self.volumes_client.wait_for_volume_status(volume_id, 'available')
+            waiters.wait_for_volume_status(self.volumes_client,
+                                           volume_id, 'available')
         self.volumes_client.delete_volume(volume_id)
 
     def _test_live_migration(self, state='ACTIVE', volume_backed=False):
@@ -152,14 +153,15 @@
         volume = self.volumes_client.create_volume(
             display_name='test')['volume']
 
-        self.volumes_client.wait_for_volume_status(volume['id'],
-                                                   'available')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume['id'], 'available')
         self.addCleanup(self._volume_clean_up, server_id, volume['id'])
 
         # Attach the volume to the server
         self.servers_client.attach_volume(server_id, volumeId=volume['id'],
                                           device='/dev/xvdb')
-        self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume['id'], 'in-use')
 
         self._migrate_server_to(server_id, target_host)
         waiters.wait_for_server_status(self.servers_client,
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 85fd4ab..3d7f4f8 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -16,9 +16,12 @@
 from tempest.api.compute.floating_ips import base
 from tempest.common.utils import data_utils
 from tempest.common import waiters
+from tempest import config
 from tempest.lib import exceptions as lib_exc
 from tempest import test
 
+CONF = config.CONF
+
 
 class FloatingIPsTestJSON(base.BaseFloatingIPsTest):
     server_id = None
@@ -38,7 +41,8 @@
         server = cls.create_test_server(wait_until='ACTIVE')
         cls.server_id = server['id']
         # Floating IP creation
-        body = cls.client.create_floating_ip()['floating_ip']
+        body = cls.client.create_floating_ip(
+            pool=CONF.network.floating_network_name)['floating_ip']
         cls.floating_ip_id = body['id']
         cls.floating_ip = body['ip']
 
@@ -62,7 +66,8 @@
     def test_allocate_floating_ip(self):
         # Positive test:Allocation of a new floating IP to a project
         # should be successful
-        body = self.client.create_floating_ip()['floating_ip']
+        body = self.client.create_floating_ip(
+            pool=CONF.network.floating_network_name)['floating_ip']
         floating_ip_id_allocated = body['id']
         self.addCleanup(self.client.delete_floating_ip,
                         floating_ip_id_allocated)
@@ -78,7 +83,8 @@
         # Positive test:Deletion of valid floating IP from project
         # should be successful
         # Creating the floating IP that is to be deleted in this method
-        floating_ip_body = self.client.create_floating_ip()['floating_ip']
+        floating_ip_body = self.client.create_floating_ip(
+            pool=CONF.network.floating_network_name)['floating_ip']
         self.addCleanup(self._try_delete_floating_ip, floating_ip_body['id'])
         # Deleting the floating IP from the project
         self.client.delete_floating_ip(floating_ip_body['id'])
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 d003967..5738293 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -14,8 +14,11 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest import config
 from tempest import test
 
+CONF = config.CONF
+
 
 class FloatingIPDetailsTestJSON(base.BaseV2ComputeTest):
 
@@ -31,7 +34,8 @@
         cls.floating_ip = []
         cls.floating_ip_id = []
         for i in range(3):
-            body = cls.client.create_floating_ip()['floating_ip']
+            body = cls.client.create_floating_ip(
+                pool=CONF.network.floating_network_name)['floating_ip']
             cls.floating_ip.append(body)
             cls.floating_ip_id.append(body['id'])
 
@@ -57,7 +61,8 @@
     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
-        body = self.client.create_floating_ip()['floating_ip']
+        body = self.client.create_floating_ip(
+            pool=CONF.network.floating_network_name)['floating_ip']
         floating_ip_id = body['id']
         self.addCleanup(self.client.delete_floating_ip,
                         floating_ip_id)
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 12b824f..32faf7d 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -41,7 +41,8 @@
         super(ServerRescueTestJSON, cls).resource_setup()
 
         # Floating IP creation
-        body = cls.floating_ips_client.create_floating_ip()['floating_ip']
+        body = cls.floating_ips_client.create_floating_ip(
+            pool=CONF.network.floating_network_name)['floating_ip']
         cls.floating_ip_id = str(body['id']).strip()
         cls.floating_ip = str(body['ip']).strip()
 
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index 2f79d47..e91857a 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -80,7 +80,7 @@
         self.assertEqual(key_name, server['key_name'])
 
     def _update_server_name(self, server_id, status, prefix_name='server'):
-        # The server name should be changed to the the provided value
+        # The server name should be changed to the provided value
         new_name = data_utils.rand_name(prefix_name)
 
         # Update the server with a new name
@@ -95,7 +95,7 @@
 
     @test.idempotent_id('5e6ccff8-349d-4852-a8b3-055df7988dd2')
     def test_update_server_name(self):
-        # The server name should be changed to the the provided value
+        # The server name should be changed to the provided value
         server = self.create_test_server(wait_until='ACTIVE')
         # Update instance name with non-ASCII characters
         prefix_name = u'\u00CD\u00F1st\u00E1\u00F1c\u00E9'
@@ -103,7 +103,7 @@
 
     @test.idempotent_id('6ac19cb1-27a3-40ec-b350-810bdc04c08e')
     def test_update_server_name_in_stop_state(self):
-        # The server name should be changed to the the provided value
+        # The server name should be changed to the provided value
         server = self.create_test_server(wait_until='ACTIVE')
         self.client.stop_server(server['id'])
         waiters.wait_for_server_status(self.client, server['id'], 'SHUTOFF')
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index b3e138f..e620e03 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -14,10 +14,11 @@
 #    under the License.
 
 import netaddr
+import testtools
 
 from tempest.api.compute import base
 from tempest import config
-from tempest.lib import decorators
+from tempest.lib import exceptions
 from tempest import test
 
 CONF = config.CONF
@@ -42,20 +43,26 @@
         server = cls.create_test_server(wait_until='ACTIVE')
         cls.server_id = server['id']
 
-    @decorators.skip_because(bug="1183436",
-                             condition=CONF.service_available.neutron)
     @test.idempotent_id('96c4e2ef-5e4d-4d7f-87f5-fed6dca18016')
     @test.services('network')
     def test_list_virtual_interfaces(self):
         # Positive test:Should be able to GET the virtual interfaces list
         # for a given server_id
-        output = self.client.list_virtual_interfaces(self.server_id)
-        self.assertIsNotNone(output)
-        virt_ifaces = output
-        self.assertNotEqual(0, len(virt_ifaces['virtual_interfaces']),
-                            'Expected virtual interfaces, got 0 interfaces.')
-        for virt_iface in virt_ifaces['virtual_interfaces']:
-            mac_address = virt_iface['mac_address']
-            self.assertTrue(netaddr.valid_mac(mac_address),
-                            "Invalid mac address detected. mac address: %s"
-                            % mac_address)
+
+        if CONF.service_available.neutron:
+            # TODO(mriedem): After a microversion implements the API for
+            # neutron, a 400 should be a failure for nova-network and neutron.
+            with testtools.ExpectedException(exceptions.BadRequest):
+                self.client.list_virtual_interfaces(self.server_id)
+        else:
+            output = self.client.list_virtual_interfaces(self.server_id)
+            self.assertIsNotNone(output)
+            virt_ifaces = output
+            self.assertNotEqual(0, len(virt_ifaces['virtual_interfaces']),
+                                'Expected virtual interfaces, got 0 '
+                                'interfaces.')
+            for virt_iface in virt_ifaces['virtual_interfaces']:
+                mac_address = virt_iface['mac_address']
+                self.assertTrue(netaddr.valid_mac(mac_address),
+                                "Invalid mac address detected. mac address: %s"
+                                % mac_address)
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 01a8e58..e9c8e30 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -52,7 +52,8 @@
     def _detach(self, server_id, volume_id):
         if self.attachment:
             self.servers_client.detach_volume(server_id, volume_id)
-            self.volumes_client.wait_for_volume_status(volume_id, 'available')
+            waiters.wait_for_volume_status(self.volumes_client,
+                                           volume_id, 'available')
 
     def _delete_volume(self):
         # Delete the created Volumes
@@ -77,15 +78,16 @@
         self.volume = self.volumes_client.create_volume(
             size=CONF.volume.volume_size, display_name='test')['volume']
         self.addCleanup(self._delete_volume)
-        self.volumes_client.wait_for_volume_status(self.volume['id'],
-                                                   'available')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       self.volume['id'], 'available')
 
         # Attach the volume to the server
         self.attachment = self.servers_client.attach_volume(
             self.server['id'],
             volumeId=self.volume['id'],
             device='/dev/%s' % self.device)['volumeAttachment']
-        self.volumes_client.wait_for_volume_status(self.volume['id'], 'in-use')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       self.volume['id'], 'in-use')
 
         self.addCleanup(self._detach, self.server['id'], self.volume['id'])
 
diff --git a/tempest/api/identity/admin/v2/test_users.py b/tempest/api/identity/admin/v2/test_users.py
index 60c4e97..d860d2f 100644
--- a/tempest/api/identity/admin/v2/test_users.py
+++ b/tempest/api/identity/admin/v2/test_users.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import time
+
 from testtools import matchers
 
 from tempest.api.identity import base
@@ -207,9 +209,11 @@
         update_user = self.users_client.update_user_password(
             self.data.user['id'], password=new_pass)['user']
         self.assertEqual(update_user['id'], self.data.user['id'])
-
-        # Validate the updated password
-        # Get a token
+        # NOTE(morganfainberg): Fernet tokens are not subsecond aware and
+        # Keystone should only be precise to the second. Sleep to ensure
+        # we are passing the second boundary.
+        time.sleep(1)
+        # Validate the updated password through getting a token.
         body = self.token_client.auth(self.data.user['name'], new_pass,
                                       self.data.tenant['name'])
         self.assertTrue('id' in body['token'])
diff --git a/tempest/api/identity/admin/v3/test_users.py b/tempest/api/identity/admin/v3/test_users.py
index e26624a..371da9c 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -79,20 +79,15 @@
         self.users_client.update_user_password(
             user['id'], password=new_password,
             original_password=original_password)
-        # TODO(lbragstad): Sleeping after the response status has been checked
-        # and the body loaded as JSON allows requests to fail-fast. The sleep
-        # is necessary because keystone will err on the side of security and
-        # invalidate tokens within a small margin of error (within the same
-        # wall clock second) after a revocation event is issued (such as a
-        # password change). Remove this once keystone and Fernet support
-        # sub-second precision, see bug 1517697 for more details.
+        # NOTE(morganfainberg): Fernet tokens are not subsecond aware and
+        # Keystone should only be precise to the second. Sleep to ensure
+        # we are passing the second boundary.
         time.sleep(1)
         resp = self.token.auth(user_id=user['id'],
                                password=new_password).response
         subject_token = resp['x-subject-token']
         # Perform GET Token to verify and confirm password is updated
         token_details = self.client.show_token(subject_token)['token']
-        self.assertEqual(resp['x-subject-token'], subject_token)
         self.assertEqual(token_details['user']['id'], user['id'])
         self.assertEqual(token_details['user']['name'], u_name)
 
diff --git a/tempest/api/identity/v2/test_users.py b/tempest/api/identity/v2/test_users.py
index ce4ee69..62ddead 100644
--- a/tempest/api/identity/v2/test_users.py
+++ b/tempest/api/identity/v2/test_users.py
@@ -55,13 +55,9 @@
         # user updates own password
         self.non_admin_users_client.update_user_own_password(
             user_id, password=new_pass, original_password=old_pass)
-        # TODO(lbragstad): Sleeping after the response status has been checked
-        # and the body loaded as JSON allows requests to fail-fast. The sleep
-        # is necessary because keystone will err on the side of security and
-        # invalidate tokens within a small margin of error (within the same
-        # wall clock second) after a revocation event is issued (such as a
-        # password change). Remove this once keystone and Fernet support
-        # sub-second precision.
+        # NOTE(morganfainberg): Fernet tokens are not subsecond aware and
+        # Keystone should only be precise to the second. Sleep to ensure
+        # we are passing the second boundary.
         time.sleep(1)
 
         # check authorization with new password
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index ab48c07..60fbe12 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -58,13 +58,9 @@
         self.non_admin_users_client.update_user_password(
             user_id, password=new_pass, original_password=old_pass)
 
-        # TODO(lbragstad): Sleeping after the response status has been checked
-        # and the body loaded as JSON allows requests to fail-fast. The sleep
-        # is necessary because keystone will err on the side of security and
-        # invalidate tokens within a small margin of error (within the same
-        # wall clock second) after a revocation event is issued (such as a
-        # password change). Remove this once keystone and Fernet support
-        # sub-second precision.
+        # NOTE(morganfainberg): Fernet tokens are not subsecond aware and
+        # Keystone should only be precise to the second. Sleep to ensure
+        # we are passing the second boundary.
         time.sleep(1)
 
         # check authorization with new password
diff --git a/tempest/api/network/admin/test_dhcp_agent_scheduler.py b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
index fcb6fce..d2ab237 100644
--- a/tempest/api/network/admin/test_dhcp_agent_scheduler.py
+++ b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
@@ -36,12 +36,12 @@
 
     @test.idempotent_id('5032b1fe-eb42-4a64-8f3b-6e189d8b5c7d')
     def test_list_dhcp_agent_hosting_network(self):
-        self.admin_client.list_dhcp_agent_hosting_network(
+        self.admin_networks_client.list_dhcp_agents_on_hosting_network(
             self.network['id'])
 
     @test.idempotent_id('30c48f98-e45d-4ffb-841c-b8aad57c7587')
     def test_list_networks_hosted_by_one_dhcp(self):
-        body = self.admin_client.list_dhcp_agent_hosting_network(
+        body = self.admin_networks_client.list_dhcp_agents_on_hosting_network(
             self.network['id'])
         agents = body['agents']
         self.assertIsNotNone(agents)
diff --git a/tempest/api/network/admin/test_routers_dvr.py b/tempest/api/network/admin/test_routers_dvr.py
index 2bc86ad..36cb15f 100644
--- a/tempest/api/network/admin/test_routers_dvr.py
+++ b/tempest/api/network/admin/test_routers_dvr.py
@@ -34,7 +34,7 @@
         # has a distributed attribute.
         super(RoutersTestDVR, cls).resource_setup()
         name = data_utils.rand_name('pretest-check')
-        router = cls.admin_routers_client.create_router(name)
+        router = cls.admin_routers_client.create_router(name=name)
         cls.admin_routers_client.delete_router(router['router']['id'])
         if 'distributed' not in router['router']:
             msg = "'distributed' flag not found. DVR Possibly not enabled"
@@ -53,7 +53,7 @@
         set to True
         """
         name = data_utils.rand_name('router')
-        router = self.admin_routers_client.create_router(name,
+        router = self.admin_routers_client.create_router(name=name,
                                                          distributed=True)
         self.addCleanup(self.admin_routers_client.delete_router,
                         router['router']['id'])
@@ -73,7 +73,7 @@
         as opposed to a "Distributed Virtual Router"
         """
         name = data_utils.rand_name('router')
-        router = self.admin_routers_client.create_router(name,
+        router = self.admin_routers_client.create_router(name=name,
                                                          distributed=False)
         self.addCleanup(self.admin_routers_client.delete_router,
                         router['router']['id'])
@@ -95,7 +95,7 @@
         """
         name = data_utils.rand_name('router')
         # router needs to be in admin state down in order to be upgraded to DVR
-        router = self.admin_routers_client.create_router(name,
+        router = self.admin_routers_client.create_router(name=name,
                                                          distributed=False,
                                                          admin_state_up=False)
         self.addCleanup(self.admin_routers_client.delete_router,
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 8e0c361..71edf74 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -233,7 +233,7 @@
         if enable_snat is not None:
             ext_gw_info['enable_snat'] = enable_snat
         body = cls.routers_client.create_router(
-            router_name, external_gateway_info=ext_gw_info,
+            name=router_name, external_gateway_info=ext_gw_info,
             admin_state_up=admin_state_up, **kwargs)
         router = body['router']
         cls.routers.append(router)
@@ -257,7 +257,7 @@
 
     @classmethod
     def delete_router(cls, router):
-        body = cls.client.list_router_interfaces(router['id'])
+        body = cls.ports_client.list_ports(device_id=router['id'])
         interfaces = body['ports']
         for i in interfaces:
             try:
diff --git a/tempest/api/network/test_floating_ips.py b/tempest/api/network/test_floating_ips.py
index ce9c4be..2156e64 100644
--- a/tempest/api/network/test_floating_ips.py
+++ b/tempest/api/network/test_floating_ips.py
@@ -59,7 +59,6 @@
         cls.router = cls.create_router(data_utils.rand_name('router-'),
                                        external_network_id=cls.ext_net_id)
         cls.create_router_interface(cls.router['id'], cls.subnet['id'])
-        cls.port = list()
         # Create two ports one each for Creation and Updating of floatingIP
         for i in range(2):
             cls.create_port(cls.network)
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index d916891..fa1ed6a 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -12,10 +12,9 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
-import itertools
-
 import netaddr
 import six
+import testtools
 
 from tempest.api.network import base
 from tempest.common import custom_matchers
@@ -376,6 +375,9 @@
 
     @test.attr(type='smoke')
     @test.idempotent_id('af774677-42a9-4e4b-bb58-16fe6a5bc1ec')
+    @test.requires_ext(extension='external-net', service='network')
+    @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."""
         body = self.networks_client.list_networks(**{'router:external': True})
@@ -387,17 +389,12 @@
         self.assertEmpty(nonexternal, "Found non-external networks"
                                       " in filtered list (%s)." % nonexternal)
         self.assertIn(CONF.network.public_network_id, networks)
-
-        subnets_iter = (network['subnets']
-                        for network in body['networks']
-                        if not network['shared'])
-        # subnets_iter is a list (iterator) of lists. This flattens it to a
-        # list of UUIDs
-        public_subnets_iter = itertools.chain(*subnets_iter)
-        body = self.subnets_client.list_subnets()
-        subnets = [sub['id'] for sub in body['subnets']
-                   if sub['id'] in public_subnets_iter]
-        self.assertEmpty(subnets, "Public subnets visible")
+        # 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")
 
 
 class BulkNetworkOpsTestJSON(base.BaseNetworkTest):
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 01afc51..11f7fc6 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -53,7 +53,7 @@
         # as we need to check the response code
         name = data_utils.rand_name('router-')
         create_body = self.routers_client.create_router(
-            name, external_gateway_info={
+            name=name, external_gateway_info={
                 "network_id": CONF.network.public_network_id},
             admin_state_up=False)
         self.addCleanup(self._delete_router, create_body['router']['id'])
@@ -97,7 +97,7 @@
 
         name = data_utils.rand_name('router-')
         create_body = self.admin_routers_client.create_router(
-            name, tenant_id=project_id)
+            name=name, tenant_id=project_id)
         self.addCleanup(self.admin_routers_client.delete_router,
                         create_body['router']['id'])
         self.assertEqual(project_id, create_body['router']['tenant_id'])
@@ -124,7 +124,7 @@
                 'network_id': CONF.network.public_network_id,
                 'enable_snat': enable_snat}
             create_body = self.admin_routers_client.create_router(
-                name, external_gateway_info=external_gateway_info)
+                name=name, external_gateway_info=external_gateway_info)
             self.addCleanup(self.admin_routers_client.delete_router,
                             create_body['router']['id'])
             # Verify snat attributes after router creation
@@ -389,7 +389,7 @@
     def test_create_distributed_router(self):
         name = data_utils.rand_name('router')
         create_body = self.admin_routers_client.create_router(
-            name, distributed=True)
+            name=name, distributed=True)
         self.addCleanup(self._delete_router,
                         create_body['router']['id'],
                         self.admin_routers_client)
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 60e6e6c..f19717e 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -13,6 +13,7 @@
 import six
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -80,8 +81,8 @@
         else:
             self.volume_id_list_without_prefix.append(
                 self.volume['id'])
-        self.admin_volume_client.wait_for_volume_status(
-            self.volume['id'], 'available')
+        waiters.wait_for_volume_status(self.admin_volume_client,
+                                       self.volume['id'], 'available')
 
     @classmethod
     def resource_cleanup(cls):
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index f2bf613..26a5a45 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -15,6 +15,7 @@
 
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -41,18 +42,17 @@
         vol_name = data_utils.rand_name(cls.__name__ + '-Volume')
         cls.name_field = cls.special_fields['name_field']
         params = {cls.name_field: vol_name}
-        cls.volume = \
-            cls.volumes_client.create_volume(**params)['volume']
-        cls.volumes_client.wait_for_volume_status(cls.volume['id'],
-                                                  'available')
+        cls.volume = cls.volumes_client.create_volume(**params)['volume']
+        waiters.wait_for_volume_status(cls.volumes_client,
+                                       cls.volume['id'], 'available')
 
         # Create a test shared snapshot for tests
         snap_name = data_utils.rand_name(cls.__name__ + '-Snapshot')
         params = {cls.name_field: snap_name}
         cls.snapshot = cls.client.create_snapshot(
             volume_id=cls.volume['id'], **params)['snapshot']
-        cls.client.wait_for_snapshot_status(cls.snapshot['id'],
-                                            'available')
+        waiters.wait_for_snapshot_status(cls.client,
+                                         cls.snapshot['id'], 'available')
 
     @classmethod
     def resource_cleanup(cls):
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index c032d9c..7202881 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -15,6 +15,7 @@
 
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -66,12 +67,14 @@
                          "to the requested name")
         self.assertIsNotNone(volume['id'],
                              "Field volume id is empty or not found.")
-        self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume['id'], 'available')
 
         # Update volume with new volume_type
         self.volumes_client.retype_volume(volume['id'],
                                           new_type=volume_types[1]['id'])
-        self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume['id'], 'available')
 
         # Get volume details and Verify
         fetched_volume = self.volumes_client.show_volume(
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index 253a3e1..bdb313f 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -15,6 +15,7 @@
 
 from tempest.api.volume import base
 from tempest.common.utils import data_utils as utils
+from tempest.common import waiters
 from tempest import test
 
 
@@ -35,7 +36,8 @@
         params = {cls.name_field: vol_name}
 
         cls.volume = cls.client.create_volume(**params)['volume']
-        cls.client.wait_for_volume_status(cls.volume['id'], 'available')
+        waiters.wait_for_volume_status(cls.client,
+                                       cls.volume['id'], 'available')
 
     @classmethod
     def resource_cleanup(cls):
@@ -61,7 +63,8 @@
         vol_name = utils.rand_name('Volume')
         params = {self.name_field: vol_name}
         temp_volume = self.client.create_volume(**params)['volume']
-        self.client.wait_for_volume_status(temp_volume['id'], 'available')
+        waiters.wait_for_volume_status(self.client,
+                                       temp_volume['id'], 'available')
 
         return temp_volume
 
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 30c6a15..b09cd2c 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -15,6 +15,7 @@
 
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
 from tempest import test
@@ -50,8 +51,8 @@
         self.addCleanup(self.backups_adm_client.delete_backup,
                         backup['id'])
         self.assertEqual(backup_name, backup['name'])
-        self.admin_volume_client.wait_for_volume_status(
-            self.volume['id'], 'available')
+        waiters.wait_for_volume_status(self.admin_volume_client,
+                                       self.volume['id'], 'available')
         self.backups_adm_client.wait_for_backup_status(backup['id'],
                                                        'available')
 
@@ -74,8 +75,8 @@
         self.assertEqual(backup['id'], restore['backup_id'])
         self.backups_adm_client.wait_for_backup_status(backup['id'],
                                                        'available')
-        self.admin_volume_client.wait_for_volume_status(
-            restore['volume_id'], 'available')
+        waiters.wait_for_volume_status(self.admin_volume_client,
+                                       restore['volume_id'], 'available')
 
     @decorators.skip_because(bug='1455043')
     @test.idempotent_id('a99c54a1-dd80-4724-8a13-13bf58d4068d')
@@ -117,8 +118,8 @@
         self.addCleanup(self.admin_volume_client.delete_volume,
                         restore['volume_id'])
         self.assertEqual(import_backup['id'], restore['backup_id'])
-        self.admin_volume_client.wait_for_volume_status(restore['volume_id'],
-                                                        'available')
+        waiters.wait_for_volume_status(self.admin_volume_client,
+                                       restore['volume_id'], 'available')
 
         # Verify if restored volume is there in volume list
         volumes = self.admin_volume_client.list_volumes()['volumes']
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 82dc2c9..14819e3 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -15,6 +15,7 @@
 
 from tempest.common import compute
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import exceptions
 from tempest.lib import exceptions as lib_exc
@@ -112,7 +113,8 @@
         volume = cls.volumes_client.create_volume(**kwargs)['volume']
 
         cls.volumes.append(volume)
-        cls.volumes_client.wait_for_volume_status(volume['id'], 'available')
+        waiters.wait_for_volume_status(cls.volumes_client,
+                                       volume['id'], 'available')
         return volume
 
     @classmethod
@@ -121,8 +123,8 @@
         snapshot = cls.snapshots_client.create_snapshot(
             volume_id=volume_id, **kwargs)['snapshot']
         cls.snapshots.append(snapshot)
-        cls.snapshots_client.wait_for_snapshot_status(snapshot['id'],
-                                                      'available')
+        waiters.wait_for_snapshot_status(cls.snapshots_client,
+                                         snapshot['id'], 'available')
         return snapshot
 
     # NOTE(afazekas): these create_* and clean_* could be defined
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 7046dcf..866db3d 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -16,6 +16,7 @@
 from testtools import matchers
 
 from tempest.api.volume import base
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -51,8 +52,8 @@
             volume_id=volume['id'])['transfer']
         transfer_id = transfer['id']
         auth_key = transfer['auth_key']
-        self.client.wait_for_volume_status(volume['id'],
-                                           'awaiting-transfer')
+        waiters.wait_for_volume_status(self.client,
+                                       volume['id'], 'awaiting-transfer')
 
         # Get a volume transfer
         body = self.client.show_volume_transfer(transfer_id)['transfer']
@@ -66,7 +67,8 @@
         # Accept a volume transfer by alt_tenant
         body = self.alt_client.accept_volume_transfer(
             transfer_id, auth_key=auth_key)['transfer']
-        self.alt_client.wait_for_volume_status(volume['id'], 'available')
+        waiters.wait_for_volume_status(self.alt_client,
+                                       volume['id'], 'available')
 
     @test.idempotent_id('ab526943-b725-4c07-b875-8e8ef87a2c30')
     def test_create_list_delete_volume_transfer(self):
@@ -78,8 +80,8 @@
         body = self.client.create_volume_transfer(
             volume_id=volume['id'])['transfer']
         transfer_id = body['id']
-        self.client.wait_for_volume_status(volume['id'],
-                                           'awaiting-transfer')
+        waiters.wait_for_volume_status(self.client,
+                                       volume['id'], 'awaiting-transfer')
 
         # List all volume transfers (looking for the one we created)
         body = self.client.list_volume_transfers()['transfers']
@@ -91,7 +93,7 @@
 
         # Delete a volume transfer
         self.client.delete_volume_transfer(transfer_id)
-        self.client.wait_for_volume_status(volume['id'], 'available')
+        waiters.wait_for_volume_status(self.client, volume['id'], 'available')
 
 
 class VolumesV1TransfersTest(VolumesV2TransfersTest):
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 5f9ea7f..9c67579 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -43,7 +43,8 @@
 
         # Create a test shared volume for attach/detach tests
         cls.volume = cls.create_volume()
-        cls.client.wait_for_volume_status(cls.volume['id'], 'available')
+        waiters.wait_for_volume_status(cls.client,
+                                       cls.volume['id'], 'available')
 
     @classmethod
     def resource_cleanup(cls):
@@ -64,9 +65,11 @@
         self.client.attach_volume(self.volume['id'],
                                   instance_uuid=self.server['id'],
                                   mountpoint=mountpoint)
-        self.client.wait_for_volume_status(self.volume['id'], 'in-use')
+        waiters.wait_for_volume_status(self.client,
+                                       self.volume['id'], 'in-use')
         self.client.detach_volume(self.volume['id'])
-        self.client.wait_for_volume_status(self.volume['id'], 'available')
+        waiters.wait_for_volume_status(self.client,
+                                       self.volume['id'], 'available')
 
     @test.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
     @testtools.skipUnless(CONF.volume_feature_enabled.bootable,
@@ -91,10 +94,11 @@
         self.client.attach_volume(self.volume['id'],
                                   instance_uuid=self.server['id'],
                                   mountpoint=mountpoint)
-        self.client.wait_for_volume_status(self.volume['id'], 'in-use')
+        waiters.wait_for_volume_status(self.client,
+                                       self.volume['id'], 'in-use')
         # NOTE(gfidente): added in reverse order because functions will be
         # called in reverse order to the order they are added (LIFO)
-        self.addCleanup(self.client.wait_for_volume_status,
+        self.addCleanup(waiters.wait_for_volume_status, self.client,
                         self.volume['id'],
                         'available')
         self.addCleanup(self.client.detach_volume, self.volume['id'])
@@ -120,7 +124,8 @@
         image_id = body["image_id"]
         self.addCleanup(self.image_client.delete_image, image_id)
         self.image_client.wait_for_image_status(image_id, 'active')
-        self.client.wait_for_volume_status(self.volume['id'], 'available')
+        waiters.wait_for_volume_status(self.client,
+                                       self.volume['id'], 'available')
 
     @test.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
     def test_reserve_unreserve_volume(self):
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index ed1e5c5..1947779 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.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 import test
 
@@ -33,7 +34,8 @@
         self.volume = self.create_volume()
         extend_size = int(self.volume['size']) + 1
         self.client.extend_volume(self.volume['id'], new_size=extend_size)
-        self.client.wait_for_volume_status(self.volume['id'], 'available')
+        waiters.wait_for_volume_status(self.client,
+                                       self.volume['id'], 'available')
         volume = self.client.show_volume(self.volume['id'])['volume']
         self.assertEqual(int(volume['size']), extend_size)
 
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index aa3ef2f..5d83bb0 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -18,6 +18,7 @@
 
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -53,7 +54,7 @@
         volume = self.client.create_volume(**kwargs)['volume']
         self.assertIn('id', volume)
         self.addCleanup(self._delete_volume, volume['id'])
-        self.client.wait_for_volume_status(volume['id'], 'available')
+        waiters.wait_for_volume_status(self.client, volume['id'], 'available')
         self.assertIn(self.name_field, volume)
         self.assertEqual(volume[self.name_field], v_name,
                          "The created volume name is not equal "
@@ -113,7 +114,8 @@
         new_volume = self.client.create_volume(**params)['volume']
         self.assertIn('id', new_volume)
         self.addCleanup(self._delete_volume, new_volume['id'])
-        self.client.wait_for_volume_status(new_volume['id'], 'available')
+        waiters.wait_for_volume_status(self.client,
+                                       new_volume['id'], 'available')
 
         params = {self.name_field: volume[self.name_field],
                   self.descrip_field: volume[self.descrip_field]}
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index c79235a..347877c 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -12,6 +12,7 @@
 
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -37,7 +38,8 @@
     def _detach(self, volume_id):
         """Detach volume."""
         self.volumes_client.detach_volume(volume_id)
-        self.volumes_client.wait_for_volume_status(volume_id, 'available')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume_id, 'available')
 
     def _list_by_param_values_and_assert(self, with_detail=False, **params):
         """list or list_details with given params and validates result."""
@@ -70,9 +72,9 @@
         self.servers_client.attach_volume(
             server['id'], volumeId=self.volume_origin['id'],
             device=mountpoint)
-        self.volumes_client.wait_for_volume_status(self.volume_origin['id'],
-                                                   'in-use')
-        self.addCleanup(self.volumes_client.wait_for_volume_status,
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       self.volume_origin['id'], 'in-use')
+        self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
                         self.volume_origin['id'], 'available')
         self.addCleanup(self.servers_client.detach_volume, server['id'],
                         self.volume_origin['id'])
@@ -171,7 +173,8 @@
         # NOTE(gfidente): size is required also when passing snapshot_id
         volume = self.volumes_client.create_volume(
             snapshot_id=snapshot['id'])['volume']
-        self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume['id'], 'available')
         self.volumes_client.delete_volume(volume['id'])
         self.volumes_client.wait_for_resource_deletion(volume['id'])
         self.cleanup_snapshot(snapshot)
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 03dfd7b..6124676 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -316,7 +316,7 @@
         external_net_id = dict(
             network_id=CONF.network.public_network_id)
         resp_body = routers_admin_client.create_router(
-            router_name,
+            name=router_name,
             external_gateway_info=external_net_id,
             tenant_id=tenant_id)
         return resp_body['router']
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index a0676b6..99933dd 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -462,12 +462,13 @@
 
     def delete(self):
         client = self.routers_client
+        ports_client = self.ports_client
         routers = self.list()
         for router in routers:
             try:
                 rid = router['id']
                 ports = [port for port
-                         in client.list_router_interfaces(rid)['ports']
+                         in ports_client.list_ports(device_id=rid)['ports']
                          if port["device_owner"] == "network:router_interface"]
                 for port in ports:
                     client.remove_router_interface(rid, port_id=port['id'])
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 48c06ff..e3c1c64 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -127,6 +127,7 @@
 from tempest.lib.services.compute import security_group_rules_client
 from tempest.lib.services.compute import security_groups_client
 from tempest.lib.services.compute import servers_client
+from tempest.lib.services.network import ports_client
 from tempest.lib.services.network import subnets_client
 from tempest.services.identity.v2.json import identity_client
 from tempest.services.identity.v2.json import roles_client
@@ -271,6 +272,14 @@
             build_interval=CONF.network.build_interval,
             build_timeout=CONF.network.build_timeout,
             **default_params)
+        self.ports = ports_client.PortsClient(
+            _auth,
+            CONF.network.catalog_type,
+            CONF.network.region or CONF.identity.region,
+            endpoint_type=CONF.network.endpoint_type,
+            build_interval=CONF.network.build_interval,
+            build_timeout=CONF.network.build_timeout,
+            **default_params)
         self.routers = routers_client.RoutersClient(
             _auth,
             CONF.network.catalog_type,
@@ -751,7 +760,7 @@
     n_body = client.routers.list_routers()
     for router in n_body['routers']:
         router_id = router['id']
-        r_body = client.networks.list_router_interfaces(router_id)
+        r_body = client.ports.list_ports(device_id=router_id)
         for port in r_body['ports']:
             if port['network_id'] == network_id:
                 return "qrouter-%s" % router_id
@@ -838,7 +847,7 @@
             LOG.warning("Duplicated router name: %s" % router['name'])
             continue
 
-        client.networks.create_router(router['name'])
+        client.networks.create_router(name=router['name'])
 
 
 def destroy_routers(routers):
@@ -1022,7 +1031,7 @@
         v_name = volume['name']
         body = client.volumes.create_volume(size=size,
                                             display_name=v_name)['volume']
-        client.volumes.wait_for_volume_status(body['id'], 'available')
+        waiters.wait_for_volume_status(client.volumes, body['id'], 'available')
 
 
 def destroy_volumes(volumes):
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 9049886..0ba322d 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -97,7 +97,14 @@
                              ca_certs=ca_certs)
     __, body = raw_http.request(endpoint, 'GET')
     client_dict[service].reset_path()
-    body = json.loads(body)
+    try:
+        body = json.loads(body)
+    except ValueError:
+        LOG.error(
+            'Failed to get a JSON response from unversioned endpoint %s '
+            '(versioned endpoint was %s). Response is:\n%s',
+            endpoint, client_dict[service].base_url, body[:100])
+        raise
     if service == 'keystone':
         versions = map(lambda x: x['id'], body['versions']['values'])
     else:
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 2d2909a..b5c4547 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -98,8 +98,8 @@
         volume = volumes_client.create_volume(
             display_name=volume_name,
             imageRef=image_id)
-        volumes_client.wait_for_volume_status(volume['volume']['id'],
-                                              'available')
+        waiters.wait_for_volume_status(volumes_client,
+                                       volume['volume']['id'], 'available')
 
         bd_map_v2 = [{
             'uuid': volume['volume']['id'],
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index 2ffc92d..5f6c8b8 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -240,7 +240,7 @@
         external_net_id = dict(
             network_id=CONF.network.public_network_id)
         resp_body = self.routers_admin_client.create_router(
-            router_name,
+            name=router_name,
             external_gateway_info=external_net_id,
             tenant_id=tenant_id)
         return resp_body['router']
diff --git a/tempest/common/validation_resources.py b/tempest/common/validation_resources.py
index 84d0143..c3c9a41 100644
--- a/tempest/common/validation_resources.py
+++ b/tempest/common/validation_resources.py
@@ -59,7 +59,9 @@
                 create_ssh_security_group(os, add_rule)
         if validation_resources['floating_ip']:
             floating_client = os.compute_floating_ips_client
-            validation_data.update(floating_client.create_floating_ip())
+            validation_data.update(
+                floating_client.create_floating_ip(
+                    pool=CONF.network.floating_network_name))
     return validation_data
 
 
diff --git a/tempest/lib/common/api_version_utils.py b/tempest/lib/common/api_version_utils.py
index 73cd280..1371b3c 100644
--- a/tempest/lib/common/api_version_utils.py
+++ b/tempest/lib/common/api_version_utils.py
@@ -82,7 +82,7 @@
 
 
 def select_request_microversion(test_min_version, cfg_min_version):
-    """Select microversion from test and configuration.
+    """Select microversion from test and configuration min version.
 
     Compare requested microversion and return the maximum
     microversion out of those.
@@ -101,7 +101,7 @@
 def assert_version_header_matches_request(api_microversion_header_name,
                                           api_microversion,
                                           response_header):
-    """Checks API microversion in resposne header
+    """Checks API microversion in response header
 
     Verify whether microversion is present in response header
     and with specified 'api_microversion' value.
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 7ce05e3..7d2eda0 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -548,7 +548,7 @@
         :param str method: The HTTP verb to use for the request
         :param str headers: Headers to use for the request if none are specifed
                             the headers
-        :param str body: Body to to send with the request
+        :param str body: Body to send with the request
         :rtype: tuple
         :return: a tuple with the first entry containing the response headers
                  and the second the response body
@@ -584,7 +584,7 @@
                              specifed the headers returned from the
                              get_headers() method are used. If the request
                              explicitly requires no headers use an empty dict.
-        :param str body: Body to to send with the request
+        :param str body: Body to send with the request
         :rtype: tuple
         :return: a tuple with the first entry containing the response headers
                  and the second the response body
diff --git a/tempest/lib/services/compute/agents_client.py b/tempest/lib/services/compute/agents_client.py
index 8b11e64..6d3a817 100644
--- a/tempest/lib/services/compute/agents_client.py
+++ b/tempest/lib/services/compute/agents_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import agents as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class AgentsClient(rest_client.RestClient):
+class AgentsClient(base_compute_client.BaseComputeClient):
     """Tests Agents API"""
 
     def list_agents(self, **params):
diff --git a/tempest/lib/services/compute/aggregates_client.py b/tempest/lib/services/compute/aggregates_client.py
index b481674..168126c 100644
--- a/tempest/lib/services/compute/aggregates_client.py
+++ b/tempest/lib/services/compute/aggregates_client.py
@@ -18,9 +18,10 @@
 from tempest.lib.api_schema.response.compute.v2_1 import aggregates as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import base_compute_client
 
 
-class AggregatesClient(rest_client.RestClient):
+class AggregatesClient(base_compute_client.BaseComputeClient):
 
     def list_aggregates(self):
         """Get aggregate list."""
diff --git a/tempest/lib/services/compute/availability_zone_client.py b/tempest/lib/services/compute/availability_zone_client.py
index 00f66d6..a911191 100644
--- a/tempest/lib/services/compute/availability_zone_client.py
+++ b/tempest/lib/services/compute/availability_zone_client.py
@@ -18,9 +18,10 @@
 from tempest.lib.api_schema.response.compute.v2_1 import availability_zone \
     as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class AvailabilityZoneClient(rest_client.RestClient):
+class AvailabilityZoneClient(base_compute_client.BaseComputeClient):
 
     def list_availability_zones(self, detail=False):
         url = 'os-availability-zone'
diff --git a/tempest/lib/services/compute/baremetal_nodes_client.py b/tempest/lib/services/compute/baremetal_nodes_client.py
index d9a712e..06dc369 100644
--- a/tempest/lib/services/compute/baremetal_nodes_client.py
+++ b/tempest/lib/services/compute/baremetal_nodes_client.py
@@ -18,9 +18,10 @@
 from tempest.lib.api_schema.response.compute.v2_1 import baremetal_nodes \
     as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class BaremetalNodesClient(rest_client.RestClient):
+class BaremetalNodesClient(base_compute_client.BaseComputeClient):
     """Tests Baremetal API"""
 
     def list_baremetal_nodes(self, **params):
diff --git a/tempest/lib/services/compute/base_compute_client.py b/tempest/lib/services/compute/base_compute_client.py
index bbadc8f..9161abb 100644
--- a/tempest/lib/services/compute/base_compute_client.py
+++ b/tempest/lib/services/compute/base_compute_client.py
@@ -23,15 +23,14 @@
 class BaseComputeClient(rest_client.RestClient):
     """Base compute service clients class to support microversion.
 
-    This class adds microversion to API request header if same is set
-    and provide interface to select appropriate JSON schema file for
+    This class adds microversion to API request header if that is set
+    and provides interface to select appropriate JSON schema file for
     response validation.
 
-    :param auth_provider: an auth provider object used to wrap requests in
+    :param auth_provider: An auth provider object used to wrap requests in
                           auth
     :param str service: The service name to use for the catalog lookup
     :param str region: The region to use for the catalog lookup
-                                             request with microversion
     :param kwargs: kwargs required by rest_client.RestClient
     """
 
@@ -79,12 +78,12 @@
         for items in schema_versions_info:
             min_version = api_version_request.APIVersionRequest(items['min'])
             max_version = api_version_request.APIVersionRequest(items['max'])
-            # This is case where self.api_microversion is None, which means
+            # This is case where COMPUTE_MICROVERSION is None, which means
             # request without microversion So select base v2.1 schema.
             if version.is_null() and items['min'] is None:
                 schema = items['schema']
                 break
-            # else select appropriate schema as per self.api_microversion
+            # else select appropriate schema as per COMPUTE_MICROVERSION
             elif version.matches(min_version, max_version):
                 schema = items['schema']
                 break
diff --git a/tempest/lib/services/compute/certificates_client.py b/tempest/lib/services/compute/certificates_client.py
index 76d830e..822756c 100644
--- a/tempest/lib/services/compute/certificates_client.py
+++ b/tempest/lib/services/compute/certificates_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import certificates as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class CertificatesClient(rest_client.RestClient):
+class CertificatesClient(base_compute_client.BaseComputeClient):
 
     def show_certificate(self, certificate_id):
         url = "os-certificates/%s" % certificate_id
diff --git a/tempest/lib/services/compute/extensions_client.py b/tempest/lib/services/compute/extensions_client.py
index 85f8f0c..afaf282 100644
--- a/tempest/lib/services/compute/extensions_client.py
+++ b/tempest/lib/services/compute/extensions_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import extensions as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class ExtensionsClient(rest_client.RestClient):
+class ExtensionsClient(base_compute_client.BaseComputeClient):
 
     def list_extensions(self):
         url = 'extensions'
diff --git a/tempest/lib/services/compute/fixed_ips_client.py b/tempest/lib/services/compute/fixed_ips_client.py
index 76ec59f..c25ac2c 100644
--- a/tempest/lib/services/compute/fixed_ips_client.py
+++ b/tempest/lib/services/compute/fixed_ips_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import fixed_ips as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class FixedIPsClient(rest_client.RestClient):
+class FixedIPsClient(base_compute_client.BaseComputeClient):
 
     def show_fixed_ip(self, fixed_ip):
         url = "os-fixed-ips/%s" % fixed_ip
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index 50f1dcc..6869f02 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -22,9 +22,10 @@
 from tempest.lib.api_schema.response.compute.v2_1 import flavors_extra_specs \
     as schema_extra_specs
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class FlavorsClient(rest_client.RestClient):
+class FlavorsClient(base_compute_client.BaseComputeClient):
 
     def list_flavors(self, detail=False, **params):
         url = 'flavors'
diff --git a/tempest/lib/services/compute/floating_ip_pools_client.py b/tempest/lib/services/compute/floating_ip_pools_client.py
index d4a0193..d3af050 100644
--- a/tempest/lib/services/compute/floating_ip_pools_client.py
+++ b/tempest/lib/services/compute/floating_ip_pools_client.py
@@ -18,9 +18,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import floating_ips as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class FloatingIPPoolsClient(rest_client.RestClient):
+class FloatingIPPoolsClient(base_compute_client.BaseComputeClient):
 
     def list_floating_ip_pools(self, params=None):
         """Gets all floating IP Pools list."""
diff --git a/tempest/lib/services/compute/floating_ips_bulk_client.py b/tempest/lib/services/compute/floating_ips_bulk_client.py
index bfcf74b..5f06009 100644
--- a/tempest/lib/services/compute/floating_ips_bulk_client.py
+++ b/tempest/lib/services/compute/floating_ips_bulk_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import floating_ips as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class FloatingIPsBulkClient(rest_client.RestClient):
+class FloatingIPsBulkClient(base_compute_client.BaseComputeClient):
 
     def create_floating_ips_bulk(self, ip_range, pool, interface):
         """Allocate floating IPs in bulk."""
diff --git a/tempest/lib/services/compute/floating_ips_client.py b/tempest/lib/services/compute/floating_ips_client.py
index 2569bf9..03e4894 100644
--- a/tempest/lib/services/compute/floating_ips_client.py
+++ b/tempest/lib/services/compute/floating_ips_client.py
@@ -19,9 +19,10 @@
 from tempest.lib.api_schema.response.compute.v2_1 import floating_ips as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import base_compute_client
 
 
-class FloatingIPsClient(rest_client.RestClient):
+class FloatingIPsClient(base_compute_client.BaseComputeClient):
 
     def list_floating_ips(self, **params):
         """Returns a list of all floating IPs filtered by any parameters."""
diff --git a/tempest/lib/services/compute/hosts_client.py b/tempest/lib/services/compute/hosts_client.py
index 269160e..0143765 100644
--- a/tempest/lib/services/compute/hosts_client.py
+++ b/tempest/lib/services/compute/hosts_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import hosts as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class HostsClient(rest_client.RestClient):
+class HostsClient(base_compute_client.BaseComputeClient):
 
     def list_hosts(self, **params):
         """List all hosts."""
diff --git a/tempest/lib/services/compute/hypervisor_client.py b/tempest/lib/services/compute/hypervisor_client.py
index 2e6df1f..5dcecc9 100644
--- a/tempest/lib/services/compute/hypervisor_client.py
+++ b/tempest/lib/services/compute/hypervisor_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import hypervisors as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class HypervisorClient(rest_client.RestClient):
+class HypervisorClient(base_compute_client.BaseComputeClient):
 
     def list_hypervisors(self, detail=False):
         """List hypervisors information."""
diff --git a/tempest/lib/services/compute/images_client.py b/tempest/lib/services/compute/images_client.py
index 30ff484..4a55ce7 100644
--- a/tempest/lib/services/compute/images_client.py
+++ b/tempest/lib/services/compute/images_client.py
@@ -19,9 +19,10 @@
 from tempest.lib.api_schema.response.compute.v2_1 import images as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import base_compute_client
 
 
-class ImagesClient(rest_client.RestClient):
+class ImagesClient(base_compute_client.BaseComputeClient):
 
     def create_image(self, server_id, **kwargs):
         """Create an image of the original server.
diff --git a/tempest/lib/services/compute/instance_usage_audit_log_client.py b/tempest/lib/services/compute/instance_usage_audit_log_client.py
index 4651b2a..1b94306 100644
--- a/tempest/lib/services/compute/instance_usage_audit_log_client.py
+++ b/tempest/lib/services/compute/instance_usage_audit_log_client.py
@@ -18,9 +18,10 @@
 from tempest.lib.api_schema.response.compute.v2_1 import \
     instance_usage_audit_logs as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class InstanceUsagesAuditLogClient(rest_client.RestClient):
+class InstanceUsagesAuditLogClient(base_compute_client.BaseComputeClient):
 
     def list_instance_usage_audit_logs(self):
         url = 'os-instance_usage_audit_log'
diff --git a/tempest/lib/services/compute/interfaces_client.py b/tempest/lib/services/compute/interfaces_client.py
index e7da5a1..80192a1 100644
--- a/tempest/lib/services/compute/interfaces_client.py
+++ b/tempest/lib/services/compute/interfaces_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import interfaces as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class InterfacesClient(rest_client.RestClient):
+class InterfacesClient(base_compute_client.BaseComputeClient):
 
     def list_interfaces(self, server_id):
         resp, body = self.get('servers/%s/os-interface' % server_id)
diff --git a/tempest/lib/services/compute/keypairs_client.py b/tempest/lib/services/compute/keypairs_client.py
index 3e3cf8d..0361b9d 100644
--- a/tempest/lib/services/compute/keypairs_client.py
+++ b/tempest/lib/services/compute/keypairs_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import keypairs as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class KeyPairsClient(rest_client.RestClient):
+class KeyPairsClient(base_compute_client.BaseComputeClient):
 
     def list_keypairs(self):
         resp, body = self.get("os-keypairs")
diff --git a/tempest/lib/services/compute/limits_client.py b/tempest/lib/services/compute/limits_client.py
index c7eba4e..efe9889 100644
--- a/tempest/lib/services/compute/limits_client.py
+++ b/tempest/lib/services/compute/limits_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import limits as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class LimitsClient(rest_client.RestClient):
+class LimitsClient(base_compute_client.BaseComputeClient):
 
     def show_limits(self):
         resp, body = self.get("limits")
diff --git a/tempest/lib/services/compute/migrations_client.py b/tempest/lib/services/compute/migrations_client.py
index 21bc37a..5eae8aa 100644
--- a/tempest/lib/services/compute/migrations_client.py
+++ b/tempest/lib/services/compute/migrations_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import migrations as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class MigrationsClient(rest_client.RestClient):
+class MigrationsClient(base_compute_client.BaseComputeClient):
 
     def list_migrations(self, **params):
         """List all migrations.
diff --git a/tempest/lib/services/compute/networks_client.py b/tempest/lib/services/compute/networks_client.py
index c0eb5ff..6c8c943 100644
--- a/tempest/lib/services/compute/networks_client.py
+++ b/tempest/lib/services/compute/networks_client.py
@@ -16,9 +16,10 @@
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class NetworksClient(rest_client.RestClient):
+class NetworksClient(base_compute_client.BaseComputeClient):
 
     def list_networks(self):
         resp, body = self.get("os-networks")
diff --git a/tempest/lib/services/compute/quota_classes_client.py b/tempest/lib/services/compute/quota_classes_client.py
index ff4eec0..9dc04ad 100644
--- a/tempest/lib/services/compute/quota_classes_client.py
+++ b/tempest/lib/services/compute/quota_classes_client.py
@@ -18,9 +18,10 @@
 from tempest.lib.api_schema.response.compute.v2_1\
     import quota_classes as classes_schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class QuotaClassesClient(rest_client.RestClient):
+class QuotaClassesClient(base_compute_client.BaseComputeClient):
 
     def show_quota_class_set(self, quota_class_id):
         """List the quota class set for a quota class."""
diff --git a/tempest/lib/services/compute/quotas_client.py b/tempest/lib/services/compute/quotas_client.py
index 697d004..184a3d7 100644
--- a/tempest/lib/services/compute/quotas_client.py
+++ b/tempest/lib/services/compute/quotas_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import quotas as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class QuotasClient(rest_client.RestClient):
+class QuotasClient(base_compute_client.BaseComputeClient):
 
     def show_quota_set(self, tenant_id, user_id=None):
         """List the quota set for a tenant."""
diff --git a/tempest/lib/services/compute/security_group_default_rules_client.py b/tempest/lib/services/compute/security_group_default_rules_client.py
index e5f291c..d57c8e0 100644
--- a/tempest/lib/services/compute/security_group_default_rules_client.py
+++ b/tempest/lib/services/compute/security_group_default_rules_client.py
@@ -18,9 +18,10 @@
 from tempest.lib.api_schema.response.compute.v2_1 import \
     security_group_default_rule as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class SecurityGroupDefaultRulesClient(rest_client.RestClient):
+class SecurityGroupDefaultRulesClient(base_compute_client.BaseComputeClient):
 
     def create_security_default_group_rule(self, **kwargs):
         """Create security group default rule.
diff --git a/tempest/lib/services/compute/security_group_rules_client.py b/tempest/lib/services/compute/security_group_rules_client.py
index c0e1245..c969b81 100644
--- a/tempest/lib/services/compute/security_group_rules_client.py
+++ b/tempest/lib/services/compute/security_group_rules_client.py
@@ -18,9 +18,10 @@
 from tempest.lib.api_schema.response.compute.v2_1 import \
     security_groups as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class SecurityGroupRulesClient(rest_client.RestClient):
+class SecurityGroupRulesClient(base_compute_client.BaseComputeClient):
 
     def create_security_group_rule(self, **kwargs):
         """Create a new security group rule.
diff --git a/tempest/lib/services/compute/security_groups_client.py b/tempest/lib/services/compute/security_groups_client.py
index 4db98c9..6b9c7e1 100644
--- a/tempest/lib/services/compute/security_groups_client.py
+++ b/tempest/lib/services/compute/security_groups_client.py
@@ -20,9 +20,10 @@
     security_groups as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import base_compute_client
 
 
-class SecurityGroupsClient(rest_client.RestClient):
+class SecurityGroupsClient(base_compute_client.BaseComputeClient):
 
     def list_security_groups(self, **params):
         """List all security groups for a user."""
diff --git a/tempest/lib/services/compute/server_groups_client.py b/tempest/lib/services/compute/server_groups_client.py
index ea60e98..e370457 100644
--- a/tempest/lib/services/compute/server_groups_client.py
+++ b/tempest/lib/services/compute/server_groups_client.py
@@ -18,9 +18,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class ServerGroupsClient(rest_client.RestClient):
+class ServerGroupsClient(base_compute_client.BaseComputeClient):
 
     def create_server_group(self, **kwargs):
         """Create the server group.
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 46c4a49..a37f167 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -21,9 +21,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class ServersClient(rest_client.RestClient):
+class ServersClient(base_compute_client.BaseComputeClient):
 
     def __init__(self, auth_provider, service, region,
                  enable_instance_password=True, **kwargs):
diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py
index 06aad77..a190e5f 100644
--- a/tempest/lib/services/compute/services_client.py
+++ b/tempest/lib/services/compute/services_client.py
@@ -19,9 +19,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import services as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class ServicesClient(rest_client.RestClient):
+class ServicesClient(base_compute_client.BaseComputeClient):
 
     def list_services(self, **params):
         url = 'os-services'
diff --git a/tempest/lib/services/compute/snapshots_client.py b/tempest/lib/services/compute/snapshots_client.py
index de776bd..be41957 100644
--- a/tempest/lib/services/compute/snapshots_client.py
+++ b/tempest/lib/services/compute/snapshots_client.py
@@ -19,9 +19,10 @@
 from tempest.lib.api_schema.response.compute.v2_1 import snapshots as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import base_compute_client
 
 
-class SnapshotsClient(rest_client.RestClient):
+class SnapshotsClient(base_compute_client.BaseComputeClient):
 
     def create_snapshot(self, volume_id, **kwargs):
         """Create a snapshot.
diff --git a/tempest/lib/services/compute/tenant_networks_client.py b/tempest/lib/services/compute/tenant_networks_client.py
index 44a97a9..04d8bab 100644
--- a/tempest/lib/services/compute/tenant_networks_client.py
+++ b/tempest/lib/services/compute/tenant_networks_client.py
@@ -16,9 +16,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import tenant_networks
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class TenantNetworksClient(rest_client.RestClient):
+class TenantNetworksClient(base_compute_client.BaseComputeClient):
 
     def list_tenant_networks(self):
         resp, body = self.get("os-tenant-networks")
diff --git a/tempest/lib/services/compute/tenant_usages_client.py b/tempest/lib/services/compute/tenant_usages_client.py
index e8da465..5a748c7 100644
--- a/tempest/lib/services/compute/tenant_usages_client.py
+++ b/tempest/lib/services/compute/tenant_usages_client.py
@@ -18,9 +18,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import tenant_usages
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class TenantUsagesClient(rest_client.RestClient):
+class TenantUsagesClient(base_compute_client.BaseComputeClient):
 
     def list_tenant_usages(self, **params):
         url = 'os-simple-tenant-usage'
diff --git a/tempest/lib/services/compute/versions_client.py b/tempest/lib/services/compute/versions_client.py
index ed82c74..eb4e7e9 100644
--- a/tempest/lib/services/compute/versions_client.py
+++ b/tempest/lib/services/compute/versions_client.py
@@ -19,9 +19,10 @@
 
 from tempest.lib.api_schema.response.compute.v2_1 import versions as schema
 from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
 
 
-class VersionsClient(rest_client.RestClient):
+class VersionsClient(base_compute_client.BaseComputeClient):
 
     def _get_base_version_url(self):
         # NOTE: The URL which is got from keystone's catalog contains
diff --git a/tempest/lib/services/compute/volumes_client.py b/tempest/lib/services/compute/volumes_client.py
index 45a44de..41d9af2 100644
--- a/tempest/lib/services/compute/volumes_client.py
+++ b/tempest/lib/services/compute/volumes_client.py
@@ -19,9 +19,10 @@
 from tempest.lib.api_schema.response.compute.v2_1 import volumes as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import base_compute_client
 
 
-class VolumesClient(rest_client.RestClient):
+class VolumesClient(base_compute_client.BaseComputeClient):
 
     def list_volumes(self, detail=False, **params):
         """List all the volumes created."""
diff --git a/tempest/lib/services/network/networks_client.py b/tempest/lib/services/network/networks_client.py
index 0926634..24c2ec5 100644
--- a/tempest/lib/services/network/networks_client.py
+++ b/tempest/lib/services/network/networks_client.py
@@ -45,3 +45,7 @@
         """
         uri = '/networks'
         return self.create_resource(uri, kwargs)
+
+    def list_dhcp_agents_on_hosting_network(self, network_id):
+        uri = '/networks/%s/dhcp-agents' % network_id
+        return self.list_resources(uri)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index c2b0bf4..1217dc9 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -262,7 +262,7 @@
         return server
 
     def create_volume(self, size=None, name=None, snapshot_id=None,
-                      imageRef=None, volume_type=None, wait_on_delete=True):
+                      imageRef=None, volume_type=None):
         if name is None:
             name = data_utils.rand_name(self.__class__.__name__)
         kwargs = {'display_name': name,
@@ -273,24 +273,18 @@
             kwargs.update({'size': size})
         volume = self.volumes_client.create_volume(**kwargs)['volume']
 
-        if wait_on_delete:
-            self.addCleanup(self.volumes_client.wait_for_resource_deletion,
-                            volume['id'])
-            self.addCleanup(self.delete_wrapper,
-                            self.volumes_client.delete_volume, volume['id'])
-        else:
-            self.addCleanup_with_wait(
-                waiter_callable=self.volumes_client.wait_for_resource_deletion,
-                thing_id=volume['id'], thing_id_param='id',
-                cleanup_callable=self.delete_wrapper,
-                cleanup_args=[self.volumes_client.delete_volume, volume['id']])
+        self.addCleanup(self.volumes_client.wait_for_resource_deletion,
+                        volume['id'])
+        self.addCleanup(self.delete_wrapper,
+                        self.volumes_client.delete_volume, volume['id'])
 
         # NOTE(e0ne): Cinder API v2 uses name instead of display_name
         if 'display_name' in volume:
             self.assertEqual(name, volume['display_name'])
         else:
             self.assertEqual(name, volume['name'])
-        self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume['id'], 'available')
         # The volume retrieved on creation has a non-up-to-date status.
         # Retrieval after it becomes active ensures correct details.
         volume = self.volumes_client.show_volume(volume['id'])['volume']
@@ -485,8 +479,8 @@
                 self.addCleanup(
                     self.delete_wrapper, self.snapshots_client.delete_snapshot,
                     snapshot_id)
-                self.snapshots_client.wait_for_snapshot_status(snapshot_id,
-                                                               'available')
+                waiters.wait_for_snapshot_status(self.snapshots_client,
+                                                 snapshot_id, 'available')
 
         image_name = snapshot_image['name']
         self.assertEqual(name, image_name)
@@ -499,14 +493,16 @@
             server['id'], volumeId=volume_to_attach['id'], device='/dev/%s'
             % CONF.compute.volume_device_name)['volumeAttachment']
         self.assertEqual(volume_to_attach['id'], volume['id'])
-        self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume['id'], 'in-use')
 
         # Return the updated volume after the attachment
         return self.volumes_client.show_volume(volume['id'])['volume']
 
     def nova_volume_detach(self, server, volume):
         self.servers_client.detach_volume(server['id'], volume['id'])
-        self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume['id'], 'available')
 
         volume = self.volumes_client.show_volume(volume['id'])['volume']
         self.assertEqual('available', volume['status'])
@@ -607,6 +603,8 @@
     def create_floating_ip(self, thing, pool_name=None):
         """Create a floating IP and associates to a server on Nova"""
 
+        if not pool_name:
+            pool_name = CONF.network.floating_network_name
         floating_ip = (self.compute_floating_ips_client.
                        create_floating_ip(pool=pool_name)['floating_ip'])
         self.addCleanup(self.delete_wrapper,
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index dcb095b..a9f2dff 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -119,7 +119,7 @@
     @test.idempotent_id('7fff3fb3-91d8-4fd0-bd7d-0204f1f180ba')
     @test.attr(type='smoke')
     @test.services('compute', 'network')
-    def test_server_basicops(self):
+    def test_server_basic_ops(self):
         keypair = self.create_keypair()
         self.security_group = self._create_security_group()
         security_groups = [{'name': self.security_group['name']}]
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 6121a90..e7223c7 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -19,6 +19,7 @@
 import testtools
 
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import exceptions
 from tempest.lib import decorators
@@ -67,14 +68,15 @@
             self.snapshots_client.delete_snapshot(snapshot['id'])
             try:
                 while self.snapshots_client.show_snapshot(
-                    snapshot['id'])['snapshot']:
+                        snapshot['id'])['snapshot']:
                     time.sleep(1)
             except lib_exc.NotFound:
                 pass
         self.addCleanup(cleaner)
-        self.volumes_client.wait_for_volume_status(volume['id'], 'available')
-        self.snapshots_client.wait_for_snapshot_status(snapshot['id'],
-                                                       'available')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume['id'], 'available')
+        waiters.wait_for_snapshot_status(self.snapshots_client,
+                                         snapshot['id'], 'available')
         self.assertEqual(snapshot_name, snapshot['display_name'])
         return snapshot
 
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 71bb50e..25d825a 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -87,7 +87,8 @@
         self.addCleanup(
             self.snapshots_client.wait_for_resource_deletion, snap['id'])
         self.addCleanup(self.snapshots_client.delete_snapshot, snap['id'])
-        self.snapshots_client.wait_for_snapshot_status(snap['id'], 'available')
+        waiters.wait_for_snapshot_status(self.snapshots_client,
+                                         snap['id'], 'available')
 
         # NOTE(e0ne): Cinder API v2 uses name instead of display_name
         if 'display_name' in snap:
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index bcef36b..5080657 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -13,7 +13,6 @@
 import time
 
 from tempest import exceptions
-from tempest.lib.common.utils import misc
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.network import base
 
@@ -55,48 +54,3 @@
         except lib_exc.NotFound:
             return True
         return False
-
-    def wait_for_resource_status(self, fetch, status, interval=None,
-                                 timeout=None):
-        """Waits for a network resource to reach a status
-
-        @param fetch: the callable to be used to query the resource status
-        @type fecth: callable that takes no parameters and returns the resource
-        @param status: the status that the resource has to reach
-        @type status: String
-        @param interval: the number of seconds to wait between each status
-          query
-        @type interval: Integer
-        @param timeout: the maximum number of seconds to wait for the resource
-          to reach the desired status
-        @type timeout: Integer
-        """
-        if not interval:
-            interval = self.build_interval
-        if not timeout:
-            timeout = self.build_timeout
-        start_time = time.time()
-
-        while time.time() - start_time <= timeout:
-            resource = fetch()
-            if resource['status'] == status:
-                return
-            time.sleep(interval)
-
-        # At this point, the wait has timed out
-        message = 'Resource %s' % (str(resource))
-        message += ' failed to reach status %s' % status
-        message += ' (current: %s)' % resource['status']
-        message += ' within the required time %s' % timeout
-        caller = misc.find_test_caller()
-        if caller:
-            message = '(%s) %s' % (caller, message)
-        raise exceptions.TimeoutException(message)
-
-    def list_router_interfaces(self, uuid):
-        uri = '/ports?device_id=%s' % uuid
-        return self.list_resources(uri)
-
-    def list_dhcp_agent_hosting_network(self, network_id):
-        uri = '/networks/%s/dhcp-agents' % network_id
-        return self.list_resources(uri)
diff --git a/tempest/services/network/json/routers_client.py b/tempest/services/network/json/routers_client.py
index 18442cf..725dd76 100644
--- a/tempest/services/network/json/routers_client.py
+++ b/tempest/services/network/json/routers_client.py
@@ -15,10 +15,13 @@
 
 class RoutersClient(base.BaseNetworkClient):
 
-    def create_router(self, name, admin_state_up=True, **kwargs):
+    def create_router(self, **kwargs):
+        """Create a router.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-networking-v2-ext.html#createRouter
+        """
         post_body = {'router': kwargs}
-        post_body['router']['name'] = name
-        post_body['router']['admin_state_up'] = admin_state_up
         uri = '/routers'
         return self.create_resource(uri, post_body)
 
diff --git a/tempest/services/network/resources.py b/tempest/services/network/resources.py
index 5512075..e78fcfe 100644
--- a/tempest/services/network/resources.py
+++ b/tempest/services/network/resources.py
@@ -14,9 +14,13 @@
 #    under the License.
 
 import abc
+import time
 
 import six
 
+from tempest import exceptions
+from tempest.lib.common.utils import misc
+
 
 class AttributeDict(dict):
     """Provide attribute access (dict.key) to dictionary values."""
@@ -67,7 +71,35 @@
             self.refresh()
             return self
 
-        return self.client.wait_for_resource_status(helper_get, status)
+        return self.wait_for_resource_status(helper_get, status)
+
+    def wait_for_resource_status(self, fetch, status):
+        """Waits for a network resource to reach a status
+
+        @param fetch: the callable to be used to query the resource status
+        @type fecth: callable that takes no parameters and returns the resource
+        @param status: the status that the resource has to reach
+        @type status: String
+        """
+        interval = self.build_interval
+        timeout = self.build_timeout
+        start_time = time.time()
+
+        while time.time() - start_time <= timeout:
+            resource = fetch()
+            if resource['status'] == status:
+                return
+            time.sleep(interval)
+
+        # At this point, the wait has timed out
+        message = 'Resource %s' % (str(resource))
+        message += ' failed to reach status %s' % status
+        message += ' (current: %s)' % resource['status']
+        message += ' within the required time %s' % timeout
+        caller = misc.find_test_caller()
+        if caller:
+            message = '(%s) %s' % (caller, message)
+        raise exceptions.TimeoutException(message)
 
 
 class DeletableNetwork(DeletableResource):
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 9ad8c27..78bda5d 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -210,7 +210,7 @@
     :param contents: a string or a file like object to read object data
                      from; if None, a zero-byte put will be done
     :param chunk_size: chunk size of data to write; it defaults to 65536;
-                       used only if the the contents object has a 'read'
+                       used only if the contents object has a 'read'
                        method, eg. file-like objects, ignored otherwise
     :param headers: additional headers to include in the request, if any
     :param query_string: if set will be appended with '?' to generated path
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index 6019cf5..ea5dbe5 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -151,42 +151,6 @@
         self.expected_success(204, resp.status)
         return rest_client.ResponseBody(resp)
 
-    def wait_for_resource_status(self, stack_identifier, resource_name,
-                                 status, failure_pattern='^.*_FAILED$'):
-        """Waits for a Resource to reach a given status."""
-        start = int(time.time())
-        fail_regexp = re.compile(failure_pattern)
-
-        while True:
-            try:
-                body = self.show_resource(
-                    stack_identifier, resource_name)['resource']
-            except lib_exc.NotFound:
-                # ignore this, as the resource may not have
-                # been created yet
-                pass
-            else:
-                resource_name = body['resource_name']
-                resource_status = body['resource_status']
-                if resource_status == status:
-                    return
-                if fail_regexp.search(resource_status):
-                    raise exceptions.StackResourceBuildErrorException(
-                        resource_name=resource_name,
-                        stack_identifier=stack_identifier,
-                        resource_status=resource_status,
-                        resource_status_reason=body['resource_status_reason'])
-
-            if int(time.time()) - start >= self.build_timeout:
-                message = ('Resource %s failed to reach %s status '
-                           '(current %s) within the required time (%s s).' %
-                           (resource_name,
-                            status,
-                            resource_status,
-                            self.build_timeout))
-                raise exceptions.TimeoutException(message)
-            time.sleep(self.build_interval)
-
     def wait_for_stack_status(self, stack_identifier, status,
                               failure_pattern='^.*_FAILED$'):
         """Waits for a Stack to reach a given status."""
diff --git a/tempest/services/volume/base/base_snapshots_client.py b/tempest/services/volume/base/base_snapshots_client.py
index 5e5637a..68503dd 100644
--- a/tempest/services/volume/base/base_snapshots_client.py
+++ b/tempest/services/volume/base/base_snapshots_client.py
@@ -10,13 +10,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import time
-
 from oslo_log import log as logging
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest import exceptions
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 
@@ -70,43 +67,6 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    # NOTE(afazekas): just for the wait function
-    def _get_snapshot_status(self, snapshot_id):
-        body = self.show_snapshot(snapshot_id)['snapshot']
-        status = body['status']
-        # NOTE(afazekas): snapshot can reach an "error"
-        # state in a "normal" lifecycle
-        if (status == 'error'):
-            raise exceptions.SnapshotBuildErrorException(
-                snapshot_id=snapshot_id)
-
-        return status
-
-    # NOTE(afazkas): Wait reinvented again. It is not in the correct layer
-    def wait_for_snapshot_status(self, snapshot_id, status):
-        """Waits for a Snapshot to reach a given status."""
-        start_time = time.time()
-        old_value = value = self._get_snapshot_status(snapshot_id)
-        while True:
-            dtime = time.time() - start_time
-            time.sleep(self.build_interval)
-            if value != old_value:
-                LOG.info('Value transition from "%s" to "%s"'
-                         'in %d second(s).', old_value,
-                         value, dtime)
-            if (value == status):
-                return value
-
-            if dtime > self.build_timeout:
-                message = ('Time Limit Exceeded! (%ds)'
-                           'while waiting for %s, '
-                           'but we got %s.' %
-                           (self.build_timeout, status, value))
-                raise exceptions.TimeoutException(message)
-            time.sleep(self.build_interval)
-            old_value = value
-            value = self._get_snapshot_status(snapshot_id)
-
     def delete_snapshot(self, snapshot_id):
         """Delete Snapshot."""
         resp, body = self.delete("snapshots/%s" % str(snapshot_id))
diff --git a/tempest/services/volume/base/base_volumes_client.py b/tempest/services/volume/base/base_volumes_client.py
index f638bb6..4344802 100644
--- a/tempest/services/volume/base/base_volumes_client.py
+++ b/tempest/services/volume/base/base_volumes_client.py
@@ -17,7 +17,6 @@
 import six
 from six.moves.urllib import parse as urllib
 
-from tempest.common import waiters
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 
@@ -148,10 +147,6 @@
         self.expected_success(202, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def wait_for_volume_status(self, volume_id, status):
-        """Waits for a Volume to reach a given status."""
-        waiters.wait_for_volume_status(self, volume_id, status)
-
     def is_resource_deleted(self, id):
         try:
             self.show_volume(id)
diff --git a/tempest/stress/cleanup.py b/tempest/stress/cleanup.py
index 1c1fb46..3b0a937 100644
--- a/tempest/stress/cleanup.py
+++ b/tempest/stress/cleanup.py
@@ -88,8 +88,8 @@
     LOG.info("Cleanup::remove %s snapshots" % len(snaps))
     for v in snaps:
         try:
-            admin_manager.snapshots_client.\
-                wait_for_snapshot_status(v['id'], 'available')
+            waiters.wait_for_snapshot_status(
+                admin_manager.snapshots_client, v['id'], 'available')
             admin_manager.snapshots_client.delete_snapshot(v['id'])
         except Exception:
             pass
@@ -105,8 +105,8 @@
     LOG.info("Cleanup::remove %s volumes" % len(vols))
     for v in vols:
         try:
-            admin_manager.volumes_client.\
-                wait_for_volume_status(v['id'], 'available')
+            waiters.wait_for_volume_status(
+                admin_manager.volumes_client, v['id'], 'available')
             admin_manager.volumes_client.delete_volume(v['id'])
         except Exception:
             pass
diff --git a/tempest/test.py b/tempest/test.py
index fe3c770..6ba4962 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -611,13 +611,25 @@
                 'dhcp': dhcp}
 
     @classmethod
-    def get_tenant_network(cls):
+    def get_tenant_network(cls, credentials_type='primary'):
         """Get the network to be used in testing
 
+        :param credentials_type: The type of credentials for which to get the
+                                 tenant network
+
         :return: network dict including 'id' and 'name'
         """
+        # Get a manager for the given credentials_type, but at least
+        # always fall back on getting the manager for primary credentials
+        if isinstance(credentials_type, six.string_types):
+            manager = cls.get_client_manager(credential_type=credentials_type)
+        elif isinstance(credentials_type, list):
+            manager = cls.get_client_manager(roles=credentials_type[1:])
+        else:
+            manager = cls.get_client_manager()
+
         # Make sure cred_provider exists and get a network client
-        networks_client = cls.get_client_manager().compute_networks_client
+        networks_client = manager.compute_networks_client
         cred_provider = cls._get_credentials_provider()
         # In case of nova network, isolated tenants are not able to list the
         # network configured in fixed_network_name, even if they can use it
diff --git a/tempest/tests/cmd/test_javelin.py b/tempest/tests/cmd/test_javelin.py
index dc7b434..2d0256a 100644
--- a/tempest/tests/cmd/test_javelin.py
+++ b/tempest/tests/cmd/test_javelin.py
@@ -213,8 +213,8 @@
                                                 name=self.fake_object['name'],
                                                 ip_version=fake_version)
 
-    def test_create_volumes(self):
-
+    @mock.patch("tempest.common.waiters.wait_for_volume_status")
+    def test_create_volumes(self, mock_wait_for_volume_status):
         self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
                                               return_value=self.fake_client))
         self.useFixture(mockpatch.PatchObject(javelin, "_get_volume_by_name",
@@ -228,12 +228,12 @@
         mocked_function.assert_called_once_with(
             size=self.fake_object['gb'],
             display_name=self.fake_object['name'])
-        mocked_function = self.fake_client.volumes.wait_for_volume_status
-        mocked_function.assert_called_once_with(
-            self.fake_object.body['volume']['id'],
+        mock_wait_for_volume_status.assert_called_once_with(
+            self.fake_client.volumes, self.fake_object.body['volume']['id'],
             'available')
 
-    def test_create_volume_existing(self):
+    @mock.patch("tempest.common.waiters.wait_for_volume_status")
+    def test_create_volume_existing(self, mock_wait_for_volume_status):
         self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
                                               return_value=self.fake_client))
         self.useFixture(mockpatch.PatchObject(javelin, "_get_volume_by_name",
@@ -245,8 +245,7 @@
 
         mocked_function = self.fake_client.volumes.create_volume
         self.assertFalse(mocked_function.called)
-        mocked_function = self.fake_client.volumes.wait_for_volume_status
-        self.assertFalse(mocked_function.called)
+        self.assertFalse(mock_wait_for_volume_status.called)
 
     def test_create_router(self):
 
@@ -257,7 +256,7 @@
         javelin.create_routers([self.fake_object])
 
         mocked_function = self.fake_client.networks.create_router
-        mocked_function.assert_called_once_with(self.fake_object['name'])
+        mocked_function.assert_called_once_with(name=self.fake_object['name'])
 
     def test_create_router_existing(self):
         self.fake_client.routers.list_routers.return_value = {
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 193abc7..dc0ba6f 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -82,6 +82,28 @@
         self.assertIn('v2.0', versions)
         self.assertIn('v3.0', versions)
 
+    def test_get_versions_invalid_response(self):
+        # When the response doesn't contain a JSON response, an error is
+        # logged.
+        mock_log_error = self.useFixture(mockpatch.PatchObject(
+            verify_tempest_config.LOG, 'error')).mock
+
+        self.useFixture(mockpatch.PatchObject(
+            verify_tempest_config, '_get_unversioned_endpoint'))
+
+        # Simulated response is not JSON.
+        sample_body = (
+            '<html><head>Sample Response</head><body>This is the sample page '
+            'for the web server. Why are you requesting it?</body></html>')
+        self.useFixture(mockpatch.Patch('httplib2.Http.request',
+                                        return_value=(None, sample_body)))
+
+        # service value doesn't matter, just needs to match what
+        # _get_api_versions puts in its client_dict.
+        self.assertRaises(ValueError, verify_tempest_config._get_api_versions,
+                          os=mock.MagicMock(), service='keystone')
+        self.assertTrue(mock_log_error.called)
+
     def test_verify_api_versions(self):
         api_services = ['cinder', 'glance', 'keystone']
         fake_os = mock.MagicMock()
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
index 6a01490..55f0c4e 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -251,7 +251,7 @@
         """Test empty alternate auth data with no effect
 
         Assert that when alt_part is defined, no auth_data is provided,
-        and the the corresponding original request element was not going to
+        and the corresponding original request element was not going to
         be changed anyways, and exception is raised
         """
         filters = {
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
index 558445d..07b577c 100644
--- a/tempest/tests/lib/test_decorators.py
+++ b/tempest/tests/lib/test_decorators.py
@@ -109,7 +109,7 @@
         t = TestFoo('test_foo')
         if expected_to_skip:
             self.assertRaises(testtools.TestCase.skipException,
-                              t.test_foo())
+                              t.test_foo)
         else:
             try:
                 t.test_foo()
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
new file mode 100644
index 0000000..dc355b4
--- /dev/null
+++ b/tempest/tests/test_base_test.py
@@ -0,0 +1,110 @@
+# Copyright 2016 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
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import mock
+
+from tempest import clients
+from tempest.common import credentials_factory as credentials
+from tempest.common import fixed_network
+from tempest import config
+from tempest import test
+from tempest.tests import base
+from tempest.tests import fake_config
+
+
+class TestBaseTestCase(base.TestCase):
+    def setUp(self):
+        super(TestBaseTestCase, self).setUp()
+        self.useFixture(fake_config.ConfigFixture())
+        self.fixed_network_name = 'fixed-net'
+        config.CONF.compute.fixed_network_name = self.fixed_network_name
+        config.CONF.service_available.neutron = True
+
+    @mock.patch.object(test.BaseTestCase, 'get_client_manager')
+    @mock.patch.object(test.BaseTestCase, '_get_credentials_provider')
+    @mock.patch.object(fixed_network, 'get_tenant_network')
+    def test_get_tenant_network(self, mock_gtn, mock_gprov, mock_gcm):
+        net_client = mock.Mock()
+        mock_prov = mock.Mock()
+        mock_gcm.return_value.compute_networks_client = net_client
+        mock_gprov.return_value = mock_prov
+
+        test.BaseTestCase.get_tenant_network()
+
+        mock_gcm.assert_called_once_with(credential_type='primary')
+        mock_gprov.assert_called_once_with()
+        mock_gtn.assert_called_once_with(mock_prov, net_client,
+                                         self.fixed_network_name)
+
+    @mock.patch.object(test.BaseTestCase, 'get_client_manager')
+    @mock.patch.object(test.BaseTestCase, '_get_credentials_provider')
+    @mock.patch.object(fixed_network, 'get_tenant_network')
+    @mock.patch.object(test.BaseTestCase, 'get_identity_version')
+    @mock.patch.object(credentials, 'is_admin_available')
+    @mock.patch.object(clients, 'Manager')
+    def test_get_tenant_network_with_nova_net(self, mock_man, mock_iaa,
+                                              mock_giv, mock_gtn, mock_gcp,
+                                              mock_gcm):
+        config.CONF.service_available.neutron = False
+        mock_prov = mock.Mock()
+        mock_admin_man = mock.Mock()
+        mock_iaa.return_value = True
+        mock_gcp.return_value = mock_prov
+        mock_man.return_value = mock_admin_man
+
+        test.BaseTestCase.get_tenant_network()
+
+        mock_man.assert_called_once_with(
+            mock_prov.get_admin_creds.return_value)
+        mock_iaa.assert_called_once_with(
+            identity_version=mock_giv.return_value)
+        mock_gcp.assert_called_once_with()
+        mock_gtn.assert_called_once_with(
+            mock_prov, mock_admin_man.compute_networks_client,
+            self.fixed_network_name)
+
+    @mock.patch.object(test.BaseTestCase, 'get_client_manager')
+    @mock.patch.object(test.BaseTestCase, '_get_credentials_provider')
+    @mock.patch.object(fixed_network, 'get_tenant_network')
+    def test_get_tenant_network_with_alt_creds(self, mock_gtn, mock_gprov,
+                                               mock_gcm):
+        net_client = mock.Mock()
+        mock_prov = mock.Mock()
+        mock_gcm.return_value.compute_networks_client = net_client
+        mock_gprov.return_value = mock_prov
+
+        test.BaseTestCase.get_tenant_network(credentials_type='alt')
+
+        mock_gcm.assert_called_once_with(credential_type='alt')
+        mock_gprov.assert_called_once_with()
+        mock_gtn.assert_called_once_with(mock_prov, net_client,
+                                         self.fixed_network_name)
+
+    @mock.patch.object(test.BaseTestCase, 'get_client_manager')
+    @mock.patch.object(test.BaseTestCase, '_get_credentials_provider')
+    @mock.patch.object(fixed_network, 'get_tenant_network')
+    def test_get_tenant_network_with_role_creds(self, mock_gtn, mock_gprov,
+                                                mock_gcm):
+        net_client = mock.Mock()
+        mock_prov = mock.Mock()
+        mock_gcm.return_value.compute_networks_client = net_client
+        mock_gprov.return_value = mock_prov
+        creds = ['foo_type', 'role1']
+
+        test.BaseTestCase.get_tenant_network(credentials_type=creds)
+
+        mock_gcm.assert_called_once_with(roles=['role1'])
+        mock_gprov.assert_called_once_with()
+        mock_gtn.assert_called_once_with(mock_prov, net_client,
+                                         self.fixed_network_name)