Merge "Cinder verifies getting volume list with marker"
diff --git a/HACKING.rst b/HACKING.rst
index 0962f80..b82f8c9 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -18,6 +18,8 @@
 - [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
+- [T112] Check that tempest.lib should not import local tempest code
 - [N322] Method's default argument shouldn't be mutable
 
 Test Data/Configuration
@@ -212,9 +214,9 @@
 things to watch out for to try to avoid issues when running your tests in
 parallel.
 
-- Resources outside of a tenant scope still have the potential to conflict. This
+- Resources outside of a project scope still have the potential to conflict. This
   is a larger concern for the admin tests since most resources and actions that
-  require admin privileges are outside of tenants.
+  require admin privileges are outside of projects.
 
 - Races between methods in the same class are not a problem because
   parallelization in tempest is at the test class level, but if there is a json
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/REVIEWING.rst b/REVIEWING.rst
index f7334ad..bd6018d 100644
--- a/REVIEWING.rst
+++ b/REVIEWING.rst
@@ -2,7 +2,7 @@
 ======================
 
 To start read the `OpenStack Common Review Checklist
-<https://wiki.openstack.org/wiki/ReviewChecklist#Common_Review_Checklist>`_
+<http://docs.openstack.org/infra/manual/developers.html#peer-review>`_
 
 
 Ensuring code is executed
diff --git a/data/tempest-plugins-registry.header b/data/tempest-plugins-registry.header
new file mode 100644
index 0000000..9821e8e
--- /dev/null
+++ b/data/tempest-plugins-registry.header
@@ -0,0 +1,23 @@
+..
+  Note to patch submitters: this file is covered by a periodic proposal
+  job.  You should edit the files data/tempest-plugins-registry.footer
+  and data/tempest-plugins-registry.header instead of this one.
+
+==========================
+ Tempest Plugin Registry
+==========================
+
+Since we've created the external plugin mechanism, it's gotten used by
+a lot of projects. The following is a list of plugins that currently
+exist.
+
+Detected Plugins
+================
+
+The following are plugins that a script has found in the openstack/
+namespace, which includes but is not limited to official OpenStack
+projects.
+
++----------------------------+-------------------------------------------------------------------------+
+|Plugin Name                 |URL                                                                      |
++----------------------------+-------------------------------------------------------------------------+
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 524c0fa..245386b 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -17,7 +17,7 @@
 in the tempest.conf file. These options are clearly labelled in the ``identity``
 section and let you specify a set of credentials for a regular user, a global
 admin user, and an alternate user, consisting of a username, password, and
-project/tenant name.
+project name.
 
 The other method to provide credentials is using the accounts.yaml file. This
 file is used to specify an arbitrary number of users available to run tests
@@ -59,13 +59,13 @@
 Dynamic Credentials (formerly known as Tenant isolation) was originally created
 to enable running Tempest in parallel.  For each test class it creates a unique
 set of user credentials to use for the tests in the class. It can create up to
-three sets of username, password, and tenant/project names for a primary user,
-an admin user, and an alternate user.  To enable and use dynamic credentials you
+three sets of username, password, and project names for a primary user,
+an admin user, and an alternate user. To enable and use dynamic credentials you
 only need to configure two things:
 
  #. A set of admin credentials with permissions to create users and
-    tenants/projects. This is specified in the ``auth`` section with the
-    ``admin_username``, ``admin_tenant_name``, ``admin_domain_name`` and
+    projects. This is specified in the ``auth`` section with the
+    ``admin_username``, ``admin_project_name``, ``admin_domain_name`` and
     ``admin_password`` options
  #. To enable dynamic credentials in the ``auth`` section with the
     ``use_dynamic_credentials`` option.
@@ -112,7 +112,7 @@
  #. Set ``use_dynamic_credentials = False`` in the ``auth`` group
 
 It is worth pointing out that each set of credentials in the accounts.yaml
-should have a unique tenant. This is required to provide proper isolation
+should have a unique project. This is required to provide proper isolation
 to the tests using the credentials, and failure to do this will likely cause
 unexpected failures in some tests.
 
@@ -123,7 +123,7 @@
 removed in a future release.**
 
 When Tempest was refactored to allow for locking test accounts, the original
-non-tenant isolated case was converted to internally work similarly to the
+non-project isolated case was converted to internally work similarly to the
 accounts.yaml file. This mechanism was then called the legacy test accounts
 provider. To use the legacy test accounts provider you can specify the sets of
 credentials in the configuration file as detailed above with following nine
@@ -131,13 +131,13 @@
 
  #. ``username``
  #. ``password``
- #. ``tenant_name``
+ #. ``project_name``
  #. ``admin_username``
  #. ``admin_password``
- #. ``admin_tenant_name``
+ #. ``admin_project_name``
  #. ``alt_username``
  #. ``alt_password``
- #. ``alt_tenant_name``
+ #. ``alt_project_name``
 
 If using Identity API v3, use the ``domain_name`` option to specify a
 domain other than the default domain.  The ``auth_version`` setting is
@@ -246,11 +246,26 @@
 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
 just specify a single network name/label to use for all server creations. The
-limitation with this is that all tenants/projects and users must be able to see
+limitation with this is that all projects and users must be able to see
 that network name/label if they are to perform a network list and be able to use
 it.
 
@@ -272,10 +287,10 @@
 """""""""""""
 If you are using an accounts file to provide credentials for running Tempest
 then you can leverage it to also specify which network should be used with
-server creations on a per tenant/project and user pair basis. This provides
+server creations on a per project and user pair basis. This provides
 the necessary flexibility to work with more intricate networking configurations
 by enabling the user to specify exactly which network to use for which
-tenants/projects. You can refer to the accounts.yaml.sample file included in
+projects. You can refer to the accounts.yaml.sample file included in
 the Tempest repo for the syntax around specifying networks in the file.
 
 However, specifying a network is not required when using an accounts file. If
@@ -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..10364db 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -11,7 +11,9 @@
    HACKING
    REVIEWING
    plugin
+   plugin-registry
    library
+   microversion_testing
 
 ------------
 Field Guides
diff --git a/doc/source/library.rst b/doc/source/library.rst
index 24ead08..64bd2c2 100644
--- a/doc/source/library.rst
+++ b/doc/source/library.rst
@@ -1,6 +1,6 @@
 .. _library:
 
-Tempest Library Doucmentation
+Tempest Library Documentation
 =============================
 
 Tempest provides a stable library interface that provides external tools or
@@ -66,3 +66,4 @@
    library/decorators
    library/rest_client
    library/utils
+   library/api_microversion_testing
diff --git a/doc/source/library/api_microversion_testing.rst b/doc/source/library/api_microversion_testing.rst
new file mode 100644
index 0000000..b7a4ba8
--- /dev/null
+++ b/doc/source/library/api_microversion_testing.rst
@@ -0,0 +1,29 @@
+.. _api_microversion_testing:
+
+API Microversion Testing Support in Temepst
+===========================================
+
+---------------------------------------------
+Framework to support API Microversion testing
+---------------------------------------------
+
+Many of the OpenStack components have implemented API microversions.
+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.
+
+This document explains the interfaces needed for microversion testing.
+
+
+The api_version_request module
+""""""""""""""""""""""""""""""
+
+.. automodule:: tempest.lib.common.api_version_request
+   :members:
+
+The api_version_utils module
+""""""""""""""""""""""""""""
+
+.. automodule:: tempest.lib.common.api_version_utils
+   :members:
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/doc/source/plugin-registry.rst b/doc/source/plugin-registry.rst
new file mode 100644
index 0000000..517e5b8
--- /dev/null
+++ b/doc/source/plugin-registry.rst
@@ -0,0 +1,23 @@
+..
+  Note to patch submitters: this file is covered by a periodic proposal
+  job.  You should edit the files data/tempest-plugins-registry.footer
+  data/tempest-plugins-registry.header instead of this one.
+
+==========================
+ Tempest Plugin Registry
+==========================
+
+Since we've created the external plugin mechanism, it's gotten used by
+a lot of projects. The following is a list of plugins that currently
+exist.
+
+Detected Plugins
+================
+
+The following will list plugins that a script has found in the openstack/
+namespace, which includes but is not limited to official OpenStack
+projects.
+
++----------------------------+-------------------------------------------------------------------------+
+|Plugin Name                 |URL                                                                      |
++----------------------------+-------------------------------------------------------------------------+
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index 29653a6..2622c22 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -55,6 +55,37 @@
   tempest.test_plugins =
       plugin_name = module.path:PluginClass
 
+Standalone Plugin vs In-repo Plugin
+-----------------------------------
+
+Since all that's required for a plugin to be detected by tempest is a valid
+setuptools entry point in the proper namespace there is no difference from the
+tempest perspective on either creating a separate python package to
+house the plugin or adding the code to an existing python project. However,
+there are tradeoffs to consider when deciding which approach to take when
+creating a new plugin.
+
+If you create a separate python project for your plugin this makes a lot of
+things much easier. Firstly it makes packaging and versioning much simpler, you
+can easily decouple the requirements for the plugin from the requirements for
+the other project. It lets you version the plugin independently and maintain a
+single version of the test code across project release boundaries (see the
+`Branchless Tempest Spec`_ for more details on this). It also greatly
+simplifies the install time story for external users. Instead of having to
+install the right version of a project in the same python namespace as tempest
+they simply need to pip install the plugin in that namespace. It also means
+that users don't have to worry about inadvertently installing a tempest plugin
+when they install another package.
+
+.. _Branchless Tempest Spec: http://specs.openstack.org/openstack/qa-specs/specs/tempest/implemented/branchless-tempest.html
+
+The sole advantage to integrating a plugin into an existing python project is
+that it enables you to land code changes at the same time you land test changes
+in the plugin. This reduces some of the burden on contributors by not having
+to land 2 changes to add a new API feature and then test it and doing it as a
+single combined commit.
+
+
 Plugin Class
 ============
 
diff --git a/etc/accounts.yaml.sample b/etc/accounts.yaml.sample
index decc659..3dbed79 100644
--- a/etc/accounts.yaml.sample
+++ b/etc/accounts.yaml.sample
@@ -3,7 +3,25 @@
 # This is required to provide isolation between test for running in parallel
 #
 # Valid fields for credentials are defined in the descendants of
-# auth.Credentials - see KeystoneV[2|3]Credentials.CONF_ATTRIBUTES
+# lib.auth.Credentials - see KeystoneV[2|3]Credentials.ATTRIBUTES
+#
+# The fields in KeystoneV3Credentials behave as follows:
+#
+# tenant_[id|name] also sets project_[id|name].
+#
+# project_[id|name] also sets tenant_[id|name].
+#
+# Providing distinct values for both tenant_[id|name] and project_[id|name]
+# will result in an InvalidCredentials exception.
+#
+# The value of project_domain_[id|name] is used for user_domain_[id|name] if
+# the latter is not specified.
+#
+# The value of user_domain_[id|name] is used for project_domain_[id|name] if
+# the latter is not specified.
+#
+# The value of domain_[id|name] is used for project_domain_[id|name] if not
+# specified and user_domain_[id|name] if not specified.
 
 - username: 'user_1'
   tenant_name: 'test_tenant_1'
diff --git a/openstack-common.conf b/openstack-common.conf
deleted file mode 100644
index acb1437..0000000
--- a/openstack-common.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-[DEFAULT]
-
-# The list of modules to copy from openstack-common
-module=install_venv_common
-module=with_venv
-module=install_venv
-
-# The base module to hold the copy of openstack.common
-base=tempest
diff --git a/releasenotes/notes/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml b/releasenotes/notes/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml
new file mode 100644
index 0000000..09ff15d
--- /dev/null
+++ b/releasenotes/notes/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml
@@ -0,0 +1,12 @@
+---
+prelude: >
+    This release is marking the start of Mitaka release support in tempest
+other:
+    - OpenStack Releases Supported at this time are **Kilo**, **Liberty**,
+      **Mitaka**
+
+      The release under current development as of this tag is Newton,
+      meaning that every Tempest commit is also tested against master during
+      the Newton cycle. However, this does not necessarily mean that using
+      Tempest as of this tag will work against a Newton (or future releases)
+      cloud.
diff --git a/releasenotes/notes/api-microversion-testing-support-2ceddd2255670932.yaml b/releasenotes/notes/api-microversion-testing-support-2ceddd2255670932.yaml
new file mode 100644
index 0000000..e98671a
--- /dev/null
+++ b/releasenotes/notes/api-microversion-testing-support-2ceddd2255670932.yaml
@@ -0,0 +1,3 @@
+---
+features:
+  - Tempest library interface addition(API Microversion testing interfaces).
\ No newline at end of file
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..b617b22 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -5,6 +5,8 @@
  .. toctree::
     :maxdepth: 1
 
+    v11.0.0
+    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/releasenotes/source/v11.0.0.rst b/releasenotes/source/v11.0.0.rst
new file mode 100644
index 0000000..84b145d
--- /dev/null
+++ b/releasenotes/source/v11.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v11.0.0 Release Notes
+=====================
+
+.. release-notes:: 11.0.0 Release Notes
+   :version: 11.0.0
diff --git a/requirements.txt b/requirements.txt
index 23357fd..3913f8f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,9 +2,8 @@
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
 pbr>=1.6 # Apache-2.0
-cliff!=1.16.0,>=1.15.0 # Apache-2.0
+cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
 anyjson>=0.3.3 # BSD
-httplib2>=0.7.5 # MIT
 jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
 testtools>=1.4.0 # MIT
 paramiko>=1.16.0 # LGPL
@@ -12,16 +11,17 @@
 testrepository>=0.0.18 # Apache-2.0/BSD
 pyOpenSSL>=0.14 # Apache-2.0
 oslo.concurrency>=3.5.0 # Apache-2.0
-oslo.config>=3.7.0 # Apache-2.0
+oslo.config>=3.9.0 # Apache-2.0
 oslo.i18n>=2.1.0 # Apache-2.0
 oslo.log>=1.14.0 # Apache-2.0
 oslo.serialization>=1.10.0 # Apache-2.0
 oslo.utils>=3.5.0 # Apache-2.0
 six>=1.9.0 # MIT
 iso8601>=0.1.9 # MIT
-fixtures>=1.3.1 # Apache-2.0/BSD
+fixtures<2.0,>=1.3.1 # Apache-2.0/BSD
 testscenarios>=0.4 # Apache-2.0/BSD
 PyYAML>=3.1.0 # MIT
 stevedore>=1.5.0 # Apache-2.0
 PrettyTable<0.8,>=0.7 # BSD
 os-testr>=0.4.1 # Apache-2.0
+urllib3>=1.8.3 # MIT
diff --git a/run_tempest.sh b/run_tempest.sh
index 8c8f25f..af01734 100755
--- a/run_tempest.sh
+++ b/run_tempest.sh
@@ -103,22 +103,25 @@
   fi
   if [ $update -eq 1 ]; then
       echo "Updating virtualenv..."
-      python tools/install_venv.py $installvenvopts
+      virtualenv $installvenvopts $venv
+      $venv/bin/pip install -U -r requirements.txt
   fi
   if [ -e ${venv} ]; then
     wrapper="${with_venv}"
   else
     if [ $always_venv -eq 1 ]; then
       # Automatically install the virtualenv
-      python tools/install_venv.py $installvenvopts
+      virtualenv $installvenvopts $venv
       wrapper="${with_venv}"
+      ${wrapper} pip install -U -r requirements.txt
     else
       echo -e "No virtual environment found...create one? (Y/n) \c"
       read use_ve
       if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
         # Install the virtualenv and run the test suite in it
-        python tools/install_venv.py $installvenvopts
+        virtualenv $installvenvopts $venv
         wrapper=${with_venv}
+        ${wrapper} pip install -U -r requirements.txt
       fi
     fi
   fi
diff --git a/run_tests.sh b/run_tests.sh
index 908056f..22314b6 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -114,22 +114,25 @@
   fi
   if [ $update -eq 1 ]; then
       echo "Updating virtualenv..."
-      python tools/install_venv.py $installvenvopts
+      virtualenv $installvenvopts $venv
+      $venv/bin/pip install -U -r requirements.txt -r test-requirements.txt
   fi
   if [ -e ${venv} ]; then
     wrapper="${with_venv}"
   else
     if [ $always_venv -eq 1 ]; then
       # Automatically install the virtualenv
-      python tools/install_venv.py $installvenvopts
+      virtualenv $installvenvopts $venv
       wrapper="${with_venv}"
+      ${wrapper} pip install -U -r requirements.txt -r test-requirements.txt
     else
       echo -e "No virtual environment found...create one? (Y/n) \c"
       read use_ve
       if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
         # Install the virtualenv and run the test suite in it
-        python tools/install_venv.py $installvenvopts
+        virtualenv $installvenvopts $venv
         wrapper=${with_venv}
+        ${wrapper} pip install -U -r requirements.txt -r test-requirements.txt
       fi
     fi
   fi
diff --git a/tempest/api/baremetal/admin/test_nodes.py b/tempest/api/baremetal/admin/test_nodes.py
index 8cc4d05..2c44665 100644
--- a/tempest/api/baremetal/admin/test_nodes.py
+++ b/tempest/api/baremetal/admin/test_nodes.py
@@ -138,14 +138,14 @@
         body = self.client.get_node_boot_device(self.node['uuid'])
         self.assertIn('boot_device', body)
         self.assertIn('persistent', body)
-        self.assertTrue(isinstance(body['boot_device'], six.string_types))
-        self.assertTrue(isinstance(body['persistent'], bool))
+        self.assertIsInstance(body['boot_device'], six.string_types)
+        self.assertIsInstance(body['persistent'], bool)
 
     @test.idempotent_id('3622bc6f-3589-4bc2-89f3-50419c66b133')
     def test_get_node_supported_boot_devices(self):
         body = self.client.get_node_supported_boot_devices(self.node['uuid'])
         self.assertIn('supported_boot_devices', body)
-        self.assertTrue(isinstance(body['supported_boot_devices'], list))
+        self.assertIsInstance(body['supported_boot_devices'], list)
 
     @test.idempotent_id('f63b6288-1137-4426-8cfe-0d5b7eb87c06')
     def test_get_console(self):
diff --git a/tempest/api/compute/admin/test_keypairs_v210.py b/tempest/api/compute/admin/test_keypairs_v210.py
new file mode 100644
index 0000000..dfa7c39
--- /dev/null
+++ b/tempest/api/compute/admin/test_keypairs_v210.py
@@ -0,0 +1,76 @@
+# Copyright 2016 NEC Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.compute.keypairs import base
+from tempest.common.utils import data_utils
+from tempest import test
+
+
+class KeyPairsV210TestJSON(base.BaseKeypairTest):
+    credentials = ['primary', 'admin']
+    min_microversion = '2.10'
+
+    @classmethod
+    def setup_clients(cls):
+        super(KeyPairsV210TestJSON, cls).setup_clients()
+        cls.client = cls.os_adm.keypairs_client
+        cls.non_admin_client = cls.os.keypairs_client
+
+    def _create_and_check_keypairs(self, user_id):
+        key_list = list()
+        for i in range(2):
+            k_name = data_utils.rand_name('keypair')
+            keypair = self._create_keypair(k_name,
+                                           keypair_type='ssh',
+                                           user_id=user_id)
+            self.assertEqual(k_name, keypair['name'],
+                             "The created keypair name is not equal "
+                             "to the requested name!")
+            self.assertEqual(user_id, keypair['user_id'],
+                             "The created keypair is not for requested user!")
+            keypair.pop('private_key', None)
+            keypair.pop('user_id')
+            key_list.append(keypair)
+        return key_list
+
+    @test.idempotent_id('3c8484af-cfb3-48f6-b8ba-d5d58bbf3eac')
+    def test_admin_manage_keypairs_for_other_users(self):
+        user_id = self.non_admin_client.user_id
+        key_list = self._create_and_check_keypairs(user_id)
+        first_keyname = key_list[0]['name']
+        keypair_detail = self.client.show_keypair(first_keyname,
+                                                  user_id=user_id)['keypair']
+        self.assertEqual(first_keyname, keypair_detail['name'])
+        self.assertEqual(user_id, keypair_detail['user_id'],
+                         "The fetched keypair is not for requested user!")
+        # Create a admin keypair
+        admin_k_name = data_utils.rand_name('keypair')
+        admin_keypair = self._create_keypair(admin_k_name, keypair_type='ssh')
+        admin_keypair.pop('private_key', None)
+        admin_keypair.pop('user_id')
+
+        # Admin fetch keypairs list of non admin user
+        keypairs = self.client.list_keypairs(user_id=user_id)['keypairs']
+        fetched_list = [keypair['keypair'] for keypair in keypairs]
+
+        # Check admin keypair is not present in non admin user keypairs list
+        self.assertNotIn(admin_keypair, fetched_list,
+                         "The fetched user keypairs has admin keypair!")
+
+        # Now check if all the created keypairs are in the fetched list
+        missing_kps = [kp for kp in key_list if kp not in fetched_list]
+        self.assertFalse(missing_kps,
+                         "Failed to find keypairs %s in fetched list"
+                         % ', '.join(m_key['name'] for m_key in missing_kps))
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index b1c42a6..94635ff 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -63,7 +63,7 @@
     def _get_host_for_server(self, server_id):
         return self._get_server_details(server_id)[self._host_key]
 
-    def _migrate_server_to(self, server_id, dest_host, volume_backed):
+    def _migrate_server_to(self, server_id, dest_host, volume_backed=False):
         block_migration = (CONF.compute_feature_enabled.
                            block_migration_for_live_migration and
                            not volume_backed)
@@ -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/api_microversion_fixture.py b/tempest/api/compute/api_microversion_fixture.py
index bf4de3e..695af52 100644
--- a/tempest/api/compute/api_microversion_fixture.py
+++ b/tempest/api/compute/api_microversion_fixture.py
@@ -14,7 +14,7 @@
 
 import fixtures
 
-from tempest.services.compute.json import base_compute_client
+from tempest.lib.services.compute import base_compute_client
 
 
 class APIMicroversionFixture(fixtures.Fixture):
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index ee21284..77ed7cd 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -18,12 +18,12 @@
 from oslo_log import log as logging
 
 from tempest.api.compute import api_microversion_fixture
-from tempest.common import api_version_utils
 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.common import api_version_utils
 from tempest.lib import exceptions as lib_exc
 import tempest.test
 
@@ -47,8 +47,8 @@
         super(BaseV2ComputeTest, cls).skip_checks()
         if not CONF.service_available.nova:
             raise cls.skipException("Nova is not available")
-        cfg_min_version = CONF.compute_feature_enabled.min_microversion
-        cfg_max_version = CONF.compute_feature_enabled.max_microversion
+        cfg_min_version = CONF.compute.min_microversion
+        cfg_max_version = CONF.compute.max_microversion
         api_version_utils.check_skip_with_microversion(cls.min_microversion,
                                                        cls.max_microversion,
                                                        cfg_min_version,
@@ -105,7 +105,7 @@
         cls.request_microversion = (
             api_version_utils.select_request_microversion(
                 cls.min_microversion,
-                CONF.compute_feature_enabled.min_microversion))
+                CONF.compute.min_microversion))
         cls.build_interval = CONF.compute.build_interval
         cls.build_timeout = CONF.compute.build_timeout
         cls.image_ref = CONF.compute.image_ref
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/keypairs/base.py b/tempest/api/compute/keypairs/base.py
index ebfb724..ad7f958 100644
--- a/tempest/api/compute/keypairs/base.py
+++ b/tempest/api/compute/keypairs/base.py
@@ -24,15 +24,21 @@
         super(BaseKeypairTest, cls).setup_clients()
         cls.client = cls.keypairs_client
 
-    def _delete_keypair(self, keypair_name):
-        self.client.delete_keypair(keypair_name)
+    def _delete_keypair(self, keypair_name, **params):
+        self.client.delete_keypair(keypair_name, **params)
 
-    def _create_keypair(self, keypair_name, pub_key=None, keypair_type=None):
+    def _create_keypair(self, keypair_name,
+                        pub_key=None, keypair_type=None,
+                        user_id=None):
         kwargs = {'name': keypair_name}
+        delete_params = {}
         if pub_key:
             kwargs.update({'public_key': pub_key})
         if keypair_type:
             kwargs.update({'type': keypair_type})
+        if user_id:
+            kwargs.update({'user_id': user_id})
+            delete_params['user_id'] = user_id
         body = self.client.create_keypair(**kwargs)['keypair']
-        self.addCleanup(self._delete_keypair, keypair_name)
+        self.addCleanup(self._delete_keypair, keypair_name, **delete_params)
         return body
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index f719bfc..87f3c86 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -38,7 +38,6 @@
     def setup_clients(cls):
         super(ServersTestJSON, cls).setup_clients()
         cls.client = cls.servers_client
-        cls.network_client = cls.os.network_client
         cls.networks_client = cls.os.networks_client
         cls.subnets_client = cls.os.subnets_client
 
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 0b33d66..c1fbb12 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.api import utils
 from tempest.common import fixed_network
 from tempest.common.utils import data_utils
 from tempest.common import waiters
@@ -89,7 +88,7 @@
                                         wait_until='ACTIVE')
 
     @test.idempotent_id('05e8a8e7-9659-459a-989d-92c2f501f4ba')
-    @utils.skip_unless_attr('multiple_images', 'Only one image found')
+    @decorators.skip_unless_attr('multiple_images', 'Only one image found')
     def test_list_servers_filter_by_image(self):
         # Filter the list of servers by image
         params = {'image': self.image_ref}
@@ -174,7 +173,7 @@
                          len([x for x in servers['servers'] if 'id' in x]))
 
     @test.idempotent_id('b3304c3b-97df-46d2-8cd3-e2b6659724e7')
-    @utils.skip_unless_attr('multiple_images', 'Only one image found')
+    @decorators.skip_unless_attr('multiple_images', 'Only one image found')
     def test_list_servers_detailed_filter_by_image(self):
         # Filter the detailed list of servers by image
         params = {'image': self.image_ref}
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_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 0df6ead..8f0e430 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -29,8 +29,6 @@
 
 class ServersNegativeTestJSON(base.BaseV2ComputeTest):
 
-    credentials = ['primary', 'alt']
-
     def setUp(self):
         super(ServersNegativeTestJSON, self).setUp()
         try:
@@ -47,7 +45,6 @@
     def setup_clients(cls):
         super(ServersNegativeTestJSON, cls).setup_clients()
         cls.client = cls.servers_client
-        cls.alt_client = cls.os_alt.servers_client
 
     @classmethod
     def resource_setup(cls):
@@ -271,16 +268,6 @@
                           self.server_id, name=new_name)
 
     @test.attr(type=['negative'])
-    @test.idempotent_id('543d84c1-dd2e-4c6d-8cb2-b9da0efaa384')
-    def test_update_server_of_another_tenant(self):
-        # Update name of a server that belongs to another tenant
-
-        new_name = self.server_id + '_new'
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_client.update_server, self.server_id,
-                          name=new_name)
-
-    @test.attr(type=['negative'])
     @test.idempotent_id('5c8e244c-dada-4590-9944-749c455b431f')
     def test_update_server_name_length_exceeds_256(self):
         # Update name of server exceed the name length limit
@@ -301,14 +288,6 @@
                           nonexistent_server)
 
     @test.attr(type=['negative'])
-    @test.idempotent_id('5c75009d-3eea-423e-bea3-61b09fd25f9c')
-    def test_delete_a_server_of_another_tenant(self):
-        # Delete a server that belongs to another tenant
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_client.delete_server,
-                          self.server_id)
-
-    @test.attr(type=['negative'])
     @test.idempotent_id('75f79124-277c-45e6-a373-a1d6803f4cc4')
     def test_delete_server_pass_negative_id(self):
         # Pass an invalid string parameter to delete server
@@ -519,3 +498,45 @@
         self.assertRaises(lib_exc.Conflict,
                           self.client.unshelve_server,
                           self.server_id)
+
+
+class ServersNegativeTestMultiTenantJSON(base.BaseV2ComputeTest):
+
+    credentials = ['primary', 'alt']
+
+    def setUp(self):
+        super(ServersNegativeTestMultiTenantJSON, self).setUp()
+        try:
+            waiters.wait_for_server_status(self.client, self.server_id,
+                                           'ACTIVE')
+        except Exception:
+            self.__class__.server_id = self.rebuild_server(self.server_id)
+
+    @classmethod
+    def setup_clients(cls):
+        super(ServersNegativeTestMultiTenantJSON, cls).setup_clients()
+        cls.alt_client = cls.os_alt.servers_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(ServersNegativeTestMultiTenantJSON, cls).resource_setup()
+        server = cls.create_test_server(wait_until='ACTIVE')
+        cls.server_id = server['id']
+
+    @test.attr(type=['negative'])
+    @test.idempotent_id('543d84c1-dd2e-4c6d-8cb2-b9da0efaa384')
+    def test_update_server_of_another_tenant(self):
+        # Update name of a server that belongs to another tenant
+
+        new_name = self.server_id + '_new'
+        self.assertRaises(lib_exc.NotFound,
+                          self.alt_client.update_server, self.server_id,
+                          name=new_name)
+
+    @test.attr(type=['negative'])
+    @test.idempotent_id('5c75009d-3eea-423e-bea3-61b09fd25f9c')
+    def test_delete_a_server_of_another_tenant(self):
+        # Delete a server that belongs to another tenant
+        self.assertRaises(lib_exc.NotFound,
+                          self.alt_client.delete_server,
+                          self.server_id)
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/test_authorization.py b/tempest/api/compute/test_authorization.py
deleted file mode 100644
index 133502c..0000000
--- a/tempest/api/compute/test_authorization.py
+++ /dev/null
@@ -1,451 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-#    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 six
-
-from oslo_log import log as logging
-
-from tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest.lib import exceptions as lib_exc
-from tempest import test
-
-CONF = config.CONF
-
-LOG = logging.getLogger(__name__)
-
-
-class AuthorizationTestJSON(base.BaseV2ComputeTest):
-
-    credentials = ['primary', 'alt']
-
-    @classmethod
-    def skip_checks(cls):
-        super(AuthorizationTestJSON, cls).skip_checks()
-        if not CONF.service_available.glance:
-            raise cls.skipException('Glance is not available.')
-
-    @classmethod
-    def setup_credentials(cls):
-        # No network resources required for this test
-        cls.set_network_resources()
-        super(AuthorizationTestJSON, cls).setup_credentials()
-
-    @classmethod
-    def setup_clients(cls):
-        super(AuthorizationTestJSON, cls).setup_clients()
-        cls.client = cls.os.servers_client
-        cls.compute_images_client = cls.os.compute_images_client
-        cls.glance_client = cls.os.image_client
-        cls.keypairs_client = cls.os.keypairs_client
-        cls.security_client = cls.os.compute_security_groups_client
-        cls.rule_client = cls.os.compute_security_group_rules_client
-
-        cls.alt_client = cls.alt_manager.servers_client
-        cls.alt_compute_images_client = cls.alt_manager.compute_images_client
-        cls.alt_keypairs_client = cls.alt_manager.keypairs_client
-        cls.alt_security_client = (
-            cls.alt_manager.compute_security_groups_client)
-        cls.alt_rule_client = (
-            cls.alt_manager.compute_security_group_rules_client)
-
-    @classmethod
-    def resource_setup(cls):
-        super(AuthorizationTestJSON, cls).resource_setup()
-        server = cls.create_test_server(wait_until='ACTIVE')
-        cls.server = cls.client.show_server(server['id'])['server']
-
-        name = data_utils.rand_name('image')
-        body = cls.glance_client.create_image(name=name,
-                                              container_format='bare',
-                                              disk_format='raw',
-                                              is_public=False)['image']
-        image_id = body['id']
-        image_file = six.StringIO(('*' * 1024))
-        body = cls.glance_client.update_image(image_id,
-                                              data=image_file)['image']
-        cls.glance_client.wait_for_image_status(image_id, 'active')
-        cls.image = cls.compute_images_client.show_image(image_id)['image']
-
-        cls.keypairname = data_utils.rand_name('keypair')
-        cls.keypairs_client.create_keypair(name=cls.keypairname)
-
-        name = data_utils.rand_name('security')
-        description = data_utils.rand_name('description')
-        cls.security_group = cls.security_client.create_security_group(
-            name=name, description=description)['security_group']
-
-        parent_group_id = cls.security_group['id']
-        ip_protocol = 'tcp'
-        from_port = 22
-        to_port = 22
-        cls.rule = cls.rule_client.create_security_group_rule(
-            parent_group_id=parent_group_id, ip_protocol=ip_protocol,
-            from_port=from_port, to_port=to_port)['security_group_rule']
-
-    @classmethod
-    def resource_cleanup(cls):
-        if hasattr(cls, 'image'):
-            cls.compute_images_client.delete_image(cls.image['id'])
-        if hasattr(cls, 'keypairname'):
-            cls.keypairs_client.delete_keypair(cls.keypairname)
-        if hasattr(cls, 'security_group'):
-            cls.security_client.delete_security_group(cls.security_group['id'])
-        super(AuthorizationTestJSON, cls).resource_cleanup()
-
-    @test.idempotent_id('56816e4a-bd34-47b5-aee9-268c3efeb5d4')
-    def test_get_server_for_alt_account_fails(self):
-        # A GET request for a server on another user's account should fail
-        self.assertRaises(lib_exc.NotFound, self.alt_client.show_server,
-                          self.server['id'])
-
-    @test.idempotent_id('fb8a4870-6d9d-44ad-8375-95d52e98d9f6')
-    def test_delete_server_for_alt_account_fails(self):
-        # A DELETE request for another user's server should fail
-        self.assertRaises(lib_exc.NotFound, self.alt_client.delete_server,
-                          self.server['id'])
-
-    @test.idempotent_id('d792f91f-1d49-4eb5-b1ff-b229c4b9dc64')
-    def test_update_server_for_alt_account_fails(self):
-        # An update server request for another user's server should fail
-        self.assertRaises(lib_exc.NotFound, self.alt_client.update_server,
-                          self.server['id'], name='test')
-
-    @test.idempotent_id('488f24df-d7f7-4207-949a-f17fcb8e8769')
-    def test_list_server_addresses_for_alt_account_fails(self):
-        # A list addresses request for another user's server should fail
-        self.assertRaises(lib_exc.NotFound, self.alt_client.list_addresses,
-                          self.server['id'])
-
-    @test.idempotent_id('00b442d0-2e72-40e7-9b1f-31772e36da01')
-    def test_list_server_addresses_by_network_for_alt_account_fails(self):
-        # A list address/network request for another user's server should fail
-        server_id = self.server['id']
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_client.list_addresses_by_network, server_id,
-                          'public')
-
-    @test.idempotent_id('cc90b35a-19f0-45d2-b680-2aabf934aa22')
-    def test_list_servers_with_alternate_tenant(self):
-        # A list on servers from one tenant should not
-        # show on alternate tenant
-        # Listing servers from alternate tenant
-        alt_server_ids = []
-        body = self.alt_client.list_servers()
-        alt_server_ids = [s['id'] for s in body['servers']]
-        self.assertNotIn(self.server['id'], alt_server_ids)
-
-    @test.idempotent_id('376dbc16-0779-4384-a723-752774799641')
-    def test_change_password_for_alt_account_fails(self):
-        # A change password request for another user's server should fail
-        self.assertRaises(lib_exc.NotFound, self.alt_client.change_password,
-                          self.server['id'], adminPass='newpass')
-
-    @test.idempotent_id('14cb5ff5-f646-45ca-8f51-09081d6c0c24')
-    def test_reboot_server_for_alt_account_fails(self):
-        # A reboot request for another user's server should fail
-        self.assertRaises(lib_exc.NotFound, self.alt_client.reboot_server,
-                          self.server['id'], type='HARD')
-
-    @test.idempotent_id('8a0bce51-cd00-480b-88ba-dbc7d8408a37')
-    def test_rebuild_server_for_alt_account_fails(self):
-        # A rebuild request for another user's server should fail
-        self.assertRaises(lib_exc.NotFound, self.alt_client.rebuild_server,
-                          self.server['id'], self.image_ref_alt)
-
-    @test.idempotent_id('e4da647e-f982-4e61-9dad-1d1abebfb933')
-    def test_resize_server_for_alt_account_fails(self):
-        # A resize request for another user's server should fail
-        self.assertRaises(lib_exc.NotFound, self.alt_client.resize_server,
-                          self.server['id'], self.flavor_ref_alt)
-
-    @test.idempotent_id('a9fe8112-0ffa-4902-b061-f892bd5fe0d3')
-    def test_create_image_for_alt_account_fails(self):
-        # A create image request for another user's server should fail
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_compute_images_client.create_image,
-                          self.server['id'], name='testImage')
-
-    @test.idempotent_id('95d445f6-babc-4f2e-aea3-aa24ec5e7f0d')
-    def test_create_server_with_unauthorized_image(self):
-        # Server creation with another user's image should fail
-        self.assertRaises(lib_exc.BadRequest, self.alt_client.create_server,
-                          name='test', imageRef=self.image['id'],
-                          flavorRef=self.flavor_ref)
-
-    @test.idempotent_id('acf8724b-142b-4044-82c3-78d31a533f24')
-    def test_create_server_fails_when_tenant_incorrect(self):
-        # BUG(sdague): this test should fail because of bad auth url,
-        # which means that when we run with a service catalog without
-        # project_id in the urls, it should fail to fail, and thus
-        # fail the test. It does not.
-        #
-        # The 400 BadRequest is clearly ambiguous, and something else
-        # is wrong about this request. This should be fixed.
-        #
-        # A create server request should fail if the tenant id does not match
-        # the current user
-        # Change the base URL to impersonate another user
-        self.alt_client.auth_provider.set_alt_auth_data(
-            request_part='url',
-            auth_data=self.client.auth_provider.auth_data
-        )
-        self.assertRaises(lib_exc.BadRequest,
-                          self.alt_client.create_server, name='test',
-                          imageRef=self.image['id'], flavorRef=self.flavor_ref)
-
-    @test.idempotent_id('f03d1ded-7fd4-4d29-bc13-e2391f29c625')
-    def test_create_keypair_in_analt_user_tenant(self):
-        """create keypair should not function for alternate tenant
-
-        POST {alt_service_url}/os-keypairs
-
-        Attempt to create a keypair against an alternate tenant by
-        changing using a different tenant's service url. This should
-        return a BadRequest. This tests basic tenant isolation protections.
-
-        NOTE(sdague): if the environment does not use project_id in
-        the service urls, this test is not valid. Skip under these
-        conditions.
-
-        """
-        if self.alt_keypairs_client.base_url == self.keypairs_client.base_url:
-            raise self.skipException("Service urls don't include project_id")
-
-        k_name = data_utils.rand_name('keypair')
-        try:
-            # Change the base URL to impersonate another user
-            self.alt_keypairs_client.auth_provider.set_alt_auth_data(
-                request_part='url',
-                auth_data=self.keypairs_client.auth_provider.auth_data
-            )
-            resp = {}
-            resp['status'] = None
-            self.assertRaises(lib_exc.BadRequest,
-                              self.alt_keypairs_client.create_keypair,
-                              name=k_name)
-        finally:
-            # Next request the base_url is back to normal
-            if (resp['status'] is not None):
-                self.alt_keypairs_client.delete_keypair(k_name)
-                LOG.error("Create keypair request should not happen "
-                          "if the tenant id does not match the current user")
-
-    @test.idempotent_id('85bcdd8f-56b4-4868-ae56-63fbf6f7e405')
-    def test_get_keypair_of_alt_account_fails(self):
-        # A GET request for another user's keypair should fail
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_keypairs_client.show_keypair,
-                          self.keypairname)
-
-    @test.idempotent_id('6d841683-a8e0-43da-a1b8-b339f7692b61')
-    def test_delete_keypair_of_alt_account_fails(self):
-        # A DELETE request for another user's keypair should fail
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_keypairs_client.delete_keypair,
-                          self.keypairname)
-
-    @test.idempotent_id('fcb2e144-36e3-4dfb-9f9f-e72fcdec5656')
-    def test_get_image_for_alt_account_fails(self):
-        # A GET request for an image on another user's account should fail
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_compute_images_client.show_image,
-                          self.image['id'])
-
-    @test.idempotent_id('9facb962-f043-4a9d-b9ee-166a32dea098')
-    def test_delete_image_for_alt_account_fails(self):
-        # A DELETE request for another user's image should fail
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_compute_images_client.delete_image,
-                          self.image['id'])
-
-    @test.idempotent_id('752c917e-83be-499d-a422-3559127f7d3c')
-    def test_create_security_group_in_analt_user_tenant(self):
-        """create security group should not function for alternate tenant
-
-        POST {alt_service_url}/os-security-groups
-
-        Attempt to create a security group against an alternate tenant
-        by changing using a different tenant's service url. This
-        should return a BadRequest. This tests basic tenant isolation
-        protections.
-
-        NOTE(sdague): if the environment does not use project_id in
-        the service urls, this test is not valid. Skip under these
-        conditions.
-
-        """
-        if self.alt_security_client.base_url == self.security_client.base_url:
-            raise self.skipException("Service urls don't include project_id")
-
-        s_name = data_utils.rand_name('security')
-        s_description = data_utils.rand_name('security')
-        try:
-            # Change the base URL to impersonate another user
-            self.alt_security_client.auth_provider.set_alt_auth_data(
-                request_part='url',
-                auth_data=self.security_client.auth_provider.auth_data
-            )
-            resp = {}
-            resp['status'] = None
-            self.assertRaises(lib_exc.BadRequest,
-                              self.alt_security_client.create_security_group,
-                              name=s_name, description=s_description)
-        finally:
-            # Next request the base_url is back to normal
-            if resp['status'] is not None:
-                self.alt_security_client.delete_security_group(resp['id'])
-                LOG.error("Create Security Group request should not happen if"
-                          "the tenant id does not match the current user")
-
-    @test.idempotent_id('9db3590f-4d15-4e5f-985e-b28514919a6f')
-    def test_get_security_group_of_alt_account_fails(self):
-        # A GET request for another user's security group should fail
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_security_client.show_security_group,
-                          self.security_group['id'])
-
-    @test.idempotent_id('155387a5-2bbc-4acf-ab06-698dae537ea5')
-    def test_delete_security_group_of_alt_account_fails(self):
-        # A DELETE request for another user's security group should fail
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_security_client.delete_security_group,
-                          self.security_group['id'])
-
-    @test.idempotent_id('b2b76de0-210a-4089-b921-591c9ec552f6')
-    def test_create_security_group_rule_in_analt_user_tenant(self):
-        """create security group rule should not function for alternate tenant
-
-        POST {alt_service_url}/os-security-group-rules
-
-        Attempt to create a security group rule against an alternate
-        tenant by changing using a different tenant's service
-        url. This should return a BadRequest. This tests basic tenant
-        isolation protections.
-
-        NOTE(sdague): if the environment does not use project_id in
-        the service urls, this test is not valid. Skip under these
-        conditions.
-
-        """
-        if self.alt_security_client.base_url == self.security_client.base_url:
-            raise self.skipException("Service urls don't include project_id")
-
-        parent_group_id = self.security_group['id']
-        ip_protocol = 'icmp'
-        from_port = -1
-        to_port = -1
-        try:
-            # Change the base URL to impersonate another user
-            self.alt_rule_client.auth_provider.set_alt_auth_data(
-                request_part='url',
-                auth_data=self.rule_client.auth_provider.auth_data
-            )
-            resp = {}
-            resp['status'] = None
-            self.assertRaises(lib_exc.BadRequest,
-                              self.alt_rule_client.
-                              create_security_group_rule,
-                              parent_group_id=parent_group_id,
-                              ip_protocol=ip_protocol,
-                              from_port=from_port, to_port=to_port)
-        finally:
-            # Next request the base_url is back to normal
-            if resp['status'] is not None:
-                self.alt_rule_client.delete_security_group_rule(resp['id'])
-                LOG.error("Create security group rule request should not "
-                          "happen if the tenant id does not match the"
-                          " current user")
-
-    @test.idempotent_id('c6044177-37ef-4ce4-b12c-270ddf26d7da')
-    def test_delete_security_group_rule_of_alt_account_fails(self):
-        # A DELETE request for another user's security group rule
-        # should fail
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_rule_client.delete_security_group_rule,
-                          self.rule['id'])
-
-    @test.idempotent_id('c5f52351-53d9-4fc9-83e5-917f7f5e3d71')
-    def test_set_metadata_of_alt_account_server_fails(self):
-        # A set metadata for another user's server should fail
-        req_metadata = {'meta1': 'data1', 'meta2': 'data2'}
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_client.set_server_metadata,
-                          self.server['id'],
-                          req_metadata)
-
-    @test.idempotent_id('fb6f51e9-df15-4939-898d-1aca38c258f0')
-    def test_set_metadata_of_alt_account_image_fails(self):
-        # A set metadata for another user's image should fail
-        req_metadata = {'meta1': 'value1', 'meta2': 'value2'}
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_compute_images_client.set_image_metadata,
-                          self.image['id'], req_metadata)
-
-    @test.idempotent_id('dea1936a-473d-49f2-92ad-97bb7aded22e')
-    def test_get_metadata_of_alt_account_server_fails(self):
-        # A get metadata for another user's server should fail
-        req_metadata = {'meta1': 'data1'}
-        self.client.set_server_metadata(self.server['id'], req_metadata)
-        self.addCleanup(self.client.delete_server_metadata_item,
-                        self.server['id'], 'meta1')
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_client.show_server_metadata_item,
-                          self.server['id'], 'meta1')
-
-    @test.idempotent_id('16b2d724-0d3b-4216-a9fa-97bd4d9cf670')
-    def test_get_metadata_of_alt_account_image_fails(self):
-        # A get metadata for another user's image should fail
-        req_metadata = {'meta1': 'value1'}
-        self.addCleanup(self.compute_images_client.delete_image_metadata_item,
-                        self.image['id'], 'meta1')
-        self.compute_images_client.set_image_metadata(self.image['id'],
-                                                      req_metadata)
-        self.assertRaises(
-            lib_exc.NotFound,
-            self.alt_compute_images_client.show_image_metadata_item,
-            self.image['id'], 'meta1')
-
-    @test.idempotent_id('79531e2e-e721-493c-8b30-a35db36fdaa6')
-    def test_delete_metadata_of_alt_account_server_fails(self):
-        # A delete metadata for another user's server should fail
-        req_metadata = {'meta1': 'data1'}
-        self.addCleanup(self.client.delete_server_metadata_item,
-                        self.server['id'], 'meta1')
-        self.client.set_server_metadata(self.server['id'], req_metadata)
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_client.delete_server_metadata_item,
-                          self.server['id'], 'meta1')
-
-    @test.idempotent_id('a5175dcf-cef8-43d6-9b77-3cb707d62e94')
-    def test_delete_metadata_of_alt_account_image_fails(self):
-        # A delete metadata for another user's image should fail
-        req_metadata = {'meta1': 'data1'}
-        self.addCleanup(self.compute_images_client.delete_image_metadata_item,
-                        self.image['id'], 'meta1')
-        self.compute_images_client.set_image_metadata(self.image['id'],
-                                                      req_metadata)
-        self.assertRaises(
-            lib_exc.NotFound,
-            self.alt_compute_images_client.delete_image_metadata_item,
-            self.image['id'], 'meta1')
-
-    @test.idempotent_id('b0c1e7a0-8853-40fd-8384-01f93d116cae')
-    def test_get_console_output_of_alt_account_server_fails(self):
-        # A Get Console Output for another user's server should fail
-        self.assertRaises(lib_exc.NotFound,
-                          self.alt_client.get_console_output,
-                          self.server['id'], length=10)
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 43f4c97..122e7cc 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -79,8 +79,8 @@
     @test.idempotent_id('cd65d997-f7e4-4966-a7e9-d5001b674fdc')
     def test_compare_tenant_quotas_with_default_quotas(self):
         # Tenants are created with the default quota values
-        defualt_quota_set = \
+        default_quota_set = \
             self.client.show_default_quota_set(self.tenant_id)['quota_set']
         tenant_quota_set = (self.client.show_quota_set(self.tenant_id)
                             ['quota_set'])
-        self.assertEqual(defualt_quota_set, tenant_quota_set)
+        self.assertEqual(default_quota_set, tenant_quota_set)
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/database/flavors/test_flavors.py b/tempest/api/database/flavors/test_flavors.py
index f75b867..bdb4383 100644
--- a/tempest/api/database/flavors/test_flavors.py
+++ b/tempest/api/database/flavors/test_flavors.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from tempest.api.database import base
+from tempest.lib import decorators
 from tempest import test
 
 
@@ -58,6 +59,7 @@
     @test.attr(type='smoke')
     @test.idempotent_id('afb2667f-4ec2-4925-bcb7-313fdcffb80d')
     @test.services('compute')
+    @decorators.skip_because(bug='1567134')
     def test_compare_db_flavors_with_os(self):
         db_flavors = self.client.list_db_flavors()['flavors']
         os_flavors = (self.os_flavors_client.list_flavors(detail=True)
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_groups.py b/tempest/api/identity/admin/v3/test_groups.py
index 010e4a0..a3ada21 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -20,12 +20,18 @@
 
 class GroupsV3TestJSON(base.BaseIdentityV3AdminTest):
 
+    @classmethod
+    def resource_setup(cls):
+        super(GroupsV3TestJSON, cls).resource_setup()
+        cls.data.setup_test_domain()
+
     @test.idempotent_id('2e80343b-6c81-4ac3-88c7-452f3e9d5129')
     def test_group_create_update_get(self):
         name = data_utils.rand_name('Group')
         description = data_utils.rand_name('Description')
         group = self.groups_client.create_group(
-            name=name, description=description)['group']
+            name=name, domain_id=self.data.domain['id'],
+            description=description)['group']
         self.addCleanup(self.groups_client.delete_group, group['id'])
         self.assertEqual(group['name'], name)
         self.assertEqual(group['description'], description)
@@ -47,7 +53,8 @@
         name = data_utils.rand_name('Group')
         old_description = data_utils.rand_name('Description')
         group = self.groups_client.create_group(
-            name=name, description=old_description)['group']
+            name=name, domain_id=self.data.domain['id'],
+            description=old_description)['group']
         self.addCleanup(self.groups_client.delete_group, group['id'])
 
         new_name = data_utils.rand_name('UpdateGroup')
@@ -61,7 +68,8 @@
     @test.idempotent_id('1598521a-2f36-4606-8df9-30772bd51339')
     def test_group_users_add_list_delete(self):
         name = data_utils.rand_name('Group')
-        group = self.groups_client.create_group(name=name)['group']
+        group = self.groups_client.create_group(
+            name=name, domain_id=self.data.domain['id'])['group']
         self.addCleanup(self.groups_client.delete_group, group['id'])
         # add user into group
         users = []
@@ -94,7 +102,8 @@
         groups = []
         for i in range(2):
             name = data_utils.rand_name('Group')
-            group = self.groups_client.create_group(name=name)['group']
+            group = self.groups_client.create_group(
+                name=name, domain_id=self.data.domain['id'])['group']
             groups.append(group)
             self.addCleanup(self.groups_client.delete_group, group['id'])
             self.groups_client.add_group_user(group['id'], user['id'])
@@ -112,7 +121,8 @@
             name = data_utils.rand_name('Group')
             description = data_utils.rand_name('Description')
             group = self.groups_client.create_group(
-                name=name, description=description)['group']
+                name=name, domain_id=self.data.domain['id'],
+                description=description)['group']
             self.addCleanup(self.groups_client.delete_group, group['id'])
             group_ids.append(group['id'])
         # List and Verify Groups
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
new file mode 100644
index 0000000..fe20349
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -0,0 +1,147 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.identity import base
+from tempest.common.utils import data_utils
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class BaseInheritsV3Test(base.BaseIdentityV3AdminTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(BaseInheritsV3Test, cls).skip_checks()
+        if not test.is_extension_enabled('OS-INHERIT', 'identity'):
+            raise cls.skipException("Inherits aren't enabled")
+
+    @classmethod
+    def resource_setup(cls):
+        super(BaseInheritsV3Test, cls).resource_setup()
+        u_name = data_utils.rand_name('user-')
+        u_desc = '%s description' % u_name
+        u_email = '%s@testmail.tm' % u_name
+        u_password = data_utils.rand_name('pass-')
+        cls.domain = cls.domains_client.create_domain(
+            data_utils.rand_name('domain-'),
+            description=data_utils.rand_name('domain-desc-'))['domain']
+        cls.project = cls.projects_client.create_project(
+            data_utils.rand_name('project-'),
+            description=data_utils.rand_name('project-desc-'),
+            domain_id=cls.domain['id'])['project']
+        cls.group = cls.groups_client.create_group(
+            name=data_utils.rand_name('group-'), project_id=cls.project['id'],
+            domain_id=cls.domain['id'])['group']
+        cls.user = cls.users_client.create_user(
+            u_name, description=u_desc, password=u_password,
+            email=u_email, project_id=cls.project['id'],
+            domain_id=cls.domain['id'])['user']
+
+    @classmethod
+    def resource_cleanup(cls):
+        cls.groups_client.delete_group(cls.group['id'])
+        cls.users_client.delete_user(cls.user['id'])
+        cls.projects_client.delete_project(cls.project['id'])
+        cls.domains_client.update_domain(cls.domain['id'], enabled=False)
+        cls.domains_client.delete_domain(cls.domain['id'])
+        super(BaseInheritsV3Test, cls).resource_cleanup()
+
+    def _list_assertions(self, body, fetched_role_ids, role_id):
+        self.assertEqual(len(body), 1)
+        self.assertIn(role_id, fetched_role_ids)
+
+
+class InheritsV3TestJSON(BaseInheritsV3Test):
+
+    @test.idempotent_id('4e6f0366-97c8-423c-b2be-41eae6ac91c8')
+    def test_inherit_assign_list_check_revoke_roles_on_domains_user(self):
+        # Create role
+        src_role = self.roles_client.create_role(
+            name=data_utils.rand_name('Role'))['role']
+        self.addCleanup(self.roles_client.delete_role, src_role['id'])
+        # Assign role on domains user
+        self.roles_client.assign_inherited_role_on_domains_user(
+            self.domain['id'], self.user['id'], src_role['id'])
+        # list role on domains user
+        roles = self.roles_client.\
+            list_inherited_project_role_for_user_on_domain(
+                self.domain['id'], self.user['id'])['roles']
+
+        fetched_role_ids = [i['id'] for i in roles]
+        self._list_assertions(roles, fetched_role_ids,
+                              src_role['id'])
+
+        # Check role on domains user
+        self.roles_client.check_user_inherited_project_role_on_domain(
+            self.domain['id'], self.user['id'], src_role['id'])
+        # Revoke role from domains user.
+        self.roles_client.revoke_inherited_role_from_user_on_domain(
+            self.domain['id'], self.user['id'], src_role['id'])
+
+    @test.idempotent_id('c7a8dda2-be50-4fb4-9a9c-e830771078b1')
+    def test_inherit_assign_list_check_revoke_roles_on_domains_group(self):
+        # Create role
+        src_role = self.roles_client.create_role(
+            name=data_utils.rand_name('Role'))['role']
+        self.addCleanup(self.roles_client.delete_role, src_role['id'])
+        # Assign role on domains group
+        self.roles_client.assign_inherited_role_on_domains_group(
+            self.domain['id'], self.group['id'], src_role['id'])
+        # List role on domains group
+        roles = self.roles_client.\
+            list_inherited_project_role_for_group_on_domain(
+                self.domain['id'], self.group['id'])['roles']
+
+        fetched_role_ids = [i['id'] for i in roles]
+        self._list_assertions(roles, fetched_role_ids,
+                              src_role['id'])
+
+        # Check role on domains group
+        self.roles_client.check_group_inherited_project_role_on_domain(
+            self.domain['id'], self.group['id'], src_role['id'])
+        # Revoke role from domains group
+        self.roles_client.revoke_inherited_role_from_group_on_domain(
+            self.domain['id'], self.group['id'], src_role['id'])
+
+    @test.idempotent_id('18b70e45-7687-4b72-8277-b8f1a47d7591')
+    def test_inherit_assign_check_revoke_roles_on_projects_user(self):
+        # Create role
+        src_role = self.roles_client.create_role(
+            name=data_utils.rand_name('Role'))['role']
+        self.addCleanup(self.roles_client.delete_role, src_role['id'])
+        # Assign role on projects user
+        self.roles_client.assign_inherited_role_on_projects_user(
+            self.project['id'], self.user['id'], src_role['id'])
+        # Check role on projects user
+        self.roles_client.check_user_has_flag_on_inherited_to_project(
+            self.project['id'], self.user['id'], src_role['id'])
+        # Revoke role from projects user
+        self.roles_client.revoke_inherited_role_from_user_on_project(
+            self.project['id'], self.user['id'], src_role['id'])
+
+    @test.idempotent_id('26021436-d5a4-4256-943c-ded01e0d4b45')
+    def test_inherit_assign_check_revoke_roles_on_projects_group(self):
+        # Create role
+        src_role = self.roles_client.create_role(
+            name=data_utils.rand_name('Role'))['role']
+        self.addCleanup(self.roles_client.delete_role, src_role['id'])
+        # Assign role on projects group
+        self.roles_client.assign_inherited_role_on_projects_group(
+            self.project['id'], self.group['id'], src_role['id'])
+        # Check role on projects group
+        self.roles_client.check_group_has_flag_on_inherited_to_project(
+            self.project['id'], self.group['id'], src_role['id'])
+        # Revoke role from projects group
+        self.roles_client.revoke_inherited_role_from_group_on_project(
+            self.project['id'], self.group['id'], src_role['id'])
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/base.py b/tempest/api/identity/base.py
index 91b3105..3bcae17 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -106,8 +106,8 @@
         cls.non_admin_roles_client = cls.os.roles_client
         cls.users_client = cls.os_adm.users_client
         cls.non_admin_users_client = cls.os.users_client
-        cls.services_client = cls.os_adm.services_v2_client
-        cls.endpoints_client = cls.os_adm.endpoints_v2_client
+        cls.services_client = cls.os_adm.identity_services_client
+        cls.endpoints_client = cls.os_adm.endpoints_client
 
     @classmethod
     def resource_setup(cls):
@@ -155,9 +155,9 @@
         cls.trusts_client = cls.os_adm.trusts_client
         cls.roles_client = cls.os_adm.roles_v3_client
         cls.token = cls.os_adm.token_v3_client
-        cls.endpoints_client = cls.os_adm.endpoints_client
+        cls.endpoints_client = cls.os_adm.endpoints_v3_client
         cls.regions_client = cls.os_adm.regions_client
-        cls.services_client = cls.os_adm.identity_services_client
+        cls.services_client = cls.os_adm.identity_services_v3_client
         cls.policies_client = cls.os_adm.policies_client
         cls.creds_client = cls.os_adm.credentials_client
         cls.groups_client = cls.os_adm.groups_client
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_projects.py b/tempest/api/identity/v3/test_projects.py
index 7049bcf..995b77e 100644
--- a/tempest/api/identity/v3/test_projects.py
+++ b/tempest/api/identity/v3/test_projects.py
@@ -33,10 +33,14 @@
         # user can successfully authenticate using his credentials and
         # project name from received projects list
         for project in resp['projects']:
+            # 'user_domain_id' needs to be specified otherwise tempest_lib
+            # assumes it to be 'default'
             token_id, body = self.non_admin_token.get_token(
                 username=self.os.credentials.username,
+                user_domain_id=self.os.credentials.user_domain_id,
                 password=self.os.credentials.password,
                 project_name=project['name'],
+                project_domain_id=project['domain_id'],
                 auth_data=True)
             self.assertNotEmpty(token_id)
             self.assertEqual(body['project']['id'], project['id'])
@@ -48,5 +52,7 @@
             lib_exc.Unauthorized,
             self.non_admin_token.get_token,
             username=self.os.credentials.username,
+            user_domain_id=self.os.credentials.user_domain_id,
             password=self.os.credentials.password,
-            project_name=alt_project_name)
+            project_name=alt_project_name,
+            project_domain_id=project['domain_id'])
diff --git a/tempest/api/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index 3151763..593bf2a 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -28,10 +28,15 @@
         user_id = creds.user_id
         username = creds.username
         password = creds.password
+        user_domain_id = creds.user_domain_id
 
-        token_id, resp = self.non_admin_token.get_token(user_id=user_id,
-                                                        password=password,
-                                                        auth_data=True)
+        # 'user_domain_id' needs to be specified otherwise tempest_lib assumes
+        # it to be 'default'
+        token_id, resp = self.non_admin_token.get_token(
+            user_id=user_id,
+            user_domain_id=user_domain_id,
+            password=password,
+            auth_data=True)
 
         self.assertNotEmpty(token_id)
         self.assertIsInstance(token_id, six.string_types)
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_floating_ips_admin_actions.py b/tempest/api/network/admin/test_floating_ips_admin_actions.py
index 6ad374b..baeaa0c 100644
--- a/tempest/api/network/admin/test_floating_ips_admin_actions.py
+++ b/tempest/api/network/admin/test_floating_ips_admin_actions.py
@@ -28,7 +28,6 @@
     @classmethod
     def setup_clients(cls):
         super(FloatingIPAdminTestJSON, cls).setup_clients()
-        cls.alt_client = cls.alt_manager.network_client
         cls.alt_floating_ips_client = cls.alt_manager.floating_ips_client
 
     @classmethod
@@ -68,7 +67,7 @@
         body = self.floating_ips_client.list_floatingips()
         floating_ip_ids = [f['id'] for f in body['floatingips']]
         # Check that nonadmin user doesn't see floating ip created from admin
-        # and floating ip that is created in another tenant (alt user)
+        # and floating ip that is created in another project (alt user)
         self.assertIn(self.floating_ip['id'], floating_ip_ids)
         self.assertNotIn(floating_ip_admin['floatingip']['id'],
                          floating_ip_ids)
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index 78d6aea..7a4547a 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -74,14 +74,14 @@
         # query and setup steps are only required if the extension is available
         # and only if the router's default type is distributed.
         if test.is_extension_enabled('dvr', 'network'):
-            cls.is_dvr_router = cls.admin_client.show_router(
+            cls.is_dvr_router = cls.admin_routers_client.show_router(
                 cls.router['id'])['router'].get('distributed', False)
             if cls.is_dvr_router:
                 cls.network = cls.create_network()
                 cls.subnet = cls.create_subnet(cls.network)
                 cls.port = cls.create_port(cls.network)
-                cls.client.add_router_interface(cls.router['id'],
-                                                port_id=cls.port['id'])
+                cls.routers_client.add_router_interface(
+                    cls.router['id'], port_id=cls.port['id'])
                 # NOTE: Sometimes we have seen this test fail with dvr in,
                 # multinode tests, since the dhcp port is not created before
                 # the test gets executed and so the router is not scheduled
@@ -92,15 +92,15 @@
                 external_gateway_info = {
                     'network_id': CONF.network.public_network_id,
                     'enable_snat': True}
-                cls.admin_client.update_router_with_snat_gw_info(
+                cls.admin_routers_client.update_router_with_snat_gw_info(
                     cls.router['id'],
                     external_gateway_info=external_gateway_info)
 
     @classmethod
     def resource_cleanup(cls):
         if cls.is_dvr_router:
-            cls.client.remove_router_interface(cls.router['id'],
-                                               port_id=cls.port['id'])
+            cls.routers_client.remove_router_interface(cls.router['id'],
+                                                       port_id=cls.port['id'])
         super(L3AgentSchedulerTestJSON, cls).resource_cleanup()
 
     @test.idempotent_id('b7ce6e89-e837-4ded-9b78-9ed3c9c6a45a')
@@ -114,7 +114,8 @@
             self.agent['id'],
             router_id=self.router['id'])
         body = (
-            self.admin_client.list_l3_agents_hosting_router(self.router['id']))
+            self.admin_routers_client.list_l3_agents_hosting_router(
+                self.router['id']))
         for agent in body['agents']:
             l3_agent_ids.append(agent['id'])
             self.assertIn('agent_type', agent)
diff --git a/tempest/api/network/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index a1a881d..7b037d5 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -24,7 +24,7 @@
         set network quota and exceed this quota
 
     v2.0 of the API is assumed.
-    It is also assumed that the per-tenant quota extension API is configured
+    It is also assumed that the per-project quota extension API is configured
     in /etc/neutron/neutron.conf as follows:
 
         quota_driver = neutron.db.quota_db.DbQuotaDriver
@@ -60,7 +60,7 @@
                         n2['network']['id'])
 
         # Try to create a third network while the quota is two
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 lib_exc.Conflict,
                 "An object with that identifier already exists\\n" +
                 "Details.*Quota exceeded for resources: \['network'\].*"):
diff --git a/tempest/api/network/admin/test_quotas.py b/tempest/api/network/admin/test_quotas.py
index d72e960..ea3d59a 100644
--- a/tempest/api/network/admin/test_quotas.py
+++ b/tempest/api/network/admin/test_quotas.py
@@ -24,13 +24,13 @@
 class QuotasTest(base.BaseAdminNetworkTest):
     """Tests the following operations in the Neutron API:
 
-        list quotas for tenants who have non-default quota values
-        show quotas for a specified tenant
-        update quotas for a specified tenant
-        reset quotas to default values for a specified tenant
+        list quotas for projects who have non-default quota values
+        show quotas for a specified project
+        update quotas for a specified project
+        reset quotas to default values for a specified project
 
     v2.0 of the API is assumed.
-    It is also assumed that the per-tenant quota extension API is configured
+    It is also assumed that the per-project quota extension API is configured
     in /etc/neutron/neutron.conf as follows:
 
         quota_driver = neutron.db.quota_db.DbQuotaDriver
@@ -49,7 +49,7 @@
         cls.identity_admin_client = cls.os_adm.identity_client
 
     def _check_quotas(self, new_quotas):
-        # Add a tenant to conduct the test
+        # Add a project to conduct the test
         project = data_utils.rand_name('test_project_')
         description = data_utils.rand_name('desc_')
         project = self.identity_utils.create_project(name=project,
@@ -57,14 +57,14 @@
         project_id = project['id']
         self.addCleanup(self.identity_utils.delete_project, project_id)
 
-        # Change quotas for tenant
+        # Change quotas for project
         quota_set = self.admin_quotas_client.update_quotas(
             project_id, **new_quotas)['quota']
         self.addCleanup(self._cleanup_quotas, project_id)
         for key, value in six.iteritems(new_quotas):
             self.assertEqual(value, quota_set[key])
 
-        # Confirm our tenant is listed among tenants with non default quotas
+        # Confirm our project is listed among projects with non default quotas
         non_default_quotas = self.admin_quotas_client.list_quotas()
         found = False
         for qs in non_default_quotas['quotas']:
@@ -72,7 +72,7 @@
                 found = True
         self.assertTrue(found)
 
-        # Confirm from API quotas were changed as requested for tenant
+        # Confirm from API quotas were changed as requested for project
         quota_set = self.admin_quotas_client.show_quotas(project_id)
         quota_set = quota_set['quota']
         for key, value in six.iteritems(new_quotas):
diff --git a/tempest/api/network/admin/test_routers_dvr.py b/tempest/api/network/admin/test_routers_dvr.py
index 3e787af..36cb15f 100644
--- a/tempest/api/network/admin/test_routers_dvr.py
+++ b/tempest/api/network/admin/test_routers_dvr.py
@@ -34,8 +34,8 @@
         # has a distributed attribute.
         super(RoutersTestDVR, cls).resource_setup()
         name = data_utils.rand_name('pretest-check')
-        router = cls.admin_client.create_router(name)
-        cls.admin_client.delete_router(router['router']['id'])
+        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"
             raise cls.skipException(msg)
@@ -53,8 +53,9 @@
         set to True
         """
         name = data_utils.rand_name('router')
-        router = self.admin_client.create_router(name, distributed=True)
-        self.addCleanup(self.admin_client.delete_router,
+        router = self.admin_routers_client.create_router(name=name,
+                                                         distributed=True)
+        self.addCleanup(self.admin_routers_client.delete_router,
                         router['router']['id'])
         self.assertTrue(router['router']['distributed'])
 
@@ -72,8 +73,9 @@
         as opposed to a "Distributed Virtual Router"
         """
         name = data_utils.rand_name('router')
-        router = self.admin_client.create_router(name, distributed=False)
-        self.addCleanup(self.admin_client.delete_router,
+        router = self.admin_routers_client.create_router(name=name,
+                                                         distributed=False)
+        self.addCleanup(self.admin_routers_client.delete_router,
                         router['router']['id'])
         self.assertFalse(router['router']['distributed'])
 
@@ -93,11 +95,12 @@
         """
         name = data_utils.rand_name('router')
         # router needs to be in admin state down in order to be upgraded to DVR
-        router = self.admin_client.create_router(name, distributed=False,
-                                                 admin_state_up=False)
-        self.addCleanup(self.admin_client.delete_router,
+        router = self.admin_routers_client.create_router(name=name,
+                                                         distributed=False,
+                                                         admin_state_up=False)
+        self.addCleanup(self.admin_routers_client.delete_router,
                         router['router']['id'])
         self.assertFalse(router['router']['distributed'])
-        router = self.admin_client.update_router(router['router']['id'],
-                                                 distributed=True)
+        router = self.admin_routers_client.update_router(
+            router['router']['id'], distributed=True)
         self.assertTrue(router['router']['distributed'])
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 1e636f1..9823345 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -32,11 +32,11 @@
     Therefore, v2.x of the Neutron API is assumed. It is also assumed that the
     following options are defined in the [network] section of etc/tempest.conf:
 
-        tenant_network_cidr with a block of cidr's from which smaller blocks
-        can be allocated for tenant networks
+        project_network_cidr with a block of cidr's from which smaller blocks
+        can be allocated for project networks
 
-        tenant_network_mask_bits with the mask bits to be used to partition the
-        block defined by tenant-network_cidr
+        project_network_mask_bits with the mask bits to be used to partition
+        the block defined by project-network_cidr
 
     Finally, it is assumed that the following option is defined in the
     [service_available] section of etc/tempest.conf
@@ -67,10 +67,10 @@
     @classmethod
     def setup_clients(cls):
         super(BaseNetworkTest, cls).setup_clients()
-        cls.client = cls.os.network_client
         cls.agents_client = cls.os.network_agents_client
         cls.network_extensions_client = cls.os.network_extensions_client
         cls.networks_client = cls.os.networks_client
+        cls.routers_client = cls.os.routers_client
         cls.subnetpools_client = cls.os.subnetpools_client
         cls.subnets_client = cls.os.subnets_client
         cls.ports_client = cls.os.ports_client
@@ -175,12 +175,12 @@
         ip_version = ip_version if ip_version is not None else cls._ip_version
         gateway_not_set = gateway == ''
         if ip_version == 4:
-            cidr = cidr or netaddr.IPNetwork(CONF.network.tenant_network_cidr)
-            mask_bits = mask_bits or CONF.network.tenant_network_mask_bits
+            cidr = cidr or netaddr.IPNetwork(CONF.network.project_network_cidr)
+            mask_bits = mask_bits or CONF.network.project_network_mask_bits
         elif ip_version == 6:
-            cidr = (
-                cidr or netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr))
-            mask_bits = mask_bits or CONF.network.tenant_network_v6_mask_bits
+            cidr = (cidr or
+                    netaddr.IPNetwork(CONF.network.project_network_v6_cidr))
+            mask_bits = mask_bits or CONF.network.project_network_v6_mask_bits
         # Find a cidr that is not in use yet and create a subnet with it
         for subnet_cidr in cidr.subnet(mask_bits):
             if gateway_not_set:
@@ -231,8 +231,8 @@
             ext_gw_info['network_id'] = external_network_id
         if enable_snat is not None:
             ext_gw_info['enable_snat'] = enable_snat
-        body = cls.client.create_router(
-            router_name, external_gateway_info=ext_gw_info,
+        body = cls.routers_client.create_router(
+            name=router_name, external_gateway_info=ext_gw_info,
             admin_state_up=admin_state_up, **kwargs)
         router = body['router']
         cls.routers.append(router)
@@ -250,22 +250,22 @@
     @classmethod
     def create_router_interface(cls, router_id, subnet_id):
         """Wrapper utility that returns a router interface."""
-        interface = cls.client.add_router_interface(router_id,
-                                                    subnet_id=subnet_id)
+        interface = cls.routers_client.add_router_interface(
+            router_id, subnet_id=subnet_id)
         return interface
 
     @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:
-                cls.client.remove_router_interface(
+                cls.routers_client.remove_router_interface(
                     router['id'],
                     subnet_id=i['fixed_ips'][0]['subnet_id'])
             except lib_exc.NotFound:
                 pass
-        cls.client.delete_router(router['id'])
+        cls.routers_client.delete_router(router['id'])
 
 
 class BaseAdminNetworkTest(BaseNetworkTest):
@@ -275,9 +275,9 @@
     @classmethod
     def setup_clients(cls):
         super(BaseAdminNetworkTest, cls).setup_clients()
-        cls.admin_client = cls.os_adm.network_client
         cls.admin_agents_client = cls.os_adm.network_agents_client
         cls.admin_networks_client = cls.os_adm.networks_client
+        cls.admin_routers_client = cls.os_adm.routers_client
         cls.admin_subnets_client = cls.os_adm.subnets_client
         cls.admin_ports_client = cls.os_adm.ports_client
         cls.admin_quotas_client = cls.os_adm.network_quotas_client
diff --git a/tempest/api/network/base_routers.py b/tempest/api/network/base_routers.py
index 3495b76f..807257f 100644
--- a/tempest/api/network/base_routers.py
+++ b/tempest/api/network/base_routers.py
@@ -33,31 +33,31 @@
         self.addCleanup(self._cleanup_router, router)
         return router
 
-    def _delete_router(self, router_id, network_client=None):
-        client = network_client or self.client
+    def _delete_router(self, router_id, routers_client=None):
+        client = routers_client or self.routers_client
         client.delete_router(router_id)
         # Asserting that the router is not found in the list
         # after deletion
-        list_body = self.client.list_routers()
+        list_body = self.routers_client.list_routers()
         routers_list = list()
         for router in list_body['routers']:
             routers_list.append(router['id'])
         self.assertNotIn(router_id, routers_list)
 
     def _add_router_interface_with_subnet_id(self, router_id, subnet_id):
-        interface = self.client.add_router_interface(router_id,
-                                                     subnet_id=subnet_id)
+        interface = self.routers_client.add_router_interface(
+            router_id, subnet_id=subnet_id)
         self.addCleanup(self._remove_router_interface_with_subnet_id,
                         router_id, subnet_id)
         self.assertEqual(subnet_id, interface['subnet_id'])
         return interface
 
     def _remove_router_interface_with_subnet_id(self, router_id, subnet_id):
-        body = self.client.remove_router_interface(router_id,
-                                                   subnet_id=subnet_id)
+        body = self.routers_client.remove_router_interface(router_id,
+                                                           subnet_id=subnet_id)
         self.assertEqual(subnet_id, body['subnet_id'])
 
     def _remove_router_interface_with_port_id(self, router_id, port_id):
-        body = self.client.remove_router_interface(router_id,
-                                                   port_id=port_id)
+        body = self.routers_client.remove_router_interface(router_id,
+                                                           port_id=port_id)
         self.assertEqual(port_id, body['port_id'])
diff --git a/tempest/api/network/test_allowed_address_pair.py b/tempest/api/network/test_allowed_address_pair.py
index 394aec1..b2892e5 100644
--- a/tempest/api/network/test_allowed_address_pair.py
+++ b/tempest/api/network/test_allowed_address_pair.py
@@ -100,7 +100,7 @@
     @test.idempotent_id('4d6d178f-34f6-4bff-a01c-0a2f8fe909e4')
     def test_update_port_with_cidr_address_pair(self):
         # Update allowed address pair with cidr
-        cidr = str(netaddr.IPNetwork(CONF.network.tenant_network_cidr))
+        cidr = str(netaddr.IPNetwork(CONF.network.project_network_cidr))
         self._update_port_with_address(cidr)
 
     @test.idempotent_id('b3f20091-6cd5-472b-8487-3516137df933')
diff --git a/tempest/api/network/test_dhcp_ipv6.py b/tempest/api/network/test_dhcp_ipv6.py
index f59ecff..77008ab 100644
--- a/tempest/api/network/test_dhcp_ipv6.py
+++ b/tempest/api/network/test_dhcp_ipv6.py
@@ -68,8 +68,8 @@
         for port in ports:
             if (port['device_owner'].startswith('network:router_interface') and
                 port['device_id'] in [r['id'] for r in self.routers]):
-                self.client.remove_router_interface(port['device_id'],
-                                                    port_id=port['id'])
+                self.routers_client.remove_router_interface(port['device_id'],
+                                                            port_id=port['id'])
             else:
                 if port['id'] in [p['id'] for p in self.ports]:
                     self.ports_client.delete_port(port['id'])
@@ -80,11 +80,11 @@
             if subnet['id'] in [s['id'] for s in self.subnets]:
                 self.subnets_client.delete_subnet(subnet['id'])
                 self._remove_from_list_by_index(self.subnets, subnet)
-        body = self.client.list_routers()
+        body = self.routers_client.list_routers()
         routers = body['routers']
         for router in routers:
             if router['id'] in [r['id'] for r in self.routers]:
-                self.client.delete_router(router['id'])
+                self.routers_client.delete_router(router['id'])
                 self._remove_from_list_by_index(self.routers, router)
 
     def _get_ips_from_subnet(self, **kwargs):
@@ -338,12 +338,12 @@
                          fixed_ips=[
                              {'subnet_id': subnet['id'],
                               'ip_address': ip}])
-        self.assertRaisesRegexp(lib_exc.Conflict,
-                                "object with that identifier already exists",
-                                self.create_port,
-                                self.network,
-                                fixed_ips=[{'subnet_id': subnet['id'],
-                                            'ip_address': ip}])
+        self.assertRaisesRegex(lib_exc.Conflict,
+                               "object with that identifier already exists",
+                               self.create_port,
+                               self.network,
+                               fixed_ips=[{'subnet_id': subnet['id'],
+                                           'ip_address': ip}])
 
     def _create_subnet_router(self, kwargs):
         subnet = self.create_subnet(self.network, **kwargs)
diff --git a/tempest/api/network/test_extensions.py b/tempest/api/network/test_extensions.py
index d71d600..2c981a1 100644
--- a/tempest/api/network/test_extensions.py
+++ b/tempest/api/network/test_extensions.py
@@ -31,7 +31,7 @@
     @test.attr(type='smoke')
     @test.idempotent_id('ef28c7e6-e646-4979-9d67-deb207bc5564')
     def test_list_show_extensions(self):
-        # List available extensions for the tenant
+        # List available extensions for the project
         expected_alias = ['security-group', 'l3_agent_scheduler',
                           'ext-gw-mode', 'binding', 'quotas',
                           'agent', 'dhcp_agent_scheduler', 'provider',
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 ffe0336..a5fb25c 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
@@ -30,12 +29,12 @@
 class NetworksTest(base.BaseNetworkTest):
     """Tests the following operations in the Neutron API:
 
-        create a network for a tenant
-        list tenant's networks
-        show a tenant network details
-        create a subnet for a tenant
-        list tenant's subnets
-        show a tenant subnet details
+        create a network for a project
+        list project's networks
+        show a project network details
+        create a subnet for a project
+        list project's subnets
+        show a project subnet details
         network update
         subnet update
         delete a network also deletes its subnets
@@ -46,15 +45,15 @@
     v2.0 of the Neutron API is assumed. It is also assumed that the following
     options are defined in the [network] section of etc/tempest.conf:
 
-        tenant_network_cidr with a block of cidr's from which smaller blocks
-        can be allocated for tenant ipv4 subnets
+        project_network_cidr with a block of cidr's from which smaller blocks
+        can be allocated for project ipv4 subnets
 
-        tenant_network_v6_cidr is the equivalent for ipv6 subnets
+        project_network_v6_cidr is the equivalent for ipv6 subnets
 
-        tenant_network_mask_bits with the mask bits to be used to partition the
-        block defined by tenant_network_cidr
+        project_network_mask_bits with the mask bits to be used to partition
+        the block defined by project_network_cidr
 
-        tenant_network_v6_mask_bits is the equivalent for ipv6 subnets
+        project_network_v6_mask_bits is the equivalent for ipv6 subnets
     """
 
     @classmethod
@@ -93,14 +92,14 @@
 
     @classmethod
     def _create_subnet_with_last_subnet_block(cls, network, ip_version):
-        # Derive last subnet CIDR block from tenant CIDR and
+        # Derive last subnet CIDR block from project CIDR and
         # create the subnet with that derived CIDR
         if ip_version == 4:
-            cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
-            mask_bits = CONF.network.tenant_network_mask_bits
+            cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+            mask_bits = CONF.network.project_network_mask_bits
         elif ip_version == 6:
-            cidr = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
-            mask_bits = CONF.network.tenant_network_v6_mask_bits
+            cidr = netaddr.IPNetwork(CONF.network.project_network_v6_cidr)
+            mask_bits = CONF.network.project_network_v6_mask_bits
 
         subnet_cidr = list(cidr.subnet(mask_bits))[-1]
         gateway_ip = str(netaddr.IPAddress(subnet_cidr) + 1)
@@ -111,11 +110,11 @@
     def _get_gateway_from_tempest_conf(cls, ip_version):
         """Return first subnet gateway for configured CIDR """
         if ip_version == 4:
-            cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
-            mask_bits = CONF.network.tenant_network_mask_bits
+            cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+            mask_bits = CONF.network.project_network_mask_bits
         elif ip_version == 6:
-            cidr = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
-            mask_bits = CONF.network.tenant_network_v6_mask_bits
+            cidr = netaddr.IPNetwork(CONF.network.project_network_v6_cidr)
+            mask_bits = CONF.network.project_network_v6_mask_bits
 
         if mask_bits >= cidr.prefixlen:
             return netaddr.IPAddress(cidr) + 1
@@ -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,35 +389,30 @@
         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):
+class BulkNetworkOpsTest(base.BaseNetworkTest):
     """Tests the following operations in the Neutron API:
 
         bulk network creation
         bulk subnet creation
         bulk port creation
-        list tenant's networks
+        list project's networks
 
     v2.0 of the Neutron API is assumed. It is also assumed that the following
     options are defined in the [network] section of etc/tempest.conf:
 
-        tenant_network_cidr with a block of cidr's from which smaller blocks
-        can be allocated for tenant networks
+        project_network_cidr with a block of cidr's from which smaller blocks
+        can be allocated for project networks
 
-        tenant_network_mask_bits with the mask bits to be used to partition the
-        block defined by tenant-network_cidr
+        project_network_mask_bits with the mask bits to be used to partition
+        the block defined by project-network_cidr
     """
 
     def _delete_networks(self, created_networks):
@@ -451,7 +448,7 @@
         # Creates 2 networks in one request
         network_list = [{'name': data_utils.rand_name('network-')},
                         {'name': data_utils.rand_name('network-')}]
-        body = self.client.create_bulk_network(networks=network_list)
+        body = self.networks_client.create_bulk_networks(networks=network_list)
         created_networks = body['networks']
         self.addCleanup(self._delete_networks, created_networks)
         # Asserting that the networks are found in the list after creation
@@ -467,11 +464,11 @@
         networks = [self.create_network(), self.create_network()]
         # Creates 2 subnets in one request
         if self._ip_version == 4:
-            cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
-            mask_bits = CONF.network.tenant_network_mask_bits
+            cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+            mask_bits = CONF.network.project_network_mask_bits
         else:
-            cidr = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
-            mask_bits = CONF.network.tenant_network_v6_mask_bits
+            cidr = netaddr.IPNetwork(CONF.network.project_network_v6_cidr)
+            mask_bits = CONF.network.project_network_v6_mask_bits
 
         cidrs = [subnet_cidr for subnet_cidr in cidr.subnet(mask_bits)]
 
@@ -486,7 +483,7 @@
             }
             subnets_list.append(p1)
         del subnets_list[1]['name']
-        body = self.client.create_bulk_subnet(subnets=subnets_list)
+        body = self.subnets_client.create_bulk_subnets(subnets=subnets_list)
         created_subnets = body['subnets']
         self.addCleanup(self._delete_subnets, created_subnets)
         # Asserting that the subnets are found in the list after creation
@@ -512,7 +509,7 @@
             }
             port_list.append(p1)
         del port_list[1]['name']
-        body = self.client.create_bulk_port(ports=port_list)
+        body = self.ports_client.create_bulk_ports(ports=port_list)
         created_ports = body['ports']
         self.addCleanup(self._delete_ports, created_ports)
         # Asserting that the ports are found in the list after creation
@@ -523,16 +520,16 @@
             self.assertIn(n['id'], ports_list)
 
 
-class BulkNetworkOpsIpV6TestJSON(BulkNetworkOpsTestJSON):
+class BulkNetworkOpsIpV6Test(BulkNetworkOpsTest):
     _ip_version = 6
 
 
-class NetworksIpV6TestJSON(NetworksTest):
+class NetworksIpV6Test(NetworksTest):
     _ip_version = 6
 
     @test.idempotent_id('e41a4888-65a6-418c-a095-f7c2ef4ad59a')
     def test_create_delete_subnet_with_gw(self):
-        net = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
+        net = netaddr.IPNetwork(CONF.network.project_network_v6_cidr)
         gateway = str(netaddr.IPAddress(net.first + 2))
         name = data_utils.rand_name('network-')
         network = self.create_network(network_name=name)
@@ -542,7 +539,7 @@
 
     @test.idempotent_id('ebb4fd95-524f-46af-83c1-0305b239338f')
     def test_create_delete_subnet_with_default_gw(self):
-        net = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
+        net = netaddr.IPNetwork(CONF.network.project_network_v6_cidr)
         gateway_ip = str(netaddr.IPAddress(net.first + 1))
         name = data_utils.rand_name('network-')
         network = self.create_network(network_name=name)
@@ -579,7 +576,7 @@
                               'Subnet are not in the same network')
 
 
-class NetworksIpV6TestAttrs(NetworksIpV6TestJSON):
+class NetworksIpV6TestAttrs(NetworksIpV6Test):
 
     @classmethod
     def skip_checks(cls):
@@ -621,7 +618,7 @@
         subnet_ids = [subnet['id'] for subnet in subnets['subnets']]
         self.assertNotIn(subnet_slaac['id'], subnet_ids,
                          "Subnet wasn't deleted")
-        self.assertRaisesRegexp(
+        self.assertRaisesRegex(
             lib_exc.Conflict,
             "There are one or more ports still in use on the network",
             self.networks_client.delete_network,
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index d7b220b..caf7f14 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -75,7 +75,7 @@
         network2 = self.create_network(network_name=name)
         network_list = [network1['id'], network2['id']]
         port_list = [{'network_id': net_id} for net_id in network_list]
-        body = self.client.create_bulk_port(ports=port_list)
+        body = self.ports_client.create_bulk_ports(ports=port_list)
         created_ports = body['ports']
         port1 = created_ports[0]
         port2 = created_ports[1]
@@ -90,12 +90,12 @@
     def _get_ipaddress_from_tempest_conf(cls):
         """Return subnet with mask bits for configured CIDR """
         if cls._ip_version == 4:
-            cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
-            cidr.prefixlen = CONF.network.tenant_network_mask_bits
+            cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+            cidr.prefixlen = CONF.network.project_network_mask_bits
 
         elif cls._ip_version == 6:
-            cidr = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
-            cidr.prefixlen = CONF.network.tenant_network_v6_mask_bits
+            cidr = netaddr.IPNetwork(CONF.network.project_network_v6_cidr)
+            cidr.prefixlen = CONF.network.project_network_v6_mask_bits
 
         return cidr
 
@@ -131,12 +131,15 @@
         body = self.ports_client.show_port(self.port['id'])
         port = body['port']
         self.assertIn('id', port)
-        # TODO(Santosh)- This is a temporary workaround to compare create_port
-        # and show_port dict elements.Remove this once extra_dhcp_opts issue
-        # gets fixed in neutron.( bug - 1365341.)
+        # NOTE(rfolco): created_at and updated_at may get inconsistent values
+        # due to possible delay between POST request and resource creation.
+        # TODO(rfolco): Neutron Bug #1365341 is fixed, can remove the key
+        # extra_dhcp_opts in the O release (K/L gate jobs still need it).
         self.assertThat(self.port,
                         custom_matchers.MatchesDictExceptForKeys
-                        (port, excluded_keys=['extra_dhcp_opts']))
+                        (port, excluded_keys=['extra_dhcp_opts',
+                                              'created_at',
+                                              'updated_at']))
 
     @test.idempotent_id('45fcdaf2-dab0-4c13-ac6c-fcddfb579dbd')
     def test_show_port_fields(self):
@@ -197,13 +200,13 @@
         subnet = self.create_subnet(network)
         self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
         router = self.create_router(data_utils.rand_name('router-'))
-        self.addCleanup(self.client.delete_router, router['id'])
+        self.addCleanup(self.routers_client.delete_router, router['id'])
         port = self.ports_client.create_port(network_id=network['id'])
         # Add router interface to port created above
-        self.client.add_router_interface(router['id'],
-                                         port_id=port['port']['id'])
-        self.addCleanup(self.client.remove_router_interface, router['id'],
-                        port_id=port['port']['id'])
+        self.routers_client.add_router_interface(router['id'],
+                                                 port_id=port['port']['id'])
+        self.addCleanup(self.routers_client.remove_router_interface,
+                        router['id'], port_id=port['port']['id'])
         # List ports filtered by router_id
         port_list = self.ports_client.list_ports(device_id=router['id'])
         ports = port_list['ports']
@@ -425,11 +428,7 @@
 
 class PortsIpV6TestJSON(PortsTestJSON):
     _ip_version = 6
-    _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
-    _tenant_network_mask_bits = CONF.network.tenant_network_v6_mask_bits
 
 
 class PortsAdminExtendedAttrsIpV6TestJSON(PortsAdminExtendedAttrsTestJSON):
     _ip_version = 6
-    _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
-    _tenant_network_mask_bits = CONF.network.tenant_network_v6_mask_bits
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 0b64be4..3654b2e 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -41,9 +41,9 @@
     @classmethod
     def resource_setup(cls):
         super(RoutersTest, cls).resource_setup()
-        cls.tenant_cidr = (CONF.network.tenant_network_cidr
+        cls.tenant_cidr = (CONF.network.project_network_cidr
                            if cls._ip_version == 4 else
-                           CONF.network.tenant_network_v6_cidr)
+                           CONF.network.project_network_v6_cidr)
 
     @test.attr(type='smoke')
     @test.idempotent_id('f64403e2-8483-4b34-8ccd-b09a87bcc68c')
@@ -52,8 +52,8 @@
         # NOTE(salv-orlando): Do not invoke self.create_router
         # as we need to check the response code
         name = data_utils.rand_name('router-')
-        create_body = self.client.create_router(
-            name, external_gateway_info={
+        create_body = self.routers_client.create_router(
+            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'])
@@ -63,24 +63,25 @@
             CONF.network.public_network_id)
         self.assertEqual(create_body['router']['admin_state_up'], False)
         # Show details of the created router
-        show_body = self.client.show_router(create_body['router']['id'])
+        show_body = self.routers_client.show_router(
+            create_body['router']['id'])
         self.assertEqual(show_body['router']['name'], name)
         self.assertEqual(
             show_body['router']['external_gateway_info']['network_id'],
             CONF.network.public_network_id)
         self.assertEqual(show_body['router']['admin_state_up'], False)
         # List routers and verify if created router is there in response
-        list_body = self.client.list_routers()
+        list_body = self.routers_client.list_routers()
         routers_list = list()
         for router in list_body['routers']:
             routers_list.append(router['id'])
         self.assertIn(create_body['router']['id'], routers_list)
         # Update the name of router and verify if it is updated
         updated_name = 'updated ' + name
-        update_body = self.client.update_router(create_body['router']['id'],
-                                                name=updated_name)
+        update_body = self.routers_client.update_router(
+            create_body['router']['id'], name=updated_name)
         self.assertEqual(update_body['router']['name'], updated_name)
-        show_body = self.client.show_router(
+        show_body = self.routers_client.show_router(
             create_body['router']['id'])
         self.assertEqual(show_body['router']['name'], updated_name)
 
@@ -95,9 +96,9 @@
         self.addCleanup(self.identity_utils.delete_project, project_id)
 
         name = data_utils.rand_name('router-')
-        create_body = self.admin_client.create_router(name,
-                                                      tenant_id=project_id)
-        self.addCleanup(self.admin_client.delete_router,
+        create_body = self.admin_routers_client.create_router(
+            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'])
 
@@ -122,9 +123,9 @@
             external_gateway_info = {
                 'network_id': CONF.network.public_network_id,
                 'enable_snat': enable_snat}
-            create_body = self.admin_client.create_router(
-                name, external_gateway_info=external_gateway_info)
-            self.addCleanup(self.admin_client.delete_router,
+            create_body = self.admin_routers_client.create_router(
+                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
             self._verify_router_gateway(create_body['router']['id'],
@@ -137,8 +138,8 @@
         subnet = self.create_subnet(network)
         router = self._create_router(data_utils.rand_name('router-'))
         # Add router interface with subnet id
-        interface = self.client.add_router_interface(router['id'],
-                                                     subnet_id=subnet['id'])
+        interface = self.routers_client.add_router_interface(
+            router['id'], subnet_id=subnet['id'])
         self.addCleanup(self._remove_router_interface_with_subnet_id,
                         router['id'], subnet['id'])
         self.assertIn('subnet_id', interface.keys())
@@ -158,7 +159,7 @@
         port_body = self.ports_client.create_port(
             network_id=network['id'])
         # add router interface to port created above
-        interface = self.client.add_router_interface(
+        interface = self.routers_client.add_router_interface(
             router['id'],
             port_id=port_body['port']['id'])
         self.addCleanup(self._remove_router_interface_with_port_id,
@@ -172,7 +173,7 @@
                          router['id'])
 
     def _verify_router_gateway(self, router_id, exp_ext_gw_info=None):
-        show_body = self.admin_client.show_router(router_id)
+        show_body = self.admin_routers_client.show_router(router_id)
         actual_ext_gw_info = show_body['router']['external_gateway_info']
         if exp_ext_gw_info is None:
             self.assertIsNone(actual_ext_gw_info)
@@ -198,7 +199,7 @@
     @test.idempotent_id('6cc285d8-46bf-4f36-9b1a-783e3008ba79')
     def test_update_router_set_gateway(self):
         router = self._create_router(data_utils.rand_name('router-'))
-        self.client.update_router(
+        self.routers_client.update_router(
             router['id'],
             external_gateway_info={
                 'network_id': CONF.network.public_network_id})
@@ -212,7 +213,7 @@
     @test.requires_ext(extension='ext-gw-mode', service='network')
     def test_update_router_set_gateway_with_snat_explicit(self):
         router = self._create_router(data_utils.rand_name('router-'))
-        self.admin_client.update_router_with_snat_gw_info(
+        self.admin_routers_client.update_router_with_snat_gw_info(
             router['id'],
             external_gateway_info={
                 'network_id': CONF.network.public_network_id,
@@ -227,7 +228,7 @@
     @test.requires_ext(extension='ext-gw-mode', service='network')
     def test_update_router_set_gateway_without_snat(self):
         router = self._create_router(data_utils.rand_name('router-'))
-        self.admin_client.update_router_with_snat_gw_info(
+        self.admin_routers_client.update_router_with_snat_gw_info(
             router['id'],
             external_gateway_info={
                 'network_id': CONF.network.public_network_id,
@@ -243,7 +244,8 @@
         router = self._create_router(
             data_utils.rand_name('router-'),
             external_network_id=CONF.network.public_network_id)
-        self.client.update_router(router['id'], external_gateway_info={})
+        self.routers_client.update_router(router['id'],
+                                          external_gateway_info={})
         self._verify_router_gateway(router['id'])
         # No gateway port expected
         list_body = self.admin_ports_client.list_ports(
@@ -257,7 +259,7 @@
         router = self._create_router(
             data_utils.rand_name('router-'),
             external_network_id=CONF.network.public_network_id)
-        self.admin_client.update_router_with_snat_gw_info(
+        self.admin_routers_client.update_router_with_snat_gw_info(
             router['id'],
             external_gateway_info={
                 'network_id': CONF.network.public_network_id,
@@ -270,9 +272,9 @@
 
     @test.idempotent_id('c86ac3a8-50bd-4b00-a6b8-62af84a0765c')
     @test.requires_ext(extension='extraroute', service='network')
-    def test_update_extra_route(self):
+    def test_update_delete_extra_route(self):
         # Create different cidr for each subnet to avoid cidr duplicate
-        # The cidr starts from tenant_cidr
+        # The cidr starts from project_cidr
         next_cidr = netaddr.IPNetwork(self.tenant_cidr)
         # Prepare to build several routes
         test_routes = []
@@ -301,9 +303,9 @@
             )
 
         test_routes.sort(key=lambda x: x['destination'])
-        extra_route = self.client.update_extra_routes(router['id'],
-                                                      routes=test_routes)
-        show_body = self.client.show_router(router['id'])
+        extra_route = self.routers_client.update_extra_routes(
+            router['id'], routes=test_routes)
+        show_body = self.routers_client.show_router(router['id'])
         # Assert the number of routes
         self.assertEqual(routes_num, len(extra_route['router']['routes']))
         self.assertEqual(routes_num, len(show_body['router']['routes']))
@@ -323,18 +325,23 @@
                              routes[i]['destination'])
             self.assertEqual(test_routes[i]['nexthop'], routes[i]['nexthop'])
 
+        self.routers_client.delete_extra_routes(router['id'])
+        show_body_after_deletion = self.routers_client.show_router(
+            router['id'])
+        self.assertEmpty(show_body_after_deletion['router']['routes'])
+
     def _delete_extra_routes(self, router_id):
-        self.client.delete_extra_routes(router_id)
+        self.routers_client.delete_extra_routes(router_id)
 
     @test.idempotent_id('a8902683-c788-4246-95c7-ad9c6d63a4d9')
     def test_update_router_admin_state(self):
         router = self._create_router(data_utils.rand_name('router-'))
         self.assertFalse(router['admin_state_up'])
         # Update router admin state
-        update_body = self.client.update_router(router['id'],
-                                                admin_state_up=True)
+        update_body = self.routers_client.update_router(router['id'],
+                                                        admin_state_up=True)
         self.assertTrue(update_body['router']['admin_state_up'])
-        show_body = self.client.show_router(router['id'])
+        show_body = self.routers_client.show_router(router['id'])
         self.assertTrue(show_body['router']['admin_state_up'])
 
     @test.attr(type='smoke')
@@ -381,21 +388,21 @@
     @test.idempotent_id('141297aa-3424-455d-aa8d-f2d95731e00a')
     def test_create_distributed_router(self):
         name = data_utils.rand_name('router')
-        create_body = self.admin_client.create_router(
-            name, distributed=True)
+        create_body = self.admin_routers_client.create_router(
+            name=name, distributed=True)
         self.addCleanup(self._delete_router,
                         create_body['router']['id'],
-                        self.admin_client)
+                        self.admin_routers_client)
         self.assertTrue(create_body['router']['distributed'])
 
     @test.idempotent_id('644d7a4a-01a1-4b68-bb8d-0c0042cb1729')
     def test_convert_centralized_router(self):
         router = self._create_router(data_utils.rand_name('router'))
         self.assertNotIn('distributed', router)
-        update_body = self.admin_client.update_router(router['id'],
-                                                      distributed=True)
+        update_body = self.admin_routers_client.update_router(router['id'],
+                                                              distributed=True)
         self.assertTrue(update_body['router']['distributed'])
-        show_body = self.admin_client.show_router(router['id'])
+        show_body = self.admin_routers_client.show_router(router['id'])
         self.assertTrue(show_body['router']['distributed'])
-        show_body = self.client.show_router(router['id'])
+        show_body = self.routers_client.show_router(router['id'])
         self.assertNotIn('distributed', show_body['router'])
diff --git a/tempest/api/network/test_routers_negative.py b/tempest/api/network/test_routers_negative.py
index 84dbd8d..cd9f6ad 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -39,15 +39,15 @@
         cls.router = cls.create_router(data_utils.rand_name('router-'))
         cls.network = cls.create_network()
         cls.subnet = cls.create_subnet(cls.network)
-        cls.tenant_cidr = (CONF.network.tenant_network_cidr
+        cls.tenant_cidr = (CONF.network.project_network_cidr
                            if cls._ip_version == 4 else
-                           CONF.network.tenant_network_v6_cidr)
+                           CONF.network.project_network_v6_cidr)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('37a94fc0-a834-45b9-bd23-9a81d2fd1e22')
     def test_router_add_gateway_invalid_network_returns_404(self):
         self.assertRaises(lib_exc.NotFound,
-                          self.client.update_router,
+                          self.routers_client.update_router,
                           self.router['id'],
                           external_gateway_info={
                               'network_id': self.router['id']})
@@ -60,7 +60,7 @@
         sub_cidr = netaddr.IPNetwork(self.tenant_cidr).next()
         self.create_subnet(alt_network, cidr=sub_cidr)
         self.assertRaises(lib_exc.BadRequest,
-                          self.client.update_router,
+                          self.routers_client.update_router,
                           self.router['id'],
                           external_gateway_info={
                               'network_id': alt_network['id']})
@@ -84,31 +84,31 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('04df80f9-224d-47f5-837a-bf23e33d1c20')
     def test_router_remove_interface_in_use_returns_409(self):
-        self.client.add_router_interface(self.router['id'],
-                                         subnet_id=self.subnet['id'])
+        self.routers_client.add_router_interface(self.router['id'],
+                                                 subnet_id=self.subnet['id'])
         self.assertRaises(lib_exc.Conflict,
-                          self.client.delete_router,
+                          self.routers_client.delete_router,
                           self.router['id'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('c2a70d72-8826-43a7-8208-0209e6360c47')
     def test_show_non_existent_router_returns_404(self):
         router = data_utils.rand_name('non_exist_router')
-        self.assertRaises(lib_exc.NotFound, self.client.show_router,
+        self.assertRaises(lib_exc.NotFound, self.routers_client.show_router,
                           router)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('b23d1569-8b0c-4169-8d4b-6abd34fad5c7')
     def test_update_non_existent_router_returns_404(self):
         router = data_utils.rand_name('non_exist_router')
-        self.assertRaises(lib_exc.NotFound, self.client.update_router,
+        self.assertRaises(lib_exc.NotFound, self.routers_client.update_router,
                           router, name="new_name")
 
     @test.attr(type=['negative'])
     @test.idempotent_id('c7edc5ad-d09d-41e6-a344-5c0c31e2e3e4')
     def test_delete_non_existent_router_returns_404(self):
         router = data_utils.rand_name('non_exist_router')
-        self.assertRaises(lib_exc.NotFound, self.client.delete_router,
+        self.assertRaises(lib_exc.NotFound, self.routers_client.delete_router,
                           router)
 
 
diff --git a/tempest/api/network/test_security_groups.py b/tempest/api/network/test_security_groups.py
index 7d0765e..5312979 100644
--- a/tempest/api/network/test_security_groups.py
+++ b/tempest/api/network/test_security_groups.py
@@ -24,7 +24,7 @@
 
 
 class SecGroupTest(base.BaseSecGroupTest):
-    _tenant_network_cidr = CONF.network.tenant_network_cidr
+    _project_network_cidr = CONF.network.project_network_cidr
 
     @classmethod
     def skip_checks(cls):
@@ -71,7 +71,7 @@
     @test.attr(type='smoke')
     @test.idempotent_id('e30abd17-fef9-4739-8617-dc26da88e686')
     def test_list_security_groups(self):
-        # Verify the that security group belonging to tenant exist in list
+        # Verify the that security group belonging to project exist in list
         body = self.security_groups_client.list_security_groups()
         security_groups = body['security_groups']
         found = None
@@ -210,7 +210,7 @@
         protocol = 'tcp'
         port_range_min = 76
         port_range_max = 77
-        ip_prefix = self._tenant_network_cidr
+        ip_prefix = self._project_network_cidr
         self._create_verify_security_group_rule(sg_id, direction,
                                                 self.ethertype, protocol,
                                                 port_range_min,
@@ -239,4 +239,4 @@
 
 class SecGroupIPv6Test(SecGroupTest):
     _ip_version = 6
-    _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
+    _project_network_cidr = CONF.network.project_network_v6_cidr
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index 5213c18..86d0b46 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -24,7 +24,7 @@
 
 
 class NegativeSecGroupTest(base.BaseSecGroupTest):
-    _tenant_network_cidr = CONF.network.tenant_network_cidr
+    _project_network_cidr = CONF.network.project_network_cidr
 
     @classmethod
     def skip_checks(cls):
@@ -110,7 +110,7 @@
         sg2_body, _ = self._create_security_group()
 
         # Create rule specifying both remote_ip_prefix and remote_group_id
-        prefix = self._tenant_network_cidr
+        prefix = self._project_network_cidr
         self.assertRaises(
             lib_exc.BadRequest,
             self.security_group_rules_client.create_security_group_rule,
@@ -214,7 +214,7 @@
 
 class NegativeSecGroupIPv6Test(NegativeSecGroupTest):
     _ip_version = 6
-    _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
+    _project_network_cidr = CONF.network.project_network_v6_cidr
 
     @test.attr(type=['negative'])
     @test.idempotent_id('7607439c-af73-499e-bf64-f687fd12a842')
@@ -223,11 +223,11 @@
 
         # Create rule with bad remote_ip_prefix
         pairs = ({'ethertype': 'IPv6',
-                  'ip_prefix': CONF.network.tenant_network_cidr},
+                  'ip_prefix': CONF.network.project_network_cidr},
                  {'ethertype': 'IPv4',
-                  'ip_prefix': CONF.network.tenant_network_v6_cidr})
+                  'ip_prefix': CONF.network.project_network_v6_cidr})
         for pair in pairs:
-            self.assertRaisesRegexp(
+            self.assertRaisesRegex(
                 lib_exc.BadRequest,
                 "Conflicting value ethertype",
                 self.security_group_rules_client.create_security_group_rule,
diff --git a/tempest/api/network/test_subnetpools_extensions.py b/tempest/api/network/test_subnetpools_extensions.py
index d027132..c6cc8e2 100644
--- a/tempest/api/network/test_subnetpools_extensions.py
+++ b/tempest/api/network/test_subnetpools_extensions.py
@@ -30,7 +30,7 @@
         Lists subnet pool.
         Show subnet pool details.
 
-    v2.0 of the Neutron API is assumed. It is assumed that subnetpools
+    v2.0 of the Neutron API is assumed. It is assumed that subnet_allocation
     options mentioned in the [network-feature-enabled] section and
     default_network option mentioned in the [network] section of
     etc/tempest.conf:
@@ -40,8 +40,8 @@
     @classmethod
     def skip_checks(cls):
         super(SubnetPoolsTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('subnetpools', 'network'):
-            msg = "subnet pools extension not enabled."
+        if not test.is_extension_enabled('subnet_allocation', 'network'):
+            msg = "subnet_allocation extension not enabled."
             raise cls.skipException(msg)
 
     @test.attr(type='smoke')
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index 26ace81..d813263 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -46,7 +46,6 @@
         cls.client = cls.orchestration_client
         cls.servers_client = cls.os.servers_client
         cls.keypairs_client = cls.os.keypairs_client
-        cls.network_client = cls.os.network_client
         cls.networks_client = cls.os.networks_client
         cls.volumes_client = cls.os.volumes_client
         cls.images_v2_client = cls.os.image_client_v2
diff --git a/tempest/api/orchestration/stacks/templates/cinder_basic.yaml b/tempest/api/orchestration/stacks/templates/cinder_basic.yaml
index ffff580..61c271c 100644
--- a/tempest/api/orchestration/stacks/templates/cinder_basic.yaml
+++ b/tempest/api/orchestration/stacks/templates/cinder_basic.yaml
@@ -1,10 +1,15 @@
 heat_template_version: 2013-05-23
 
+parameters:
+  volume_size:
+    type: number
+    default: 1
+
 resources:
     volume:
         type: OS::Cinder::Volume
         properties:
-            size: 1
+            size: { get_param: volume_size }
             description: a descriptive description
             name: volume_name
 
diff --git a/tempest/api/orchestration/stacks/templates/cinder_basic_delete_retain.yaml b/tempest/api/orchestration/stacks/templates/cinder_basic_delete_retain.yaml
index b660c19..0bc6d69 100644
--- a/tempest/api/orchestration/stacks/templates/cinder_basic_delete_retain.yaml
+++ b/tempest/api/orchestration/stacks/templates/cinder_basic_delete_retain.yaml
@@ -1,11 +1,16 @@
 heat_template_version: 2013-05-23
 
+parameters:
+  volume_size:
+    type: number
+    default: 1
+
 resources:
     volume:
         deletion_policy: 'Retain'
         type: OS::Cinder::Volume
         properties:
-            size: 1
+            size: { get_param: volume_size }
             description: a descriptive description
             name: volume_name
 
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index 001bc08..3f45634 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -44,7 +44,6 @@
     @classmethod
     def setup_clients(cls):
         super(NeutronResourcesTestJSON, cls).setup_clients()
-        cls.network_client = cls.os.network_client
         cls.subnets_client = cls.os.subnets_client
         cls.ports_client = cls.os.ports_client
 
@@ -58,8 +57,8 @@
                             cls._create_keypair()['name'])
         cls.external_network_id = CONF.network.public_network_id
 
-        tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
-        mask_bits = CONF.network.tenant_network_mask_bits
+        tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+        mask_bits = CONF.network.project_network_mask_bits
         cls.subnet_cidr = tenant_cidr.subnet(mask_bits).next()
 
         # create the stack
@@ -151,7 +150,7 @@
     def test_created_router(self):
         """Verifies created router."""
         router_id = self.test_resources.get('Router')['physical_resource_id']
-        body = self.network_client.show_router(router_id)
+        body = self.routers_client.show_router(router_id)
         router = body['router']
         self.assertEqual(self.neutron_basic_template['resources'][
             'Router']['properties']['name'], router['name'])
diff --git a/tempest/api/orchestration/stacks/test_volumes.py b/tempest/api/orchestration/stacks/test_volumes.py
index 37e68ef..a5aaf6e 100644
--- a/tempest/api/orchestration/stacks/test_volumes.py
+++ b/tempest/api/orchestration/stacks/test_volumes.py
@@ -32,8 +32,7 @@
         self.assertIsNotNone(volume_id)
         volume = self.volumes_client.show_volume(volume_id)['volume']
         self.assertEqual('available', volume.get('status'))
-        self.assertEqual(template['resources']['volume']['properties'][
-            'size'], volume.get('size'))
+        self.assertEqual(CONF.volume.volume_size, volume.get('size'))
 
         # Some volume properties have been renamed with Cinder v2
         if CONF.volume_feature_enabled.api_v2:
@@ -51,8 +50,8 @@
     def _outputs_verify(self, stack_identifier, template):
         self.assertEqual('available',
                          self.get_stack_output(stack_identifier, 'status'))
-        self.assertEqual(str(template['resources']['volume']['properties'][
-            'size']), self.get_stack_output(stack_identifier, 'size'))
+        self.assertEqual(str(CONF.volume.volume_size),
+                         self.get_stack_output(stack_identifier, 'size'))
         self.assertEqual(template['resources']['volume']['properties'][
             'description'], self.get_stack_output(stack_identifier,
                                                   'display_description'))
@@ -65,7 +64,12 @@
         """Create and delete a volume via OS::Cinder::Volume."""
         stack_name = data_utils.rand_name('heat')
         template = self.read_template('cinder_basic')
-        stack_identifier = self.create_stack(stack_name, template)
+        stack_identifier = self.create_stack(
+            stack_name,
+            template,
+            parameters={
+                'volume_size': CONF.volume.volume_size
+            })
         self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
 
         # Verify with cinder that the volume exists, with matching details
@@ -94,7 +98,12 @@
         """Ensure the 'Retain' deletion policy is respected."""
         stack_name = data_utils.rand_name('heat')
         template = self.read_template('cinder_basic_delete_retain')
-        stack_identifier = self.create_stack(stack_name, template)
+        stack_identifier = self.create_stack(
+            stack_name,
+            template,
+            parameters={
+                'volume_size': CONF.volume.volume_size
+            })
         self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
 
         # Verify with cinder that the volume exists, with matching details
diff --git a/tempest/api/utils.py b/tempest/api/utils.py
deleted file mode 100644
index 00c93b7..0000000
--- a/tempest/api/utils.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-#    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.
-
-"""Common utilities used in testing."""
-
-from tempest import test
-
-
-class skip_unless_attr(object):
-    """Decorator that skips a test if a specified attr exists and is True."""
-    def __init__(self, attr, msg=None):
-        self.attr = attr
-        self.message = msg or ("Test case attribute %s not found "
-                               "or False") % attr
-
-    def __call__(self, func):
-        def _skipper(*args, **kw):
-            """Wrapped skipper function."""
-            testobj = args[0]
-            if not getattr(testobj, self.attr, False):
-                raise test.BaseTestCase.skipException(self.message)
-            func(*args, **kw)
-        _skipper.__name__ = func.__name__
-        _skipper.__doc__ = func.__doc__
-        return _skipper
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_services.py b/tempest/api/volume/admin/test_volume_services.py
index 47130de..755365d 100644
--- a/tempest/api/volume/admin/test_volume_services.py
+++ b/tempest/api/volume/admin/test_volume_services.py
@@ -12,9 +12,6 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
-
-from tempest.lib import decorators
-
 from tempest.api.volume import base
 from tempest import config
 from tempest import test
@@ -23,6 +20,12 @@
 CONF = config.CONF
 
 
+def _get_host(host):
+    if CONF.volume_feature_enabled.volume_services:
+        host = host.split('@')[0]
+    return host
+
+
 class VolumesServicesV2TestJSON(base.BaseVolumeAdminTest):
     """Tests Volume Services API.
 
@@ -34,7 +37,10 @@
         super(VolumesServicesV2TestJSON, cls).resource_setup()
         cls.services = (cls.admin_volume_services_client.list_services()
                         ['services'])
-        cls.host_name = cls.services[0]['host']
+        # NOTE: Cinder service-list API returns the list contains
+        # "<host name>@<driver name>" like "nova-compute01@lvmdriver-1".
+        # So here picks <host name> up as a host.
+        cls.host_name = _get_host(cls.services[0]['host'])
         cls.binary_name = cls.services[0]['binary']
 
     @test.idempotent_id('e0218299-0a59-4f43-8b2b-f1c035b3d26d')
@@ -51,16 +57,10 @@
         for service in services:
             self.assertEqual(self.binary_name, service['binary'])
 
-    @decorators.skip_because(bug="1530144")
     @test.idempotent_id('178710e4-7596-4e08-9333-745cb8bc4f8d')
     def test_get_service_by_host_name(self):
-        def get_host(host):
-            if CONF.volume_feature_enabled.volume_services:
-                host = host.split('@')[0]
-            return host
-
         services_on_host = [service for service in self.services if
-                            get_host(service['host']) == self.host_name]
+                            _get_host(service['host']) == self.host_name]
 
         services = (self.admin_volume_services_client.list_services(
             host=self.host_name)['services'])
@@ -80,7 +80,7 @@
             host=self.host_name, binary=self.binary_name))['services']
 
         self.assertEqual(1, len(services))
-        self.assertEqual(self.host_name, services[0]['host'])
+        self.assertEqual(self.host_name, _get_host(services[0]['host']))
         self.assertEqual(self.binary_name, services[0]['binary'])
 
 
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..e52216f 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -17,6 +17,7 @@
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib import exceptions
 from tempest import test
 import testtools
 
@@ -34,7 +35,6 @@
     @classmethod
     def resource_setup(cls):
         super(VolumesV2ActionsTest, cls).resource_setup()
-
         # Create a test shared instance
         srv_name = data_utils.rand_name(cls.__name__ + '-Instance')
         cls.server = cls.create_server(
@@ -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):
@@ -60,13 +61,15 @@
     @test.services('compute')
     def test_attach_detach_volume_to_instance(self):
         # Volume is attached and detached successfully from an instance
-        mountpoint = '/dev/vdc'
         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')
+                                  mountpoint='/dev/%s' %
+                                             CONF.compute.volume_device_name)
+        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,
@@ -87,21 +90,24 @@
     @test.services('compute')
     def test_get_volume_attachment(self):
         # Verify that a volume's attachment information is retrieved
-        mountpoint = '/dev/vdc'
         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')
+                                  mountpoint='/dev/%s' %
+                                             CONF.compute.volume_device_name)
+        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'])
         volume = self.client.show_volume(self.volume['id'])['volume']
         self.assertIn('attachments', volume)
         attachment = self.client.get_attachment_from_volume(volume)
-        self.assertEqual(mountpoint, attachment['device'])
+        self.assertEqual('/dev/%s' %
+                         CONF.compute.volume_device_name,
+                         attachment['device'])
         self.assertEqual(self.server['id'], attachment['server_id'])
         self.assertEqual(self.volume['id'], attachment['id'])
         self.assertEqual(self.volume['id'], attachment['volume_id'])
@@ -118,9 +124,18 @@
             self.volume['id'], image_name=image_name,
             disk_format=CONF.volume.disk_format)['os-volume_upload_image']
         image_id = body["image_id"]
-        self.addCleanup(self.image_client.delete_image, image_id)
+        self.addCleanup(self._cleanup_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')
+
+    def _cleanup_image(self, image_id):
+        # Ignores the image deletion
+        # in the case that image wasn't created in the first place
+        try:
+            self.image_client.delete_image(image_id)
+        except exceptions.NotFound:
+            pass
 
     @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..866e676 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."""
@@ -66,13 +68,12 @@
             name=server_name,
             wait_until='ACTIVE')
         self.addCleanup(self.servers_client.delete_server, server['id'])
-        mountpoint = '/dev/%s' % CONF.compute.volume_device_name
         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,
+            device='/dev/%s' % CONF.compute.volume_device_name)
+        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 +172,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/api_schema/response/__init__.py b/tempest/api_schema/response/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api_schema/response/__init__.py
+++ /dev/null
diff --git a/tempest/api_schema/response/compute/__init__.py b/tempest/api_schema/response/compute/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api_schema/response/compute/__init__.py
+++ /dev/null
diff --git a/tempest/api_schema/response/compute/v2_1/__init__.py b/tempest/api_schema/response/compute/v2_1/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api_schema/response/compute/v2_1/__init__.py
+++ /dev/null
diff --git a/tempest/api_schema/response/compute/v2_1/keypairs.py b/tempest/api_schema/response/compute/v2_1/keypairs.py
deleted file mode 100644
index 9c04c79..0000000
--- a/tempest/api_schema/response/compute/v2_1/keypairs.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# Copyright 2014 NEC Corporation.  All rights reserved.
-#
-#    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.
-
-get_keypair = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'keypair': {
-                'type': 'object',
-                'properties': {
-                    'public_key': {'type': 'string'},
-                    'name': {'type': 'string'},
-                    'fingerprint': {'type': 'string'},
-                    'user_id': {'type': 'string'},
-                    'deleted': {'type': 'boolean'},
-                    'created_at': {'type': 'string'},
-                    'updated_at': {'type': ['string', 'null']},
-                    'deleted_at': {'type': ['string', 'null']},
-                    'id': {'type': 'integer'}
-
-                },
-                'additionalProperties': False,
-                # When we run the get keypair API, response body includes
-                # all the above mentioned attributes.
-                # But in Nova API sample file, response body includes only
-                # 'public_key', 'name' & 'fingerprint'. So only 'public_key',
-                # 'name' & 'fingerprint' are defined as 'required'.
-                'required': ['public_key', 'name', 'fingerprint']
-            }
-        },
-        'additionalProperties': False,
-        'required': ['keypair']
-    }
-}
-
-create_keypair = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'keypair': {
-                'type': 'object',
-                'properties': {
-                    'fingerprint': {'type': 'string'},
-                    'name': {'type': 'string'},
-                    'public_key': {'type': 'string'},
-                    'user_id': {'type': 'string'},
-                    'private_key': {'type': 'string'}
-                },
-                'additionalProperties': False,
-                # When create keypair API is being called with 'Public key'
-                # (Importing keypair) then, response body does not contain
-                # 'private_key' So it is not defined as 'required'
-                'required': ['fingerprint', 'name', 'public_key', 'user_id']
-            }
-        },
-        'additionalProperties': False,
-        'required': ['keypair']
-    }
-}
-
-delete_keypair = {
-    'status_code': [202],
-}
-
-list_keypairs = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'keypairs': {
-                'type': 'array',
-                'items': {
-                    'type': 'object',
-                    'properties': {
-                        'keypair': {
-                            'type': 'object',
-                            'properties': {
-                                'public_key': {'type': 'string'},
-                                'name': {'type': 'string'},
-                                'fingerprint': {'type': 'string'}
-                            },
-                            'additionalProperties': False,
-                            'required': ['public_key', 'name', 'fingerprint']
-                        }
-                    },
-                    'additionalProperties': False,
-                    'required': ['keypair']
-                }
-            }
-        },
-        'additionalProperties': False,
-        'required': ['keypairs']
-    }
-}
diff --git a/tempest/clients.py b/tempest/clients.py
index 60baca6..0eded8b 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -46,6 +46,7 @@
 from tempest.lib.services.compute.instance_usage_audit_log_client import \
     InstanceUsagesAuditLogClient
 from tempest.lib.services.compute.interfaces_client import InterfacesClient
+from tempest.lib.services.compute.keypairs_client import KeyPairsClient
 from tempest.lib.services.compute.limits_client import LimitsClient
 from tempest.lib.services.compute.migrations_client import MigrationsClient
 from tempest.lib.services.compute.networks_client import NetworksClient \
@@ -87,6 +88,8 @@
 from tempest.lib.services.network.ports_client import PortsClient
 from tempest.lib.services.network.quotas_client import QuotasClient \
     as NetworkQuotasClient
+from tempest.lib.services.network.security_group_rules_client import \
+    SecurityGroupRulesClient
 from tempest.lib.services.network.security_groups_client import \
     SecurityGroupsClient
 from tempest.lib.services.network.subnetpools_client import SubnetpoolsClient
@@ -94,7 +97,6 @@
 from tempest import manager
 from tempest.services.baremetal.v1.json.baremetal_client import \
     BaremetalClient
-from tempest.services.compute.json.keypairs_client import KeyPairsClient
 from tempest.services.data_processing.v1_1.data_processing_client import \
     DataProcessingClient
 from tempest.services.database.json.flavors_client import \
@@ -103,42 +105,34 @@
     DatabaseLimitsClient
 from tempest.services.database.json.versions_client import \
     DatabaseVersionsClient
-from tempest.services.identity.v2.json.endpoints_client import \
-    EndpointsClient as EndpointsV2Client
-from tempest.services.identity.v2.json.identity_client import \
-    IdentityClient
-from tempest.services.identity.v2.json.roles_client import \
-    RolesClient
+from tempest.services.identity.v2.json.endpoints_client import EndpointsClient
+from tempest.services.identity.v2.json.identity_client import IdentityClient
+from tempest.services.identity.v2.json.roles_client import RolesClient
 from tempest.services.identity.v2.json.services_client import \
-    ServicesClient as ServicesV2Client
-from tempest.services.identity.v2.json.tenants_client import \
-    TenantsClient
-from tempest.services.identity.v2.json.users_client import \
-    UsersClient
+    ServicesClient as IdentityServicesClient
+from tempest.services.identity.v2.json.tenants_client import TenantsClient
+from tempest.services.identity.v2.json.users_client import UsersClient
 from tempest.services.identity.v3.json.credentials_client import \
-    CredentialsClient as CredentialsV3Client
+    CredentialsClient
 from tempest.services.identity.v3.json.domains_client import DomainsClient
 from tempest.services.identity.v3.json.endpoints_client import \
-    EndPointClient as EndPointV3Client
-from tempest.services.identity.v3.json.groups_client import \
-    GroupsClient as GroupsV3Client
-from tempest.services.identity.v3.json.identity_client import IdentityV3Client
-from tempest.services.identity.v3.json.policies_client import \
-    PoliciesClient as PoliciesV3Client
+    EndPointsClient as EndPointsV3Client
+from tempest.services.identity.v3.json.groups_client import GroupsClient
+from tempest.services.identity.v3.json.identity_client import \
+    IdentityClient as IdentityV3Client
+from tempest.services.identity.v3.json.policies_client import PoliciesClient
 from tempest.services.identity.v3.json.projects_client import ProjectsClient
-from tempest.services.identity.v3.json.regions_client import \
-    RegionsClient as RegionsV3Client
+from tempest.services.identity.v3.json.regions_client import RegionsClient
 from tempest.services.identity.v3.json.roles_client import \
     RolesClient as RolesV3Client
 from tempest.services.identity.v3.json.services_client import \
     ServicesClient as IdentityServicesV3Client
 from tempest.services.identity.v3.json.trusts_client import TrustsClient
-from tempest.services.identity.v3.json.users_clients import UsersV3Client
+from tempest.services.identity.v3.json.users_clients import \
+    UsersClient as UsersV3Client
 from tempest.services.image.v1.json.images_client import ImagesClient
 from tempest.services.image.v2.json.images_client import ImagesClientV2
-from tempest.services.network.json.network_client import NetworkClient
-from tempest.services.network.json.security_group_rules_client import \
-    SecurityGroupRulesClient
+from tempest.services.network.json.routers_client import RoutersClient
 from tempest.services.object_storage.account_client import AccountClient
 from tempest.services.object_storage.container_client import ContainerClient
 from tempest.services.object_storage.object_client import ObjectClient
@@ -242,14 +236,6 @@
             build_interval=CONF.network.build_interval,
             build_timeout=CONF.network.build_timeout,
             **self.default_params)
-        self.network_client = NetworkClient(
-            self.auth_provider,
-            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,
-            **self.default_params)
         self.networks_client = NetworksClient(
             self.auth_provider,
             CONF.network.catalog_type,
@@ -314,6 +300,14 @@
             build_interval=CONF.network.build_interval,
             build_timeout=CONF.network.build_timeout,
             **self.default_params)
+        self.routers_client = RoutersClient(
+            self.auth_provider,
+            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,
+            **self.default_params)
         self.security_group_rules_client = SecurityGroupRulesClient(
             self.auth_provider,
             CONF.network.catalog_type,
@@ -486,18 +480,16 @@
         # Clients below use the admin endpoint type of Keystone API v2
         params_v2_admin = params.copy()
         params_v2_admin['endpoint_type'] = CONF.identity.v2_admin_endpoint_type
-        self.endpoints_v2_client = EndpointsV2Client(self.auth_provider,
-                                                     **params_v2_admin)
+        self.endpoints_client = EndpointsClient(self.auth_provider,
+                                                **params_v2_admin)
         self.identity_client = IdentityClient(self.auth_provider,
                                               **params_v2_admin)
         self.tenants_client = TenantsClient(self.auth_provider,
                                             **params_v2_admin)
-        self.roles_client = RolesClient(self.auth_provider,
-                                        **params_v2_admin)
-        self.users_client = UsersClient(self.auth_provider,
-                                        **params_v2_admin)
-        self.services_v2_client = ServicesV2Client(self.auth_provider,
-                                                   **params_v2_admin)
+        self.roles_client = RolesClient(self.auth_provider, **params_v2_admin)
+        self.users_client = UsersClient(self.auth_provider, **params_v2_admin)
+        self.identity_services_client = IdentityServicesClient(
+            self.auth_provider, **params_v2_admin)
 
         # Clients below use the public endpoint type of Keystone API v2
         params_v2_public = params.copy()
@@ -519,18 +511,17 @@
                                                    **params_v3)
         self.trusts_client = TrustsClient(self.auth_provider, **params_v3)
         self.users_v3_client = UsersV3Client(self.auth_provider, **params_v3)
-        self.endpoints_client = EndPointV3Client(self.auth_provider,
-                                                 **params_v3)
+        self.endpoints_v3_client = EndPointsV3Client(self.auth_provider,
+                                                     **params_v3)
         self.roles_v3_client = RolesV3Client(self.auth_provider, **params_v3)
-        self.identity_services_client = IdentityServicesV3Client(
+        self.identity_services_v3_client = IdentityServicesV3Client(
             self.auth_provider, **params_v3)
-        self.policies_client = PoliciesV3Client(self.auth_provider,
-                                                **params_v3)
+        self.policies_client = PoliciesClient(self.auth_provider, **params_v3)
         self.projects_client = ProjectsClient(self.auth_provider, **params_v3)
-        self.regions_client = RegionsV3Client(self.auth_provider, **params_v3)
-        self.credentials_client = CredentialsV3Client(self.auth_provider,
-                                                      **params_v3)
-        self.groups_client = GroupsV3Client(self.auth_provider, **params_v3)
+        self.regions_client = RegionsClient(self.auth_provider, **params_v3)
+        self.credentials_client = CredentialsClient(self.auth_provider,
+                                                    **params_v3)
+        self.groups_client = GroupsClient(self.auth_provider, **params_v3)
 
         # Token clients do not use the catalog. They only need default_params.
         # They read auth_url, so they should only be set if the corresponding
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index b95111a..a154d0b 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -103,7 +103,7 @@
 from tempest.services.identity.v2.json import roles_client
 from tempest.services.identity.v2.json import tenants_client
 from tempest.services.identity.v2.json import users_client
-from tempest.services.network.json import network_client
+from tempest.services.network.json import routers_client
 
 LOG = None
 CONF = config.CONF
@@ -169,20 +169,20 @@
         endpoint_type='adminURL',
         **params
     )
-    network_admin = None
     networks_admin = None
+    routers_admin = None
     subnets_admin = None
     neutron_iso_networks = False
     if (CONF.service_available.neutron and
         CONF.auth.create_isolated_networks):
         neutron_iso_networks = True
-        network_admin = network_client.NetworkClient(
+        networks_admin = networks_client.NetworksClient(
             _auth,
             CONF.network.catalog_type,
             CONF.network.region or CONF.identity.region,
             endpoint_type='adminURL',
             **params)
-        networks_admin = networks_client.NetworksClient(
+        routers_admin = routers_client.RoutersClient(
             _auth,
             CONF.network.catalog_type,
             CONF.network.region or CONF.identity.region,
@@ -195,12 +195,13 @@
             endpoint_type='adminURL',
             **params)
     return (identity_admin, tenants_admin, roles_admin, users_admin,
-            neutron_iso_networks, network_admin, networks_admin, subnets_admin)
+            neutron_iso_networks, networks_admin, routers_admin,
+            subnets_admin)
 
 
 def create_resources(opts, resources):
     (identity_admin, tenants_admin, roles_admin, users_admin,
-     neutron_iso_networks, network_admin, networks_admin,
+     neutron_iso_networks, networks_admin, routers_admin,
      subnets_admin) = get_admin_clients(opts)
     roles = roles_admin.list_roles()['roles']
     for u in resources['users']:
@@ -246,8 +247,8 @@
         for u in resources['users']:
             tenant = identity.get_tenant_by_name(tenants_admin, u['tenant'])
             network_name, router_name = create_network_resources(
-                network_admin, networks_admin, subnets_admin, tenant['id'],
-                u['name'])
+                networks_admin, routers_admin, subnets_admin,
+                tenant['id'], u['name'])
             u['network'] = network_name
             u['router'] = router_name
         LOG.info('Networks created')
@@ -273,8 +274,9 @@
     LOG.info('Resources deployed successfully!')
 
 
-def create_network_resources(network_admin_client, networks_admin_client,
-                             subnets_admin_client, tenant_id, name):
+def create_network_resources(networks_admin_client,
+                             routers_admin_client, subnets_admin_client,
+                             tenant_id, name):
 
     def _create_network(name):
         resp_body = networks_admin_client.create_network(
@@ -282,8 +284,8 @@
         return resp_body['network']
 
     def _create_subnet(subnet_name, network_id):
-        base_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
-        mask_bits = CONF.network.tenant_network_mask_bits
+        base_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+        mask_bits = CONF.network.project_network_mask_bits
         for subnet_cidr in base_cidr.subnet(mask_bits):
             try:
                 resp_body = subnets_admin_client.\
@@ -305,14 +307,14 @@
     def _create_router(router_name):
         external_net_id = dict(
             network_id=CONF.network.public_network_id)
-        resp_body = network_admin_client.create_router(
-            router_name,
+        resp_body = routers_admin_client.create_router(
+            name=router_name,
             external_gateway_info=external_net_id,
             tenant_id=tenant_id)
         return resp_body['router']
 
     def _add_router_interface(router_id, subnet_id):
-        network_admin_client.add_router_interface(router_id,
+        routers_admin_client.add_router_interface(router_id,
                                                   subnet_id=subnet_id)
 
     network_name = name + "-network"
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index 5a52043..caba4b5 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -117,7 +117,6 @@
 
         if is_dry_run:
             self.dry_run_data["_tenants_to_clean"] = {}
-            f = open(DRY_RUN_JSON, 'w+')
 
         admin_mgr = self.admin_mgr
         # Always cleanup tempest and alt tempest tenants unless
@@ -146,9 +145,9 @@
             svc.run()
 
         if is_dry_run:
-            f.write(json.dumps(self.dry_run_data, sort_keys=True,
-                               indent=2, separators=(',', ': ')))
-            f.close()
+            with open(DRY_RUN_JSON, 'w+') as f:
+                f.write(json.dumps(self.dry_run_data, sort_keys=True,
+                                   indent=2, separators=(',', ': ')))
 
         self._remove_admin_user_roles()
 
@@ -191,7 +190,7 @@
         rl_cl = self.admin_mgr.roles_client
 
         tenant = identity.get_tenant_by_name(tn_cl,
-                                             CONF.auth.admin_tenant_name)
+                                             CONF.auth.admin_project_name)
         self.admin_tenant_id = tenant['id']
 
         user = identity.get_user_by_username(tn_cl, self.admin_tenant_id,
@@ -281,16 +280,15 @@
             svc = service(admin_mgr, **kwargs)
             svc.run()
 
-        f = open(SAVED_STATE_JSON, 'w+')
-        f.write(json.dumps(data,
-                           sort_keys=True, indent=2, separators=(',', ': ')))
-        f.close()
+        with open(SAVED_STATE_JSON, 'w+') as f:
+            f.write(json.dumps(data,
+                    sort_keys=True, indent=2, separators=(',', ': ')))
 
     def _load_json(self):
         try:
-            json_file = open(SAVED_STATE_JSON)
-            self.json_data = json.load(json_file)
-            json_file.close()
+            with open(SAVED_STATE_JSON) as json_file:
+                self.json_data = json.load(json_file)
+
         except IOError as ex:
             LOG.exception("Failed loading saved state, please be sure you"
                           " have first run cleanup with --init-saved-state "
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 0640a4e..f51fc53 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -33,7 +33,7 @@
 CONF_TENANTS = None
 CONF_USERS = None
 
-IS_CEILOMETER = None
+IS_AODH = None
 IS_CINDER = None
 IS_GLANCE = None
 IS_HEAT = None
@@ -51,14 +51,14 @@
     global CONF_PUB_ROUTER
     global CONF_TENANTS
     global CONF_USERS
-    global IS_CEILOMETER
+    global IS_AODH
     global IS_CINDER
     global IS_GLANCE
     global IS_HEAT
     global IS_NEUTRON
     global IS_NOVA
 
-    IS_CEILOMETER = CONF.service_available.ceilometer
+    IS_AODH = CONF.service_available.aodh
     IS_CINDER = CONF.service_available.cinder
     IS_GLANCE = CONF.service_available.glance
     IS_HEAT = CONF.service_available.heat
@@ -70,25 +70,25 @@
     CONF_PRIV_NETWORK_NAME = CONF.compute.fixed_network_name
     CONF_PUB_NETWORK = CONF.network.public_network_id
     CONF_PUB_ROUTER = CONF.network.public_router_id
-    CONF_TENANTS = [CONF.auth.admin_tenant_name,
-                    CONF.identity.tenant_name,
-                    CONF.identity.alt_tenant_name]
+    CONF_TENANTS = [CONF.auth.admin_project_name,
+                    CONF.identity.project_name,
+                    CONF.identity.alt_project_name]
     CONF_USERS = [CONF.auth.admin_username, CONF.identity.username,
                   CONF.identity.alt_username]
 
     if IS_NEUTRON:
         CONF_PRIV_NETWORK = _get_network_id(CONF.compute.fixed_network_name,
-                                            CONF.auth.admin_tenant_name)
+                                            CONF.auth.admin_project_name)
         CONF_NETWORKS = [CONF_PUB_NETWORK, CONF_PRIV_NETWORK]
 
 
-def _get_network_id(net_name, tenant_name):
+def _get_network_id(net_name, project_name):
     am = credentials.AdminManager()
     net_cl = am.networks_client
     tn_cl = am.tenants_client
 
     networks = net_cl.list_networks()
-    tenant = identity.get_tenant_by_name(tn_cl, tenant_name)
+    tenant = identity.get_tenant_by_name(tn_cl, project_name)
     t_id = tenant['id']
     n_id = None
     for net in networks['networks']:
@@ -382,7 +382,6 @@
 class NetworkService(BaseService):
     def __init__(self, manager, **kwargs):
         super(NetworkService, self).__init__(kwargs)
-        self.client = manager.network_client
         self.networks_client = manager.networks_client
         self.subnets_client = manager.subnets_client
         self.ports_client = manager.ports_client
@@ -390,6 +389,7 @@
         self.metering_labels_client = manager.metering_labels_client
         self.metering_label_rules_client = manager.metering_label_rules_client
         self.security_groups_client = manager.security_groups_client
+        self.routers_client = manager.routers_client
 
     def _filter_by_conf_networks(self, item_list):
         if not item_list or not all(('network_id' in i for i in item_list)):
@@ -449,7 +449,7 @@
 class NetworkRouterService(NetworkService):
 
     def list(self):
-        client = self.client
+        client = self.routers_client
         routers = client.list_routers(**self.tenant_filter)
         routers = routers['routers']
         if self.is_preserve:
@@ -460,13 +460,14 @@
         return routers
 
     def delete(self):
-        client = self.client
+        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'])
@@ -709,7 +710,7 @@
 class TelemetryAlarmService(BaseService):
     def __init__(self, manager, **kwargs):
         super(TelemetryAlarmService, self).__init__(kwargs)
-        self.client = manager.telemetry_client
+        self.client = manager.alarming_client
 
     def list(self):
         client = self.client
@@ -908,7 +909,7 @@
         if not self.is_save_state:
             tenants = [tenant for tenant in tenants if (tenant['id']
                        not in self.saved_state_json['tenants'].keys()
-                       and tenant['name'] != CONF.auth.admin_tenant_name)]
+                       and tenant['name'] != CONF.auth.admin_project_name)]
 
         if self.is_preserve:
             tenants = [tenant for tenant in tenants if tenant['name']
@@ -975,7 +976,7 @@
 
 def get_tenant_cleanup_services():
     tenant_services = []
-    if IS_CEILOMETER:
+    if IS_AODH:
         tenant_services.append(TelemetryAlarmService)
     if IS_NOVA:
         tenant_services.append(ServerService)
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index e474d86..2a4e314 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -127,13 +127,15 @@
 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 networks_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
 from tempest.services.identity.v2.json import tenants_client
 from tempest.services.identity.v2.json import users_client
 from tempest.services.image.v2.json import images_client
-from tempest.services.network.json import network_client
+from tempest.services.network.json import routers_client
 from tempest.services.object_storage import container_client
 from tempest.services.object_storage import object_client
 from tempest.services.telemetry.json import alarming_client
@@ -262,7 +264,23 @@
             build_interval=CONF.volume.build_interval,
             build_timeout=CONF.volume.build_timeout,
             **default_params)
-        self.networks = network_client.NetworkClient(
+        self.networks = networks_client.NetworksClient(
+            _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.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,
             CONF.network.region or CONF.identity.region,
@@ -739,10 +757,10 @@
 def _get_router_namespace(client, network):
     network_id = _get_resource_by_name(client.networks,
                                        'networks', network)['id']
-    n_body = client.networks.list_routers()
+    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
@@ -756,7 +774,7 @@
     # we cannot assume they all have the same signature so we need to discard
     # the unused response first value it two values are being returned.
     body = get_resources()
-    if type(body) == tuple:
+    if isinstance(body, tuple):
         body = body[1]
     if isinstance(body, dict):
         body = body[resource]
@@ -824,12 +842,12 @@
         client = client_for_user(router['owner'])
 
         # only create a router if the name isn't here
-        body = client.networks.list_routers()
+        body = client.routers.list_routers()
         if any(item['name'] == router['name'] for item in body['routers']):
             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):
@@ -841,9 +859,9 @@
         for subnet in router['subnet']:
             subnet_id = _get_resource_by_name(client.networks,
                                               'subnets', subnet)['id']
-            client.networks.remove_router_interface(router_id,
-                                                    subnet_id=subnet_id)
-        client.networks.delete_router(router_id)
+            client.routers.remove_router_interface(router_id,
+                                                   subnet_id=subnet_id)
+        client.routers.delete_router(router_id)
 
 
 def add_router_interface(routers):
@@ -856,13 +874,13 @@
             subnet_id = _get_resource_by_name(client.networks,
                                               'subnets', subnet)['id']
             # connect routers to their subnets
-            client.networks.add_router_interface(router_id,
-                                                 subnet_id=subnet_id)
+            client.routers.add_router_interface(router_id,
+                                                subnet_id=subnet_id)
         # connect routers to external network if set to "gateway"
         if router['gateway']:
             if CONF.network.public_network_id:
                 ext_net = CONF.network.public_network_id
-                client.networks._update_router(
+                client.routers._update_router(
                     router_id, set_enable_snat=True,
                     external_gateway_info={"network_id": ext_net})
             else:
@@ -1013,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/run_stress.py b/tempest/cmd/run_stress.py
old mode 100644
new mode 100755
index 6fe3928..9c8552f
--- a/tempest/cmd/run_stress.py
+++ b/tempest/cmd/run_stress.py
@@ -23,6 +23,7 @@
     # unittest in python 2.6 does not contain loader, so uses unittest2
     from unittest2 import loader
 import traceback
+import warnings
 
 from cliff import command
 from oslo_log import log as logging
@@ -74,7 +75,17 @@
 
 class TempestRunStress(command.Command):
 
+    @staticmethod
+    def display_deprecation_warning():
+        warnings.simplefilter('once', category=DeprecationWarning)
+        warnings.warn(
+            'Stress tests are deprecated and will be removed from Tempest '
+            'in the Newton release.',
+            DeprecationWarning)
+        warnings.resetwarnings()
+
     def get_parser(self, prog_name):
+        self.display_deprecation_warning()
         pa = super(TempestRunStress, self).get_parser(prog_name)
         pa = add_arguments(pa)
         return pa
@@ -146,6 +157,7 @@
 
 
 def main():
+    TempestRunStress.display_deprecation_warning()
     parser = argparse.ArgumentParser(description='Run stress tests')
     pa = add_arguments(parser)
     ns = pa.parse_args()
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 5e5e127..f29973d 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -15,7 +15,6 @@
 #    under the License.
 
 import argparse
-import httplib2
 import os
 import sys
 import traceback
@@ -29,6 +28,7 @@
 from tempest import clients
 from tempest.common import credentials_factory as credentials
 from tempest import config
+import tempest.lib.common.http
 
 
 CONF = config.CONF
@@ -91,13 +91,21 @@
     }
     client_dict[service].skip_path()
     endpoint = _get_unversioned_endpoint(client_dict[service].base_url)
-    dscv = CONF.identity.disable_ssl_certificate_validation
-    ca_certs = CONF.identity.ca_certificates_file
-    raw_http = httplib2.Http(disable_ssl_certificate_validation=dscv,
-                             ca_certs=ca_certs)
-    __, body = raw_http.request(endpoint, 'GET')
+
+    http = tempest.lib.common.http.ClosingHttp(
+        CONF.identity.disable_ssl_certificate_validation,
+        CONF.identity.ca_certificates_file)
+
+    __, body = 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:
@@ -354,8 +362,6 @@
     outfile = sys.stdout
     if update:
         conf_file = _get_config_file()
-        if opts.output:
-            outfile = open(opts.output, 'w+')
         CONF_PARSER = moves.configparser.SafeConfigParser()
         CONF_PARSER.optionxform = str
         CONF_PARSER.readfp(conf_file)
@@ -378,8 +384,9 @@
         display_results(results, update, replace)
         if update:
             conf_file.close()
-            CONF_PARSER.write(outfile)
-        outfile.close()
+            if opts.output:
+                with open(opts.output, 'w+') as outfile:
+                    CONF_PARSER.write(outfile)
     finally:
         icreds.clear_creds()
 
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index b14012e..b5c4547 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -17,9 +17,9 @@
 from oslo_utils import excutils
 
 from tempest.common import fixed_network
-from tempest.common import service_client
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common import rest_client
 from tempest.lib.common.utils import data_utils
 
 CONF = config.CONF
@@ -49,10 +49,6 @@
 
     # TODO(jlanoux) add support of wait_until PINGABLE/SSHABLE
 
-    name = name
-    flavor = flavor
-    image_id = image_id
-
     if name is None:
         name = data_utils.rand_name(__name__ + "-instance")
     if flavor is None:
@@ -102,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'],
@@ -129,7 +125,7 @@
         servers = \
             [s for s in body_servers['servers'] if s['name'].startswith(name)]
     else:
-        body = service_client.ResponseBody(body.response, body['server'])
+        body = rest_client.ResponseBody(body.response, body['server'])
         servers = [body]
 
     # The name of the method to associate a floating IP to as server is too
@@ -152,14 +148,12 @@
 
             except Exception:
                 with excutils.save_and_reraise_exception():
-                    if ('preserve_server_on_error' not in kwargs
-                        or kwargs['preserve_server_on_error'] is False):
-                        for server in servers:
-                            try:
-                                clients.servers_client.delete_server(
-                                    server['id'])
-                            except Exception:
-                                LOG.exception('Deleting server %s failed'
-                                              % server['id'])
+                    for server in servers:
+                        try:
+                            clients.servers_client.delete_server(
+                                server['id'])
+                        except Exception:
+                            LOG.exception('Deleting server %s failed'
+                                          % server['id'])
 
     return body, servers
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 0ca14a9..58157ef 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -256,7 +256,7 @@
 
     if credential_type not in CREDENTIAL_TYPES:
         raise exceptions.InvalidCredentials()
-    conf_attributes = ['username', 'password', 'tenant_name']
+    conf_attributes = ['username', 'password', 'project_name']
 
     if identity_version == 'v3':
         conf_attributes.append('domain_name')
@@ -269,6 +269,11 @@
             params[attr] = getattr(_section, attr)
         else:
             params[attr] = getattr(_section, prefix + "_" + attr)
+    # NOTE(andreaf) v2 API still uses tenants, so we must translate project
+    # to tenant before building the Credentials object
+    if identity_version == 'v2':
+        params['tenant_name'] = params.get('project_name')
+        params.pop('project_name', None)
     # Build and validate credentials. We are reading configured credentials,
     # so validate them even if fill_in is False
     credentials = get_credentials(fill_in=fill_in,
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index 0a01bd4..d374be4 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -62,8 +62,8 @@
          self.users_admin_client,
          self.roles_admin_client,
          self.domains_admin_client,
-         self.network_admin_client,
          self.networks_admin_client,
+         self.routers_admin_client,
          self.subnets_admin_client,
          self.ports_admin_client,
          self.security_groups_admin_client) = self._get_admin_clients()
@@ -92,14 +92,15 @@
         os = clients.Manager(self.default_admin_creds)
         if self.identity_version == 'v2':
             return (os.identity_client, os.tenants_client, os.users_client,
-                    os.roles_client, None, os.network_client,
-                    os.networks_client, os.subnets_client, os.ports_client,
-                    os.security_groups_client)
+                    os.roles_client, None,
+                    os.networks_client, os.routers_client, os.subnets_client,
+                    os.ports_client, os.security_groups_client)
         else:
             return (os.identity_v3_client, os.projects_client,
                     os.users_v3_client, os.roles_v3_client, os.domains_client,
-                    os.network_client, os.networks_client, os.subnets_client,
-                    os.ports_client, os.security_groups_client)
+                    os.networks_client, os.routers_client,
+                    os.subnets_client, os.ports_client,
+                    os.security_groups_client)
 
     def _create_creds(self, suffix="", admin=False, roles=None):
         """Create random credentials under the following schema.
@@ -206,8 +207,8 @@
         return resp_body['network']
 
     def _create_subnet(self, subnet_name, tenant_id, network_id):
-        base_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
-        mask_bits = CONF.network.tenant_network_mask_bits
+        base_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+        mask_bits = CONF.network.project_network_mask_bits
         for subnet_cidr in base_cidr.subnet(mask_bits):
             try:
                 if self.network_resources:
@@ -237,14 +238,14 @@
     def _create_router(self, router_name, tenant_id):
         external_net_id = dict(
             network_id=CONF.network.public_network_id)
-        resp_body = self.network_admin_client.create_router(
-            router_name,
+        resp_body = self.routers_admin_client.create_router(
+            name=router_name,
             external_gateway_info=external_net_id,
             tenant_id=tenant_id)
         return resp_body['router']
 
     def _add_router_interface(self, router_id, subnet_id):
-        self.network_admin_client.add_router_interface(router_id,
+        self.routers_admin_client.add_router_interface(router_id,
                                                        subnet_id=subnet_id)
 
     def get_credentials(self, credential_type):
@@ -295,9 +296,9 @@
         return self.get_credentials(roles)
 
     def _clear_isolated_router(self, router_id, router_name):
-        net_client = self.network_admin_client
+        client = self.routers_admin_client
         try:
-            net_client.delete_router(router_id)
+            client.delete_router(router_id)
         except lib_exc.NotFound:
             LOG.warning('router with name: %s not found for delete' %
                         router_name)
@@ -331,7 +332,7 @@
                             (secgroup['name'], secgroup['id']))
 
     def _clear_isolated_net_resources(self):
-        net_client = self.network_admin_client
+        client = self.routers_admin_client
         for cred in self._creds:
             creds = self._creds.get(cred)
             if (not creds or not any([creds.router, creds.network,
@@ -344,7 +345,7 @@
             if (not self.network_resources or
                     (self.network_resources.get('router') and creds.subnet)):
                 try:
-                    net_client.remove_router_interface(
+                    client.remove_router_interface(
                         creds.router['id'],
                         subnet_id=creds.subnet['id'])
                 except lib_exc.NotFound:
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index baf796d..00062de 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -306,13 +306,32 @@
 
     def connect(self):
         """Connect to SSL port and apply per-connection parameters."""
-        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        if self.timeout is not None:
-            # '0' microseconds
-            sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO,
-                            struct.pack('LL', self.timeout, 0))
-        self.sock = OpenSSLConnectionDelegator(self.context, sock)
-        self.sock.connect((self.host, self.port))
+        try:
+            addresses = socket.getaddrinfo(self.host,
+                                           self.port,
+                                           socket.AF_UNSPEC,
+                                           socket.SOCK_STREAM)
+        except OSError as msg:
+            raise exc.RestClientException(msg)
+        for res in addresses:
+            af, socktype, proto, canonname, sa = res
+            sock = socket.socket(af, socket.SOCK_STREAM)
+
+            if self.timeout is not None:
+                # '0' microseconds
+                sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO,
+                                struct.pack('LL', self.timeout, 0))
+            self.sock = OpenSSLConnectionDelegator(self.context, sock)
+            try:
+                self.sock.connect(sa)
+            except OSError as msg:
+                if self.sock:
+                    self.sock = None
+                    continue
+            break
+        if self.sock is None:
+            # Happen only when all results have failed.
+            raise exc.RestClientException('Cannot connect to %s' % self.host)
 
     def close(self):
         if self.sock:
diff --git a/tempest/common/negative_rest_client.py b/tempest/common/negative_rest_client.py
index d97411c..3495a24 100644
--- a/tempest/common/negative_rest_client.py
+++ b/tempest/common/negative_rest_client.py
@@ -15,30 +15,19 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common import service_client
 from tempest import config
+from tempest.lib.common import rest_client
 
 CONF = config.CONF
 
 
-class NegativeRestClient(service_client.ServiceClient):
+class NegativeRestClient(rest_client.RestClient):
     """Version of RestClient that does not raise exceptions."""
-    def __init__(self, auth_provider, service,
-                 build_interval=None, build_timeout=None,
-                 disable_ssl_certificate_validation=None,
-                 ca_certs=None, trace_requests=None):
+    def __init__(self, auth_provider, service, **kwargs):
         region, endpoint_type = self._get_region_and_endpoint_type(service)
         super(NegativeRestClient, self).__init__(
-            auth_provider,
-            service,
-            region,
-            endpoint_type=endpoint_type,
-            build_interval=build_interval,
-            build_timeout=build_timeout,
-            disable_ssl_certificate_validation=(
-                disable_ssl_certificate_validation),
-            ca_certs=ca_certs,
-            trace_requests=trace_requests)
+            auth_provider, service, region, endpoint_type=endpoint_type,
+            **kwargs)
 
     def _get_region_and_endpoint_type(self, service):
         """Returns the region for a specific service"""
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index 97cc2ec..f3df387 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -331,6 +331,7 @@
     def _wrap_creds_with_network(self, hash):
         creds_dict = self.hash_dict['creds'][hash]
         # Make sure a domain scope if defined for users in case of V3
+        # Make sure a tenant is available in case of V2
         creds_dict = self._extend_credentials(creds_dict)
         # This just builds a Credentials object, it does not validate
         # nor fill  with missing fields.
@@ -356,4 +357,17 @@
             user_domain_fields = set(['user_domain_name', 'user_domain_id'])
             if not user_domain_fields.intersection(set(creds_dict.keys())):
                 creds_dict['user_domain_name'] = self.credentials_domain
+        # NOTE(andreaf) In case of v2, replace project with tenant if project
+        # is provided and tenant is not
+        if self.identity_version == 'v2':
+            if ('project_name' in creds_dict and
+                    'tenant_name' in creds_dict and
+                    creds_dict['project_name'] != creds_dict['tenant_name']):
+                clean_creds = self._sanitize_creds(creds_dict)
+                msg = 'Cannot specify project and tenant at the same time %s'
+                raise exceptions.InvalidCredentials(msg % clean_creds)
+            if ('project_name' in creds_dict and
+                    'tenant_name' not in creds_dict):
+                creds_dict['tenant_name'] = creds_dict['project_name']
+                creds_dict.pop('project_name')
         return creds_dict
diff --git a/tempest/common/service_client.py b/tempest/common/service_client.py
deleted file mode 100644
index 14a3bd6..0000000
--- a/tempest/common/service_client.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright 2015 NEC Corporation.  All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.lib.common import rest_client
-
-
-class ServiceClient(rest_client.RestClient):
-
-    def __init__(self, auth_provider, service, region,
-                 endpoint_type=None, build_interval=None, build_timeout=None,
-                 disable_ssl_certificate_validation=None, ca_certs=None,
-                 trace_requests=None):
-
-        dscv = disable_ssl_certificate_validation
-        params = {
-            'disable_ssl_certificate_validation': dscv,
-            'ca_certs': ca_certs,
-            'trace_requests': trace_requests
-        }
-
-        if endpoint_type is not None:
-            params.update({'endpoint_type': endpoint_type})
-        if build_interval is not None:
-            params.update({'build_interval': build_interval})
-        if build_timeout is not None:
-            params.update({'build_timeout': build_timeout})
-        super(ServiceClient, self).__init__(auth_provider, service, region,
-                                            **params)
-
-
-class ResponseBody(dict):
-    """Class that wraps an http response and dict body into a single value.
-
-    Callers that receive this object will normally use it as a dict but
-    can extract the response if needed.
-    """
-
-    def __init__(self, response, body=None):
-        body_data = body or {}
-        self.update(body_data)
-        self.response = response
-
-    def __str__(self):
-        body = super(ResponseBody, self).__str__()
-        return "response: %s\nBody: %s" % (self.response, body)
-
-
-class ResponseBodyData(object):
-    """Class that wraps an http response and string data into a single value"""
-
-    def __init__(self, response, data):
-        self.response = response
-        self.data = data
-
-    def __str__(self):
-        return "response: %s\nBody: %s" % (self.response, self.data)
-
-
-class ResponseBodyList(list):
-    """Class that wraps an http response and list body into a single value.
-
-    Callers that receive this object will normally use it as a list but
-    can extract the response if needed.
-    """
-
-    def __init__(self, response, body=None):
-        body_data = body or []
-        self.extend(body_data)
-        self.response = response
-
-    def __str__(self):
-        body = super(ResponseBodyList, self).__str__()
-        return "response: %s\nBody: %s" % (self.response, body)
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index cfd9df6..3a215a0 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -102,7 +102,12 @@
         cmd = "ip addr %s| awk '/ether/ {print $2}'" % show_nic
         return self.exec_command(cmd).strip().lower()
 
-    def get_nic_name(self, address):
+    def get_nic_name_by_mac(self, address):
+        cmd = "ip -o link | awk '/%s/ {print $2}'" % address
+        nic = self.exec_command(cmd)
+        return nic.strip().strip(":").lower()
+
+    def get_nic_name_by_ip(self, address):
         cmd = "ip -o addr | awk '/%s/ {print $2}'" % address
         nic = self.exec_command(cmd)
         return nic.strip().strip(":").lower()
@@ -113,7 +118,7 @@
 
     def assign_static_ip(self, nic, addr):
         cmd = "sudo ip addr add {ip}/{mask} dev {nic}".format(
-            ip=addr, mask=CONF.network.tenant_network_mask_bits,
+            ip=addr, mask=CONF.network.project_network_mask_bits,
             nic=nic
         )
         return self.exec_command(cmd)
@@ -142,7 +147,7 @@
     def _renew_lease_udhcpc(self, fixed_ip=None):
         """Renews DHCP lease via udhcpc client. """
         file_path = '/var/run/udhcpc.'
-        nic_name = self.get_nic_name(fixed_ip)
+        nic_name = self.get_nic_name_by_ip(fixed_ip)
         pid = self.exec_command('cat {path}{nic}.pid'.
                                 format(path=file_path, nic=nic_name))
         pid = pid.strip()
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/config.py b/tempest/config.py
index 0e1b273..b787b19 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -56,7 +56,7 @@
                     "number of concurrent test processes."),
     cfg.BoolOpt('use_dynamic_credentials',
                 default=True,
-                help="Allows test cases to create/destroy tenants and "
+                help="Allows test cases to create/destroy projects and "
                      "users. This option requires that OpenStack Identity "
                      "API admin credentials are known. If false, isolated "
                      "test cases and parallel execution, can still be "
@@ -81,23 +81,26 @@
                 default=True,
                 help="If use_dynamic_credentials is set to True and Neutron "
                      "is enabled Tempest will try to create a usable network, "
-                     "subnet, and router when needed for each tenant it "
+                     "subnet, and router when needed for each project it "
                      "creates. However in some neutron configurations, like "
                      "with VLAN provider networks, this doesn't work. So if "
                      "set to False the isolated networks will not be created"),
     cfg.StrOpt('admin_username',
                help="Username for an administrative user. This is needed for "
-                    "authenticating requests made by tenant isolation to "
+                    "authenticating requests made by project isolation to "
                     "create users and projects",
                deprecated_group='identity'),
-    cfg.StrOpt('admin_tenant_name',
-               help="Tenant name to use for an  administrative user. This is "
-                    "needed for authenticating requests made by tenant "
+    cfg.StrOpt('admin_project_name',
+               help="Project name to use for an  administrative user. This is "
+                    "needed for authenticating requests made by project "
                     "isolation to create users and projects",
-               deprecated_group='identity'),
+               deprecated_opts=[cfg.DeprecatedOpt('admin_tenant_name',
+                                                  group='auth'),
+                                cfg.DeprecatedOpt('admin_tenant_name',
+                                                  group='identity')]),
     cfg.StrOpt('admin_password',
                help="Password to use for an  administrative user. This is "
-                    "needed for authenticating requests made by tenant "
+                    "needed for authenticating requests made by project "
                     "isolation to create users and projects",
                secret=True,
                deprecated_group='identity'),
@@ -158,8 +161,9 @@
     cfg.StrOpt('username',
                help="Username to use for Nova API requests.",
                deprecated_for_removal=True),
-    cfg.StrOpt('tenant_name',
-               help="Tenant name to use for Nova API requests.",
+    cfg.StrOpt('project_name',
+               deprecated_name='tenant_name',
+               help="Project name to use for Nova API requests.",
                deprecated_for_removal=True),
     cfg.StrOpt('admin_role',
                default='admin',
@@ -176,8 +180,9 @@
                help="Username of alternate user to use for Nova API "
                     "requests.",
                deprecated_for_removal=True),
-    cfg.StrOpt('alt_tenant_name',
-               help="Alternate user's Tenant name to use for Nova API "
+    cfg.StrOpt('alt_project_name',
+               deprecated_name='alt_tenant_name',
+               help="Alternate user's Project name to use for Nova API "
                     "requests.",
                deprecated_for_removal=True),
     cfg.StrOpt('alt_password',
@@ -246,10 +251,10 @@
                     "no OS-EXT-STS extension available"),
     cfg.StrOpt('fixed_network_name',
                help="Name of the fixed network that is visible to all test "
-                    "tenants. If multiple networks are available for a tenant"
-                    " this is the network which will be used for creating "
-                    "servers if tempest does not create a network or a "
-                    "network is not specified elsewhere. It may be used for "
+                    "projects. If multiple networks are available for a "
+                    "project, this is the network which will be used for "
+                    "creating servers if tempest does not create a network or "
+                    "s network is not specified elsewhere. It may be used for "
                     "ssh validation only if floating IPs are disabled."),
     cfg.StrOpt('catalog_type',
                default='compute',
@@ -281,13 +286,7 @@
                help=('The minimum number of compute nodes expected. This will '
                      'be utilized by some multinode specific tests to ensure '
                      'that requests match the expected size of the cluster '
-                     'you are testing with.'))
-]
-
-compute_features_group = cfg.OptGroup(name='compute-feature-enabled',
-                                      title="Enabled Compute Service Features")
-
-ComputeFeaturesGroup = [
+                     'you are testing with.')),
     cfg.StrOpt('min_microversion',
                default=None,
                help="Lower version of the test target microversion range. "
@@ -296,7 +295,8 @@
                     "min_microversion and max_microversion. "
                     "If both values are not specified, Tempest avoids tests "
                     "which require a microversion. Valid values are string "
-                    "with format 'X.Y' or string 'latest'"),
+                    "with format 'X.Y' or string 'latest'",
+                    deprecated_group='compute-feature-enabled'),
     cfg.StrOpt('max_microversion',
                default=None,
                help="Upper version of the test target microversion range. "
@@ -305,7 +305,14 @@
                     "min_microversion and max_microversion. "
                     "If both values are not specified, Tempest avoids tests "
                     "which require a microversion. Valid values are string "
-                    "with format 'X.Y' or string 'latest'"),
+                    "with format 'X.Y' or string 'latest'",
+                    deprecated_group='compute-feature-enabled'),
+]
+
+compute_features_group = cfg.OptGroup(name='compute-feature-enabled',
+                                      title="Enabled Compute Service Features")
+
+ComputeFeaturesGroup = [
     cfg.BoolOpt('disk_config',
                 default=True,
                 help="If false, skip disk config tests"),
@@ -404,6 +411,15 @@
     cfg.BoolOpt('config_drive',
                 default=True,
                 help='Enable special configuration drive with metadata.'),
+    cfg.ListOpt('scheduler_available_filters',
+                default=['all'],
+                help="A list of enabled filters that nova will accept as hints"
+                     " to the scheduler when creating a server. A special "
+                     "entry 'all' indicates all filters are enabled. Empty "
+                     "list indicates all filters are disabled. The full "
+                     "available list of filters is in nova.conf: "
+                     "DEFAULT.scheduler_available_filters"),
+
 ]
 
 
@@ -482,21 +498,26 @@
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
                help="The endpoint type to use for the network service."),
-    cfg.StrOpt('tenant_network_cidr',
+    cfg.StrOpt('project_network_cidr',
+               deprecated_name='tenant_network_cidr',
                default="10.100.0.0/16",
-               help="The cidr block to allocate tenant ipv4 subnets from"),
-    cfg.IntOpt('tenant_network_mask_bits',
+               help="The cidr block to allocate project ipv4 subnets from"),
+    cfg.IntOpt('project_network_mask_bits',
+               deprecated_name='tenant_network_mask_bits',
                default=28,
-               help="The mask bits for tenant ipv4 subnets"),
-    cfg.StrOpt('tenant_network_v6_cidr',
+               help="The mask bits for project ipv4 subnets"),
+    cfg.StrOpt('project_network_v6_cidr',
+               deprecated_name='tenant_network_v6_cidr',
                default="2003::/48",
-               help="The cidr block to allocate tenant ipv6 subnets from"),
-    cfg.IntOpt('tenant_network_v6_mask_bits',
+               help="The cidr block to allocate project ipv6 subnets from"),
+    cfg.IntOpt('project_network_v6_mask_bits',
+               deprecated_name='tenant_network_v6_mask_bits',
                default=64,
-               help="The mask bits for tenant ipv6 subnets"),
-    cfg.BoolOpt('tenant_networks_reachable',
+               help="The mask bits for project ipv6 subnets"),
+    cfg.BoolOpt('project_networks_reachable',
+                deprecated_name='tenant_networks_reachable',
                 default=False,
-                help="Whether tenant networks can be reached directly from "
+                help="Whether project networks can be reached directly from "
                      "the test client. This must be set to True when the "
                      "'fixed' ssh_connect_method is selected."),
     cfg.StrOpt('public_network_id',
@@ -953,7 +974,7 @@
 
 DataProcessingFeaturesGroup = [
     cfg.ListOpt('plugins',
-                default=["vanilla", "hdp"],
+                default=["vanilla", "cdh"],
                 deprecated_group="data_processing-feature-enabled",
                 help="List of enabled data processing plugins")
 ]
@@ -992,7 +1013,7 @@
                 default=False,
                 help='Allows a full cleaning process after a stress test.'
                      ' Caution : this cleanup will remove every objects of'
-                     ' every tenant.')
+                     ' every project.')
 ]
 
 
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 86e8460..031df7f 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -176,20 +176,6 @@
     message = "Invalid structure of table with details"
 
 
-class InvalidAPIVersionString(TempestException):
-    message = ("API Version String %(version)s is of invalid format. Must "
-               "be of format MajorNum.MinorNum or string 'latest'.")
-
-
-class JSONSchemaNotFound(TempestException):
-    message = ("JSON Schema for %(version)s is not found in \n"
-               " %(schema_versions_info)s")
-
-
-class InvalidAPIVersionRange(TempestException):
-    message = ("API Min Version is greater than Max version")
-
-
 class CommandFailed(Exception):
     def __init__(self, returncode, cmd, output, stderr):
         super(CommandFailed, self).__init__()
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index c666c96..9ebb17e 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -119,11 +119,6 @@
 
     T108
     """
-    if './tempest/api/network/' in filename:
-        # Network API tests are migrating from Tempest to Neutron repo now.
-        # So here should avoid network API tests checks.
-        return
-
     msg = "T108: hyphen should not be specified at the end of rand_name()"
     if RAND_NAME_HYPHEN_RE.match(logical_line):
         return 0, msg
@@ -151,7 +146,7 @@
 
 def _common_service_clients_check(logical_line, physical_line, filename,
                                   ignored_list_file=None):
-    if 'tempest/services/' not in filename:
+    if not re.match('tempest/(lib/)?services/.*', filename):
         return False
 
     if ignored_list_file is not None:
@@ -225,6 +220,27 @@
         yield (0, msg)
 
 
+def dont_import_local_tempest_into_lib(logical_line, filename):
+    """Check that tempest.lib should not import local tempest code
+
+    T112
+    """
+    if 'tempest/lib/' not in filename:
+        return
+
+    if not ('from tempest' in logical_line
+            or 'import tempest' in logical_line):
+        return
+
+    if ('from tempest.lib' in logical_line
+        or 'import tempest.lib' in logical_line):
+        return
+
+    msg = ("T112: tempest.lib should not import local tempest code to avoid "
+           "circular dependency")
+    yield (0, msg)
+
+
 def factory(register):
     register(import_no_clients_in_api_and_scenario_tests)
     register(scenario_tests_need_service_tags)
@@ -236,3 +252,4 @@
     register(no_testtools_skip_decorator)
     register(get_resources_on_service_clients)
     register(delete_resources_on_service_clients)
+    register(dont_import_local_tempest_into_lib)
diff --git a/tempest/hacking/ignored_list_T110.txt b/tempest/hacking/ignored_list_T110.txt
index 50a5477..380c173 100644
--- a/tempest/hacking/ignored_list_T110.txt
+++ b/tempest/hacking/ignored_list_T110.txt
@@ -4,4 +4,4 @@
 ./tempest/services/volume/base/base_qos_client.py
 ./tempest/services/volume/base/base_backups_client.py
 ./tempest/services/baremetal/base.py
-./tempest/services/network/json/network_client.py
+./tempest/services/network/json/routers_client.py
diff --git a/tempest/lib/api_schema/response/compute/v2_1/servers.py b/tempest/lib/api_schema/response/compute/v2_1/servers.py
index 485c51a..3289f04 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -267,12 +267,13 @@
     'type': 'object',
     'properties': {
         'id': {'type': 'string'},
-        'device': {'type': 'string'},
+        'device': {'type': ['string', 'null']},
         'volumeId': {'type': 'string'},
         'serverId': {'type': ['integer', 'string']}
     },
     'additionalProperties': False,
-    'required': ['id', 'device', 'volumeId', 'serverId']
+    # 'device' is optional in response.
+    'required': ['id', 'volumeId', 'serverId']
 }
 
 attach_volume = {
diff --git a/tempest/api_schema/response/compute/v2_2/__init__.py b/tempest/lib/api_schema/response/compute/v2_2/__init__.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2_2/__init__.py
rename to tempest/lib/api_schema/response/compute/v2_2/__init__.py
diff --git a/tempest/api_schema/response/compute/v2_2/keypairs.py b/tempest/lib/api_schema/response/compute/v2_2/keypairs.py
similarity index 95%
rename from tempest/api_schema/response/compute/v2_2/keypairs.py
rename to tempest/lib/api_schema/response/compute/v2_2/keypairs.py
index 5d8d24d..0bb7771 100644
--- a/tempest/api_schema/response/compute/v2_2/keypairs.py
+++ b/tempest/lib/api_schema/response/compute/v2_2/keypairs.py
@@ -14,7 +14,7 @@
 
 import copy
 
-from tempest.api_schema.response.compute.v2_1 import keypairs
+from tempest.lib.api_schema.response.compute.v2_1 import keypairs
 
 get_keypair = copy.deepcopy(keypairs.get_keypair)
 get_keypair['response_body']['properties']['keypair'][
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 806acb5..a6833be 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -31,6 +31,39 @@
 LOG = logging.getLogger(__name__)
 
 
+def replace_version(url, new_version):
+    parts = urlparse.urlparse(url)
+    version_path = '/%s' % new_version
+    path, subs = re.subn(r'(^|/)+v\d+(?:\.\d+)?',
+                         version_path,
+                         parts.path,
+                         count=1)
+    if not subs:
+        path = '%s%s' % (parts.path.rstrip('/'), version_path)
+    url = urlparse.urlunparse((parts.scheme,
+                               parts.netloc,
+                               path,
+                               parts.params,
+                               parts.query,
+                               parts.fragment))
+    return url
+
+
+def apply_url_filters(url, filters):
+    if filters.get('api_version', None) is not None:
+        url = replace_version(url, filters['api_version'])
+    parts = urlparse.urlparse(url)
+    if filters.get('skip_path', None) is not None and parts.path != '':
+        url = urlparse.urlunparse((parts.scheme,
+                                   parts.netloc,
+                                   '/',
+                                   parts.params,
+                                   parts.query,
+                                   parts.fragment))
+
+    return url
+
+
 @six.add_metaclass(abc.ABCMeta)
 class AuthProvider(object):
     """Provide authentication"""
@@ -319,19 +352,10 @@
                     _base_url = ep['endpoints'][0].get(endpoint_type)
                 break
         if _base_url is None:
-            raise exceptions.EndpointNotFound(service)
-
-        parts = urlparse.urlparse(_base_url)
-        if filters.get('api_version', None) is not None:
-            path = "/" + filters['api_version']
-            noversion_path = "/".join(parts.path.split("/")[2:])
-            if noversion_path != "":
-                path += "/" + noversion_path
-            _base_url = _base_url.replace(parts.path, path)
-        if filters.get('skip_path', None) is not None and parts.path != '':
-            _base_url = _base_url.replace(parts.path, "/")
-
-        return _base_url
+            raise exceptions.EndpointNotFound(
+                "service: %s, region: %s, endpoint_type: %s" %
+                (service, region, endpoint_type))
+        return apply_url_filters(_base_url, filters)
 
     def is_expired(self, auth_data):
         _, access = auth_data
@@ -442,18 +466,7 @@
         _base_url = filtered_catalog[0].get('url', None)
         if _base_url is None:
                 raise exceptions.EndpointNotFound(service)
-
-        parts = urlparse.urlparse(_base_url)
-        if filters.get('api_version', None) is not None:
-            path = "/" + filters['api_version']
-            noversion_path = "/".join(parts.path.split("/")[2:])
-            if noversion_path != "":
-                path += "/" + noversion_path
-            _base_url = _base_url.replace(parts.path, path)
-        if filters.get('skip_path', None) is not None:
-            _base_url = _base_url.replace(parts.path, "/")
-
-        return _base_url
+        return apply_url_filters(_base_url, filters)
 
     def is_expired(self, auth_data):
         _, access = auth_data
@@ -598,6 +611,9 @@
         return None not in (self.username, self.password)
 
 
+COLLISIONS = [('project_name', 'tenant_name'), ('project_id', 'tenant_id')]
+
+
 class KeystoneV3Credentials(Credentials):
     """Credentials suitable for the Keystone Identity V3 API"""
 
@@ -606,6 +622,16 @@
                   'project_name', 'tenant_id', 'tenant_name', 'user_domain_id',
                   'user_domain_name', 'user_id']
 
+    def _apply_credentials(self, attr):
+        for (key1, key2) in COLLISIONS:
+            val1 = attr.get(key1)
+            val2 = attr.get(key2)
+            if val1 and val2 and val1 != val2:
+                msg = ('Cannot have conflicting values for %s and %s' %
+                       (key1, key2))
+                raise exceptions.InvalidCredentials(msg)
+        super(KeystoneV3Credentials, self)._apply_credentials(attr)
+
     def __setattr__(self, key, value):
         parent = super(KeystoneV3Credentials, self)
         # for tenant_* set both project and tenant
@@ -633,8 +659,10 @@
                 parent.__setattr__('user_domain_name', value)
         # support domain_name coming from config
         if key == 'domain_name':
-            parent.__setattr__('user_domain_name', value)
-            parent.__setattr__('project_domain_name', value)
+            if self.user_domain_name is None:
+                parent.__setattr__('user_domain_name', value)
+            if self.project_domain_name is None:
+                parent.__setattr__('project_domain_name', value)
         # finally trigger default behaviour for all attributes
         parent.__setattr__(key, value)
 
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index 3adeecd..be3aa49 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -173,9 +173,9 @@
 
     @staticmethod
     def _import_name(node):
-        if type(node) == ast.Import:
+        if isinstance(node, ast.Import):
             return node.names[0].name
-        elif type(node) == ast.ImportFrom:
+        elif isinstance(node, ast.ImportFrom):
             return '%s.%s' % (node.module, node.names[0].name)
 
     def _add_import_for_test_uuid(self, patcher, src_parsed, source_path):
diff --git a/tempest/lib/cmd/skip_tracker.py b/tempest/lib/cmd/skip_tracker.py
index b5c9b95..b7d6a24 100755
--- a/tempest/lib/cmd/skip_tracker.py
+++ b/tempest/lib/cmd/skip_tracker.py
@@ -81,21 +81,23 @@
     DEF_RE = re.compile(r'\s*def (\w+)\(')
     bug_found = False
     results = []
-    lines = open(path, 'rb').readlines()
-    for x, line in enumerate(lines):
-        if not bug_found:
-            res = BUG_RE.match(line)
-            if res:
-                bug_no = int(res.group(1))
-                debug("Found bug skip %s on line %d", bug_no, x + 1)
-                bug_found = True
-        else:
-            res = DEF_RE.match(line)
-            if res:
-                method = res.group(1)
-                debug("Found test method %s skips for bug %d", method, bug_no)
-                results.append((method, bug_no))
-                bug_found = False
+    with open(path, 'rb') as content:
+        lines = content.readlines()
+        for x, line in enumerate(lines):
+            if not bug_found:
+                res = BUG_RE.match(line)
+                if res:
+                    bug_no = int(res.group(1))
+                    debug("Found bug skip %s on line %d", bug_no, x + 1)
+                    bug_found = True
+            else:
+                res = DEF_RE.match(line)
+                if res:
+                    method = res.group(1)
+                    debug("Found test method %s skips for bug %d",
+                          method, bug_no)
+                    results.append((method, bug_no))
+                    bug_found = False
     return results
 
 
diff --git a/tempest/common/api_version_request.py b/tempest/lib/common/api_version_request.py
similarity index 92%
rename from tempest/common/api_version_request.py
rename to tempest/lib/common/api_version_request.py
index d8a5b56..b2b68a6 100644
--- a/tempest/common/api_version_request.py
+++ b/tempest/lib/common/api_version_request.py
@@ -14,7 +14,7 @@
 
 import re
 
-from tempest import exceptions
+from tempest.lib import exceptions
 
 
 # Define the minimum and maximum version of the API across all of the
@@ -39,6 +39,11 @@
     This class provides convenience methods for manipulation
     and comparison of version numbers that we need to do to
     implement microversions.
+
+    :param version_string: String representation of APIVersionRequest.
+            Correct format is 'X.Y', where 'X' and 'Y' are int values.
+            None value should be used to create Null APIVersionRequest,
+            which is equal to 0.0
     """
 
     # NOTE: This 'latest' version is a magic number, we assume any
@@ -47,13 +52,7 @@
     latest_ver_minor = 99999
 
     def __init__(self, version_string=None):
-        """Create an API version request object.
-
-        :param version_string: String representation of APIVersionRequest.
-            Correct format is 'X.Y', where 'X' and 'Y' are int values.
-            None value should be used to create Null APIVersionRequest,
-            which is equal to 0.0
-        """
+        """Create an API version request object."""
         # NOTE(gmann): 'version_string' as String "None" will be considered as
         # invalid version string.
         self.ver_major = 0
@@ -77,6 +76,12 @@
         return ("API Version Request: %s" % self.get_string())
 
     def is_null(self):
+        """Checks whether version is null.
+
+        Return True if version object is null otherwise False.
+
+        :returns: boolean
+        """
         return self.ver_major == 0 and self.ver_minor == 0
 
     def _format_type_error(self, other):
@@ -120,9 +125,9 @@
         greater than or equal to the minimum version and less than
         or equal to the maximum version.
 
-        @param min_version: Minimum acceptable version.
-        @param max_version: Maximum acceptable version.
-        @returns: boolean
+        :param min_version: Minimum acceptable version.
+        :param max_version: Maximum acceptable version.
+        :returns: boolean
 
         If min_version is null then there is no minimum limit.
         If max_version is null then there is no maximum limit.
diff --git a/tempest/common/api_version_utils.py b/tempest/lib/common/api_version_utils.py
similarity index 77%
rename from tempest/common/api_version_utils.py
rename to tempest/lib/common/api_version_utils.py
index 7c7e96a..1371b3c 100644
--- a/tempest/common/api_version_utils.py
+++ b/tempest/lib/common/api_version_utils.py
@@ -14,8 +14,8 @@
 
 import testtools
 
-from tempest.common import api_version_request
-from tempest import exceptions
+from tempest.lib.common import api_version_request
+from tempest.lib import exceptions
 
 
 LATEST_MICROVERSION = 'latest'
@@ -35,6 +35,20 @@
 
 def check_skip_with_microversion(test_min_version, test_max_version,
                                  cfg_min_version, cfg_max_version):
+    """Checks API microversions range and returns whether test needs to be skip
+
+    Compare the test and configured microversion range and returns
+    whether test microversion range is out of configured one.
+    This method can be used to skip the test based on configured and test
+    microversion range.
+
+    :param test_min_version: Test Minimum Microversion
+    :param test_max_version: Test Maximum Microversion
+    :param cfg_min_version: Configured Minimum Microversion
+    :param cfg_max_version: Configured Maximum Microversion
+    :returns: boolean
+    """
+
     min_version = api_version_request.APIVersionRequest(test_min_version)
     max_version = api_version_request.APIVersionRequest(test_max_version)
     config_min_version = api_version_request.APIVersionRequest(cfg_min_version)
@@ -68,6 +82,16 @@
 
 
 def select_request_microversion(test_min_version, cfg_min_version):
+    """Select microversion from test and configuration min version.
+
+    Compare requested microversion and return the maximum
+    microversion out of those.
+
+    :param test_min_version: Test Minimum Microversion
+    :param cfg_min_version: Configured Minimum Microversion
+    :returns: Selected microversion string
+    """
+
     test_version = api_version_request.APIVersionRequest(test_min_version)
     cfg_version = api_version_request.APIVersionRequest(cfg_min_version)
     max_version = cfg_version if cfg_version >= test_version else test_version
@@ -77,15 +101,15 @@
 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.
 
-    @param: api_microversion_header_name: Microversion header name
+    :param api_microversion_header_name: Microversion header name
             Example- "X-OpenStack-Nova-API-Version"
-    @param: api_microversion: Microversion number like "2.10"
-    @param: response_header: Response header where microversion is
+    :param api_microversion: Microversion number like "2.10"
+    :param response_header: Response header where microversion is
             expected to be present.
     """
     api_microversion_header_name = api_microversion_header_name.lower()
diff --git a/tempest/lib/common/http.py b/tempest/lib/common/http.py
index b3793bc..dffc5f9 100644
--- a/tempest/lib/common/http.py
+++ b/tempest/lib/common/http.py
@@ -1,5 +1,4 @@
-# Copyright 2013 OpenStack Foundation
-# Copyright 2013 Citrix Systems, Inc.
+# Copyright 2016 OpenStack Foundation
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -14,12 +13,43 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import httplib2
+import urllib3
 
 
-class ClosingHttp(httplib2.Http):
-    def request(self, *args, **kwargs):
+class ClosingHttp(urllib3.poolmanager.PoolManager):
+    def __init__(self, disable_ssl_certificate_validation=False,
+                 ca_certs=None):
+        kwargs = {}
+
+        if disable_ssl_certificate_validation:
+            urllib3.disable_warnings()
+            kwargs['cert_reqs'] = 'CERT_NONE'
+
+        if ca_certs:
+            kwargs['cert_reqs'] = 'CERT_REQUIRED'
+            kwargs['ca_certs'] = ca_certs
+
+        super(ClosingHttp, self).__init__(**kwargs)
+
+    def request(self, url, method, *args, **kwargs):
+
+        class Response(dict):
+            def __init__(self, info):
+                for key, value in info.getheaders().items():
+                    self[key.lower()] = value
+                self.status = info.status
+                self['status'] = str(self.status)
+                self.reason = info.reason
+                self.version = info.version
+                self['content-location'] = url
+
         original_headers = kwargs.get('headers', {})
         new_headers = dict(original_headers, connection='close')
         new_kwargs = dict(kwargs, headers=new_headers)
-        return super(ClosingHttp, self).request(*args, **new_kwargs)
+
+        # Follow up to 5 redirections. Don't raise an exception if
+        # it's exceeded but return the HTTP 3XX response instead.
+        retry = urllib3.util.Retry(raise_on_redirect=False, redirect=5)
+        r = super(ClosingHttp, self).request(method, url, retries=retry,
+                                             *args, **new_kwargs)
+        return Response(r), r.data
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 7ce05e3..d001d27 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -248,10 +248,10 @@
         :param str url: the relative url to send the post request to
         :param dict body: the request body
         :param dict headers: The headers to use for the request
-        :param dict extra_headers: If the headers returned by the get_headers()
-                                   method are to be used but additional headers
-                                   are needed in the request pass them in as a
-                                   dict
+        :param bool extra_headers: Boolean value than indicates if the headers
+                                   returned by the get_headers() method are to
+                                   be used but additional headers are needed in
+                                   the request pass them in as a dict.
         :return: a tuple with the first entry containing the response headers
                  and the second the response body
         :rtype: tuple
@@ -263,10 +263,10 @@
 
         :param str url: the relative url to send the post request to
         :param dict headers: The headers to use for the request
-        :param dict extra_headers: If the headers returned by the get_headers()
-                                   method are to be used but additional headers
-                                   are needed in the request pass them in as a
-                                   dict
+        :param bool extra_headers: Boolean value than indicates if the headers
+                                   returned by the get_headers() method are to
+                                   be used but additional headers are needed in
+                                   the request pass them in as a dict.
         :return: a tuple with the first entry containing the response headers
                  and the second the response body
         :rtype: tuple
@@ -279,10 +279,10 @@
         :param str url: the relative url to send the post request to
         :param dict headers: The headers to use for the request
         :param dict body: the request body
-        :param dict extra_headers: If the headers returned by the get_headers()
-                                   method are to be used but additional headers
-                                   are needed in the request pass them in as a
-                                   dict
+        :param bool extra_headers: Boolean value than indicates if the headers
+                                   returned by the get_headers() method are to
+                                   be used but additional headers are needed in
+                                   the request pass them in as a dict.
         :return: a tuple with the first entry containing the response headers
                  and the second the response body
         :rtype: tuple
@@ -295,10 +295,10 @@
         :param str url: the relative url to send the post request to
         :param dict body: the request body
         :param dict headers: The headers to use for the request
-        :param dict extra_headers: If the headers returned by the get_headers()
-                                   method are to be used but additional headers
-                                   are needed in the request pass them in as a
-                                   dict
+        :param bool extra_headers: Boolean value than indicates if the headers
+                                   returned by the get_headers() method are to
+                                   be used but additional headers are needed in
+                                   the request pass them in as a dict.
         :return: a tuple with the first entry containing the response headers
                  and the second the response body
         :rtype: tuple
@@ -311,10 +311,10 @@
         :param str url: the relative url to send the post request to
         :param dict body: the request body
         :param dict headers: The headers to use for the request
-        :param dict extra_headers: If the headers returned by the get_headers()
-                                   method are to be used but additional headers
-                                   are needed in the request pass them in as a
-                                   dict
+        :param bool extra_headers: Boolean value than indicates if the headers
+                                   returned by the get_headers() method are to
+                                   be used but additional headers are needed in
+                                   the request pass them in as a dict.
         :return: a tuple with the first entry containing the response headers
                  and the second the response body
         :rtype: tuple
@@ -326,10 +326,10 @@
 
         :param str url: the relative url to send the post request to
         :param dict headers: The headers to use for the request
-        :param dict extra_headers: If the headers returned by the get_headers()
-                                   method are to be used but additional headers
-                                   are needed in the request pass them in as a
-                                   dict
+        :param bool extra_headers: Boolean value than indicates if the headers
+                                   returned by the get_headers() method are to
+                                   be used but additional headers are needed in
+                                   the request pass them in as a dict.
         :return: a tuple with the first entry containing the response headers
                  and the second the response body
         :rtype: tuple
@@ -341,10 +341,10 @@
 
         :param str url: the relative url to send the post request to
         :param dict headers: The headers to use for the request
-        :param dict extra_headers: If the headers returned by the get_headers()
-                                   method are to be used but additional headers
-                                   are needed in the request pass them in as a
-                                   dict
+        :param bool extra_headers: Boolean value than indicates if the headers
+                                   returned by the get_headers() method are to
+                                   be used but additional headers are needed in
+                                   the request pass them in as a dict.
         :return: a tuple with the first entry containing the response headers
                  and the second the response body
         :rtype: tuple
@@ -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
@@ -576,15 +576,15 @@
 
         :param str method: The HTTP verb to use for the request
         :param str url: Relative url to send the request to
-        :param dict extra_headers: If specified without the headers kwarg the
-                                   headers sent with the request will be the
-                                   combination from the get_headers() method
-                                   and this kwarg
+        :param bool extra_headers: Boolean value than indicates if the headers
+                                   returned by the get_headers() method are to
+                                   be used but additional headers are needed in
+                                   the request pass them in as a dict.
         :param dict headers: Headers to use for the request if none are
                              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
@@ -620,7 +620,6 @@
             headers = self.get_headers()
         elif extra_headers:
             try:
-                headers = headers.copy()
                 headers.update(self.get_headers())
             except (ValueError, TypeError):
                 headers = self.get_headers()
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index 2bf7cdd..b9b2ae9 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -153,6 +153,20 @@
     message = "Invalid structure of table with details"
 
 
+class InvalidAPIVersionString(TempestException):
+    message = ("API Version String %(version)s is of invalid format. Must "
+               "be of format MajorNum.MinorNum or string 'latest'.")
+
+
+class JSONSchemaNotFound(TempestException):
+    message = ("JSON Schema for %(version)s is not found in\n"
+               " %(schema_versions_info)s")
+
+
+class InvalidAPIVersionRange(TempestException):
+    message = ("The API version range is invalid.")
+
+
 class BadAltAuth(TempestException):
     """Used when trying and failing to change to alt creds.
 
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/services/compute/json/base_compute_client.py b/tempest/lib/services/compute/base_compute_client.py
similarity index 76%
rename from tempest/services/compute/json/base_compute_client.py
rename to tempest/lib/services/compute/base_compute_client.py
index 8cfde4b..9161abb 100644
--- a/tempest/services/compute/json/base_compute_client.py
+++ b/tempest/lib/services/compute/base_compute_client.py
@@ -11,16 +11,29 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
-from tempest.lib.common import rest_client
 
-from tempest.common import api_version_request
-from tempest.common import api_version_utils
-from tempest import exceptions
+from tempest.lib.common import api_version_request
+from tempest.lib.common import api_version_utils
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions
 
 COMPUTE_MICROVERSION = None
 
 
 class BaseComputeClient(rest_client.RestClient):
+    """Base compute service clients class to support microversion.
+
+    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
+                          auth
+    :param str service: The service name to use for the catalog lookup
+    :param str region: The region to use for the catalog lookup
+    :param kwargs: kwargs required by rest_client.RestClient
+    """
+
     api_microversion_header_name = 'X-OpenStack-Nova-API-Version'
 
     def __init__(self, auth_provider, service, region,
@@ -50,9 +63,10 @@
         """Get JSON schema
 
         This method provides the matching schema for requested
-        microversion (self.api_microversion).
+        microversion.
+
         :param schema_versions_info: List of dict which provides schema
-        information with range of valid versions.
+                                     information with range of valid versions.
         Example -
         schema_versions_info = [
             {'min': None, 'max': '2.1', 'schema': schemav21},
@@ -64,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..e377c84 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'
@@ -132,7 +133,9 @@
             resp, body)
         return rest_client.ResponseBody(resp, body)
 
-    def unset_flavor_extra_spec(self, flavor_id, key):
+    def unset_flavor_extra_spec(self, flavor_id, key):  # noqa
+        # NOTE: This noqa is for passing T111 check and we cannot rename
+        #       to keep backwards compatibility.
         """Unset extra Specs from the mentioned flavor."""
         resp, body = self.delete('flavors/%s/os-extra_specs/%s' %
                                  (flavor_id, key))
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..16b5edd 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."""
@@ -60,7 +61,11 @@
         self.validate_response(schema.update_host, resp, body)
         return rest_client.ResponseBody(resp, body)
 
-    def startup_host(self, hostname):
+    def startup_host(self, hostname):  # noqa
+        # NOTE: This noqa is for passing T110 check and we cannot rename
+        #       to keep backwards compatibility. Actually, the root problem
+        #       of this is a wrong API design. GET operation should not change
+        #       resource status, but current API does that.
         """Startup a host."""
 
         resp, body = self.get("os-hosts/%s/startup" % hostname)
@@ -68,7 +73,11 @@
         self.validate_response(schema.startup_host, resp, body)
         return rest_client.ResponseBody(resp, body)
 
-    def shutdown_host(self, hostname):
+    def shutdown_host(self, hostname):  # noqa
+        # NOTE: This noqa is for passing T110 check and we cannot rename
+        #       to keep backwards compatibility. Actually, the root problem
+        #       of this is a wrong API design. GET operation should not change
+        #       resource status, but current API does that.
         """Shutdown a host."""
 
         resp, body = self.get("os-hosts/%s/shutdown" % hostname)
@@ -76,7 +85,11 @@
         self.validate_response(schema.shutdown_host, resp, body)
         return rest_client.ResponseBody(resp, body)
 
-    def reboot_host(self, hostname):
+    def reboot_host(self, hostname):  # noqa
+        # NOTE: This noqa is for passing T110 check and we cannot rename
+        #       to keep backwards compatibility. Actually, the root problem
+        #       of this is a wrong API design. GET operation should not change
+        #       resource status, but current API does that.
         """Reboot a host."""
 
         resp, body = self.get("os-hosts/%s/reboot" % hostname)
diff --git a/tempest/lib/services/compute/hypervisor_client.py b/tempest/lib/services/compute/hypervisor_client.py
index 2e6df1f..23c304e 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."""
@@ -62,7 +63,9 @@
         self.validate_response(schema.get_hypervisor_uptime, resp, body)
         return rest_client.ResponseBody(resp, body)
 
-    def search_hypervisor(self, hypervisor_name):
+    def search_hypervisor(self, hypervisor_name):  # noqa
+        # NOTE: This noqa is for passing T110 check and we cannot rename
+        #       to keep backwards compatibility.
         """Search specified hypervisor."""
         resp, body = self.get('os-hypervisors/%s/search' % hypervisor_name)
         body = json.loads(body)
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..7b8e6b2 100644
--- a/tempest/lib/services/compute/keypairs_client.py
+++ b/tempest/lib/services/compute/keypairs_client.py
@@ -14,22 +14,36 @@
 #    under the License.
 
 from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
 
-from tempest.lib.api_schema.response.compute.v2_1 import keypairs as schema
+from tempest.lib.api_schema.response.compute.v2_1 import keypairs as schemav21
+from tempest.lib.api_schema.response.compute.v2_2 import keypairs as schemav22
 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")
+    schema_versions_info = [{'min': None, 'max': '2.1', 'schema': schemav21},
+                            {'min': '2.2', 'max': None, 'schema': schemav22}]
+
+    def list_keypairs(self, **params):
+        url = 'os-keypairs'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.list_keypairs, resp, body)
         return rest_client.ResponseBody(resp, body)
 
-    def show_keypair(self, keypair_name):
-        resp, body = self.get("os-keypairs/%s" % keypair_name)
+    def show_keypair(self, keypair_name, **params):
+        url = "os-keypairs/%s" % keypair_name
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_keypair, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -42,10 +56,15 @@
         post_body = json.dumps({'keypair': kwargs})
         resp, body = self.post("os-keypairs", body=post_body)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.create_keypair, resp, body)
         return rest_client.ResponseBody(resp, body)
 
-    def delete_keypair(self, keypair_name):
-        resp, body = self.delete("os-keypairs/%s" % keypair_name)
+    def delete_keypair(self, keypair_name, **params):
+        url = "os-keypairs/%s" % keypair_name
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.delete(url)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.delete_keypair, resp, body)
         return rest_client.ResponseBody(resp, body)
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 5898f93..eb4e7e9 100644
--- a/tempest/lib/services/compute/versions_client.py
+++ b/tempest/lib/services/compute/versions_client.py
@@ -12,24 +12,30 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
+import re
+
 from oslo_serialization import jsonutils as json
 from six.moves import urllib
 
 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 gotten from keystone's catalog contains
-        # API version and project-id like "v2/{project-id}", but we need
-        # to access the URL which doesn't contain them for getting API
-        # versions. For that, here should use raw_request() instead of
-        # get().
+        # NOTE: The URL which is got from keystone's catalog contains
+        # API version and project-id like "/app-name/v2/{project-id}" or
+        # "/v2/{project-id}", but we need to access the URL which doesn't
+        # contain API version for getting API versions. For that, here
+        # should use raw_request() instead of get().
         endpoint = self.base_url
-        url = urllib.parse.urlparse(endpoint)
-        return '%s://%s/' % (url.scheme, url.netloc)
+        url = urllib.parse.urlsplit(endpoint)
+        new_path = re.split(r'(^|/)+v\d+(\.\d+)?', url.path)[0]
+        url = list(url)
+        url[2] = new_path + '/'
+        return urllib.parse.urlunsplit(url)
 
     def list_versions(self):
         version_url = self._get_base_version_url()
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/lib/services/network/ports_client.py b/tempest/lib/services/network/ports_client.py
index 1793d6a..eba11d3 100644
--- a/tempest/lib/services/network/ports_client.py
+++ b/tempest/lib/services/network/ports_client.py
@@ -10,6 +10,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.network import base
 
 
@@ -45,3 +46,10 @@
         """
         uri = '/ports'
         return self.create_resource(uri, kwargs)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_port(id)
+        except lib_exc.NotFound:
+            return True
+        return False
diff --git a/tempest/lib/services/network/quotas_client.py b/tempest/lib/services/network/quotas_client.py
index b5cf35b..752b253 100644
--- a/tempest/lib/services/network/quotas_client.py
+++ b/tempest/lib/services/network/quotas_client.py
@@ -22,7 +22,9 @@
         uri = '/quotas/%s' % tenant_id
         return self.update_resource(uri, put_body)
 
-    def reset_quotas(self, tenant_id):
+    def reset_quotas(self, tenant_id):  # noqa
+        # NOTE: This noqa is for passing T111 check and we cannot rename
+        #       to keep backwards compatibility.
         uri = '/quotas/%s' % tenant_id
         return self.delete_resource(uri)
 
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index eb29176..988ee1a 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -63,9 +63,9 @@
         cls.servers_client = cls.manager.servers_client
         cls.interface_client = cls.manager.interfaces_client
         # Neutron network client
-        cls.network_client = cls.manager.network_client
         cls.networks_client = cls.manager.networks_client
         cls.ports_client = cls.manager.ports_client
+        cls.routers_client = cls.manager.routers_client
         cls.subnets_client = cls.manager.subnets_client
         cls.floating_ips_client = cls.manager.floating_ips_client
         cls.security_groups_client = cls.manager.security_groups_client
@@ -216,7 +216,7 @@
                 networks = kwargs.pop('networks')
 
             # If there are no networks passed to us we look up
-            # for the tenant's private networks and create a port
+            # for the project's private networks and create a port
             # if there is only one private network. The same behaviour
             # as we would expect when passing the call to the clients
             # with no networks
@@ -261,7 +261,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,
@@ -272,24 +272,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']
@@ -393,8 +387,6 @@
         if properties is None:
             properties = {}
         name = data_utils.rand_name('%s-' % name)
-        image_file = open(path, 'rb')
-        self.addCleanup(image_file.close)
         params = {
             'name': name,
             'container_format': fmt,
@@ -405,7 +397,8 @@
         image = self.image_client.create_image(**params)['image']
         self.addCleanup(self.image_client.delete_image, image['id'])
         self.assertEqual("queued", image['status'])
-        self.image_client.update_image(image['id'], data=image_file)
+        with open(path, 'rb') as image_file:
+            self.image_client.update_image(image['id'], data=image_file)
         return image['id']
 
     def glance_image_create(self):
@@ -485,8 +478,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 +492,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 +602,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,
@@ -689,18 +686,20 @@
         super(NetworkScenarioTest, cls).resource_setup()
         cls.tenant_id = cls.manager.identity_client.tenant_id
 
-    def _create_network(self, client=None, networks_client=None,
-                        tenant_id=None, namestart='network-smoke-'):
-        if not client:
-            client = self.network_client
+    def _create_network(self, networks_client=None,
+                        routers_client=None, tenant_id=None,
+                        namestart='network-smoke-'):
         if not networks_client:
             networks_client = self.networks_client
+        if not routers_client:
+            routers_client = self.routers_client
         if not tenant_id:
-            tenant_id = client.tenant_id
+            tenant_id = networks_client.tenant_id
         name = data_utils.rand_name(namestart)
         result = networks_client.create_network(name=name, tenant_id=tenant_id)
         network = net_resources.DeletableNetwork(
-            networks_client=networks_client, **result['network'])
+            networks_client=networks_client, routers_client=routers_client,
+            **result['network'])
         self.assertEqual(network.name, name)
         self.addCleanup(self.delete_wrapper, network.delete)
         return network
@@ -719,7 +718,7 @@
 
     def _list_routers(self, *args, **kwargs):
         """List routers using admin creds """
-        routers_list = self.admin_manager.network_client.list_routers(
+        routers_list = self.admin_manager.routers_client.list_routers(
             *args, **kwargs)
         return routers_list['routers']
 
@@ -735,16 +734,17 @@
             *args, **kwargs)
         return agents_list['agents']
 
-    def _create_subnet(self, network, client=None, subnets_client=None,
-                       namestart='subnet-smoke', **kwargs):
+    def _create_subnet(self, network, subnets_client=None,
+                       routers_client=None, namestart='subnet-smoke',
+                       **kwargs):
         """Create a subnet for the given network
 
         within the cidr block configured for tenant networks.
         """
-        if not client:
-            client = self.network_client
         if not subnets_client:
             subnets_client = self.subnets_client
+        if not routers_client:
+            routers_client = self.routers_client
 
         def cidr_in_use(cidr, tenant_id):
             """Check cidr existence
@@ -759,11 +759,11 @@
 
         if ip_version == 6:
             tenant_cidr = netaddr.IPNetwork(
-                CONF.network.tenant_network_v6_cidr)
-            num_bits = CONF.network.tenant_network_v6_mask_bits
+                CONF.network.project_network_v6_cidr)
+            num_bits = CONF.network.project_network_v6_mask_bits
         else:
-            tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
-            num_bits = CONF.network.tenant_network_mask_bits
+            tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+            num_bits = CONF.network.project_network_mask_bits
 
         result = None
         str_cidr = None
@@ -791,8 +791,8 @@
                     raise
         self.assertIsNotNone(result, 'Unable to allocate tenant network')
         subnet = net_resources.DeletableSubnet(
-            network_client=client, subnets_client=subnets_client,
-            **result['subnet'])
+            subnets_client=subnets_client,
+            routers_client=routers_client, **result['subnet'])
         self.assertEqual(subnet.cidr, str_cidr)
         self.addCleanup(self.delete_wrapper, subnet.delete)
         return subnet
@@ -903,7 +903,7 @@
                                            private_key,
                                            should_connect=True,
                                            servers_for_debug=None):
-        if not CONF.network.tenant_networks_reachable:
+        if not CONF.network.project_networks_reachable:
             msg = 'Tenant networks not configured to be reachable.'
             LOG.info(msg)
             return
@@ -992,7 +992,7 @@
         sg_dict['tenant_id'] = tenant_id
         result = client.create_security_group(**sg_dict)
         secgroup = net_resources.DeletableSecurityGroup(
-            client=client,
+            client=client, routers_client=self.routers_client,
             **result['security_group']
         )
         self.assertEqual(secgroup.name, sg_name)
@@ -1128,7 +1128,7 @@
         routes traffic to the public network.
         """
         if not client:
-            client = self.network_client
+            client = self.routers_client
         if not tenant_id:
             tenant_id = client.tenant_id
         router_id = CONF.network.public_router_id
@@ -1147,14 +1147,14 @@
     def _create_router(self, client=None, tenant_id=None,
                        namestart='router-smoke'):
         if not client:
-            client = self.network_client
+            client = self.routers_client
         if not tenant_id:
             tenant_id = client.tenant_id
         name = data_utils.rand_name(namestart)
         result = client.create_router(name=name,
                                       admin_state_up=True,
                                       tenant_id=tenant_id)
-        router = net_resources.DeletableRouter(client=client,
+        router = net_resources.DeletableRouter(routers_client=client,
                                                **result['router'])
         self.assertEqual(router.name, name)
         self.addCleanup(self.delete_wrapper, router.delete)
@@ -1164,15 +1164,14 @@
         router.update(admin_state_up=admin_state_up)
         self.assertEqual(admin_state_up, router.admin_state_up)
 
-    def create_networks(self, client=None, networks_client=None,
-                        subnets_client=None, tenant_id=None,
-                        dns_nameservers=None):
+    def create_networks(self, networks_client=None,
+                        routers_client=None, subnets_client=None,
+                        tenant_id=None, dns_nameservers=None):
         """Create a network with a subnet connected to a router.
 
         The baremetal driver is a special case since all nodes are
         on the same shared network.
 
-        :param client: network client to create resources with.
         :param tenant_id: id of tenant to create resources in.
         :param dns_nameservers: list of dns servers to send to subnet.
         :returns: network, subnet, router
@@ -1192,12 +1191,14 @@
             subnet = None
         else:
             network = self._create_network(
-                client=client, networks_client=networks_client,
+                networks_client=networks_client,
                 tenant_id=tenant_id)
-            router = self._get_router(client=client, tenant_id=tenant_id)
+            router = self._get_router(client=routers_client,
+                                      tenant_id=tenant_id)
 
-            subnet_kwargs = dict(network=network, client=client,
-                                 subnets_client=subnets_client)
+            subnet_kwargs = dict(network=network,
+                                 subnets_client=subnets_client,
+                                 routers_client=routers_client)
             # use explicit check because empty list is a valid option
             if dns_nameservers is not None:
                 subnet_kwargs['dns_nameservers'] = dns_nameservers
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 2cbe6dc..4c2d31b 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -38,9 +38,9 @@
     @classmethod
     def skip_checks(cls):
         super(TestNetworkAdvancedServerOps, cls).skip_checks()
-        if not (CONF.network.tenant_networks_reachable
+        if not (CONF.network.project_networks_reachable
                 or CONF.network.public_network_id):
-            msg = ('Either tenant_networks_reachable must be "true", or '
+            msg = ('Either project_networks_reachable must be "true", or '
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
 
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 4d03ed7..dfa4815 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -59,7 +59,7 @@
      Determine which types of networks to test as follows:
 
      * Configure tenant network checks (via the
-       'tenant_networks_reachable' key) if the Tempest host should
+       'project_networks_reachable' key) if the Tempest host should
        have direct connectivity to tenant networks.  This is likely to
        be the case if Tempest is running on the same host as a
        single-node devstack installation with IP namespaces disabled.
@@ -81,9 +81,9 @@
     @classmethod
     def skip_checks(cls):
         super(TestNetworkBasicOps, cls).skip_checks()
-        if not (CONF.network.tenant_networks_reachable
+        if not (CONF.network.project_networks_reachable
                 or CONF.network.public_network_id):
-            msg = ('Either tenant_networks_reachable must be "true", or '
+            msg = ('Either project_networks_reachable must be "true", or '
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
         for ext in ['router', 'security-group']:
@@ -250,9 +250,8 @@
         interface = self.interface_client.create_interface(
             server_id=server['id'],
             net_id=self.new_net.id)['interfaceAttachment']
-        self.addCleanup(self.network_client.wait_for_resource_deletion,
-                        'port',
-                        interface['port_id'], client=self.ports_client)
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        interface['port_id'])
         self.addCleanup(self.delete_wrapper,
                         self.interface_client.delete_interface,
                         server['id'], interface['port_id'])
@@ -680,7 +679,7 @@
         # TODO(yfried): refactor this test to be used for other agents (dhcp)
         # as well
 
-        list_hosts = (self.admin_manager.network_client.
+        list_hosts = (self.admin_manager.routers_client.
                       list_l3_agents_hosting_router)
         schedule_router = (self.admin_manager.network_agents_client.
                            create_router_on_l3_agent)
@@ -692,8 +691,8 @@
         self._setup_network_and_servers()
 
         # NOTE(kevinbenton): we have to use the admin credentials to check
-        # for the distributed flag because self.router only has a tenant view.
-        admin = self.admin_manager.network_client.show_router(self.router.id)
+        # for the distributed flag because self.router only has a project view.
+        admin = self.admin_manager.routers_client.show_router(self.router.id)
         if admin['router'].get('distributed', False):
             msg = "Rescheduling test does not apply to distributed routers."
             raise self.skipException(msg)
@@ -776,7 +775,7 @@
         private_key = self._get_server_key(server)
         ssh_client = self.get_remote_client(fip.floating_ip_address,
                                             private_key=private_key)
-        spoof_nic = ssh_client.get_nic_name(spoof_port["mac_address"])
+        spoof_nic = ssh_client.get_nic_name_by_mac(spoof_port["mac_address"])
         dhcp_ports = self._list_ports(device_owner="network:dhcp",
                                       network_id=self.new_net["id"])
         new_net_dhcp = dhcp_ports[0]["fixed_ips"][0]["ip_address"]
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index fc33dd9..a52d8f9 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -44,9 +44,9 @@
         if not (CONF.network_feature_enabled.ipv6
                 and CONF.network_feature_enabled.ipv6_subnet_attributes):
             raise cls.skipException('IPv6 or its attributes not supported')
-        if not (CONF.network.tenant_networks_reachable
+        if not (CONF.network.project_networks_reachable
                 or CONF.network.public_network_id):
-            msg = ('Either tenant_networks_reachable must be "true", or '
+            msg = ('Either project_networks_reachable must be "true", or '
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
         if CONF.baremetal.driver_enabled:
@@ -145,7 +145,7 @@
                                   "ports: %s")
                          % (self.network_v6, ports))
         mac6 = ports[0]
-        ssh.set_nic_state(ssh.get_nic_name(mac6))
+        ssh.set_nic_state(ssh.get_nic_name_by_mac(mac6))
 
     def _prepare_and_test(self, address6_mode, n_subnets6=1, dualnet=False):
         net_list = self.prepare_network(address6_mode=address6_mode,
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 18bd764..adc9008 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -12,6 +12,7 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from oslo_log import log
 
 from tempest import clients
 from tempest.common.utils import data_utils
@@ -20,6 +21,7 @@
 from tempest import test
 
 CONF = config.CONF
+LOG = log.getLogger(__name__)
 
 
 class TestSecurityGroupsBasicOps(manager.NetworkScenarioTest):
@@ -43,6 +45,12 @@
     success - ping returns
     failure - ping_timeout reached
 
+    multi-node:
+        Multi-Node mode is enabled when CONF.compute.min_compute_nodes > 1.
+        Tests connectivity between servers on different compute nodes.
+        When enabled, test will boot each new server to different
+        compute nodes.
+
     setup:
         for primary tenant:
             1. create a network&subnet
@@ -128,9 +136,9 @@
             msg = ('Not currently supported when using vnic_type'
                    ' direct or macvtap')
             raise cls.skipException(msg)
-        if not (CONF.network.tenant_networks_reachable or
+        if not (CONF.network.project_networks_reachable or
                 CONF.network.public_network_id):
-            msg = ('Either tenant_networks_reachable must be "true", or '
+            msg = ('Either project_networks_reachable must be "true", or '
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
         if not test.is_extension_enabled('security-group', 'network'):
@@ -151,6 +159,14 @@
     @classmethod
     def resource_setup(cls):
         super(TestSecurityGroupsBasicOps, cls).resource_setup()
+
+        cls.multi_node = CONF.compute.min_compute_nodes > 1 and \
+            test.is_scheduler_filter_enabled("DifferentHostFilter")
+        if cls.multi_node:
+            LOG.info("Working in Multi Node mode")
+        else:
+            LOG.info("Working in Single Node mode")
+
         cls.floating_ips = {}
         cls.tenants = {}
         creds = cls.manager.credentials
@@ -162,6 +178,12 @@
         cls.floating_ip_access = not CONF.network.public_router_id
 
     def setUp(self):
+        """Set up a single tenant with an accessible server.
+
+        If multi-host is enabled, save created server uuids.
+        """
+        self.servers = []
+
         super(TestSecurityGroupsBasicOps, self).setUp()
         self._deploy_tenant(self.primary_tenant)
         self._verify_network_details(self.primary_tenant)
@@ -178,7 +200,7 @@
             client=tenant.manager.security_groups_client
         )
 
-        # don't use default secgroup since it allows in-tenant traffic
+        # don't use default secgroup since it allows in-project traffic
         def_sg = self._create_empty_security_group(
             namestart='secgroup_general-',
             tenant_id=tenant.creds.tenant_id,
@@ -233,21 +255,44 @@
         # and distributed routers; 'device_owner' is "" by default.
         return port['device_owner'].startswith('network:router_interface')
 
-    def _create_server(self, name, tenant, security_groups=None):
-        """creates a server and assigns to security group"""
+    def _create_server(self, name, tenant, security_groups=None, **kwargs):
+        """Creates a server and assigns it to security group.
+
+        If multi-host is enabled, Ensures servers are created on different
+        compute nodes, by storing created servers' ids and uses different_host
+        as scheduler_hints on creation.
+        Validates servers are created as requested, using admin client.
+        """
         if security_groups is None:
             security_groups = [tenant.security_groups['default']]
         security_groups_names = [{'name': s['name']} for s in security_groups]
+        if self.multi_node:
+            kwargs["scheduler_hints"] = {'different_host': self.servers}
         server = self.create_server(
             name=name,
             networks=[{'uuid': tenant.network.id}],
             key_name=tenant.keypair['name'],
             security_groups=security_groups_names,
             wait_until='ACTIVE',
-            clients=tenant.manager)
+            clients=tenant.manager,
+            **kwargs)
         self.assertEqual(
             sorted([s['name'] for s in security_groups]),
             sorted([s['name'] for s in server['security_groups']]))
+
+        # Verify servers are on different compute nodes
+        if self.multi_node:
+            adm_get_server = self.admin_manager.servers_client.show_server
+            new_host = adm_get_server(server["id"])["server"][
+                "OS-EXT-SRV-ATTR:host"]
+            host_list = [adm_get_server(s)["server"]["OS-EXT-SRV-ATTR:host"]
+                         for s in self.servers]
+            self.assertNotIn(new_host, host_list,
+                             message="Failed to boot servers on different "
+                                     "Compute nodes.")
+
+            self.servers.append(server["id"])
+
         return server
 
     def _create_tenant_servers(self, tenant, num=1):
@@ -262,7 +307,7 @@
 
     def _set_access_point(self, tenant):
         # creates a server in a secgroup with rule allowing external ssh
-        # in order to access tenant internal network
+        # in order to access project internal network
         # workaround ip namespace
         secgroups = tenant.security_groups.values()
         name = 'server-{tenant}-access_point'.format(
@@ -282,8 +327,8 @@
 
     def _create_tenant_network(self, tenant):
         network, subnet, router = self.create_networks(
-            client=tenant.manager.network_client,
             networks_client=tenant.manager.networks_client,
+            routers_client=tenant.manager.routers_client,
             subnets_client=tenant.manager.subnets_client)
         tenant.set_network(network, subnet, router)
 
@@ -348,6 +393,7 @@
         )
         self._create_security_group_rule(
             secgroup=tenant.security_groups['default'],
+            security_groups_client=tenant.manager.security_groups_client,
             **ruleset
         )
         access_point_ssh = self._connect_to_access_point(tenant)
@@ -427,7 +473,7 @@
         if not self.credentials_provider.is_multi_tenant():
             raise self.skipException("No secondary tenant defined")
         try:
-            # deploy new tenant
+            # deploy new project
             self._deploy_tenant(self.alt_tenant)
             self._verify_network_details(self.alt_tenant)
             self._verify_mac_addr(self.alt_tenant)
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 4ce57db..25d825a 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -10,6 +10,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log as logging
+
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
@@ -17,6 +19,7 @@
 from tempest import test
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
 class TestVolumeBootPattern(manager.ScenarioTest):
@@ -32,6 +35,11 @@
      * Boot an additional instance from the new snapshot based volume
      * Check written content in the instance booted from snapshot
     """
+
+    # Boot from volume scenario is quite slow, and needs extra
+    # breathing room to get through deletes in the time allotted.
+    TIMEOUT_SCALING_FACTOR = 2
+
     @classmethod
     def skip_checks(cls):
         super(TestVolumeBootPattern, cls).skip_checks()
@@ -79,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:
@@ -101,42 +110,53 @@
     @test.attr(type='smoke')
     @test.services('compute', 'volume', 'image')
     def test_volume_boot_pattern(self):
+        LOG.info("Creating keypair and security group")
         keypair = self.create_keypair()
         security_group = self._create_security_group()
 
         # create an instance from volume
+        LOG.info("Booting instance 1 from volume")
         volume_origin = self._create_volume_from_image()
         instance_1st = self._boot_instance_from_volume(volume_origin['id'],
                                                        keypair, security_group)
+        LOG.info("Booted first instance: %s" % instance_1st)
 
         # write content to volume on instance
+        LOG.info("Setting timestamp in instance %s" % instance_1st)
         ip_instance_1st = self.get_server_ip(instance_1st)
         timestamp = self.create_timestamp(ip_instance_1st,
                                           private_key=keypair['private_key'])
 
         # delete instance
+        LOG.info("Deleting first instance: %s" % instance_1st)
         self._delete_server(instance_1st)
 
         # create a 2nd instance from volume
         instance_2nd = self._boot_instance_from_volume(volume_origin['id'],
                                                        keypair, security_group)
+        LOG.info("Booted second instance %s" % instance_2nd)
 
         # check the content of written file
+        LOG.info("Getting timestamp in instance %s" % instance_2nd)
         ip_instance_2nd = self.get_server_ip(instance_2nd)
         timestamp2 = self.get_timestamp(ip_instance_2nd,
                                         private_key=keypair['private_key'])
         self.assertEqual(timestamp, timestamp2)
 
         # snapshot a volume
+        LOG.info("Creating snapshot from volume: %s" % volume_origin['id'])
         snapshot = self._create_snapshot_from_volume(volume_origin['id'])
 
         # create a 3rd instance from snapshot
+        LOG.info("Creating third instance from snapshot: %s" % snapshot['id'])
         volume = self._create_volume_from_snapshot(snapshot['id'])
         server_from_snapshot = (
             self._boot_instance_from_volume(volume['id'],
                                             keypair, security_group))
 
         # check the content of written file
+        LOG.info("Logging into third instance to get timestamp: %s" %
+                 server_from_snapshot)
         server_from_snapshot_ip = self.get_server_ip(server_from_snapshot)
         timestamp3 = self.get_timestamp(server_from_snapshot_ip,
                                         private_key=keypair['private_key'])
diff --git a/tempest/services/baremetal/base.py b/tempest/services/baremetal/base.py
index d8cb99d..6e24801 100644
--- a/tempest/services/baremetal/base.py
+++ b/tempest/services/baremetal/base.py
@@ -16,7 +16,7 @@
 import six
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
 def handle_errors(f):
@@ -39,7 +39,7 @@
     return wrapper
 
 
-class BaremetalClient(service_client.ServiceClient):
+class BaremetalClient(rest_client.RestClient):
     """Base Tempest REST client for Ironic API."""
 
     uri_prefix = ''
diff --git a/tempest/services/compute/__init__.py b/tempest/services/compute/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/compute/__init__.py
+++ /dev/null
diff --git a/tempest/services/compute/json/__init__.py b/tempest/services/compute/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/compute/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/compute/json/keypairs_client.py b/tempest/services/compute/json/keypairs_client.py
deleted file mode 100644
index ec9b1e0..0000000
--- a/tempest/services/compute/json/keypairs_client.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from oslo_serialization import jsonutils as json
-
-from tempest.api_schema.response.compute.v2_1 import keypairs as schemav21
-from tempest.api_schema.response.compute.v2_2 import keypairs as schemav22
-from tempest.common import service_client
-from tempest.services.compute.json import base_compute_client
-
-
-class KeyPairsClient(base_compute_client.BaseComputeClient):
-
-    schema_versions_info = [{'min': None, 'max': '2.1', 'schema': schemav21},
-                            {'min': '2.2', 'max': None, 'schema': schemav22}]
-
-    def list_keypairs(self):
-        resp, body = self.get("os-keypairs")
-        body = json.loads(body)
-        schema = self.get_schema(self.schema_versions_info)
-        self.validate_response(schema.list_keypairs, resp, body)
-        return service_client.ResponseBody(resp, body)
-
-    def show_keypair(self, keypair_name):
-        resp, body = self.get("os-keypairs/%s" % keypair_name)
-        body = json.loads(body)
-        schema = self.get_schema(self.schema_versions_info)
-        self.validate_response(schema.get_keypair, resp, body)
-        return service_client.ResponseBody(resp, body)
-
-    def create_keypair(self, **kwargs):
-        post_body = json.dumps({'keypair': kwargs})
-        resp, body = self.post("os-keypairs", body=post_body)
-        body = json.loads(body)
-        schema = self.get_schema(self.schema_versions_info)
-        self.validate_response(schema.create_keypair, resp, body)
-        return service_client.ResponseBody(resp, body)
-
-    def delete_keypair(self, keypair_name):
-        resp, body = self.delete("os-keypairs/%s" % keypair_name)
-        schema = self.get_schema(self.schema_versions_info)
-        self.validate_response(schema.delete_keypair, resp, body)
-        return service_client.ResponseBody(resp, body)
diff --git a/tempest/services/data_processing/v1_1/data_processing_client.py b/tempest/services/data_processing/v1_1/data_processing_client.py
index 5aa2622..c74672f 100644
--- a/tempest/services/data_processing/v1_1/data_processing_client.py
+++ b/tempest/services/data_processing/v1_1/data_processing_client.py
@@ -14,10 +14,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class DataProcessingClient(service_client.ServiceClient):
+class DataProcessingClient(rest_client.RestClient):
 
     def _request_and_check_resp(self, request_func, uri, resp_status):
         """Make a request and check response status code.
@@ -26,7 +26,7 @@
         """
         resp, body = request_func(uri)
         self.expected_success(resp_status, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def _request_and_check_resp_data(self, request_func, uri, resp_status):
         """Make a request and check response status code.
@@ -47,7 +47,7 @@
         resp, body = request_func(uri, headers=headers, *args, **kwargs)
         self.expected_success(resp_status, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_node_group_templates(self):
         """List all node group templates for a user."""
diff --git a/tempest/services/database/json/flavors_client.py b/tempest/services/database/json/flavors_client.py
index dbb5172..bd8ffb0 100644
--- a/tempest/services/database/json/flavors_client.py
+++ b/tempest/services/database/json/flavors_client.py
@@ -16,10 +16,10 @@
 from oslo_serialization import jsonutils as json
 from six.moves import urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class DatabaseFlavorsClient(service_client.ServiceClient):
+class DatabaseFlavorsClient(rest_client.RestClient):
 
     def list_db_flavors(self, params=None):
         url = 'flavors'
@@ -29,10 +29,10 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_db_flavor(self, db_flavor_id):
         resp, body = self.get("flavors/%s" % db_flavor_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/database/json/limits_client.py b/tempest/services/database/json/limits_client.py
index da495d7..a1c58c2 100644
--- a/tempest/services/database/json/limits_client.py
+++ b/tempest/services/database/json/limits_client.py
@@ -16,10 +16,10 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class DatabaseLimitsClient(service_client.ServiceClient):
+class DatabaseLimitsClient(rest_client.RestClient):
 
     def list_db_limits(self, params=None):
         """List all limits."""
@@ -29,4 +29,4 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/database/json/versions_client.py b/tempest/services/database/json/versions_client.py
index 7a560d9..2f28203 100644
--- a/tempest/services/database/json/versions_client.py
+++ b/tempest/services/database/json/versions_client.py
@@ -16,24 +16,14 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class DatabaseVersionsClient(service_client.ServiceClient):
+class DatabaseVersionsClient(rest_client.RestClient):
 
-    def __init__(self, auth_provider, service, region,
-                 endpoint_type=None, build_interval=None, build_timeout=None,
-                 disable_ssl_certificate_validation=None, ca_certs=None,
-                 trace_requests=None):
-        dscv = disable_ssl_certificate_validation
+    def __init__(self, auth_provider, service, region, **kwargs):
         super(DatabaseVersionsClient, self).__init__(
-            auth_provider, service, region,
-            endpoint_type=endpoint_type,
-            build_interval=build_interval,
-            build_timeout=build_timeout,
-            disable_ssl_certificate_validation=dscv,
-            ca_certs=ca_certs,
-            trace_requests=trace_requests)
+            auth_provider, service, region, **kwargs)
         self.skip_path()
 
     def list_db_versions(self, params=None):
@@ -45,4 +35,4 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/endpoints_client.py b/tempest/services/identity/v2/json/endpoints_client.py
index ff9907d..ba9f867 100644
--- a/tempest/services/identity/v2/json/endpoints_client.py
+++ b/tempest/services/identity/v2/json/endpoints_client.py
@@ -14,10 +14,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class EndpointsClient(service_client.ServiceClient):
+class EndpointsClient(rest_client.RestClient):
     api_version = "v2.0"
 
     def create_endpoint(self, service_id, region_id, **kwargs):
@@ -33,18 +33,18 @@
         resp, body = self.post('/endpoints', post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_endpoints(self):
         """List Endpoints - Returns Endpoints."""
         resp, body = self.get('/endpoints')
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_endpoint(self, endpoint_id):
         """Delete an endpoint."""
         url = '/endpoints/%s' % endpoint_id
         resp, body = self.delete(url)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/identity_client.py b/tempest/services/identity/v2/json/identity_client.py
index f045bb7..6caff0e 100644
--- a/tempest/services/identity/v2/json/identity_client.py
+++ b/tempest/services/identity/v2/json/identity_client.py
@@ -12,10 +12,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class IdentityClient(service_client.ServiceClient):
+class IdentityClient(rest_client.RestClient):
     api_version = "v2.0"
 
     def show_api_description(self):
@@ -24,24 +24,24 @@
         resp, body = self.get(url)
         self.expected_success([200, 203], resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_token(self, token_id):
         """Get token details."""
         resp, body = self.get("tokens/%s" % token_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_token(self, token_id):
         """Delete a token."""
         resp, body = self.delete("tokens/%s" % token_id)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_extensions(self):
         """List all the extensions."""
         resp, body = self.get('/extensions')
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/roles_client.py b/tempest/services/identity/v2/json/roles_client.py
index ef6dfe9..acd97c6 100644
--- a/tempest/services/identity/v2/json/roles_client.py
+++ b/tempest/services/identity/v2/json/roles_client.py
@@ -12,10 +12,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class RolesClient(service_client.ServiceClient):
+class RolesClient(rest_client.RestClient):
     api_version = "v2.0"
 
     def create_role(self, **kwargs):
@@ -28,14 +28,14 @@
         resp, body = self.post('OS-KSADM/roles', post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_role(self, role_id):
         """Get a role by its id."""
         resp, body = self.get('OS-KSADM/roles/%s' % role_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_role(self, role_id):
         """Delete a role."""
@@ -49,7 +49,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def assign_user_role(self, tenant_id, user_id, role_id):
         """Add roles to a user on a tenant."""
@@ -57,18 +57,18 @@
                               (tenant_id, user_id, role_id), "")
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_user_role(self, tenant_id, user_id, role_id):
         """Removes a role assignment for a user on a tenant."""
         resp, body = self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
                                  (tenant_id, user_id, role_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_roles(self):
         """Returns roles."""
         resp, body = self.get('OS-KSADM/roles')
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/services_client.py b/tempest/services/identity/v2/json/services_client.py
index 436d00d..d8be6c6 100644
--- a/tempest/services/identity/v2/json/services_client.py
+++ b/tempest/services/identity/v2/json/services_client.py
@@ -14,10 +14,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class ServicesClient(service_client.ServiceClient):
+class ServicesClient(rest_client.RestClient):
     api_version = "v2.0"
 
     def create_service(self, name, type, **kwargs):
@@ -31,7 +31,7 @@
         resp, body = self.post('/OS-KSADM/services', post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_service(self, service_id):
         """Get Service."""
@@ -39,18 +39,18 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_services(self):
         """List Service - Returns Services."""
         resp, body = self.get('/OS-KSADM/services')
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_service(self, service_id):
         """Delete Service."""
         url = '/OS-KSADM/services/%s' % service_id
         resp, body = self.delete(url)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/tenants_client.py b/tempest/services/identity/v2/json/tenants_client.py
index 937ae6f..034938e 100644
--- a/tempest/services/identity/v2/json/tenants_client.py
+++ b/tempest/services/identity/v2/json/tenants_client.py
@@ -14,10 +14,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class TenantsClient(service_client.ServiceClient):
+class TenantsClient(rest_client.RestClient):
     api_version = "v2.0"
 
     def create_tenant(self, name, **kwargs):
@@ -36,27 +36,27 @@
         resp, body = self.post('tenants', post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_tenant(self, tenant_id):
         """Delete a tenant."""
         resp, body = self.delete('tenants/%s' % str(tenant_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_tenant(self, tenant_id):
         """Get tenant details."""
         resp, body = self.get('tenants/%s' % str(tenant_id))
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_tenants(self):
         """Returns tenants."""
         resp, body = self.get('tenants')
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_tenant(self, tenant_id, **kwargs):
         """Updates a tenant."""
@@ -74,11 +74,11 @@
         resp, body = self.post('tenants/%s' % tenant_id, post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_tenant_users(self, tenant_id):
         """List users for a Tenant."""
         resp, body = self.get('/tenants/%s/users' % tenant_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/users_client.py b/tempest/services/identity/v2/json/users_client.py
index 5327638..5f8127f 100644
--- a/tempest/services/identity/v2/json/users_client.py
+++ b/tempest/services/identity/v2/json/users_client.py
@@ -12,10 +12,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class UsersClient(service_client.ServiceClient):
+class UsersClient(rest_client.RestClient):
     api_version = "v2.0"
 
     def create_user(self, name, password, tenant_id, email, **kwargs):
@@ -33,7 +33,7 @@
         resp, body = self.post('users', post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_user(self, user_id, **kwargs):
         """Updates a user."""
@@ -41,27 +41,27 @@
         resp, body = self.put('users/%s' % user_id, put_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_user(self, user_id):
         """GET a user."""
         resp, body = self.get("users/%s" % user_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_user(self, user_id):
         """Delete a user."""
         resp, body = self.delete("users/%s" % user_id)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_users(self):
         """Get the list of users."""
         resp, body = self.get("users")
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def enable_disable_user(self, user_id, **kwargs):
         """Enables or disables a user.
@@ -77,7 +77,7 @@
         resp, body = self.put('users/%s/enabled' % user_id, put_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_user_password(self, user_id, **kwargs):
         """Update User Password."""
@@ -89,7 +89,7 @@
         resp, body = self.put('users/%s/OS-KSADM/password' % user_id, put_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_user_own_password(self, user_id, **kwargs):
         """User updates own password"""
@@ -104,7 +104,7 @@
         resp, body = self.patch('OS-KSCRUD/users/%s' % user_id, patch_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_user_ec2_credentials(self, user_id, **kwargs):
         # TODO(piyush): Current api-site doesn't contain this API description.
@@ -115,23 +115,23 @@
                                post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_user_ec2_credentials(self, user_id, access):
         resp, body = self.delete('/users/%s/credentials/OS-EC2/%s' %
                                  (user_id, access))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_user_ec2_credentials(self, user_id):
         resp, body = self.get('/users/%s/credentials/OS-EC2' % user_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_user_ec2_credentials(self, user_id, access):
         resp, body = self.get('/users/%s/credentials/OS-EC2/%s' %
                               (user_id, access))
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/credentials_client.py b/tempest/services/identity/v3/json/credentials_client.py
index 753e960..6ab94d0 100644
--- a/tempest/services/identity/v3/json/credentials_client.py
+++ b/tempest/services/identity/v3/json/credentials_client.py
@@ -19,10 +19,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class CredentialsClient(service_client.ServiceClient):
+class CredentialsClient(rest_client.RestClient):
     api_version = "v3"
 
     def create_credential(self, **kwargs):
@@ -36,7 +36,7 @@
         self.expected_success(201, resp.status)
         body = json.loads(body)
         body['credential']['blob'] = json.loads(body['credential']['blob'])
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_credential(self, credential_id, **kwargs):
         """Updates a credential.
@@ -49,7 +49,7 @@
         self.expected_success(200, resp.status)
         body = json.loads(body)
         body['credential']['blob'] = json.loads(body['credential']['blob'])
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_credential(self, credential_id):
         """To GET Details of a credential."""
@@ -57,17 +57,17 @@
         self.expected_success(200, resp.status)
         body = json.loads(body)
         body['credential']['blob'] = json.loads(body['credential']['blob'])
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_credentials(self):
         """Lists out all the available credentials."""
         resp, body = self.get('credentials')
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_credential(self, credential_id):
         """Deletes a credential."""
         resp, body = self.delete('credentials/%s' % credential_id)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/domains_client.py b/tempest/services/identity/v3/json/domains_client.py
index 626a474..d129a0a 100644
--- a/tempest/services/identity/v3/json/domains_client.py
+++ b/tempest/services/identity/v3/json/domains_client.py
@@ -15,10 +15,10 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class DomainsClient(service_client.ServiceClient):
+class DomainsClient(rest_client.RestClient):
     api_version = "v3"
 
     def create_domain(self, name, **kwargs):
@@ -34,13 +34,13 @@
         resp, body = self.post('domains', post_body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_domain(self, domain_id):
         """Deletes a domain."""
         resp, body = self.delete('domains/%s' % str(domain_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_domains(self, params=None):
         """List Domains."""
@@ -50,7 +50,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_domain(self, domain_id, **kwargs):
         """Updates a domain."""
@@ -67,11 +67,11 @@
         resp, body = self.patch('domains/%s' % domain_id, post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_domain(self, domain_id):
         """Get Domain details."""
         resp, body = self.get('domains/%s' % domain_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/endpoints_client.py b/tempest/services/identity/v3/json/endpoints_client.py
index 8ab7464..db30508 100644
--- a/tempest/services/identity/v3/json/endpoints_client.py
+++ b/tempest/services/identity/v3/json/endpoints_client.py
@@ -19,10 +19,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class EndPointClient(service_client.ServiceClient):
+class EndPointsClient(rest_client.RestClient):
     api_version = "v3"
 
     def list_endpoints(self):
@@ -30,7 +30,7 @@
         resp, body = self.get('endpoints')
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_endpoint(self, **kwargs):
         """Create endpoint.
@@ -42,7 +42,7 @@
         resp, body = self.post('endpoints', post_body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_endpoint(self, endpoint_id, **kwargs):
         """Updates an endpoint with given parameters.
@@ -54,17 +54,17 @@
         resp, body = self.patch('endpoints/%s' % endpoint_id, post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_endpoint(self, endpoint_id):
         """Delete endpoint."""
         resp_header, resp_body = self.delete('endpoints/%s' % endpoint_id)
         self.expected_success(204, resp_header.status)
-        return service_client.ResponseBody(resp_header, resp_body)
+        return rest_client.ResponseBody(resp_header, resp_body)
 
     def show_endpoint(self, endpoint_id):
         """Get endpoint."""
         resp_header, resp_body = self.get('endpoints/%s' % endpoint_id)
         self.expected_success(200, resp_header.status)
         resp_body = json.loads(resp_body)
-        return service_client.ResponseBody(resp_header, resp_body)
+        return rest_client.ResponseBody(resp_header, resp_body)
diff --git a/tempest/services/identity/v3/json/groups_client.py b/tempest/services/identity/v3/json/groups_client.py
index 6ed85cf..1a495f8 100644
--- a/tempest/services/identity/v3/json/groups_client.py
+++ b/tempest/services/identity/v3/json/groups_client.py
@@ -19,10 +19,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class GroupsClient(service_client.ServiceClient):
+class GroupsClient(rest_client.RestClient):
     api_version = "v3"
 
     def create_group(self, **kwargs):
@@ -35,21 +35,21 @@
         resp, body = self.post('groups', post_body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_group(self, group_id):
         """Get group details."""
         resp, body = self.get('groups/%s' % group_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_groups(self):
         """Lists the groups."""
         resp, body = self.get('groups')
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_group(self, group_id, **kwargs):
         """Updates a group.
@@ -61,36 +61,36 @@
         resp, body = self.patch('groups/%s' % group_id, post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_group(self, group_id):
         """Delete a group."""
         resp, body = self.delete('groups/%s' % str(group_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def add_group_user(self, group_id, user_id):
         """Add user into group."""
         resp, body = self.put('groups/%s/users/%s' % (group_id, user_id),
                               None)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_group_users(self, group_id):
         """List users in group."""
         resp, body = self.get('groups/%s/users' % group_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_group_user(self, group_id, user_id):
         """Delete user in group."""
         resp, body = self.delete('groups/%s/users/%s' % (group_id, user_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def check_group_user_existence(self, group_id, user_id):
         """Check user in group."""
         resp, body = self.head('groups/%s/users/%s' % (group_id, user_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index fed33c5..8177e35 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -15,10 +15,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class IdentityV3Client(service_client.ServiceClient):
+class IdentityClient(rest_client.RestClient):
     api_version = "v3"
 
     def show_api_description(self):
@@ -27,7 +27,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_token(self, resp_token):
         """Get token details."""
@@ -35,11 +35,11 @@
         resp, body = self.get("auth/tokens", headers=headers)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_token(self, resp_token):
         """Deletes token."""
         headers = {'X-Subject-Token': resp_token}
         resp, body = self.delete("auth/tokens", headers=headers)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/policies_client.py b/tempest/services/identity/v3/json/policies_client.py
index 639ed6d..f28db9a 100644
--- a/tempest/services/identity/v3/json/policies_client.py
+++ b/tempest/services/identity/v3/json/policies_client.py
@@ -19,10 +19,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class PoliciesClient(service_client.ServiceClient):
+class PoliciesClient(rest_client.RestClient):
     api_version = "v3"
 
     def create_policy(self, **kwargs):
@@ -35,14 +35,14 @@
         resp, body = self.post('policies', post_body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_policies(self):
         """Lists the policies."""
         resp, body = self.get('policies')
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_policy(self, policy_id):
         """Lists out the given policy."""
@@ -50,7 +50,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_policy(self, policy_id, **kwargs):
         """Updates a policy.
@@ -63,11 +63,11 @@
         resp, body = self.patch(url, post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_policy(self, policy_id):
         """Deletes the policy."""
         url = "policies/%s" % policy_id
         resp, body = self.delete(url)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/projects_client.py b/tempest/services/identity/v3/json/projects_client.py
index 2fa822f..dc553d0 100644
--- a/tempest/services/identity/v3/json/projects_client.py
+++ b/tempest/services/identity/v3/json/projects_client.py
@@ -16,10 +16,10 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class ProjectsClient(service_client.ServiceClient):
+class ProjectsClient(rest_client.RestClient):
     api_version = "v3"
 
     def create_project(self, name, **kwargs):
@@ -37,7 +37,7 @@
         resp, body = self.post('projects', post_body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_projects(self, params=None):
         url = "projects"
@@ -46,7 +46,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_project(self, project_id, **kwargs):
         body = self.show_project(project_id)['project']
@@ -65,17 +65,17 @@
         resp, body = self.patch('projects/%s' % project_id, post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_project(self, project_id):
         """GET a Project."""
         resp, body = self.get("projects/%s" % project_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_project(self, project_id):
         """Delete a project."""
         resp, body = self.delete('projects/%s' % str(project_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/regions_client.py b/tempest/services/identity/v3/json/regions_client.py
index bc4b7a1..90dd9d7 100644
--- a/tempest/services/identity/v3/json/regions_client.py
+++ b/tempest/services/identity/v3/json/regions_client.py
@@ -20,10 +20,10 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class RegionsClient(service_client.ServiceClient):
+class RegionsClient(rest_client.RestClient):
     api_version = "v3"
 
     def create_region(self, region_id=None, **kwargs):
@@ -45,7 +45,7 @@
         resp, body = method(url, req_body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_region(self, region_id, **kwargs):
         """Updates a region.
@@ -57,7 +57,7 @@
         resp, body = self.patch('regions/%s' % region_id, post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_region(self, region_id):
         """Get region."""
@@ -65,7 +65,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_regions(self, params=None):
         """List regions."""
@@ -75,10 +75,10 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_region(self, region_id):
         """Delete region."""
         resp, body = self.delete('regions/%s' % region_id)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/roles_client.py b/tempest/services/identity/v3/json/roles_client.py
index b10c02e..bdb0490 100644
--- a/tempest/services/identity/v3/json/roles_client.py
+++ b/tempest/services/identity/v3/json/roles_client.py
@@ -14,10 +14,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class RolesClient(service_client.ServiceClient):
+class RolesClient(rest_client.RestClient):
     api_version = "v3"
 
     def create_role(self, **kwargs):
@@ -30,21 +30,21 @@
         resp, body = self.post('roles', post_body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_role(self, role_id):
         """GET a Role."""
         resp, body = self.get('roles/%s' % str(role_id))
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_roles(self):
         """Get the list of Roles."""
         resp, body = self.get("roles")
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_role(self, role_id, **kwargs):
         """Update a Role.
@@ -56,27 +56,27 @@
         resp, body = self.patch('roles/%s' % str(role_id), post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_role(self, role_id):
         """Delete a role."""
         resp, body = self.delete('roles/%s' % str(role_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def assign_user_role_on_project(self, project_id, user_id, role_id):
         """Add roles to a user on a project."""
         resp, body = self.put('projects/%s/users/%s/roles/%s' %
                               (project_id, user_id, role_id), None)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def assign_user_role_on_domain(self, domain_id, user_id, role_id):
         """Add roles to a user on a domain."""
         resp, body = self.put('domains/%s/users/%s/roles/%s' %
                               (domain_id, user_id, role_id), None)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_user_roles_on_project(self, project_id, user_id):
         """list roles of a user on a project."""
@@ -84,7 +84,7 @@
                               (project_id, user_id))
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_user_roles_on_domain(self, domain_id, user_id):
         """list roles of a user on a domain."""
@@ -92,21 +92,21 @@
                               (domain_id, user_id))
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_role_from_user_on_project(self, project_id, user_id, role_id):
         """Delete role of a user on a project."""
         resp, body = self.delete('projects/%s/users/%s/roles/%s' %
                                  (project_id, user_id, role_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_role_from_user_on_domain(self, domain_id, user_id, role_id):
         """Delete role of a user on a domain."""
         resp, body = self.delete('domains/%s/users/%s/roles/%s' %
                                  (domain_id, user_id, role_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def check_user_role_existence_on_project(self, project_id,
                                              user_id, role_id):
@@ -114,7 +114,7 @@
         resp, body = self.head('projects/%s/users/%s/roles/%s' %
                                (project_id, user_id, role_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     def check_user_role_existence_on_domain(self, domain_id,
                                             user_id, role_id):
@@ -122,21 +122,21 @@
         resp, body = self.head('domains/%s/users/%s/roles/%s' %
                                (domain_id, user_id, role_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     def assign_group_role_on_project(self, project_id, group_id, role_id):
         """Add roles to a user on a project."""
         resp, body = self.put('projects/%s/groups/%s/roles/%s' %
                               (project_id, group_id, role_id), None)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def assign_group_role_on_domain(self, domain_id, group_id, role_id):
         """Add roles to a user on a domain."""
         resp, body = self.put('domains/%s/groups/%s/roles/%s' %
                               (domain_id, group_id, role_id), None)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_group_roles_on_project(self, project_id, group_id):
         """list roles of a user on a project."""
@@ -144,7 +144,7 @@
                               (project_id, group_id))
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_group_roles_on_domain(self, domain_id, group_id):
         """list roles of a user on a domain."""
@@ -152,21 +152,21 @@
                               (domain_id, group_id))
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_role_from_group_on_project(self, project_id, group_id, role_id):
         """Delete role of a user on a project."""
         resp, body = self.delete('projects/%s/groups/%s/roles/%s' %
                                  (project_id, group_id, role_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_role_from_group_on_domain(self, domain_id, group_id, role_id):
         """Delete role of a user on a domain."""
         resp, body = self.delete('domains/%s/groups/%s/roles/%s' %
                                  (domain_id, group_id, role_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def check_role_from_group_on_project_existence(self, project_id,
                                                    group_id, role_id):
@@ -174,7 +174,7 @@
         resp, body = self.head('projects/%s/groups/%s/roles/%s' %
                                (project_id, group_id, role_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     def check_role_from_group_on_domain_existence(self, domain_id,
                                                   group_id, role_id):
@@ -182,4 +182,134 @@
         resp, body = self.head('domains/%s/groups/%s/roles/%s' %
                                (domain_id, group_id, role_id))
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
+
+    def assign_inherited_role_on_domains_user(
+            self, domain_id, user_id, role_id):
+        """Assigns a role to a user on projects owned by a domain."""
+        resp, body = self.put(
+            "OS-INHERIT/domains/%s/users/%s/roles/%s/inherited_to_projects"
+            % (domain_id, user_id, role_id), None)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def revoke_inherited_role_from_user_on_domain(
+            self, domain_id, user_id, role_id):
+        """Revokes an inherited project role from a user on a domain."""
+        resp, body = self.delete(
+            "OS-INHERIT/domains/%s/users/%s/roles/%s/inherited_to_projects"
+            % (domain_id, user_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_inherited_project_role_for_user_on_domain(
+            self, domain_id, user_id):
+        """Lists the inherited project roles on a domain for a user."""
+        resp, body = self.get(
+            "OS-INHERIT/domains/%s/users/%s/roles/inherited_to_projects"
+            % (domain_id, user_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def check_user_inherited_project_role_on_domain(
+            self, domain_id, user_id, role_id):
+        """Checks whether a user has an inherited project role on a domain."""
+        resp, body = self.head(
+            "OS-INHERIT/domains/%s/users/%s/roles/%s/inherited_to_projects"
+            % (domain_id, user_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def assign_inherited_role_on_domains_group(
+            self, domain_id, group_id, role_id):
+        """Assigns a role to a group on projects owned by a domain."""
+        resp, body = self.put(
+            "OS-INHERIT/domains/%s/groups/%s/roles/%s/inherited_to_projects"
+            % (domain_id, group_id, role_id), None)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def revoke_inherited_role_from_group_on_domain(
+            self, domain_id, group_id, role_id):
+        """Revokes an inherited project role from a group on a domain."""
+        resp, body = self.delete(
+            "OS-INHERIT/domains/%s/groups/%s/roles/%s/inherited_to_projects"
+            % (domain_id, group_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_inherited_project_role_for_group_on_domain(
+            self, domain_id, group_id):
+        """Lists the inherited project roles on a domain for a group."""
+        resp, body = self.get(
+            "OS-INHERIT/domains/%s/groups/%s/roles/inherited_to_projects"
+            % (domain_id, group_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def check_group_inherited_project_role_on_domain(
+            self, domain_id, group_id, role_id):
+        """Checks whether a group has an inherited project role on a domain."""
+        resp, body = self.head(
+            "OS-INHERIT/domains/%s/groups/%s/roles/%s/inherited_to_projects"
+            % (domain_id, group_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def assign_inherited_role_on_projects_user(
+            self, project_id, user_id, role_id):
+        """Assigns a role to a user on projects in a subtree."""
+        resp, body = self.put(
+            "OS-INHERIT/projects/%s/users/%s/roles/%s/inherited_to_projects"
+            % (project_id, user_id, role_id), None)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def revoke_inherited_role_from_user_on_project(
+            self, project_id, user_id, role_id):
+        """Revokes an inherited role from a user on a project."""
+        resp, body = self.delete(
+            "OS-INHERIT/projects/%s/users/%s/roles/%s/inherited_to_projects"
+            % (project_id, user_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def check_user_has_flag_on_inherited_to_project(
+            self, project_id, user_id, role_id):
+        """Checks whether a user has a role assignment"""
+        """with the inherited_to_projects flag on a project."""
+        resp, body = self.head(
+            "OS-INHERIT/projects/%s/users/%s/roles/%s/inherited_to_projects"
+            % (project_id, user_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def assign_inherited_role_on_projects_group(
+            self, project_id, group_id, role_id):
+        """Assigns a role to a group on projects in a subtree."""
+        resp, body = self.put(
+            "OS-INHERIT/projects/%s/groups/%s/roles/%s/inherited_to_projects"
+            % (project_id, group_id, role_id), None)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def revoke_inherited_role_from_group_on_project(
+            self, project_id, group_id, role_id):
+        """Revokes an inherited role from a group on a project."""
+        resp, body = self.delete(
+            "OS-INHERIT/projects/%s/groups/%s/roles/%s/inherited_to_projects"
+            % (project_id, group_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def check_group_has_flag_on_inherited_to_project(
+            self, project_id, group_id, role_id):
+        """Checks whether a group has a role assignment"""
+        """with the inherited_to_projects flag on a project."""
+        resp, body = self.head(
+            "OS-INHERIT/projects/%s/groups/%s/roles/%s/inherited_to_projects"
+            % (project_id, group_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/services/identity/v3/json/services_client.py b/tempest/services/identity/v3/json/services_client.py
index dd65f1d..e863016 100644
--- a/tempest/services/identity/v3/json/services_client.py
+++ b/tempest/services/identity/v3/json/services_client.py
@@ -19,10 +19,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class ServicesClient(service_client.ServiceClient):
+class ServicesClient(rest_client.RestClient):
     api_version = "v3"
 
     def update_service(self, service_id, **kwargs):
@@ -35,7 +35,7 @@
         resp, body = self.patch('services/%s' % service_id, patch_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_service(self, service_id):
         """Get Service."""
@@ -43,7 +43,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_service(self, **kwargs):
         """Creates a service.
@@ -55,16 +55,16 @@
         resp, body = self.post("services", body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_service(self, serv_id):
         url = "services/" + serv_id
         resp, body = self.delete(url)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_services(self):
         resp, body = self.get('services')
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/trusts_client.py b/tempest/services/identity/v3/json/trusts_client.py
index 42b2bdb..dedee05 100644
--- a/tempest/services/identity/v3/json/trusts_client.py
+++ b/tempest/services/identity/v3/json/trusts_client.py
@@ -14,10 +14,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class TrustsClient(service_client.ServiceClient):
+class TrustsClient(rest_client.RestClient):
     api_version = "v3"
 
     def create_trust(self, **kwargs):
@@ -30,13 +30,13 @@
         resp, body = self.post('OS-TRUST/trusts', post_body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_trust(self, trust_id):
         """Deletes a trust."""
         resp, body = self.delete("OS-TRUST/trusts/%s" % trust_id)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_trusts(self, trustor_user_id=None, trustee_user_id=None):
         """GET trusts."""
@@ -50,21 +50,21 @@
             resp, body = self.get("OS-TRUST/trusts")
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_trust(self, trust_id):
         """GET trust."""
         resp, body = self.get("OS-TRUST/trusts/%s" % trust_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_trust_roles(self, trust_id):
         """GET roles delegated by a trust."""
         resp, body = self.get("OS-TRUST/trusts/%s/roles" % trust_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_trust_role(self, trust_id, role_id):
         """GET role delegated by a trust."""
@@ -72,11 +72,11 @@
                               % (trust_id, role_id))
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def check_trust_role(self, trust_id, role_id):
         """HEAD Check if role is delegated by a trust."""
         resp, body = self.head("OS-TRUST/trusts/%s/roles/%s"
                                % (trust_id, role_id))
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/users_clients.py b/tempest/services/identity/v3/json/users_clients.py
index 85c2e79..3ab8eab 100644
--- a/tempest/services/identity/v3/json/users_clients.py
+++ b/tempest/services/identity/v3/json/users_clients.py
@@ -15,10 +15,10 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class UsersV3Client(service_client.ServiceClient):
+class UsersClient(rest_client.RestClient):
     api_version = "v3"
 
     def create_user(self, user_name, password=None, project_id=None,
@@ -41,7 +41,7 @@
         resp, body = self.post('users', post_body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_user(self, user_id, name, **kwargs):
         """Updates a user."""
@@ -70,7 +70,7 @@
         resp, body = self.patch('users/%s' % user_id, post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_user_password(self, user_id, **kwargs):
         """Update a user password
@@ -81,14 +81,14 @@
         update_user = json.dumps({'user': kwargs})
         resp, _ = self.post('users/%s/password' % user_id, update_user)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     def list_user_projects(self, user_id):
         """Lists the projects on which a user has roles assigned."""
         resp, body = self.get('users/%s/projects' % user_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_users(self, params=None):
         """Get the list of users."""
@@ -98,24 +98,24 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_user(self, user_id):
         """GET a user."""
         resp, body = self.get("users/%s" % user_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_user(self, user_id):
         """Deletes a User."""
         resp, body = self.delete("users/%s" % user_id)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_user_groups(self, user_id):
         """Lists groups which a user belongs to."""
         resp, body = self.get('users/%s/groups' % user_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/image/v1/json/images_client.py b/tempest/services/image/v1/json/images_client.py
index b581203..3f256ec 100644
--- a/tempest/services/image/v1/json/images_client.py
+++ b/tempest/services/image/v1/json/images_client.py
@@ -24,34 +24,22 @@
 from six.moves.urllib import parse as urllib
 
 from tempest.common import glance_http
-from tempest.common import service_client
 from tempest import exceptions
+from tempest.lib.common import rest_client
 from tempest.lib.common.utils import misc as misc_utils
 from tempest.lib import exceptions as lib_exc
 
 LOG = logging.getLogger(__name__)
 
 
-class ImagesClient(service_client.ServiceClient):
+class ImagesClient(rest_client.RestClient):
 
-    def __init__(self, auth_provider, catalog_type, region, endpoint_type=None,
-                 build_interval=None, build_timeout=None,
-                 disable_ssl_certificate_validation=None,
-                 ca_certs=None, trace_requests=None):
+    def __init__(self, auth_provider, catalog_type, region, **kwargs):
         super(ImagesClient, self).__init__(
-            auth_provider,
-            catalog_type,
-            region,
-            endpoint_type=endpoint_type,
-            build_interval=build_interval,
-            build_timeout=build_timeout,
-            disable_ssl_certificate_validation=(
-                disable_ssl_certificate_validation),
-            ca_certs=ca_certs,
-            trace_requests=trace_requests)
+            auth_provider, catalog_type, region, **kwargs)
         self._http = None
-        self.dscv = disable_ssl_certificate_validation
-        self.ca_certs = ca_certs
+        self.dscv = kwargs.get("disable_ssl_certificate_validation")
+        self.ca_certs = kwargs.get("ca_certs")
 
     def _image_meta_from_headers(self, headers):
         meta = {'properties': {}}
@@ -130,7 +118,7 @@
         self._error_checker('POST', '/v1/images', headers, data, resp,
                             body_iter)
         body = json.loads(''.join([c for c in body_iter]))
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def _update_with_data(self, image_id, headers, data):
         url = '/v1/images/%s' % image_id
@@ -139,7 +127,7 @@
         self._error_checker('PUT', url, headers, data,
                             resp, body_iter)
         body = json.loads(''.join([c for c in body_iter]))
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     @property
     def http(self):
@@ -158,7 +146,7 @@
         resp, body = self.post('v1/images', None, headers)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_image(self, image_id, **kwargs):
         headers = {}
@@ -172,13 +160,13 @@
         resp, body = self.put(url, None, headers)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_image(self, image_id):
         url = 'v1/images/%s' % image_id
         resp, body = self.delete(url)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_images(self, detail=False, **kwargs):
         """Return a list of all images filtered by input parameters.
@@ -208,20 +196,20 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def get_image_meta(self, image_id):
         url = 'v1/images/%s' % image_id
         resp, __ = self.head(url)
         self.expected_success(200, resp.status)
         body = self._image_meta_from_headers(resp)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_image(self, image_id):
         url = 'v1/images/%s' % image_id
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBodyData(resp, body)
+        return rest_client.ResponseBodyData(resp, body)
 
     def is_resource_deleted(self, id):
         try:
@@ -240,7 +228,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_shared_images(self, tenant_id):
         """List shared images with the specified tenant"""
@@ -248,7 +236,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def add_member(self, member_id, image_id, **kwargs):
         """Add a member to an image.
@@ -260,13 +248,13 @@
         body = json.dumps({'member': kwargs})
         resp, __ = self.put(url, body)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     def delete_member(self, member_id, image_id):
         url = 'v1/images/%s/members/%s' % (image_id, member_id)
         resp, __ = self.delete(url)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     # NOTE(afazekas): just for the wait function
     def _get_image_status(self, image_id):
diff --git a/tempest/services/image/v2/json/images_client.py b/tempest/services/image/v2/json/images_client.py
index b4744e4..4e037af 100644
--- a/tempest/services/image/v2/json/images_client.py
+++ b/tempest/services/image/v2/json/images_client.py
@@ -17,30 +17,18 @@
 from six.moves.urllib import parse as urllib
 
 from tempest.common import glance_http
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 
 
-class ImagesClientV2(service_client.ServiceClient):
+class ImagesClientV2(rest_client.RestClient):
 
-    def __init__(self, auth_provider, catalog_type, region, endpoint_type=None,
-                 build_interval=None, build_timeout=None,
-                 disable_ssl_certificate_validation=None, ca_certs=None,
-                 trace_requests=None):
+    def __init__(self, auth_provider, catalog_type, region, **kwargs):
         super(ImagesClientV2, self).__init__(
-            auth_provider,
-            catalog_type,
-            region,
-            endpoint_type=endpoint_type,
-            build_interval=build_interval,
-            build_timeout=build_timeout,
-            disable_ssl_certificate_validation=(
-                disable_ssl_certificate_validation),
-            ca_certs=ca_certs,
-            trace_requests=trace_requests)
+            auth_provider, catalog_type, region, **kwargs)
         self._http = None
-        self.dscv = disable_ssl_certificate_validation
-        self.ca_certs = ca_certs
+        self.dscv = kwargs.get("disable_ssl_certificate_validation")
+        self.ca_certs = kwargs.get("ca_certs")
 
     def _get_http(self):
         return glance_http.HTTPClient(auth_provider=self.auth_provider,
@@ -66,7 +54,7 @@
         resp, body = self.patch('v2/images/%s' % image_id, data, headers)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_image(self, **kwargs):
         """Create an image.
@@ -78,25 +66,25 @@
         resp, body = self.post('v2/images', data)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def deactivate_image(self, image_id):
         url = 'v2/images/%s/actions/deactivate' % image_id
         resp, body = self.post(url, None)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def reactivate_image(self, image_id):
         url = 'v2/images/%s/actions/reactivate' % image_id
         resp, body = self.post(url, None)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_image(self, image_id):
         url = 'v2/images/%s' % image_id
         resp, _ = self.delete(url)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     def list_images(self, params=None):
         url = 'v2/images'
@@ -107,14 +95,14 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_image(self, image_id):
         url = 'v2/images/%s' % image_id
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
         try:
@@ -134,32 +122,32 @@
         resp, body = self.http.raw_request('PUT', url, headers=headers,
                                            body=data)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_image_file(self, image_id):
         url = 'v2/images/%s/file' % image_id
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBodyData(resp, body)
+        return rest_client.ResponseBodyData(resp, body)
 
     def add_image_tag(self, image_id, tag):
         url = 'v2/images/%s/tags/%s' % (image_id, tag)
         resp, body = self.put(url, body=None)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_image_tag(self, image_id, tag):
         url = 'v2/images/%s/tags/%s' % (image_id, tag)
         resp, _ = self.delete(url)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     def list_image_members(self, image_id):
         url = 'v2/images/%s/members' % image_id
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_image_member(self, image_id, **kwargs):
         """Create an image member.
@@ -172,7 +160,7 @@
         resp, body = self.post(url, data)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_image_member(self, image_id, member_id, **kwargs):
         """Update an image member.
@@ -185,33 +173,33 @@
         resp, body = self.put(url, data)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_image_member(self, image_id, member_id):
         url = 'v2/images/%s/members/%s' % (image_id, member_id)
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, json.loads(body))
+        return rest_client.ResponseBody(resp, json.loads(body))
 
     def delete_image_member(self, image_id, member_id):
         url = 'v2/images/%s/members/%s' % (image_id, member_id)
         resp, _ = self.delete(url)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     def show_schema(self, schema):
         url = 'v2/schemas/%s' % schema
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_resource_types(self):
         url = '/v2/metadefs/resource_types'
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_namespace(self, **kwargs):
         """Create a namespace.
@@ -223,14 +211,14 @@
         resp, body = self.post('/v2/metadefs/namespaces', data)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_namespace(self, namespace):
         url = '/v2/metadefs/namespaces/%s' % namespace
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_namespace(self, namespace, **kwargs):
         """Update a namespace.
@@ -247,10 +235,10 @@
         resp, body = self.put(url, body=data)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_namespace(self, namespace):
         url = '/v2/metadefs/namespaces/%s' % namespace
         resp, _ = self.delete(url)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/services/network/json/base.py b/tempest/services/network/json/base.py
deleted file mode 100644
index 6ebc245..0000000
--- a/tempest/services/network/json/base.py
+++ /dev/null
@@ -1,71 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.common import service_client
-
-
-class BaseNetworkClient(service_client.ServiceClient):
-
-    """Base class for Tempest REST clients for Neutron.
-
-    Child classes use v2 of the Neutron API, since the V1 API has been
-    removed from the code base.
-    """
-
-    version = '2.0'
-    uri_prefix = "v2.0"
-
-    def list_resources(self, uri, **filters):
-        req_uri = self.uri_prefix + uri
-        if filters:
-            req_uri += '?' + urllib.urlencode(filters, doseq=1)
-        resp, body = self.get(req_uri)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def delete_resource(self, uri):
-        req_uri = self.uri_prefix + uri
-        resp, body = self.delete(req_uri)
-        self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def show_resource(self, uri, **fields):
-        # fields is a dict which key is 'fields' and value is a
-        # list of field's name. An example:
-        # {'fields': ['id', 'name']}
-        req_uri = self.uri_prefix + uri
-        if fields:
-            req_uri += '?' + urllib.urlencode(fields, doseq=1)
-        resp, body = self.get(req_uri)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def create_resource(self, uri, post_data):
-        req_uri = self.uri_prefix + uri
-        req_post_data = json.dumps(post_data)
-        resp, body = self.post(req_uri, req_post_data)
-        body = json.loads(body)
-        self.expected_success(201, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def update_resource(self, uri, post_data):
-        req_uri = self.uri_prefix + uri
-        req_post_data = json.dumps(post_data)
-        resp, body = self.put(req_uri, req_post_data)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
deleted file mode 100644
index 5106b73..0000000
--- a/tempest/services/network/json/network_client.py
+++ /dev/null
@@ -1,229 +0,0 @@
-#    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 time
-
-from tempest import exceptions
-from tempest.lib.common.utils import misc
-from tempest.lib import exceptions as lib_exc
-from tempest.services.network.json import base
-
-
-class NetworkClient(base.BaseNetworkClient):
-
-    """Tempest REST client for Neutron.
-
-    Uses v2 of the Neutron API, since the V1 API has been removed from the
-    code base.
-
-    Implements create, delete, update, list and show for the basic Neutron
-    abstractions (networks, sub-networks, routers, ports and floating IP):
-
-    Implements add/remove interface to router using subnet ID / port ID
-
-    It also implements list, show, update and reset for OpenStack Networking
-    quotas
-    """
-
-    def create_bulk_network(self, **kwargs):
-        """create bulk network
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2.html#bulkCreateNetwork
-        """
-        uri = '/networks'
-        return self.create_resource(uri, kwargs)
-
-    def create_bulk_subnet(self, **kwargs):
-        """create bulk subnet
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2.html#bulkCreateSubnet
-        """
-        uri = '/subnets'
-        return self.create_resource(uri, kwargs)
-
-    def create_bulk_port(self, **kwargs):
-        """create bulk port
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2.html#bulkCreatePorts
-        """
-        uri = '/ports'
-        return self.create_resource(uri, kwargs)
-
-    def wait_for_resource_deletion(self, resource_type, id, client=None):
-        """Waits for a resource to be deleted."""
-        start_time = int(time.time())
-        while True:
-            if self.is_resource_deleted(resource_type, id, client=client):
-                return
-            if int(time.time()) - start_time >= self.build_timeout:
-                raise exceptions.TimeoutException
-            time.sleep(self.build_interval)
-
-    def is_resource_deleted(self, resource_type, id, client=None):
-        if client is None:
-            client = self
-        method = 'show_' + resource_type
-        try:
-            getattr(client, method)(id)
-        except AttributeError:
-            raise Exception("Unknown resource type %s " % resource_type)
-        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 create_router(self, name, admin_state_up=True, **kwargs):
-        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)
-
-    def _update_router(self, router_id, set_enable_snat, **kwargs):
-        uri = '/routers/%s' % router_id
-        body = self.show_resource(uri)
-        update_body = {}
-        update_body['name'] = kwargs.get('name', body['router']['name'])
-        update_body['admin_state_up'] = kwargs.get(
-            'admin_state_up', body['router']['admin_state_up'])
-        cur_gw_info = body['router']['external_gateway_info']
-        if cur_gw_info:
-            # TODO(kevinbenton): setting the external gateway info is not
-            # allowed for a regular tenant. If the ability to update is also
-            # merged, a test case for this will need to be added similar to
-            # the SNAT case.
-            cur_gw_info.pop('external_fixed_ips', None)
-            if not set_enable_snat:
-                cur_gw_info.pop('enable_snat', None)
-        update_body['external_gateway_info'] = kwargs.get(
-            'external_gateway_info', body['router']['external_gateway_info'])
-        if 'distributed' in kwargs:
-            update_body['distributed'] = kwargs['distributed']
-        update_body = dict(router=update_body)
-        return self.update_resource(uri, update_body)
-
-    def update_router(self, router_id, **kwargs):
-        """Update a router leaving enable_snat to its default value."""
-        # If external_gateway_info contains enable_snat the request will fail
-        # with 404 unless executed with admin client, and therefore we instruct
-        # _update_router to not set this attribute
-        # NOTE(salv-orlando): The above applies as long as Neutron's default
-        # policy is to restrict enable_snat usage to admins only.
-        return self._update_router(router_id, set_enable_snat=False, **kwargs)
-
-    def show_router(self, router_id, **fields):
-        uri = '/routers/%s' % router_id
-        return self.show_resource(uri, **fields)
-
-    def delete_router(self, router_id):
-        uri = '/routers/%s' % router_id
-        return self.delete_resource(uri)
-
-    def list_routers(self, **filters):
-        uri = '/routers'
-        return self.list_resources(uri, **filters)
-
-    def update_router_with_snat_gw_info(self, router_id, **kwargs):
-        """Update a router passing also the enable_snat attribute.
-
-        This method must be execute with admin credentials, otherwise the API
-        call will return a 404 error.
-        """
-        return self._update_router(router_id, set_enable_snat=True, **kwargs)
-
-    def add_router_interface(self, router_id, **kwargs):
-        """Add router interface.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2-ext.html#addRouterInterface
-        """
-        uri = '/routers/%s/add_router_interface' % router_id
-        return self.update_resource(uri, kwargs)
-
-    def remove_router_interface(self, router_id, **kwargs):
-        """Remove router interface.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2-ext.html#removeRouterInterface
-        """
-        uri = '/routers/%s/remove_router_interface' % router_id
-        return self.update_resource(uri, kwargs)
-
-    def list_router_interfaces(self, uuid):
-        uri = '/ports?device_id=%s' % uuid
-        return self.list_resources(uri)
-
-    def list_l3_agents_hosting_router(self, router_id):
-        uri = '/routers/%s/l3-agents' % router_id
-        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)
-
-    def update_extra_routes(self, router_id, **kwargs):
-        """Update Extra routes.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2-ext.html#updateExtraRoutes
-        """
-        uri = '/routers/%s' % router_id
-        put_body = {'router': kwargs}
-        return self.update_resource(uri, put_body)
-
-    def delete_extra_routes(self, router_id):
-        uri = '/routers/%s' % router_id
-        put_body = {
-            'router': {
-                'routes': None
-            }
-        }
-        return self.update_resource(uri, put_body)
diff --git a/tempest/services/network/json/routers_client.py b/tempest/services/network/json/routers_client.py
new file mode 100644
index 0000000..725dd76
--- /dev/null
+++ b/tempest/services/network/json/routers_client.py
@@ -0,0 +1,119 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.network import base
+
+
+class RoutersClient(base.BaseNetworkClient):
+
+    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}
+        uri = '/routers'
+        return self.create_resource(uri, post_body)
+
+    def _update_router(self, router_id, set_enable_snat, **kwargs):
+        uri = '/routers/%s' % router_id
+        body = self.show_resource(uri)
+        update_body = {}
+        update_body['name'] = kwargs.get('name', body['router']['name'])
+        update_body['admin_state_up'] = kwargs.get(
+            'admin_state_up', body['router']['admin_state_up'])
+        cur_gw_info = body['router']['external_gateway_info']
+        if cur_gw_info:
+            # TODO(kevinbenton): setting the external gateway info is not
+            # allowed for a regular tenant. If the ability to update is also
+            # merged, a test case for this will need to be added similar to
+            # the SNAT case.
+            cur_gw_info.pop('external_fixed_ips', None)
+            if not set_enable_snat:
+                cur_gw_info.pop('enable_snat', None)
+        update_body['external_gateway_info'] = kwargs.get(
+            'external_gateway_info', body['router']['external_gateway_info'])
+        if 'distributed' in kwargs:
+            update_body['distributed'] = kwargs['distributed']
+        update_body = dict(router=update_body)
+        return self.update_resource(uri, update_body)
+
+    def update_router(self, router_id, **kwargs):
+        """Update a router leaving enable_snat to its default value."""
+        # If external_gateway_info contains enable_snat the request will fail
+        # with 404 unless executed with admin client, and therefore we instruct
+        # _update_router to not set this attribute
+        # NOTE(salv-orlando): The above applies as long as Neutron's default
+        # policy is to restrict enable_snat usage to admins only.
+        return self._update_router(router_id, set_enable_snat=False, **kwargs)
+
+    def show_router(self, router_id, **fields):
+        uri = '/routers/%s' % router_id
+        return self.show_resource(uri, **fields)
+
+    def delete_router(self, router_id):
+        uri = '/routers/%s' % router_id
+        return self.delete_resource(uri)
+
+    def list_routers(self, **filters):
+        uri = '/routers'
+        return self.list_resources(uri, **filters)
+
+    def update_extra_routes(self, router_id, **kwargs):
+        """Update Extra routes.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-networking-v2-ext.html#updateExtraRoutes
+        """
+        uri = '/routers/%s' % router_id
+        put_body = {'router': kwargs}
+        return self.update_resource(uri, put_body)
+
+    def delete_extra_routes(self, router_id):
+        uri = '/routers/%s' % router_id
+        put_body = {
+            'router': {
+                'routes': None
+            }
+        }
+        return self.update_resource(uri, put_body)
+
+    def update_router_with_snat_gw_info(self, router_id, **kwargs):
+        """Update a router passing also the enable_snat attribute.
+
+        This method must be execute with admin credentials, otherwise the API
+        call will return a 404 error.
+        """
+        return self._update_router(router_id, set_enable_snat=True, **kwargs)
+
+    def add_router_interface(self, router_id, **kwargs):
+        """Add router interface.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-networking-v2-ext.html#addRouterInterface
+        """
+        uri = '/routers/%s/add_router_interface' % router_id
+        return self.update_resource(uri, kwargs)
+
+    def remove_router_interface(self, router_id, **kwargs):
+        """Remove router interface.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-networking-v2-ext.html#removeRouterInterface
+        """
+        uri = '/routers/%s/remove_router_interface' % router_id
+        return self.update_resource(uri, kwargs)
+
+    def list_l3_agents_hosting_router(self, router_id):
+        uri = '/routers/%s/l3-agents' % router_id
+        return self.list_resources(uri)
diff --git a/tempest/services/network/json/security_group_rules_client.py b/tempest/services/network/json/security_group_rules_client.py
deleted file mode 100644
index b2ba5b2..0000000
--- a/tempest/services/network/json/security_group_rules_client.py
+++ /dev/null
@@ -1,33 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.services.network.json import base
-
-
-class SecurityGroupRulesClient(base.BaseNetworkClient):
-
-    def create_security_group_rule(self, **kwargs):
-        uri = '/security-group-rules'
-        post_data = {'security_group_rule': kwargs}
-        return self.create_resource(uri, post_data)
-
-    def show_security_group_rule(self, security_group_rule_id, **fields):
-        uri = '/security-group-rules/%s' % security_group_rule_id
-        return self.show_resource(uri, **fields)
-
-    def delete_security_group_rule(self, security_group_rule_id):
-        uri = '/security-group-rules/%s' % security_group_rule_id
-        return self.delete_resource(uri)
-
-    def list_security_group_rules(self, **filters):
-        uri = '/security-group-rules'
-        return self.list_resources(uri, **filters)
diff --git a/tempest/services/network/resources.py b/tempest/services/network/resources.py
index 0a7da92..329c54d 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."""
@@ -37,8 +41,8 @@
 
     def __init__(self, *args, **kwargs):
         self.client = kwargs.pop('client', None)
-        self.network_client = kwargs.pop('network_client', None)
         self.networks_client = kwargs.pop('networks_client', None)
+        self.routers_client = kwargs.pop('routers_client', None)
         self.subnets_client = kwargs.pop('subnets_client', None)
         self.ports_client = kwargs.pop('ports_client', None)
         super(DeletableResource, self).__init__(*args, **kwargs)
@@ -66,7 +70,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):
@@ -89,12 +121,12 @@
 
     def add_to_router(self, router_id):
         self._router_ids.add(router_id)
-        self.network_client.add_router_interface(router_id,
+        self.routers_client.add_router_interface(router_id,
                                                  subnet_id=self.id)
 
     def delete(self):
         for router_id in self._router_ids.copy():
-            self.network_client.remove_router_interface(router_id,
+            self.routers_client.remove_router_interface(router_id,
                                                         subnet_id=self.id)
             self._router_ids.remove(router_id)
         self.subnets_client.delete_subnet(self.id)
@@ -109,14 +141,14 @@
         return self.update(external_gateway_info=dict())
 
     def update(self, *args, **kwargs):
-        result = self.client.update_router(self.id,
-                                           *args,
-                                           **kwargs)
+        result = self.routers_client.update_router(self.id,
+                                                   *args,
+                                                   **kwargs)
         return super(DeletableRouter, self).update(**result['router'])
 
     def delete(self):
         self.unset_gateway()
-        self.client.delete_router(self.id)
+        self.routers_client.delete_router(self.id)
 
 
 class DeletableFloatingIp(DeletableResource):
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 2c7fe29..6012a92 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -18,10 +18,10 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class AccountClient(service_client.ServiceClient):
+class AccountClient(rest_client.RestClient):
 
     def create_account(self, data=None,
                        params=None,
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
index 73c25db..5a26bfc 100644
--- a/tempest/services/object_storage/container_client.py
+++ b/tempest/services/object_storage/container_client.py
@@ -18,10 +18,10 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class ContainerClient(service_client.ServiceClient):
+class ContainerClient(rest_client.RestClient):
 
     def create_container(
             self, container_name,
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 5890e33..78bda5d 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -17,10 +17,10 @@
 from six.moves import http_client as httplib
 from six.moves.urllib import parse as urlparse
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class ObjectClient(service_client.ServiceClient):
+class ObjectClient(rest_client.RestClient):
 
     def create_object(self, container, object_name, data,
                       params=None, metadata=None, headers=None):
@@ -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 c0f5309..ea5dbe5 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -19,12 +19,12 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
 from tempest import exceptions
+from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 
 
-class OrchestrationClient(service_client.ServiceClient):
+class OrchestrationClient(rest_client.RestClient):
 
     def list_stacks(self, params=None):
         """Lists all stacks for a user."""
@@ -36,7 +36,7 @@
         resp, body = self.get(uri)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_stack(self, name, disable_rollback=True, parameters=None,
                      timeout_mins=60, template=None, template_url=None,
@@ -56,7 +56,7 @@
         resp, body = self.post(uri, headers=headers, body=body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_stack(self, stack_identifier, name, disable_rollback=True,
                      parameters=None, timeout_mins=60, template=None,
@@ -75,7 +75,7 @@
         uri = "stacks/%s" % stack_identifier
         resp, body = self.put(uri, headers=headers, body=body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def _prepare_update_create(self, name, disable_rollback=True,
                                parameters=None, timeout_mins=60,
@@ -111,7 +111,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def suspend_stack(self, stack_identifier):
         """Suspend a stack."""
@@ -119,7 +119,7 @@
         body = {'suspend': None}
         resp, body = self.post(url, json.dumps(body))
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     def resume_stack(self, stack_identifier):
         """Resume a stack."""
@@ -127,7 +127,7 @@
         body = {'resume': None}
         resp, body = self.post(url, json.dumps(body))
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     def list_resources(self, stack_identifier):
         """Returns the details of a single resource."""
@@ -135,7 +135,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_resource(self, stack_identifier, resource_name):
         """Returns the details of a single resource."""
@@ -143,49 +143,13 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_stack(self, stack_identifier):
         """Deletes the specified Stack."""
         resp, _ = self.delete("stacks/%s" % str(stack_identifier))
         self.expected_success(204, resp.status)
-        return service_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)
+        return rest_client.ResponseBody(resp)
 
     def wait_for_stack_status(self, stack_identifier, status,
                               failure_pattern='^.*_FAILED$'):
@@ -224,7 +188,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_events(self, stack_identifier):
         """Returns list of all events for a stack."""
@@ -232,7 +196,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_resource_events(self, stack_identifier, resource_name):
         """Returns list of all events for a resource from stack."""
@@ -241,7 +205,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_event(self, stack_identifier, resource_name, event_id):
         """Returns the details of a single stack's event."""
@@ -250,7 +214,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_template(self, stack_identifier):
         """Returns the template for the stack."""
@@ -258,7 +222,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def _validate_template(self, post_body):
         """Returns the validation request result."""
@@ -266,7 +230,7 @@
         resp, body = self.post('validate', post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def validate_template(self, template, parameters=None):
         """Returns the validation result for a template with parameters."""
@@ -293,21 +257,21 @@
         resp, body = self.get('resource_types')
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_resource_type(self, resource_type_name):
         """Return the schema of a resource type."""
         url = 'resource_types/%s' % resource_type_name
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, json.loads(body))
+        return rest_client.ResponseBody(resp, json.loads(body))
 
     def show_resource_type_template(self, resource_type_name):
         """Return the template of a resource type."""
         url = 'resource_types/%s/template' % resource_type_name
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, json.loads(body))
+        return rest_client.ResponseBody(resp, json.loads(body))
 
     def create_software_config(self, name=None, config=None, group=None,
                                inputs=None, outputs=None, options=None):
@@ -318,7 +282,7 @@
         resp, body = self.post(url, headers=headers, body=body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_software_config(self, conf_id):
         """Returns a software configuration resource."""
@@ -326,14 +290,14 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_software_config(self, conf_id):
         """Deletes a specific software configuration."""
         url = 'software_configs/%s' % str(conf_id)
         resp, _ = self.delete(url)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     def create_software_deploy(self, server_id=None, config_id=None,
                                action=None, status=None,
@@ -348,7 +312,7 @@
         resp, body = self.post(url, headers=headers, body=body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_software_deploy(self, deploy_id=None, server_id=None,
                                config_id=None, action=None, status=None,
@@ -363,7 +327,7 @@
         resp, body = self.put(url, headers=headers, body=body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_software_deployments(self):
         """Returns a list of all deployments."""
@@ -371,7 +335,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_software_deployment(self, deploy_id):
         """Returns a specific software deployment."""
@@ -379,7 +343,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_software_deployment_metadata(self, server_id):
         """Return a config metadata for a specific server."""
@@ -387,14 +351,14 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_software_deploy(self, deploy_id):
         """Deletes a specific software deployment."""
         url = 'software_deployments/%s' % str(deploy_id)
         resp, _ = self.delete(url)
         self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp)
+        return rest_client.ResponseBody(resp)
 
     def _prep_software_config_create(self, name=None, conf=None, group=None,
                                      inputs=None, outputs=None, options=None):
diff --git a/tempest/services/telemetry/json/alarming_client.py b/tempest/services/telemetry/json/alarming_client.py
index ce14211..703efdf 100644
--- a/tempest/services/telemetry/json/alarming_client.py
+++ b/tempest/services/telemetry/json/alarming_client.py
@@ -16,10 +16,10 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class AlarmingClient(service_client.ServiceClient):
+class AlarmingClient(rest_client.RestClient):
 
     version = '2'
     uri_prefix = "v2"
@@ -42,21 +42,21 @@
         resp, body = self.get(uri)
         self.expected_success(200, resp.status)
         body = self.deserialize(body)
-        return service_client.ResponseBodyList(resp, body)
+        return rest_client.ResponseBodyList(resp, body)
 
     def show_alarm(self, alarm_id):
         uri = '%s/alarms/%s' % (self.uri_prefix, alarm_id)
         resp, body = self.get(uri)
         self.expected_success(200, resp.status)
         body = self.deserialize(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_alarm_history(self, alarm_id):
         uri = "%s/alarms/%s/history" % (self.uri_prefix, alarm_id)
         resp, body = self.get(uri)
         self.expected_success(200, resp.status)
         body = self.deserialize(body)
-        return service_client.ResponseBodyList(resp, body)
+        return rest_client.ResponseBodyList(resp, body)
 
     def delete_alarm(self, alarm_id):
         uri = "%s/alarms/%s" % (self.uri_prefix, alarm_id)
@@ -64,7 +64,7 @@
         self.expected_success(204, resp.status)
         if body:
             body = self.deserialize(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_alarm(self, **kwargs):
         uri = "%s/alarms" % self.uri_prefix
@@ -72,7 +72,7 @@
         resp, body = self.post(uri, body)
         self.expected_success(201, resp.status)
         body = self.deserialize(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_alarm(self, alarm_id, **kwargs):
         uri = "%s/alarms/%s" % (self.uri_prefix, alarm_id)
@@ -80,14 +80,14 @@
         resp, body = self.put(uri, body)
         self.expected_success(200, resp.status)
         body = self.deserialize(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_alarm_state(self, alarm_id):
         uri = "%s/alarms/%s/state" % (self.uri_prefix, alarm_id)
         resp, body = self.get(uri)
         self.expected_success(200, resp.status)
         body = self.deserialize(body)
-        return service_client.ResponseBodyData(resp, body)
+        return rest_client.ResponseBodyData(resp, body)
 
     def alarm_set_state(self, alarm_id, state):
         uri = "%s/alarms/%s/state" % (self.uri_prefix, alarm_id)
@@ -95,4 +95,4 @@
         resp, body = self.put(uri, body)
         self.expected_success(200, resp.status)
         body = self.deserialize(body)
-        return service_client.ResponseBodyData(resp, body)
+        return rest_client.ResponseBodyData(resp, body)
diff --git a/tempest/services/telemetry/json/telemetry_client.py b/tempest/services/telemetry/json/telemetry_client.py
index abdeba2..df7d916 100644
--- a/tempest/services/telemetry/json/telemetry_client.py
+++ b/tempest/services/telemetry/json/telemetry_client.py
@@ -16,10 +16,10 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class TelemetryClient(service_client.ServiceClient):
+class TelemetryClient(rest_client.RestClient):
 
     version = '2'
     uri_prefix = "v2"
@@ -36,7 +36,7 @@
         resp, body = self.post(uri, body)
         self.expected_success(200, resp.status)
         body = self.deserialize(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def _helper_list(self, uri, query=None, period=None):
         uri_dict = {}
@@ -51,7 +51,7 @@
         resp, body = self.get(uri)
         self.expected_success(200, resp.status)
         body = self.deserialize(body)
-        return service_client.ResponseBodyList(resp, body)
+        return rest_client.ResponseBodyList(resp, body)
 
     def list_resources(self, query=None):
         uri = '%s/resources' % self.uri_prefix
@@ -78,4 +78,4 @@
         resp, body = self.get(uri)
         self.expected_success(200, resp.status)
         body = self.deserialize(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/admin/base_hosts_client.py b/tempest/services/volume/base/admin/base_hosts_client.py
index 074f87f..382e9a8 100644
--- a/tempest/services/volume/base/admin/base_hosts_client.py
+++ b/tempest/services/volume/base/admin/base_hosts_client.py
@@ -16,10 +16,10 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class BaseHostsClient(service_client.ServiceClient):
+class BaseHostsClient(rest_client.RestClient):
     """Client class to send CRUD Volume Hosts API requests"""
 
     def list_hosts(self, **params):
@@ -32,4 +32,4 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/admin/base_quotas_client.py b/tempest/services/volume/base/admin/base_quotas_client.py
index e063a31..83816f2 100644
--- a/tempest/services/volume/base/admin/base_quotas_client.py
+++ b/tempest/services/volume/base/admin/base_quotas_client.py
@@ -15,10 +15,10 @@
 from oslo_serialization import jsonutils
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class BaseQuotasClient(service_client.ServiceClient):
+class BaseQuotasClient(rest_client.RestClient):
     """Client class to send CRUD Volume Quotas API requests"""
 
     TYPE = "json"
@@ -30,7 +30,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = jsonutils.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_quota_set(self, tenant_id, params=None):
         """List the quota set for a tenant."""
@@ -42,7 +42,7 @@
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = jsonutils.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_quota_usage(self, tenant_id):
         """List the quota set for a tenant."""
@@ -60,10 +60,10 @@
         resp, body = self.put('os-quota-sets/%s' % tenant_id, put_body)
         self.expected_success(200, resp.status)
         body = jsonutils.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_quota_set(self, tenant_id):
         """Delete the tenant's quota set."""
         resp, body = self.delete('os-quota-sets/%s' % tenant_id)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/admin/base_services_client.py b/tempest/services/volume/base/admin/base_services_client.py
index 3626469..861eb92 100644
--- a/tempest/services/volume/base/admin/base_services_client.py
+++ b/tempest/services/volume/base/admin/base_services_client.py
@@ -16,10 +16,10 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class BaseServicesClient(service_client.ServiceClient):
+class BaseServicesClient(rest_client.RestClient):
 
     def list_services(self, **params):
         url = 'os-services'
@@ -29,4 +29,4 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/admin/base_types_client.py b/tempest/services/volume/base/admin/base_types_client.py
index f117f0a..95ddff6 100644
--- a/tempest/services/volume/base/admin/base_types_client.py
+++ b/tempest/services/volume/base/admin/base_types_client.py
@@ -16,11 +16,11 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 
 
-class BaseTypesClient(service_client.ServiceClient):
+class BaseTypesClient(rest_client.RestClient):
     """Client class to send CRUD Volume Types API requests"""
 
     def is_resource_deleted(self, resource):
@@ -56,7 +56,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_volume_type(self, volume_id):
         """Returns the details of a single volume_type."""
@@ -64,7 +64,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_volume_type(self, **kwargs):
         """Create volume type.
@@ -76,13 +76,13 @@
         resp, body = self.post('types', post_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_volume_type(self, volume_id):
         """Deletes the Specified Volume_type."""
         resp, body = self.delete("types/%s" % str(volume_id))
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_volume_types_extra_specs(self, vol_type_id, **params):
         """List all the volume_types extra specs created.
@@ -100,7 +100,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_volume_type_extra_specs(self, vol_type_id, extra_specs_name):
         """Returns the details of a single volume_type extra spec."""
@@ -109,7 +109,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_volume_type_extra_specs(self, vol_type_id, extra_specs):
         """Creates a new Volume_type extra spec.
@@ -122,14 +122,14 @@
         resp, body = self.post(url, post_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_volume_type_extra_specs(self, vol_id, extra_spec_name):
         """Deletes the Specified Volume_type extra spec."""
         resp, body = self.delete("types/%s/extra_specs/%s" % (
             (str(vol_id)), str(extra_spec_name)))
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_volume_type_extra_specs(self, vol_type_id, extra_spec_name,
                                        extra_specs):
@@ -146,7 +146,7 @@
         resp, body = self.put(url, put_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_encryption_type(self, vol_type_id):
         """Get the volume encryption type for the specified volume type.
@@ -157,7 +157,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_encryption_type(self, vol_type_id, **kwargs):
         """Create encryption type.
@@ -171,11 +171,11 @@
         resp, body = self.post(url, post_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_encryption_type(self, vol_type_id):
         """Delete the encryption type for the specified volume-type."""
         resp, body = self.delete(
             "/types/%s/encryption/provider" % str(vol_type_id))
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/base_availability_zone_client.py b/tempest/services/volume/base/base_availability_zone_client.py
index b63fdc2..1c2deba 100644
--- a/tempest/services/volume/base/base_availability_zone_client.py
+++ b/tempest/services/volume/base/base_availability_zone_client.py
@@ -15,13 +15,13 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class BaseAvailabilityZoneClient(service_client.ServiceClient):
+class BaseAvailabilityZoneClient(rest_client.RestClient):
 
     def list_availability_zones(self):
         resp, body = self.get('os-availability-zone')
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/base_backups_client.py b/tempest/services/volume/base/base_backups_client.py
index d6f2a50..780da7b 100644
--- a/tempest/services/volume/base/base_backups_client.py
+++ b/tempest/services/volume/base/base_backups_client.py
@@ -17,12 +17,12 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
 from tempest import exceptions
+from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 
 
-class BaseBackupsClient(service_client.ServiceClient):
+class BaseBackupsClient(rest_client.RestClient):
     """Client class to send CRUD Volume backup API requests"""
 
     def create_backup(self, **kwargs):
@@ -31,7 +31,7 @@
         resp, body = self.post('backups', post_body)
         body = json.loads(body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def restore_backup(self, backup_id, **kwargs):
         """Restore volume from backup."""
@@ -39,13 +39,13 @@
         resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
         body = json.loads(body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_backup(self, backup_id):
         """Delete a backup of volume."""
         resp, body = self.delete('backups/%s' % (str(backup_id)))
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_backup(self, backup_id):
         """Returns the details of a single backup."""
@@ -53,7 +53,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_backups(self, detail=False):
         """Information for all the tenant's backups."""
@@ -63,7 +63,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def export_backup(self, backup_id):
         """Export backup metadata record."""
@@ -71,7 +71,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def import_backup(self, **kwargs):
         """Import backup metadata record."""
@@ -79,7 +79,7 @@
         resp, body = self.post("backups/import_record", post_body)
         body = json.loads(body)
         self.expected_success(201, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def wait_for_backup_status(self, backup_id, status):
         """Waits for a Backup to reach a given status."""
diff --git a/tempest/services/volume/base/base_extensions_client.py b/tempest/services/volume/base/base_extensions_client.py
index afc3f6b..b90fe94 100644
--- a/tempest/services/volume/base/base_extensions_client.py
+++ b/tempest/services/volume/base/base_extensions_client.py
@@ -15,14 +15,14 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class BaseExtensionsClient(service_client.ServiceClient):
+class BaseExtensionsClient(rest_client.RestClient):
 
     def list_extensions(self):
         url = 'extensions'
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/base_qos_client.py b/tempest/services/volume/base/base_qos_client.py
index f68f9aa..2d9f02a 100644
--- a/tempest/services/volume/base/base_qos_client.py
+++ b/tempest/services/volume/base/base_qos_client.py
@@ -16,12 +16,12 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
 from tempest import exceptions
+from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 
 
-class BaseQosSpecsClient(service_client.ServiceClient):
+class BaseQosSpecsClient(rest_client.RestClient):
     """Client class to send CRUD QoS API requests"""
 
     def is_resource_deleted(self, qos_id):
@@ -77,14 +77,14 @@
         resp, body = self.post('qos-specs', post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_qos(self, qos_id, force=False):
         """Delete the specified QoS specification."""
         resp, body = self.delete(
             "qos-specs/%s?force=%s" % (str(qos_id), force))
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_qos(self):
         """List all the QoS specifications created."""
@@ -92,7 +92,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_qos(self, qos_id):
         """Get the specified QoS specification."""
@@ -100,7 +100,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def set_qos_key(self, qos_id, **kwargs):
         """Set the specified keys/values of QoS specification.
@@ -112,7 +112,7 @@
         resp, body = self.put('qos-specs/%s' % qos_id, put_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def unset_qos_key(self, qos_id, keys):
         """Unset the specified keys of QoS specification.
@@ -124,7 +124,7 @@
         put_body = json.dumps({'keys': keys})
         resp, body = self.put('qos-specs/%s/delete_keys' % qos_id, put_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def associate_qos(self, qos_id, vol_type_id):
         """Associate the specified QoS with specified volume-type."""
@@ -132,7 +132,7 @@
         url += "?vol_type_id=%s" % vol_type_id
         resp, body = self.get(url)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_association_qos(self, qos_id):
         """Get the association of the specified QoS specification."""
@@ -140,7 +140,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def disassociate_qos(self, qos_id, vol_type_id):
         """Disassociate the specified QoS with specified volume-type."""
@@ -148,11 +148,11 @@
         url += "?vol_type_id=%s" % vol_type_id
         resp, body = self.get(url)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def disassociate_all_qos(self, qos_id):
         """Disassociate the specified QoS with all associations."""
         url = "qos-specs/%s/disassociate_all" % str(qos_id)
         resp, body = self.get(url)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/base_snapshots_client.py b/tempest/services/volume/base/base_snapshots_client.py
index ed2a642..68503dd 100644
--- a/tempest/services/volume/base/base_snapshots_client.py
+++ b/tempest/services/volume/base/base_snapshots_client.py
@@ -10,21 +10,18 @@
 #    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.common import service_client
-from tempest import exceptions
+from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 
 
 LOG = logging.getLogger(__name__)
 
 
-class BaseSnapshotsClient(service_client.ServiceClient):
+class BaseSnapshotsClient(rest_client.RestClient):
     """Base Client class to send CRUD Volume API requests."""
 
     create_resp = 200
@@ -40,7 +37,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_snapshot(self, snapshot_id):
         """Returns the details of a single snapshot."""
@@ -48,7 +45,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_snapshot(self, **kwargs):
         """Creates a new snapshot.
@@ -60,7 +57,7 @@
         resp, body = self.post('snapshots', post_body)
         body = json.loads(body)
         self.expected_success(self.create_resp, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_snapshot(self, snapshot_id, **kwargs):
         """Updates a snapshot."""
@@ -68,50 +65,13 @@
         resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_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)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_snapshot(self, snapshot_id):
         """Delete Snapshot."""
         resp, body = self.delete("snapshots/%s" % str(snapshot_id))
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
         try:
@@ -130,7 +90,7 @@
         post_body = json.dumps({'os-reset_status': {"status": status}})
         resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_snapshot_status(self, snapshot_id, **kwargs):
         """Update the specified snapshot's status."""
@@ -143,7 +103,7 @@
         url = 'snapshots/%s/action' % str(snapshot_id)
         resp, body = self.post(url, post_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_snapshot_metadata(self, snapshot_id, metadata):
         """Create metadata for the snapshot."""
@@ -152,7 +112,7 @@
         resp, body = self.post(url, put_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_snapshot_metadata(self, snapshot_id):
         """Get metadata of the snapshot."""
@@ -160,7 +120,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_snapshot_metadata(self, snapshot_id, **kwargs):
         """Update metadata for the snapshot."""
@@ -173,7 +133,7 @@
         resp, body = self.put(url, put_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_snapshot_metadata_item(self, snapshot_id, id, **kwargs):
         """Update metadata item for the snapshot."""
@@ -186,18 +146,18 @@
         resp, body = self.put(url, put_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_snapshot_metadata_item(self, snapshot_id, id):
         """Delete metadata item for the snapshot."""
         url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
         resp, body = self.delete(url)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def force_delete_snapshot(self, snapshot_id):
         """Force Delete Snapshot."""
         post_body = json.dumps({'os-force_delete': {}})
         resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/base_volumes_client.py b/tempest/services/volume/base/base_volumes_client.py
index 5c0c7f7..4344802 100644
--- a/tempest/services/volume/base/base_volumes_client.py
+++ b/tempest/services/volume/base/base_volumes_client.py
@@ -17,12 +17,11 @@
 import six
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
-from tempest.common import waiters
+from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 
 
-class BaseVolumesClient(service_client.ServiceClient):
+class BaseVolumesClient(rest_client.RestClient):
     """Base client class to send CRUD Volume API requests"""
 
     create_resp = 200
@@ -61,7 +60,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_volume(self, volume_id):
         """Returns the details of a single volume."""
@@ -69,7 +68,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_volume(self, **kwargs):
         """Creates a new Volume.
@@ -83,7 +82,7 @@
         resp, body = self.post('volumes', post_body)
         body = json.loads(body)
         self.expected_success(self.create_resp, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_volume(self, volume_id, **kwargs):
         """Updates the Specified Volume."""
@@ -91,13 +90,13 @@
         resp, body = self.put('volumes/%s' % volume_id, put_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_volume(self, volume_id):
         """Deletes the Specified Volume."""
         resp, body = self.delete("volumes/%s" % str(volume_id))
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def upload_volume(self, volume_id, **kwargs):
         """Uploads a volume in Glance."""
@@ -106,7 +105,7 @@
         resp, body = self.post(url, post_body)
         body = json.loads(body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def attach_volume(self, volume_id, **kwargs):
         """Attaches a volume to a given instance on a given mountpoint."""
@@ -114,7 +113,7 @@
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def set_bootable_volume(self, volume_id, **kwargs):
         """set a bootable flag for a volume - true or false."""
@@ -122,7 +121,7 @@
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def detach_volume(self, volume_id):
         """Detaches a volume from an instance."""
@@ -130,7 +129,7 @@
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def reserve_volume(self, volume_id):
         """Reserves a volume."""
@@ -138,7 +137,7 @@
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def unreserve_volume(self, volume_id):
         """Restore a reserved volume ."""
@@ -146,11 +145,7 @@
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
         self.expected_success(202, resp.status)
-        return service_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)
+        return rest_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
         try:
@@ -170,14 +165,14 @@
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def reset_volume_status(self, volume_id, **kwargs):
         """Reset the Specified Volume's Status."""
         post_body = json.dumps({'os-reset_status': kwargs})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def volume_begin_detaching(self, volume_id):
         """Volume Begin Detaching."""
@@ -185,7 +180,7 @@
         post_body = json.dumps({'os-begin_detaching': {}})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def volume_roll_detaching(self, volume_id):
         """Volume Roll Detaching."""
@@ -193,7 +188,7 @@
         post_body = json.dumps({'os-roll_detaching': {}})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_volume_transfer(self, **kwargs):
         """Create a volume transfer."""
@@ -201,7 +196,7 @@
         resp, body = self.post('os-volume-transfer', post_body)
         body = json.loads(body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_volume_transfer(self, transfer_id):
         """Returns the details of a volume transfer."""
@@ -209,7 +204,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_volume_transfers(self, **params):
         """List all the volume transfers created."""
@@ -219,13 +214,13 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_volume_transfer(self, transfer_id):
         """Delete a volume transfer."""
         resp, body = self.delete("os-volume-transfer/%s" % str(transfer_id))
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def accept_volume_transfer(self, transfer_id, **kwargs):
         """Accept a volume transfer."""
@@ -234,7 +229,7 @@
         resp, body = self.post(url, post_body)
         body = json.loads(body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_volume_readonly(self, volume_id, **kwargs):
         """Update the Specified Volume readonly."""
@@ -242,14 +237,14 @@
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def force_delete_volume(self, volume_id):
         """Force Delete Volume."""
         post_body = json.dumps({'os-force_delete': {}})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
         self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_volume_metadata(self, volume_id, metadata):
         """Create metadata for the volume."""
@@ -258,7 +253,7 @@
         resp, body = self.post(url, put_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_volume_metadata(self, volume_id):
         """Get metadata of the volume."""
@@ -266,7 +261,7 @@
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_volume_metadata(self, volume_id, metadata):
         """Update metadata for the volume."""
@@ -275,7 +270,7 @@
         resp, body = self.put(url, put_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def update_volume_metadata_item(self, volume_id, id, meta_item):
         """Update metadata item for the volume."""
@@ -284,14 +279,14 @@
         resp, body = self.put(url, put_body)
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_volume_metadata_item(self, volume_id, id):
         """Delete metadata item for the volume."""
         url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
         resp, body = self.delete(url)
         self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def retype_volume(self, volume_id, **kwargs):
         """Updates volume with new volume type."""
diff --git a/tempest/stress/__init__.py b/tempest/stress/__init__.py
index 987a023..e69de29 100644
--- a/tempest/stress/__init__.py
+++ b/tempest/stress/__init__.py
@@ -1,22 +0,0 @@
-# Copyright 2016 NEC Corporation.  All rights reserved.
-#
-# 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 warnings
-
-warnings.simplefilter('once', category=DeprecationWarning)
-warnings.warn(
-    'Stress tests are deprecated and will be removed from Tempest '
-    'in the Newton release.',
-    DeprecationWarning)
-warnings.resetwarnings()
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 cee4e7e..6ba4962 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -180,6 +180,19 @@
     return False
 
 
+def is_scheduler_filter_enabled(filter_name):
+    """Check the list of enabled compute scheduler filters from config. """
+
+    filters = CONF.compute_feature_enabled.scheduler_available_filters
+    if len(filters) == 0:
+        return False
+    if 'all' in filters:
+        return True
+    if filter_name in filters:
+        return True
+    return False
+
+
 at_exit_set = set()
 
 
@@ -236,6 +249,9 @@
     # Client manager class to use in this test case.
     client_manager = clients.Manager
 
+    # A way to adjust slow test classes
+    TIMEOUT_SCALING_FACTOR = 1
+
     @classmethod
     def setUpClass(cls):
         # It should never be overridden by descendants
@@ -406,7 +422,7 @@
         at_exit_set.add(self.__class__)
         test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
         try:
-            test_timeout = int(test_timeout)
+            test_timeout = int(test_timeout) * self.TIMEOUT_SCALING_FACTOR
         except ValueError:
             test_timeout = 0
         if test_timeout > 0:
@@ -595,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/base.py b/tempest/tests/base.py
deleted file mode 100644
index fe9268e..0000000
--- a/tempest/tests/base.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2013 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 oslotest import base
-from oslotest import moxstubout
-
-
-class TestCase(base.BaseTestCase):
-
-    def setUp(self):
-        super(TestCase, self).setUp()
-        mox_fixture = self.useFixture(moxstubout.MoxStubout())
-        self.mox = mox_fixture.mox
-        self.stubs = mox_fixture.stubs
-
-    def patch(self, target, **kwargs):
-        """Returns a started `mock.patch` object for the supplied target.
-
-        The caller may then call the returned patcher to create a mock object.
-
-        The caller does not need to call stop() on the returned
-        patcher object, as this method automatically adds a cleanup
-        to the test class to stop the patcher.
-
-        :param target: String module.class or module.object expression to patch
-        :param **kwargs: Passed as-is to `mock.patch`. See mock documentation
-                         for details.
-        """
-        p = mock.patch(target, **kwargs)
-        m = p.start()
-        self.addCleanup(p.stop)
-        return m
diff --git a/tempest/tests/cmd/test_javelin.py b/tempest/tests/cmd/test_javelin.py
index 3380f84..b8c9969 100644
--- a/tempest/tests/cmd/test_javelin.py
+++ b/tempest/tests/cmd/test_javelin.py
@@ -17,7 +17,7 @@
 
 from tempest.cmd import javelin
 from tempest.lib import exceptions as lib_exc
-from tempest.tests import base
+from tempest.tests.lib import base
 
 
 class JavelinUnitTest(base.TestCase):
@@ -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,22 +245,21 @@
 
         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):
 
-        self.fake_client.networks.list_routers.return_value = {'routers': []}
+        self.fake_client.routers.list_routers.return_value = {'routers': []}
         self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
                                               return_value=self.fake_client))
 
         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.networks.list_routers.return_value = {
+        self.fake_client.routers.list_routers.return_value = {
             'routers': [self.fake_object]}
         self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
                                               return_value=self.fake_client))
@@ -405,7 +404,7 @@
 
         javelin.destroy_routers([self.fake_object])
 
-        mocked_function = self.fake_client.networks.delete_router
+        mocked_function = self.fake_client.routers.delete_router
         mocked_function.assert_called_once_with(
             self.fake_object['router_id'])
 
diff --git a/tempest/tests/cmd/test_list_plugins.py b/tempest/tests/cmd/test_list_plugins.py
index 17ddb18..782dde7 100644
--- a/tempest/tests/cmd/test_list_plugins.py
+++ b/tempest/tests/cmd/test_list_plugins.py
@@ -14,7 +14,7 @@
 
 import subprocess
 
-from tempest.tests import base
+from tempest.tests.lib import base
 
 
 class TestTempestListPlugins(base.TestCase):
diff --git a/tempest/tests/cmd/test_tempest_init.py b/tempest/tests/cmd/test_tempest_init.py
index 1048a52..6c5326a 100644
--- a/tempest/tests/cmd/test_tempest_init.py
+++ b/tempest/tests/cmd/test_tempest_init.py
@@ -18,7 +18,7 @@
 import fixtures
 
 from tempest.cmd import init
-from tempest.tests import base
+from tempest.tests.lib import base
 
 
 class TestTempestInit(base.TestCase):
@@ -36,9 +36,8 @@
         testr_conf_file = init.TESTR_CONF % (top_level_path, discover_path)
 
         conf_path = conf_dir.join('.testr.conf')
-        conf_file = open(conf_path, 'r')
-        self.addCleanup(conf_file.close)
-        self.assertEqual(conf_file.read(), testr_conf_file)
+        with open(conf_path, 'r') as conf_file:
+            self.assertEqual(conf_file.read(), testr_conf_file)
 
     def test_generate_sample_config(self):
         local_dir = self.useFixture(fixtures.TempDir())
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 193abc7..9df07a1 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -19,8 +19,8 @@
 
 from tempest.cmd import verify_tempest_config
 from tempest import config
-from tempest.tests import base
 from tempest.tests import fake_config
+from tempest.tests.lib import base
 
 
 class TestGetAPIVersions(base.TestCase):
@@ -49,8 +49,9 @@
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': {'values': [{'id': 'v2.0'}, {'id': 'v3.0'}]}}
         fake_resp = json.dumps(fake_resp)
-        self.useFixture(mockpatch.Patch('httplib2.Http.request',
-                                        return_value=(None, fake_resp)))
+        self.useFixture(mockpatch.Patch(
+            'tempest.lib.common.http.ClosingHttp.request',
+            return_value=(None, fake_resp)))
         fake_os = mock.MagicMock()
         versions = verify_tempest_config._get_api_versions(fake_os, 'keystone')
         self.assertIn('v2.0', versions)
@@ -62,8 +63,9 @@
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': [{'id': 'v1.0'}, {'id': 'v2.0'}]}
         fake_resp = json.dumps(fake_resp)
-        self.useFixture(mockpatch.Patch('httplib2.Http.request',
-                                        return_value=(None, fake_resp)))
+        self.useFixture(mockpatch.Patch(
+            'tempest.lib.common.http.ClosingHttp.request',
+            return_value=(None, fake_resp)))
         fake_os = mock.MagicMock()
         versions = verify_tempest_config._get_api_versions(fake_os, 'cinder')
         self.assertIn('v1.0', versions)
@@ -75,13 +77,37 @@
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': [{'id': 'v2.0'}, {'id': 'v3.0'}]}
         fake_resp = json.dumps(fake_resp)
-        self.useFixture(mockpatch.Patch('httplib2.Http.request',
-                                        return_value=(None, fake_resp)))
+        self.useFixture(mockpatch.Patch(
+            'tempest.lib.common.http.ClosingHttp.request',
+            return_value=(None, fake_resp)))
         fake_os = mock.MagicMock()
         versions = verify_tempest_config._get_api_versions(fake_os, 'nova')
         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(
+            'tempest.lib.common.http.ClosingHttp.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()
@@ -100,14 +126,14 @@
                 verify_tempest_config.verify_api_versions(fake_os, 'foo', True)
                 self.assertFalse(verify_mock.called)
 
-    def test_verify_keystone_api_versions_no_v3(self):
+    @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+    def test_verify_keystone_api_versions_no_v3(self, mock_request):
         self.useFixture(mockpatch.PatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': {'values': [{'id': 'v2.0'}]}}
         fake_resp = json.dumps(fake_resp)
-        self.useFixture(mockpatch.Patch('httplib2.Http.request',
-                                        return_value=(None, fake_resp)))
+        mock_request.return_value = (None, fake_resp)
         fake_os = mock.MagicMock()
         with mock.patch.object(verify_tempest_config,
                                'print_and_or_update') as print_mock:
@@ -116,14 +142,14 @@
                                            'identity-feature-enabled',
                                            False, True)
 
-    def test_verify_keystone_api_versions_no_v2(self):
+    @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+    def test_verify_keystone_api_versions_no_v2(self, mock_request):
         self.useFixture(mockpatch.PatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': {'values': [{'id': 'v3.0'}]}}
         fake_resp = json.dumps(fake_resp)
-        self.useFixture(mockpatch.Patch('httplib2.Http.request',
-                                        return_value=(None, fake_resp)))
+        mock_request.return_value = (None, fake_resp)
         fake_os = mock.MagicMock()
         with mock.patch.object(verify_tempest_config,
                                'print_and_or_update') as print_mock:
@@ -132,14 +158,14 @@
                                            'identity-feature-enabled',
                                            False, True)
 
-    def test_verify_cinder_api_versions_no_v2(self):
+    @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+    def test_verify_cinder_api_versions_no_v2(self, mock_request):
         self.useFixture(mockpatch.PatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': [{'id': 'v1.0'}]}
         fake_resp = json.dumps(fake_resp)
-        self.useFixture(mockpatch.Patch('httplib2.Http.request',
-                                        return_value=(None, fake_resp)))
+        mock_request.return_value = (None, fake_resp)
         fake_os = mock.MagicMock()
         with mock.patch.object(verify_tempest_config,
                                'print_and_or_update') as print_mock:
@@ -147,14 +173,14 @@
         print_mock.assert_called_once_with('api_v2', 'volume-feature-enabled',
                                            False, True)
 
-    def test_verify_cinder_api_versions_no_v1(self):
+    @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+    def test_verify_cinder_api_versions_no_v1(self, mock_request):
         self.useFixture(mockpatch.PatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': [{'id': 'v2.0'}]}
         fake_resp = json.dumps(fake_resp)
-        self.useFixture(mockpatch.Patch('httplib2.Http.request',
-                                        return_value=(None, fake_resp)))
+        mock_request.return_value = (None, fake_resp)
         fake_os = mock.MagicMock()
         with mock.patch.object(verify_tempest_config,
                                'print_and_or_update') as print_mock:
diff --git a/tempest/tests/common/test_admin_available.py b/tempest/tests/common/test_admin_available.py
index 75401db..c803541 100644
--- a/tempest/tests/common/test_admin_available.py
+++ b/tempest/tests/common/test_admin_available.py
@@ -17,8 +17,8 @@
 
 from tempest.common import credentials_factory as credentials
 from tempest import config
-from tempest.tests import base
 from tempest.tests import fake_config
+from tempest.tests.lib import base
 
 
 class TestAdminAvailable(base.TestCase):
@@ -36,19 +36,19 @@
                              dynamic_creds, group='auth')
         if use_accounts_file:
             accounts = [{'username': 'u1',
-                         'tenant_name': 't1',
+                         'project_name': 't1',
                          'password': 'p'},
                         {'username': 'u2',
-                         'tenant_name': 't2',
+                         'project_name': 't2',
                          'password': 'p'}]
             if admin_creds == 'role':
                 accounts.append({'username': 'admin',
-                                 'tenant_name': 'admin',
+                                 'project_name': 'admin',
                                  'password': 'p',
                                  'roles': ['admin']})
             elif admin_creds == 'type':
                 accounts.append({'username': 'admin',
-                                 'tenant_name': 'admin',
+                                 'project_name': 'admin',
                                  'password': 'p',
                                  'types': ['admin']})
             self.useFixture(mockpatch.Patch(
@@ -63,17 +63,17 @@
                                             return_value=False))
             if admin_creds:
                 username = 'u'
-                tenant = 't'
+                project = 't'
                 password = 'p'
                 domain = 'd'
             else:
                 username = None
-                tenant = None
+                project = None
                 password = None
                 domain = None
 
             cfg.CONF.set_default('admin_username', username, group='auth')
-            cfg.CONF.set_default('admin_tenant_name', tenant, group='auth')
+            cfg.CONF.set_default('admin_project_name', project, group='auth')
             cfg.CONF.set_default('admin_password', password, group='auth')
             cfg.CONF.set_default('admin_domain_name', domain, group='auth')
 
diff --git a/tempest/tests/common/test_alt_available.py b/tempest/tests/common/test_alt_available.py
index db3f5ec..cb1de16 100644
--- a/tempest/tests/common/test_alt_available.py
+++ b/tempest/tests/common/test_alt_available.py
@@ -17,8 +17,8 @@
 
 from tempest.common import credentials_factory as credentials
 from tempest import config
-from tempest.tests import base
 from tempest.tests import fake_config
+from tempest.tests.lib import base
 
 
 class TestAltAvailable(base.TestCase):
@@ -36,7 +36,7 @@
                              dynamic_creds, group='auth')
         if use_accounts_file:
             accounts = [dict(username="u%s" % ii,
-                             tenant_name="t%s" % ii,
+                             project_name="t%s" % ii,
                              password="p") for ii in creds]
             self.useFixture(mockpatch.Patch(
                 'tempest.common.preprov_creds.read_accounts_yaml',
@@ -52,19 +52,19 @@
             for ii in range(0, 2):
                 if len(creds) > ii:
                     username = 'u%s' % creds[ii]
-                    tenant = 't%s' % creds[ii]
+                    project = 't%s' % creds[ii]
                     password = 'p'
                     domain = 'd'
                 else:
                     username = None
-                    tenant = None
+                    project = None
                     password = None
                     domain = None
 
                 cfg.CONF.set_default('%susername' % cred_prefix[ii], username,
                                      group='identity')
-                cfg.CONF.set_default('%stenant_name' % cred_prefix[ii], tenant,
-                                     group='identity')
+                cfg.CONF.set_default('%sproject_name' % cred_prefix[ii],
+                                     project, group='identity')
                 cfg.CONF.set_default('%spassword' % cred_prefix[ii], password,
                                      group='identity')
                 cfg.CONF.set_default('%sdomain_name' % cred_prefix[ii], domain,
diff --git a/tempest/tests/common/test_configured_creds.py b/tempest/tests/common/test_configured_creds.py
index be24595..3c104b2 100644
--- a/tempest/tests/common/test_configured_creds.py
+++ b/tempest/tests/common/test_configured_creds.py
@@ -21,9 +21,9 @@
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.identity.v2 import token_client as v2_client
 from tempest.lib.services.identity.v3 import token_client as v3_client
-from tempest.tests import base
 from tempest.tests import fake_config
-from tempest.tests import fake_identity
+from tempest.tests.lib import base
+from tempest.tests.lib import fake_identity
 
 
 class ConfiguredV2CredentialsTests(base.TestCase):
diff --git a/tempest/tests/common/test_credentials.py b/tempest/tests/common/test_credentials.py
index 136ac02..6fc490e 100644
--- a/tempest/tests/common/test_credentials.py
+++ b/tempest/tests/common/test_credentials.py
@@ -15,8 +15,8 @@
 from tempest.common import credentials_factory as credentials
 from tempest import config
 from tempest import exceptions
-from tempest.tests import base
 from tempest.tests import fake_config
+from tempest.tests.lib import base
 
 
 class TestLegacyCredentialsProvider(base.TestCase):
diff --git a/tempest/tests/common/test_custom_matchers.py b/tempest/tests/common/test_custom_matchers.py
index 2656a47..d664961 100644
--- a/tempest/tests/common/test_custom_matchers.py
+++ b/tempest/tests/common/test_custom_matchers.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 from tempest.common import custom_matchers
-from tempest.tests import base
+from tempest.tests.lib import base
 
 from testtools.tests.matchers import helpers
 
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index 059269e..e1d9023 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -18,9 +18,9 @@
 
 from tempest.common import credentials_factory as credentials
 from tempest.common import dynamic_creds
-from tempest.common import service_client
 from tempest import config
 from tempest import exceptions
+from tempest.lib.common import rest_client
 from tempest.lib.services.identity.v2 import token_client as json_token_client
 from tempest.services.identity.v2.json import identity_client as \
     json_iden_client
@@ -30,11 +30,11 @@
     json_tenants_client
 from tempest.services.identity.v2.json import users_client as \
     json_users_client
-from tempest.services.network.json import network_client as json_network_client
-from tempest.tests import base
+from tempest.services.network.json import routers_client
 from tempest.tests import fake_config
-from tempest.tests import fake_http
-from tempest.tests import fake_identity
+from tempest.tests.lib import base
+from tempest.tests.lib import fake_http
+from tempest.tests.lib import fake_identity
 
 
 class TestDynamicCredentialProvider(base.TestCase):
@@ -47,7 +47,6 @@
         super(TestDynamicCredentialProvider, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
         self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
-        self.fake_http = fake_http.fake_httplib2(return_type=200)
         self.stubs.Set(json_token_client.TokenClient, 'raw_request',
                        fake_identity._fake_v2_response)
         cfg.CONF.set_default('operator_role', 'FakeRole',
@@ -58,10 +57,8 @@
 
     def test_tempest_client(self):
         creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
-        self.assertTrue(isinstance(creds.identity_admin_client,
-                                   json_iden_client.IdentityClient))
-        self.assertTrue(isinstance(creds.network_admin_client,
-                                   json_network_client.NetworkClient))
+        self.assertIsInstance(creds.identity_admin_client,
+                              json_iden_client.IdentityClient)
 
     def _get_fake_admin_creds(self):
         return credentials.get_credentials(
@@ -74,7 +71,7 @@
         user_fix = self.useFixture(mockpatch.PatchObject(
             json_users_client.UsersClient,
             'create_user',
-            return_value=(service_client.ResponseBody
+            return_value=(rest_client.ResponseBody
                           (200, {'user': {'id': id, 'name': name}}))))
         return user_fix
 
@@ -82,7 +79,7 @@
         tenant_fix = self.useFixture(mockpatch.PatchObject(
             json_tenants_client.TenantsClient,
             'create_tenant',
-            return_value=(service_client.ResponseBody
+            return_value=(rest_client.ResponseBody
                           (200, {'tenant': {'id': id, 'name': name}}))))
         return tenant_fix
 
@@ -90,7 +87,7 @@
         roles_fix = self.useFixture(mockpatch.PatchObject(
             json_roles_client.RolesClient,
             'list_roles',
-            return_value=(service_client.ResponseBody
+            return_value=(rest_client.ResponseBody
                           (200,
                            {'roles': [{'id': id, 'name': name},
                             {'id': '1', 'name': 'FakeRole'},
@@ -101,7 +98,7 @@
         roles_fix = self.useFixture(mockpatch.PatchObject(
             json_roles_client.RolesClient,
             'list_roles',
-            return_value=(service_client.ResponseBody
+            return_value=(rest_client.ResponseBody
                           (200,
                            {'roles': [{'id': '1234', 'name': 'role1'},
                             {'id': '1', 'name': 'FakeRole'},
@@ -112,7 +109,7 @@
         tenant_fix = self.useFixture(mockpatch.PatchObject(
             json_roles_client.RolesClient,
             'assign_user_role',
-            return_value=(service_client.ResponseBody
+            return_value=(rest_client.ResponseBody
                           (200, {}))))
         return tenant_fix
 
@@ -120,7 +117,7 @@
         roles_fix = self.useFixture(mockpatch.PatchObject(
             json_roles_client.RolesClient,
             'list_roles',
-            return_value=(service_client.ResponseBody
+            return_value=(rest_client.ResponseBody
                           (200, {'roles': [{'id': '1',
                                  'name': 'FakeRole'}]}))))
         return roles_fix
@@ -129,7 +126,7 @@
         ec2_creds_fix = self.useFixture(mockpatch.PatchObject(
             json_users_client.UsersClient,
             'list_user_ec2_credentials',
-            return_value=(service_client.ResponseBody
+            return_value=(rest_client.ResponseBody
                           (200, {'credentials': [{
                                  'access': 'fake_access',
                                  'secret': 'fake_secret',
@@ -154,7 +151,7 @@
 
     def _mock_router_create(self, id, name):
         router_fix = self.useFixture(mockpatch.PatchObject(
-            json_network_client.NetworkClient,
+            routers_client.RoutersClient,
             'create_router',
             return_value={'router': {'id': id, 'name': name}}))
         return router_fix
@@ -295,7 +292,7 @@
         subnet = mock.patch.object(creds.subnets_admin_client,
                                    'delete_subnet')
         subnet_mock = subnet.start()
-        router = mock.patch.object(creds.network_admin_client,
+        router = mock.patch.object(creds.routers_admin_client,
                                    'delete_router')
         router_mock = router.start()
 
@@ -321,7 +318,7 @@
         self._mock_subnet_create(creds, '1234', 'fake_subnet')
         self._mock_router_create('1234', 'fake_router')
         router_interface_mock = self.patch(
-            'tempest.services.network.json.network_client.NetworkClient.'
+            'tempest.services.network.json.routers_client.RoutersClient.'
             'add_router_interface')
         primary_creds = creds.get_primary_creds()
         router_interface_mock.assert_called_once_with('1234', subnet_id='1234')
@@ -353,7 +350,7 @@
         self._mock_subnet_create(creds, '1234', 'fake_subnet')
         self._mock_router_create('1234', 'fake_router')
         router_interface_mock = self.patch(
-            'tempest.services.network.json.network_client.NetworkClient.'
+            'tempest.services.network.json.routers_client.RoutersClient.'
             'add_router_interface')
         creds.get_primary_creds()
         router_interface_mock.assert_called_once_with('1234', subnet_id='1234')
@@ -386,11 +383,11 @@
         subnet = mock.patch.object(creds.subnets_admin_client,
                                    'delete_subnet')
         subnet_mock = subnet.start()
-        router = mock.patch.object(creds.network_admin_client,
+        router = mock.patch.object(creds.routers_admin_client,
                                    'delete_router')
         router_mock = router.start()
         remove_router_interface_mock = self.patch(
-            'tempest.services.network.json.network_client.NetworkClient.'
+            'tempest.services.network.json.routers_client.RoutersClient.'
             'remove_router_interface')
         return_values = ({'status': 200}, {'ports': []})
         port_list_mock = mock.patch.object(creds.ports_admin_client,
@@ -404,7 +401,7 @@
             side_effect=side_effect)
         secgroup_list_mock.start()
 
-        return_values = (fake_http.fake_httplib({}, status=204), {})
+        return_values = fake_http.fake_http_response({}, status=204), ''
         remove_secgroup_mock = self.patch(
             'tempest.lib.services.network.security_groups_client.'
             'SecurityGroupsClient.delete', return_value=return_values)
@@ -461,7 +458,7 @@
         self._mock_subnet_create(creds, '1234', 'fake_alt_subnet')
         self._mock_router_create('1234', 'fake_alt_router')
         router_interface_mock = self.patch(
-            'tempest.services.network.json.network_client.NetworkClient.'
+            'tempest.services.network.json.routers_client.RoutersClient.'
             'add_router_interface')
         alt_creds = creds.get_alt_creds()
         router_interface_mock.assert_called_once_with('1234', subnet_id='1234')
@@ -485,7 +482,7 @@
         self._mock_subnet_create(creds, '1234', 'fake_admin_subnet')
         self._mock_router_create('1234', 'fake_admin_router')
         router_interface_mock = self.patch(
-            'tempest.services.network.json.network_client.NetworkClient.'
+            'tempest.services.network.json.routers_client.RoutersClient.'
             'add_router_interface')
         self._mock_list_roles('123456', 'admin')
         admin_creds = creds.get_admin_creds()
@@ -521,7 +518,7 @@
         subnet = mock.patch.object(creds.subnets_admin_client,
                                    'delete_subnet')
         subnet_mock = subnet.start()
-        router = mock.patch.object(creds.network_admin_client,
+        router = mock.patch.object(creds.routers_admin_client,
                                    'delete_router')
         router_mock = router.start()
 
diff --git a/tempest/tests/common/test_preprov_creds.py b/tempest/tests/common/test_preprov_creds.py
index 7188e5f..36d6c3d 100644
--- a/tempest/tests/common/test_preprov_creds.py
+++ b/tempest/tests/common/test_preprov_creds.py
@@ -28,10 +28,9 @@
 from tempest.lib import auth
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.identity.v2 import token_client
-from tempest.tests import base
 from tempest.tests import fake_config
-from tempest.tests import fake_http
-from tempest.tests import fake_identity
+from tempest.tests.lib import base
+from tempest.tests.lib import fake_identity
 
 
 class TestPreProvisionedCredentials(base.TestCase):
@@ -48,7 +47,6 @@
         super(TestPreProvisionedCredentials, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
         self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
-        self.fake_http = fake_http.fake_httplib2(return_type=200)
         self.stubs.Set(token_client.TokenClient, 'raw_request',
                        fake_identity._fake_v2_response)
         self.useFixture(lockutils_fixtures.ExternalLockFixture())
@@ -325,7 +323,7 @@
                                                     'id': 'fake-id',
                                                     'label': 'network-2'}]}):
             creds = test_accounts_class.get_creds_by_roles(['role-7'])
-        self.assertTrue(isinstance(creds, cred_provider.TestResources))
+        self.assertIsInstance(creds, cred_provider.TestResources)
         network = creds.network
         self.assertIsNotNone(network)
         self.assertIn('name', network)
diff --git a/tempest/tests/common/test_service_clients.py b/tempest/tests/common/test_service_clients.py
deleted file mode 100644
index 7e9ff9d..0000000
--- a/tempest/tests/common/test_service_clients.py
+++ /dev/null
@@ -1,142 +0,0 @@
-# Copyright 2015 NEC Corporation.  All rights reserved.
-#
-#    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
-import random
-import six
-
-from tempest.services.baremetal.v1.json import baremetal_client
-from tempest.services.data_processing.v1_1 import data_processing_client
-from tempest.services.database.json import flavors_client as db_flavor_client
-from tempest.services.database.json import versions_client as db_version_client
-from tempest.services.identity.v2.json import identity_client as \
-    identity_v2_identity_client
-from tempest.services.identity.v3.json import credentials_client
-from tempest.services.identity.v3.json import endpoints_client
-from tempest.services.identity.v3.json import identity_client as \
-    identity_v3_identity_client
-from tempest.services.identity.v3.json import policies_client
-from tempest.services.identity.v3.json import regions_client
-from tempest.services.identity.v3.json import services_client
-from tempest.services.image.v1.json import images_client
-from tempest.services.image.v2.json import images_client as images_v2_client
-from tempest.services.network.json import network_client
-from tempest.services.object_storage import account_client
-from tempest.services.object_storage import container_client
-from tempest.services.object_storage import object_client
-from tempest.services.orchestration.json import orchestration_client
-from tempest.services.telemetry.json import alarming_client
-from tempest.services.telemetry.json import telemetry_client
-from tempest.services.volume.v1.json.admin import hosts_client \
-    as volume_hosts_client
-from tempest.services.volume.v1.json.admin import quotas_client \
-    as volume_quotas_client
-from tempest.services.volume.v1.json.admin import services_client \
-    as volume_services_client
-from tempest.services.volume.v1.json.admin import types_client \
-    as volume_types_client
-from tempest.services.volume.v1.json import availability_zone_client \
-    as volume_az_client
-from tempest.services.volume.v1.json import backups_client
-from tempest.services.volume.v1.json import extensions_client \
-    as volume_extensions_client
-from tempest.services.volume.v1.json import qos_client
-from tempest.services.volume.v1.json import snapshots_client
-from tempest.services.volume.v1.json import volumes_client
-from tempest.services.volume.v2.json.admin import hosts_client \
-    as volume_v2_hosts_client
-from tempest.services.volume.v2.json.admin import quotas_client \
-    as volume_v2_quotas_client
-from tempest.services.volume.v2.json.admin import services_client \
-    as volume_v2_services_client
-from tempest.services.volume.v2.json.admin import types_client \
-    as volume_v2_types_client
-from tempest.services.volume.v2.json import availability_zone_client \
-    as volume_v2_az_client
-from tempest.services.volume.v2.json import backups_client \
-    as volume_v2_backups_client
-from tempest.services.volume.v2.json import extensions_client \
-    as volume_v2_extensions_client
-from tempest.services.volume.v2.json import qos_client as volume_v2_qos_client
-from tempest.services.volume.v2.json import snapshots_client \
-    as volume_v2_snapshots_client
-from tempest.services.volume.v2.json import volumes_client as \
-    volume_v2_volumes_client
-from tempest.tests import base
-
-
-class TestServiceClient(base.TestCase):
-
-    @mock.patch('tempest.lib.common.rest_client.RestClient.__init__')
-    def test_service_client_creations_with_specified_args(self, mock_init):
-        test_clients = [
-            baremetal_client.BaremetalClient,
-            data_processing_client.DataProcessingClient,
-            db_flavor_client.DatabaseFlavorsClient,
-            db_version_client.DatabaseVersionsClient,
-            network_client.NetworkClient,
-            account_client.AccountClient,
-            container_client.ContainerClient,
-            object_client.ObjectClient,
-            orchestration_client.OrchestrationClient,
-            telemetry_client.TelemetryClient,
-            alarming_client.AlarmingClient,
-            qos_client.QosSpecsClient,
-            volume_hosts_client.HostsClient,
-            volume_quotas_client.QuotasClient,
-            volume_services_client.ServicesClient,
-            volume_types_client.TypesClient,
-            volume_az_client.AvailabilityZoneClient,
-            backups_client.BackupsClient,
-            volume_extensions_client.ExtensionsClient,
-            snapshots_client.SnapshotsClient,
-            volumes_client.VolumesClient,
-            volume_v2_hosts_client.HostsClient,
-            volume_v2_quotas_client.QuotasClient,
-            volume_v2_services_client.ServicesClient,
-            volume_v2_types_client.TypesClient,
-            volume_v2_az_client.AvailabilityZoneClient,
-            volume_v2_backups_client.BackupsClient,
-            volume_v2_extensions_client.ExtensionsClient,
-            volume_v2_qos_client.QosSpecsClient,
-            volume_v2_snapshots_client.SnapshotsClient,
-            volume_v2_volumes_client.VolumesClient,
-            identity_v2_identity_client.IdentityClient,
-            credentials_client.CredentialsClient,
-            endpoints_client.EndPointClient,
-            identity_v3_identity_client.IdentityV3Client,
-            policies_client.PoliciesClient,
-            regions_client.RegionsClient,
-            services_client.ServicesClient,
-            images_client.ImagesClient,
-            images_v2_client.ImagesClientV2
-        ]
-
-        for client in test_clients:
-            fake_string = six.text_type(random.randint(1, 0x7fffffff))
-            auth = 'auth' + fake_string
-            service = 'service' + fake_string
-            region = 'region' + fake_string
-            params = {
-                'endpoint_type': 'URL' + fake_string,
-                'build_interval': random.randint(1, 100),
-                'build_timeout': random.randint(1, 100),
-                'disable_ssl_certificate_validation':
-                    True if random.randint(0, 1) else False,
-                'ca_certs': None,
-                'trace_requests': 'foo' + fake_string
-            }
-            client(auth, service, region, **params)
-            mock_init.assert_called_once_with(auth, service, region, **params)
-            mock_init.reset_mock()
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index c7cc638..e0cef62 100644
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -19,7 +19,8 @@
 from tempest.common import waiters
 from tempest import exceptions
 from tempest.services.volume.base import base_volumes_client
-from tempest.tests import base
+from tempest.tests.lib import base
+import tempest.tests.utils as utils
 
 
 class TestImageWaiters(base.TestCase):
@@ -37,17 +38,24 @@
         # Ensure waiter returns before build_timeout
         self.assertTrue((end_time - start_time) < 10)
 
-    def test_wait_for_image_status_timeout(self):
+    @mock.patch('time.sleep')
+    def test_wait_for_image_status_timeout(self, mock_sleep):
+        time_mock = self.patch('time.time')
+        time_mock.side_effect = utils.generate_timeout_series(1)
+
         self.client.show_image.return_value = ({'status': 'saving'})
         self.assertRaises(exceptions.TimeoutException,
                           waiters.wait_for_image_status,
                           self.client, 'fake_image_id', 'active')
+        mock_sleep.assert_called_once_with(1)
 
-    def test_wait_for_image_status_error_on_image_create(self):
+    @mock.patch('time.sleep')
+    def test_wait_for_image_status_error_on_image_create(self, mock_sleep):
         self.client.show_image.return_value = ({'status': 'ERROR'})
         self.assertRaises(exceptions.AddImageException,
                           waiters.wait_for_image_status,
                           self.client, 'fake_image_id', 'active')
+        mock_sleep.assert_called_once_with(1)
 
     @mock.patch.object(time, 'sleep')
     def test_wait_for_volume_status_error_restoring(self, mock_sleep):
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 9c2b99e..22cf47a 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -19,8 +19,8 @@
 
 from tempest.common.utils.linux import remote_client
 from tempest import config
-from tempest.tests import base
 from tempest.tests import fake_config
+from tempest.tests.lib import base
 
 
 class TestRemoteClient(base.TestCase):
diff --git a/tempest/tests/common/utils/test_file_utils.py b/tempest/tests/common/utils/test_file_utils.py
index 937aefa..1a14592 100644
--- a/tempest/tests/common/utils/test_file_utils.py
+++ b/tempest/tests/common/utils/test_file_utils.py
@@ -16,7 +16,7 @@
 import mock
 
 from tempest.common.utils import file_utils
-from tempest.tests import base
+from tempest.tests.lib import base
 
 
 class TestFileUtils(base.TestCase):
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index c45f6da..edd7186 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -46,7 +46,7 @@
             lock_path=str(os.environ.get('OS_TEST_LOCK_PATH')),
         )
         self.conf.set_default('auth_version', 'v2', group='identity')
-        for config_option in ['username', 'password', 'tenant_name']:
+        for config_option in ['username', 'password', 'project_name']:
             # Identity group items
             for prefix in ['', 'alt_', 'admin_']:
                 if prefix == 'admin_':
diff --git a/tempest/tests/fake_http.py b/tempest/tests/fake_http.py
deleted file mode 100644
index d714055..0000000
--- a/tempest/tests/fake_http.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright 2013 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 copy
-
-import httplib2
-
-
-class fake_httplib2(object):
-
-    def __init__(self, return_type=None, *args, **kwargs):
-        self.return_type = return_type
-
-    def request(self, uri, method="GET", body=None, headers=None,
-                redirections=5, connection_type=None):
-        if not self.return_type:
-            fake_headers = httplib2.Response(headers)
-            return_obj = {
-                'uri': uri,
-                'method': method,
-                'body': body,
-                'headers': headers
-            }
-            return (fake_headers, return_obj)
-        elif isinstance(self.return_type, int):
-            body = "fake_body"
-            header_info = {
-                'content-type': 'text/plain',
-                'status': str(self.return_type),
-                'content-length': len(body)
-            }
-            resp_header = httplib2.Response(header_info)
-            return (resp_header, body)
-        else:
-            msg = "unsupported return type %s" % self.return_type
-            raise TypeError(msg)
-
-
-class fake_httplib(object):
-    def __init__(self, headers, body=None,
-                 version=1.0, status=200, reason="Ok"):
-        """Initialization of fake httplib
-
-        :param headers: dict representing HTTP response headers
-        :param body: file-like object
-        :param version: HTTP Version
-        :param status: Response status code
-        :param reason: Status code related message.
-        """
-        self.body = body
-        self.status = status
-        self.reason = reason
-        self.version = version
-        self.headers = headers
-
-    def getheaders(self):
-        return copy.deepcopy(self.headers).items()
-
-    def getheader(self, key, default):
-        return self.headers.get(key, default)
-
-    def read(self, amt):
-        return self.body.read(amt)
diff --git a/tempest/tests/fake_identity.py b/tempest/tests/fake_identity.py
deleted file mode 100644
index d0de927..0000000
--- a/tempest/tests/fake_identity.py
+++ /dev/null
@@ -1,163 +0,0 @@
-# Copyright 2014 IBM Corp.
-# All Rights Reserved.
-#
-#    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 httplib2
-from oslo_serialization import jsonutils as json
-
-FAKE_AUTH_URL = 'http://fake_uri.com/auth'
-
-TOKEN = "fake_token"
-ALT_TOKEN = "alt_fake_token"
-
-# Fake Identity v2 constants
-COMPUTE_ENDPOINTS_V2 = {
-    "endpoints": [
-        {
-            "adminURL": "http://fake_url/v2/first_endpoint/admin",
-            "region": "NoMatchRegion",
-            "internalURL": "http://fake_url/v2/first_endpoint/internal",
-            "publicURL": "http://fake_url/v2/first_endpoint/public"
-        },
-        {
-            "adminURL": "http://fake_url/v2/second_endpoint/admin",
-            "region": "FakeRegion",
-            "internalURL": "http://fake_url/v2/second_endpoint/internal",
-            "publicURL": "http://fake_url/v2/second_endpoint/public"
-        },
-    ],
-    "type": "compute",
-    "name": "nova"
-}
-
-CATALOG_V2 = [COMPUTE_ENDPOINTS_V2, ]
-
-ALT_IDENTITY_V2_RESPONSE = {
-    "access": {
-        "token": {
-            "expires": "2020-01-01T00:00:10Z",
-            "id": ALT_TOKEN,
-            "tenant": {
-                "id": "fake_tenant_id"
-            },
-        },
-        "user": {
-            "id": "fake_user_id",
-        },
-        "serviceCatalog": CATALOG_V2,
-    },
-}
-
-IDENTITY_V2_RESPONSE = {
-    "access": {
-        "token": {
-            "expires": "2020-01-01T00:00:10Z",
-            "id": TOKEN,
-            "tenant": {
-                "id": "fake_tenant_id"
-            },
-        },
-        "user": {
-            "id": "fake_user_id",
-        },
-        "serviceCatalog": CATALOG_V2,
-    },
-}
-
-# Fake Identity V3 constants
-COMPUTE_ENDPOINTS_V3 = {
-    "endpoints": [
-        {
-            "id": "first_compute_fake_service",
-            "interface": "public",
-            "region": "NoMatchRegion",
-            "url": "http://fake_url/v3/first_endpoint/api"
-        },
-        {
-            "id": "second_fake_service",
-            "interface": "public",
-            "region": "FakeRegion",
-            "url": "http://fake_url/v3/second_endpoint/api"
-        },
-        {
-            "id": "third_fake_service",
-            "interface": "admin",
-            "region": "MiddleEarthRegion",
-            "url": "http://fake_url/v3/third_endpoint/api"
-        }
-
-    ],
-    "type": "compute",
-    "id": "fake_compute_endpoint"
-}
-
-CATALOG_V3 = [COMPUTE_ENDPOINTS_V3, ]
-
-IDENTITY_V3_RESPONSE = {
-    "token": {
-        "methods": [
-            "token",
-            "password"
-        ],
-        "expires_at": "2020-01-01T00:00:10.000123Z",
-        "project": {
-            "domain": {
-                "id": "fake_domain_id",
-                "name": "fake"
-            },
-            "id": "project_id",
-            "name": "project_name"
-        },
-        "user": {
-            "domain": {
-                "id": "fake_domain_id",
-                "name": "domain_name"
-            },
-            "id": "fake_user_id",
-            "name": "username"
-        },
-        "issued_at": "2013-05-29T16:55:21.468960Z",
-        "catalog": CATALOG_V3
-    }
-}
-
-ALT_IDENTITY_V3 = IDENTITY_V3_RESPONSE
-
-
-def _fake_v3_response(self, uri, method="GET", body=None, headers=None,
-                      redirections=5, connection_type=None):
-    fake_headers = {
-        "status": "201",
-        "x-subject-token": TOKEN
-    }
-    return (httplib2.Response(fake_headers),
-            json.dumps(IDENTITY_V3_RESPONSE))
-
-
-def _fake_v2_response(self, uri, method="GET", body=None, headers=None,
-                      redirections=5, connection_type=None):
-    return (httplib2.Response({"status": "200"}),
-            json.dumps(IDENTITY_V2_RESPONSE))
-
-
-def _fake_auth_failure_response():
-    # the response body isn't really used in this case, but lets send it anyway
-    # to have a safe check in some future change on the rest client.
-    body = {
-        "unauthorized": {
-            "message": "Unauthorized",
-            "code": "401"
-        }
-    }
-    return httplib2.Response({"status": "401"}), json.dumps(body)
diff --git a/tempest/tests/common/test_api_version_request.py b/tempest/tests/lib/common/test_api_version_request.py
similarity index 97%
rename from tempest/tests/common/test_api_version_request.py
rename to tempest/tests/lib/common/test_api_version_request.py
index 38fbfc1..bdaa936 100644
--- a/tempest/tests/common/test_api_version_request.py
+++ b/tempest/tests/lib/common/test_api_version_request.py
@@ -12,9 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common import api_version_request
-from tempest import exceptions
-from tempest.tests import base
+from tempest.lib.common import api_version_request
+from tempest.lib import exceptions
+from tempest.tests.lib import base
 
 
 class APIVersionRequestTests(base.TestCase):
diff --git a/tempest/tests/common/test_api_version_utils.py b/tempest/tests/lib/common/test_api_version_utils.py
similarity index 97%
rename from tempest/tests/common/test_api_version_utils.py
rename to tempest/tests/lib/common/test_api_version_utils.py
index 501f954..591b87e 100644
--- a/tempest/tests/common/test_api_version_utils.py
+++ b/tempest/tests/lib/common/test_api_version_utils.py
@@ -14,9 +14,9 @@
 
 import testtools
 
-from tempest.common import api_version_utils
-from tempest import exceptions
-from tempest.tests import base
+from tempest.lib.common import api_version_utils
+from tempest.lib import exceptions
+from tempest.tests.lib import base
 
 
 class TestVersionSkipLogic(base.TestCase):
diff --git a/tempest/tests/lib/common/utils/test_data_utils.py b/tempest/tests/lib/common/utils/test_data_utils.py
index 07502d0..493df89 100644
--- a/tempest/tests/lib/common/utils/test_data_utils.py
+++ b/tempest/tests/lib/common/utils/test_data_utils.py
@@ -24,15 +24,15 @@
     def test_rand_uuid(self):
         actual = data_utils.rand_uuid()
         self.assertIsInstance(actual, str)
-        self.assertRegexpMatches(actual, "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]"
-                                         "{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
+        self.assertRegex(actual, "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]"
+                         "{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
         actual2 = data_utils.rand_uuid()
         self.assertNotEqual(actual, actual2)
 
     def test_rand_uuid_hex(self):
         actual = data_utils.rand_uuid_hex()
         self.assertIsInstance(actual, str)
-        self.assertRegexpMatches(actual, "^[0-9a-f]{32}$")
+        self.assertRegex(actual, "^[0-9a-f]{32}$")
 
         actual2 = data_utils.rand_uuid_hex()
         self.assertNotEqual(actual, actual2)
@@ -52,14 +52,14 @@
     def test_rand_name_with_prefix(self):
         actual = data_utils.rand_name(prefix='prefix-str')
         self.assertIsInstance(actual, str)
-        self.assertRegexpMatches(actual, "^prefix-str-")
+        self.assertRegex(actual, "^prefix-str-")
         actual2 = data_utils.rand_name(prefix='prefix-str')
         self.assertNotEqual(actual, actual2)
 
     def test_rand_password(self):
         actual = data_utils.rand_password()
         self.assertIsInstance(actual, str)
-        self.assertRegexpMatches(actual, "[A-Za-z0-9~!@#$%^&*_=+]{15,}")
+        self.assertRegex(actual, "[A-Za-z0-9~!@#$%^&*_=+]{15,}")
         actual2 = data_utils.rand_password()
         self.assertNotEqual(actual, actual2)
 
@@ -67,7 +67,7 @@
         actual = data_utils.rand_password(8)
         self.assertIsInstance(actual, str)
         self.assertEqual(len(actual), 8)
-        self.assertRegexpMatches(actual, "[A-Za-z0-9~!@#$%^&*_=+]{8}")
+        self.assertRegex(actual, "[A-Za-z0-9~!@#$%^&*_=+]{8}")
         actual2 = data_utils.rand_password(8)
         self.assertNotEqual(actual, actual2)
 
@@ -75,14 +75,14 @@
         actual = data_utils.rand_password(2)
         self.assertIsInstance(actual, str)
         self.assertEqual(len(actual), 3)
-        self.assertRegexpMatches(actual, "[A-Za-z0-9~!@#$%^&*_=+]{3}")
+        self.assertRegex(actual, "[A-Za-z0-9~!@#$%^&*_=+]{3}")
         actual2 = data_utils.rand_password(2)
         self.assertNotEqual(actual, actual2)
 
     def test_rand_url(self):
         actual = data_utils.rand_url()
         self.assertIsInstance(actual, str)
-        self.assertRegexpMatches(actual, "^https://url-[0-9]*\.com$")
+        self.assertRegex(actual, "^https://url-[0-9]*\.com$")
         actual2 = data_utils.rand_url()
         self.assertNotEqual(actual, actual2)
 
@@ -96,8 +96,8 @@
     def test_rand_mac_address(self):
         actual = data_utils.rand_mac_address()
         self.assertIsInstance(actual, str)
-        self.assertRegexpMatches(actual, "^([0-9a-f][0-9a-f]:){5}"
-                                         "[0-9a-f][0-9a-f]$")
+        self.assertRegex(actual, "^([0-9a-f][0-9a-f]:){5}"
+                         "[0-9a-f][0-9a-f]$")
 
         actual2 = data_utils.rand_mac_address()
         self.assertNotEqual(actual, actual2)
@@ -117,12 +117,12 @@
     def test_random_bytes(self):
         actual = data_utils.random_bytes()  # default size=1024
         self.assertIsInstance(actual, str)
-        self.assertRegexpMatches(actual, "^[\x00-\xFF]{1024}")
+        self.assertRegex(actual, "^[\x00-\xFF]{1024}")
         actual2 = data_utils.random_bytes()
         self.assertNotEqual(actual, actual2)
 
         actual = data_utils.random_bytes(size=2048)
-        self.assertRegexpMatches(actual, "^[\x00-\xFF]{2048}")
+        self.assertRegex(actual, "^[\x00-\xFF]{2048}")
 
     def test_get_ipv6_addr_by_EUI64(self):
         actual = data_utils.get_ipv6_addr_by_EUI64('2001:db8::',
diff --git a/tempest/tests/lib/fake_auth_provider.py b/tempest/tests/lib/fake_auth_provider.py
index 7f00fb8..8095453 100644
--- a/tempest/tests/lib/fake_auth_provider.py
+++ b/tempest/tests/lib/fake_auth_provider.py
@@ -16,15 +16,16 @@
 
 class FakeAuthProvider(object):
 
-    def __init__(self, creds_dict=None):
+    def __init__(self, creds_dict=None, fake_base_url=None):
         creds_dict = creds_dict or {}
         self.credentials = FakeCredentials(creds_dict)
+        self.fake_base_url = fake_base_url
 
     def auth_request(self, method, url, headers=None, body=None, filters=None):
         return url, headers, body
 
     def base_url(self, filters, auth_data=None):
-        return "https://example.com"
+        return self.fake_base_url or "https://example.com"
 
 
 class FakeCredentials(object):
diff --git a/tempest/tests/lib/fake_http.py b/tempest/tests/lib/fake_http.py
index eda202d..397c856 100644
--- a/tempest/tests/lib/fake_http.py
+++ b/tempest/tests/lib/fake_http.py
@@ -14,8 +14,6 @@
 
 import copy
 
-import httplib2
-
 
 class fake_httplib2(object):
 
@@ -25,7 +23,7 @@
     def request(self, uri, method="GET", body=None, headers=None,
                 redirections=5, connection_type=None):
         if not self.return_type:
-            fake_headers = httplib2.Response(headers)
+            fake_headers = fake_http_response(headers)
             return_obj = {
                 'uri': uri,
                 'method': method,
@@ -37,20 +35,20 @@
             body = body or "fake_body"
             header_info = {
                 'content-type': 'text/plain',
-                'status': str(self.return_type),
                 'content-length': len(body)
             }
-            resp_header = httplib2.Response(header_info)
+            resp_header = fake_http_response(header_info,
+                                             status=self.return_type)
             return (resp_header, body)
         else:
             msg = "unsupported return type %s" % self.return_type
             raise TypeError(msg)
 
 
-class fake_httplib(object):
+class fake_http_response(dict):
     def __init__(self, headers, body=None,
                  version=1.0, status=200, reason="Ok"):
-        """Fake httplib implementation
+        """Initialization of fake HTTP Response
 
         :param headers: dict representing HTTP response headers
         :param body: file-like object
@@ -60,10 +58,15 @@
         """
         self.body = body
         self.status = status
+        self['status'] = str(self.status)
         self.reason = reason
         self.version = version
         self.headers = headers
 
+        if headers:
+            for key, value in headers.items():
+                self[key.lower()] = value
+
     def getheaders(self):
         return copy.deepcopy(self.headers).items()
 
diff --git a/tempest/tests/lib/fake_identity.py b/tempest/tests/lib/fake_identity.py
index bac2676..5732065 100644
--- a/tempest/tests/lib/fake_identity.py
+++ b/tempest/tests/lib/fake_identity.py
@@ -13,9 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
-import httplib2
+from tempest.tests.lib import fake_http
 
 FAKE_AUTH_URL = 'http://fake_uri.com/auth'
 
@@ -139,16 +139,15 @@
 def _fake_v3_response(self, uri, method="GET", body=None, headers=None,
                       redirections=5, connection_type=None):
     fake_headers = {
-        "status": "201",
         "x-subject-token": TOKEN
     }
-    return (httplib2.Response(fake_headers),
+    return (fake_http.fake_http_response(fake_headers, status=201),
             json.dumps(IDENTITY_V3_RESPONSE))
 
 
 def _fake_v2_response(self, uri, method="GET", body=None, headers=None,
                       redirections=5, connection_type=None):
-    return (httplib2.Response({"status": "200"}),
+    return (fake_http.fake_http_response({}, status=200),
             json.dumps(IDENTITY_V2_RESPONSE))
 
 
@@ -161,4 +160,4 @@
             "code": "401"
         }
     }
-    return httplib2.Response({"status": "401"}), json.dumps(body)
+    return fake_http.fake_http_response({}, status=401), json.dumps(body)
diff --git a/tempest/tests/lib/services/compute/base.py b/tempest/tests/lib/services/compute/base.py
index 5602044..c805de2 100644
--- a/tempest/tests/lib/services/compute/base.py
+++ b/tempest/tests/lib/services/compute/base.py
@@ -12,11 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import httplib2
 from oslo_serialization import jsonutils as json
 from oslotest import mockpatch
 
 from tempest.tests.lib import base
+from tempest.tests.lib import fake_http
 
 
 class BaseComputeServiceTest(base.TestCase):
@@ -26,11 +26,8 @@
             json_body = json.dumps(body)
             if to_utf:
                 json_body = json_body.encode('utf-8')
-        resp_dict = {'status': status}
-        if headers:
-            resp_dict.update(headers)
-        response = (httplib2.Response(resp_dict), json_body)
-        return response
+        resp = fake_http.fake_http_response(headers, status=status), json_body
+        return resp
 
     def check_service_client_function(self, function, function2mock,
                                       body, to_utf=False, status=200,
diff --git a/tempest/tests/services/compute/test_base_compute_client.py b/tempest/tests/lib/services/compute/test_base_compute_client.py
similarity index 71%
rename from tempest/tests/services/compute/test_base_compute_client.py
rename to tempest/tests/lib/services/compute/test_base_compute_client.py
index 1a78247..49d29b3 100644
--- a/tempest/tests/services/compute/test_base_compute_client.py
+++ b/tempest/tests/lib/services/compute/test_base_compute_client.py
@@ -12,16 +12,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import httplib2
 import mock
-from oslotest import mockpatch
 
-from tempest.api.compute import api_microversion_fixture
-from tempest import exceptions
 from tempest.lib.common import rest_client
-from tempest.services.compute.json import base_compute_client
-from tempest.tests import fake_auth_provider
-from tempest.tests.services.compute import base
+from tempest.lib import exceptions
+from tempest.lib.services.compute import base_compute_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib import fake_http
+from tempest.tests.lib.services.compute import base
 
 
 class TestMicroversionHeaderCheck(base.BaseComputeServiceTest):
@@ -31,30 +29,35 @@
         fake_auth = fake_auth_provider.FakeAuthProvider()
         self.client = base_compute_client.BaseComputeClient(
             fake_auth, 'compute', 'regionOne')
-        self.useFixture(api_microversion_fixture.APIMicroversionFixture('2.2'))
+        base_compute_client.COMPUTE_MICROVERSION = '2.2'
 
-    def _check_microverion_header_in_response(self, fake_response):
-        def request(*args, **kwargs):
-            return (httplib2.Response(fake_response), {})
+    def tearDown(self):
+        super(TestMicroversionHeaderCheck, self).tearDown()
+        base_compute_client.COMPUTE_MICROVERSION = None
 
-        self.useFixture(mockpatch.PatchObject(
-            rest_client.RestClient,
-            'request',
-            side_effect=request))
-
-    def test_correct_microverion_in_response(self):
-        fake_response = {self.client.api_microversion_header_name: '2.2'}
-        self._check_microverion_header_in_response(fake_response)
+    @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+    def test_correct_microverion_in_response(self, mock_request):
+        response = fake_http.fake_http_response(
+            headers={self.client.api_microversion_header_name: '2.2'},
+        )
+        mock_request.return_value = response, ''
         self.client.get('fake_url')
 
-    def test_incorrect_microverion_in_response(self):
-        fake_response = {self.client.api_microversion_header_name: '2.3'}
-        self._check_microverion_header_in_response(fake_response)
+    @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+    def test_incorrect_microverion_in_response(self, mock_request):
+        response = fake_http.fake_http_response(
+            headers={self.client.api_microversion_header_name: '2.3'},
+        )
+        mock_request.return_value = response, ''
         self.assertRaises(exceptions.InvalidHTTPResponseHeader,
                           self.client.get, 'fake_url')
 
-    def test_no_microverion_header_in_response(self):
-        self._check_microverion_header_in_response({})
+    @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+    def test_no_microverion_header_in_response(self, mock_request):
+        response = fake_http.fake_http_response(
+            headers={},
+        )
+        mock_request.return_value = response, ''
         self.assertRaises(exceptions.InvalidHTTPResponseHeader,
                           self.client.get, 'fake_url')
 
@@ -77,8 +80,11 @@
         super(TestSchemaVersionsNone, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
         self.client = DummyServiceClient1(fake_auth, 'compute', 'regionOne')
-        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
-            self.api_microversion))
+        base_compute_client.COMPUTE_MICROVERSION = self.api_microversion
+
+    def tearDown(self):
+        super(TestSchemaVersionsNone, self).tearDown()
+        base_compute_client.COMPUTE_MICROVERSION = None
 
     def test_schema(self):
         self.assertEqual(self.expected_schema,
@@ -132,8 +138,11 @@
         super(TestSchemaVersionsNotFound, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
         self.client = DummyServiceClient2(fake_auth, 'compute', 'regionOne')
-        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
-            self.api_microversion))
+        base_compute_client.COMPUTE_MICROVERSION = self.api_microversion
+
+    def tearDown(self):
+        super(TestSchemaVersionsNotFound, self).tearDown()
+        base_compute_client.COMPUTE_MICROVERSION = None
 
     def test_schema(self):
         self.assertRaises(exceptions.JSONSchemaNotFound,
@@ -155,7 +164,7 @@
     def test_no_microverion_header_in_raw_request(self):
         def raw_request(*args, **kwargs):
             self.assertNotIn('X-OpenStack-Nova-API-Version', kwargs['headers'])
-            return (httplib2.Response({'status': 200}), {})
+            return (fake_http.fake_http_response({}, status=200), '')
 
         with mock.patch.object(rest_client.RestClient,
                                'raw_request') as mock_get:
@@ -170,7 +179,11 @@
         fake_auth = fake_auth_provider.FakeAuthProvider()
         self.client = base_compute_client.BaseComputeClient(
             fake_auth, 'compute', 'regionOne')
-        self.useFixture(api_microversion_fixture.APIMicroversionFixture('2.2'))
+        base_compute_client.COMPUTE_MICROVERSION = '2.2'
+
+    def tearDown(self):
+        super(TestClientWithMicroversionHeader, self).tearDown()
+        base_compute_client.COMPUTE_MICROVERSION = None
 
     def test_microverion_header(self):
         header = self.client.get_headers()
@@ -183,9 +196,9 @@
             self.assertIn('X-OpenStack-Nova-API-Version', kwargs['headers'])
             self.assertEqual('2.2',
                              kwargs['headers']['X-OpenStack-Nova-API-Version'])
-            return (httplib2.Response(
-                {'status': 200,
-                 self.client.api_microversion_header_name: '2.2'}), {})
+            return (fake_http.fake_http_response(
+                headers={self.client.api_microversion_header_name: '2.2'},
+                status=200), '')
 
         with mock.patch.object(rest_client.RestClient,
                                'raw_request') as mock_get:
diff --git a/tempest/tests/lib/services/compute/test_flavors_client.py b/tempest/tests/lib/services/compute/test_flavors_client.py
index 795aff7..e22b4fe 100644
--- a/tempest/tests/lib/services/compute/test_flavors_client.py
+++ b/tempest/tests/lib/services/compute/test_flavors_client.py
@@ -13,13 +13,13 @@
 #    under the License.
 
 import copy
-import httplib2
 
 from oslo_serialization import jsonutils as json
 from oslotest import mockpatch
 
 from tempest.lib.services.compute import flavors_client
 from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib import fake_http
 from tempest.tests.lib.services.compute import base
 
 
@@ -117,7 +117,7 @@
         body = json.dumps({'flavors': [TestFlavorsClient.FAKE_FLAVOR]})
         if bytes_body:
             body = body.encode('utf-8')
-        response = (httplib2.Response({'status': 200}), body)
+        response = fake_http.fake_http_response({}, status=200), body
         self.useFixture(mockpatch.Patch(
             'tempest.lib.common.rest_client.RestClient.get',
             return_value=response))
diff --git a/tempest/tests/lib/services/compute/test_server_groups_client.py b/tempest/tests/lib/services/compute/test_server_groups_client.py
index f1f2906..cb163a8 100644
--- a/tempest/tests/lib/services/compute/test_server_groups_client.py
+++ b/tempest/tests/lib/services/compute/test_server_groups_client.py
@@ -12,12 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import httplib2
-
 from oslotest import mockpatch
 from tempest.tests.lib import fake_auth_provider
 
 from tempest.lib.services.compute import server_groups_client
+from tempest.tests.lib import fake_http
 from tempest.tests.lib.services.compute import base
 
 
@@ -50,7 +49,7 @@
         self._test_create_server_group(bytes_body=True)
 
     def test_delete_server_group(self):
-        response = (httplib2.Response({'status': 204}), None)
+        response = fake_http.fake_http_response({}, status=204), ''
         self.useFixture(mockpatch.Patch(
             'tempest.lib.common.rest_client.RestClient.delete',
             return_value=response))
diff --git a/tempest/tests/lib/services/compute/test_versions_client.py b/tempest/tests/lib/services/compute/test_versions_client.py
index 5ac2f2d..fc6c1d2 100644
--- a/tempest/tests/lib/services/compute/test_versions_client.py
+++ b/tempest/tests/lib/services/compute/test_versions_client.py
@@ -94,3 +94,42 @@
 
     def test_get_version_by_url_with_bytes_body(self):
         self._test_get_version_by_url(bytes_body=True)
+
+    def _test_get_base_version_url(self, url, expected_base_url):
+        auth = fake_auth_provider.FakeAuthProvider(fake_base_url=url)
+        client = versions_client.VersionsClient(auth, 'compute', 'regionOne')
+        self.assertEqual(expected_base_url, client._get_base_version_url())
+
+    def test_get_base_version_url(self):
+        self._test_get_base_version_url('https://bar.org/v2/123',
+                                        'https://bar.org/')
+        self._test_get_base_version_url('https://bar.org/v2.1/123',
+                                        'https://bar.org/')
+        self._test_get_base_version_url('https://bar.org/v2.15/123',
+                                        'https://bar.org/')
+        self._test_get_base_version_url('https://bar.org/v22.2/123',
+                                        'https://bar.org/')
+        self._test_get_base_version_url('https://bar.org/v22/123',
+                                        'https://bar.org/')
+
+    def test_get_base_version_url_app_name(self):
+        self._test_get_base_version_url('https://bar.org/compute/v2/123',
+                                        'https://bar.org/compute/')
+        self._test_get_base_version_url('https://bar.org/compute/v2.1/123',
+                                        'https://bar.org/compute/')
+        self._test_get_base_version_url('https://bar.org/compute/v2.15/123',
+                                        'https://bar.org/compute/')
+        self._test_get_base_version_url('https://bar.org/compute/v22.2/123',
+                                        'https://bar.org/compute/')
+        self._test_get_base_version_url('https://bar.org/compute/v22/123',
+                                        'https://bar.org/compute/')
+
+    def test_get_base_version_url_double_slash(self):
+        self._test_get_base_version_url('https://bar.org//v2/123',
+                                        'https://bar.org/')
+        self._test_get_base_version_url('https://bar.org//v2.1/123',
+                                        'https://bar.org/')
+        self._test_get_base_version_url('https://bar.org/compute//v2/123',
+                                        'https://bar.org/compute/')
+        self._test_get_base_version_url('https://bar.org/compute//v2.1/123',
+                                        'https://bar.org/compute/')
diff --git a/tempest/tests/lib/services/identity/v2/test_token_client.py b/tempest/tests/lib/services/identity/v2/test_token_client.py
index dd3533a..d91ecdc 100644
--- a/tempest/tests/lib/services/identity/v2/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v2/test_token_client.py
@@ -14,8 +14,7 @@
 
 import json
 
-import httplib2
-from oslotest import mockpatch
+import mock
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions
@@ -28,7 +27,6 @@
 
     def setUp(self):
         super(TestTokenClientV2, self).setUp()
-        self.fake_200_http = fake_http.fake_httplib2(return_type=200)
 
     def test_init_without_authurl(self):
         self.assertRaises(exceptions.IdentityError,
@@ -36,10 +34,15 @@
 
     def test_auth(self):
         token_client_v2 = token_client.TokenClient('fake_url')
-        post_mock = self.useFixture(mockpatch.PatchObject(
-            token_client_v2, 'post', return_value=self.fake_200_http.request(
-                'fake_url', body={'access': {'token': 'fake_token'}})))
-        resp = token_client_v2.auth('fake_user', 'fake_pass')
+        response = fake_http.fake_http_response(
+            None, status=200,
+        )
+        body = {'access': {'token': 'fake_token'}}
+
+        with mock.patch.object(token_client_v2, 'post') as post_mock:
+            post_mock.return_value = response, body
+            resp = token_client_v2.auth('fake_user', 'fake_pass')
+
         self.assertIsInstance(resp, rest_client.ResponseBody)
         req_dict = json.dumps({
             'auth': {
@@ -49,15 +52,21 @@
                 },
             }
         }, sort_keys=True)
-        post_mock.mock.assert_called_once_with('fake_url/tokens',
-                                               body=req_dict)
+        post_mock.assert_called_once_with('fake_url/tokens',
+                                          body=req_dict)
 
     def test_auth_with_tenant(self):
         token_client_v2 = token_client.TokenClient('fake_url')
-        post_mock = self.useFixture(mockpatch.PatchObject(
-            token_client_v2, 'post', return_value=self.fake_200_http.request(
-                'fake_url', body={'access': {'token': 'fake_token'}})))
-        resp = token_client_v2.auth('fake_user', 'fake_pass', 'fake_tenant')
+        response = fake_http.fake_http_response(
+            None, status=200,
+        )
+        body = {'access': {'token': 'fake_token'}}
+
+        with mock.patch.object(token_client_v2, 'post') as post_mock:
+            post_mock.return_value = response, body
+            resp = token_client_v2.auth('fake_user', 'fake_pass',
+                                        'fake_tenant')
+
         self.assertIsInstance(resp, rest_client.ResponseBody)
         req_dict = json.dumps({
             'auth': {
@@ -68,25 +77,31 @@
                 },
             }
         }, sort_keys=True)
-        post_mock.mock.assert_called_once_with('fake_url/tokens',
-                                               body=req_dict)
+        post_mock.assert_called_once_with('fake_url/tokens',
+                                          body=req_dict)
 
     def test_request_with_str_body(self):
         token_client_v2 = token_client.TokenClient('fake_url')
-        self.useFixture(mockpatch.PatchObject(
-            token_client_v2, 'raw_request', return_value=(
-                httplib2.Response({'status': '200'}),
-                str('{"access": {"token": "fake_token"}}'))))
-        resp, body = token_client_v2.request('GET', 'fake_uri')
-        self.assertIsInstance(resp, httplib2.Response)
+        response = fake_http.fake_http_response(
+            None, status=200,
+        )
+        body = str('{"access": {"token": "fake_token"}}')
+
+        with mock.patch.object(token_client_v2, 'raw_request') as mock_raw_r:
+            mock_raw_r.return_value = response, body
+            resp, body = token_client_v2.request('GET', 'fake_uri')
         self.assertIsInstance(body, dict)
 
     def test_request_with_bytes_body(self):
         token_client_v2 = token_client.TokenClient('fake_url')
-        self.useFixture(mockpatch.PatchObject(
-            token_client_v2, 'raw_request', return_value=(
-                httplib2.Response({'status': '200'}),
-                bytes(b'{"access": {"token": "fake_token"}}'))))
-        resp, body = token_client_v2.request('GET', 'fake_uri')
-        self.assertIsInstance(resp, httplib2.Response)
+
+        response = fake_http.fake_http_response(
+            None, status=200,
+        )
+        body = b'{"access": {"token": "fake_token"}}'
+
+        with mock.patch.object(token_client_v2, 'raw_request') as mock_raw_r:
+            mock_raw_r.return_value = response, body
+            resp, body = token_client_v2.request('GET', 'fake_uri')
+
         self.assertIsInstance(body, dict)
diff --git a/tempest/tests/lib/services/identity/v3/test_token_client.py b/tempest/tests/lib/services/identity/v3/test_token_client.py
index bb4dae3..52b8e01 100644
--- a/tempest/tests/lib/services/identity/v3/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_token_client.py
@@ -14,8 +14,7 @@
 
 import json
 
-import httplib2
-from oslotest import mockpatch
+import mock
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions
@@ -24,11 +23,10 @@
 from tempest.tests.lib import fake_http
 
 
-class TestTokenClientV2(base.TestCase):
+class TestTokenClientV3(base.TestCase):
 
     def setUp(self):
-        super(TestTokenClientV2, self).setUp()
-        self.fake_201_http = fake_http.fake_httplib2(return_type=201)
+        super(TestTokenClientV3, self).setUp()
 
     def test_init_without_authurl(self):
         self.assertRaises(exceptions.IdentityError,
@@ -36,10 +34,16 @@
 
     def test_auth(self):
         token_client_v3 = token_client.V3TokenClient('fake_url')
-        post_mock = self.useFixture(mockpatch.PatchObject(
-            token_client_v3, 'post', return_value=self.fake_201_http.request(
-                'fake_url', body={'access': {'token': 'fake_token'}})))
-        resp = token_client_v3.auth(username='fake_user', password='fake_pass')
+        response = fake_http.fake_http_response(
+            None, status=201,
+        )
+        body = {'access': {'token': 'fake_token'}}
+
+        with mock.patch.object(token_client_v3, 'post') as post_mock:
+            post_mock.return_value = response, body
+            resp = token_client_v3.auth(username='fake_user',
+                                        password='fake_pass')
+
         self.assertIsInstance(resp, rest_client.ResponseBody)
         req_dict = json.dumps({
             'auth': {
@@ -54,19 +58,24 @@
                 },
             }
         }, sort_keys=True)
-        post_mock.mock.assert_called_once_with('fake_url/auth/tokens',
-                                               body=req_dict)
+        post_mock.assert_called_once_with('fake_url/auth/tokens',
+                                          body=req_dict)
 
     def test_auth_with_project_id_and_domain_id(self):
         token_client_v3 = token_client.V3TokenClient('fake_url')
-        post_mock = self.useFixture(mockpatch.PatchObject(
-            token_client_v3, 'post', return_value=self.fake_201_http.request(
-                'fake_url', body={'access': {'token': 'fake_token'}})))
-        resp = token_client_v3.auth(
-            username='fake_user', password='fake_pass',
-            project_id='fcac2a055a294e4c82d0a9c21c620eb4',
-            user_domain_id='14f4a9a99973404d8c20ba1d2af163ff',
-            project_domain_id='291f63ae9ac54ee292ca09e5f72d9676')
+        response = fake_http.fake_http_response(
+            None, status=201,
+        )
+        body = {'access': {'token': 'fake_token'}}
+
+        with mock.patch.object(token_client_v3, 'post') as post_mock:
+            post_mock.return_value = response, body
+            resp = token_client_v3.auth(
+                username='fake_user', password='fake_pass',
+                project_id='fcac2a055a294e4c82d0a9c21c620eb4',
+                user_domain_id='14f4a9a99973404d8c20ba1d2af163ff',
+                project_domain_id='291f63ae9ac54ee292ca09e5f72d9676')
+
         self.assertIsInstance(resp, rest_client.ResponseBody)
         req_dict = json.dumps({
             'auth': {
@@ -92,16 +101,22 @@
                 }
             }
         }, sort_keys=True)
-        post_mock.mock.assert_called_once_with('fake_url/auth/tokens',
-                                               body=req_dict)
+        post_mock.assert_called_once_with('fake_url/auth/tokens',
+                                          body=req_dict)
 
     def test_auth_with_tenant(self):
-        token_client_v2 = token_client.V3TokenClient('fake_url')
-        post_mock = self.useFixture(mockpatch.PatchObject(
-            token_client_v2, 'post', return_value=self.fake_201_http.request(
-                'fake_url', body={'access': {'token': 'fake_token'}})))
-        resp = token_client_v2.auth(username='fake_user', password='fake_pass',
-                                    project_name='fake_tenant')
+        token_client_v3 = token_client.V3TokenClient('fake_url')
+        response = fake_http.fake_http_response(
+            None, status=201,
+        )
+        body = {'access': {'token': 'fake_token'}}
+
+        with mock.patch.object(token_client_v3, 'post') as post_mock:
+            post_mock.return_value = response, body
+            resp = token_client_v3.auth(username='fake_user',
+                                        password='fake_pass',
+                                        project_name='fake_tenant')
+
         self.assertIsInstance(resp, rest_client.ResponseBody)
         req_dict = json.dumps({
             'auth': {
@@ -121,25 +136,32 @@
             }
         }, sort_keys=True)
 
-        post_mock.mock.assert_called_once_with('fake_url/auth/tokens',
-                                               body=req_dict)
+        post_mock.assert_called_once_with('fake_url/auth/tokens',
+                                          body=req_dict)
 
     def test_request_with_str_body(self):
         token_client_v3 = token_client.V3TokenClient('fake_url')
-        self.useFixture(mockpatch.PatchObject(
-            token_client_v3, 'raw_request', return_value=(
-                httplib2.Response({"status": "200"}),
-                str('{"access": {"token": "fake_token"}}'))))
-        resp, body = token_client_v3.request('GET', 'fake_uri')
-        self.assertIsInstance(resp, httplib2.Response)
+        response = fake_http.fake_http_response(
+            None, status=200,
+        )
+        body = str('{"access": {"token": "fake_token"}}')
+
+        with mock.patch.object(token_client_v3, 'raw_request') as mock_raw_r:
+            mock_raw_r.return_value = response, body
+            resp, body = token_client_v3.request('GET', 'fake_uri')
+
         self.assertIsInstance(body, dict)
 
     def test_request_with_bytes_body(self):
         token_client_v3 = token_client.V3TokenClient('fake_url')
-        self.useFixture(mockpatch.PatchObject(
-            token_client_v3, 'raw_request', return_value=(
-                httplib2.Response({"status": "200"}),
-                bytes(b'{"access": {"token": "fake_token"}}'))))
-        resp, body = token_client_v3.request('GET', 'fake_uri')
-        self.assertIsInstance(resp, httplib2.Response)
+
+        response = fake_http.fake_http_response(
+            None, status=200,
+        )
+        body = b'{"access": {"token": "fake_token"}}'
+
+        with mock.patch.object(token_client_v3, 'raw_request') as mock_raw_r:
+            mock_raw_r.return_value = response, body
+            resp, body = token_client_v3.request('GET', 'fake_uri')
+
         self.assertIsInstance(body, dict)
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
index f7bc7e4..5502c01 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -24,7 +24,6 @@
 from tempest.lib.services.identity.v3 import token_client as v3_client
 from tempest.tests.lib import base
 from tempest.tests.lib import fake_credentials
-from tempest.tests.lib import fake_http
 from tempest.tests.lib import fake_identity
 
 
@@ -42,7 +41,6 @@
 
     def setUp(self):
         super(BaseAuthTestsSetUp, self).setUp()
-        self.fake_http = fake_http.fake_httplib2(return_type=200)
         self.stubs.Set(auth, 'get_credentials', fake_get_credentials)
         self.auth_provider = self._auth(self.credentials,
                                         fake_identity.FAKE_AUTH_URL)
@@ -251,7 +249,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 = {
@@ -382,6 +380,31 @@
         expected = 'http://fake_url/'
         self._test_base_url_helper(expected, self.filters)
 
+    def test_base_url_with_unversioned_endpoint(self):
+        auth_data = {
+            'serviceCatalog': [
+                {
+                    'type': 'identity',
+                    'endpoints': [
+                        {
+                            'region': 'FakeRegion',
+                            'publicURL': 'http://fake_url'
+                        }
+                    ]
+                }
+            ]
+        }
+
+        filters = {
+            'service': 'identity',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'api_version': 'v2.0'
+        }
+
+        expected = 'http://fake_url/v2.0'
+        self._test_base_url_helper(expected, filters, ('token', auth_data))
+
     def test_token_not_expired(self):
         expiry_data = datetime.datetime.utcnow() + datetime.timedelta(days=1)
         self._verify_expiry(expiry_data=expiry_data, should_be_expired=False)
@@ -478,3 +501,132 @@
         expected = self._get_result_url_from_endpoint(
             self._endpoints[0]['endpoints'][2])
         self._test_base_url_helper(expected, self.filters)
+
+    # Overwrites v2 test
+    def test_base_url_with_unversioned_endpoint(self):
+        auth_data = {
+            'catalog': [
+                {
+                    'type': 'identity',
+                    'endpoints': [
+                        {
+                            'region': 'FakeRegion',
+                            'url': 'http://fake_url',
+                            'interface': 'public'
+                        }
+                    ]
+                }
+            ]
+        }
+
+        filters = {
+            'service': 'identity',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'api_version': 'v3'
+        }
+
+        expected = 'http://fake_url/v3'
+        self._test_base_url_helper(expected, filters, ('token', auth_data))
+
+
+class TestKeystoneV3Credentials(base.TestCase):
+    def testSetAttrUserDomain(self):
+        creds = auth.KeystoneV3Credentials()
+        creds.user_domain_name = 'user_domain'
+        creds.domain_name = 'domain'
+        self.assertEqual('user_domain', creds.user_domain_name)
+        creds = auth.KeystoneV3Credentials()
+        creds.domain_name = 'domain'
+        creds.user_domain_name = 'user_domain'
+        self.assertEqual('user_domain', creds.user_domain_name)
+
+    def testSetAttrProjectDomain(self):
+        creds = auth.KeystoneV3Credentials()
+        creds.project_domain_name = 'project_domain'
+        creds.domain_name = 'domain'
+        self.assertEqual('project_domain', creds.user_domain_name)
+        creds = auth.KeystoneV3Credentials()
+        creds.domain_name = 'domain'
+        creds.project_domain_name = 'project_domain'
+        self.assertEqual('project_domain', creds.project_domain_name)
+
+    def testProjectTenantNoCollision(self):
+        creds = auth.KeystoneV3Credentials(tenant_id='tenant')
+        self.assertEqual('tenant', creds.project_id)
+        creds = auth.KeystoneV3Credentials(project_id='project')
+        self.assertEqual('project', creds.tenant_id)
+        creds = auth.KeystoneV3Credentials(tenant_name='tenant')
+        self.assertEqual('tenant', creds.project_name)
+        creds = auth.KeystoneV3Credentials(project_name='project')
+        self.assertEqual('project', creds.tenant_name)
+
+    def testProjectTenantCollision(self):
+        attrs = {'tenant_id': 'tenant', 'project_id': 'project'}
+        self.assertRaises(
+            exceptions.InvalidCredentials, auth.KeystoneV3Credentials, **attrs)
+        attrs = {'tenant_name': 'tenant', 'project_name': 'project'}
+        self.assertRaises(
+            exceptions.InvalidCredentials, auth.KeystoneV3Credentials, **attrs)
+
+
+class TestReplaceVersion(base.TestCase):
+    def test_version_no_trailing_path(self):
+        self.assertEqual(
+            'http://localhost:35357/v2.0',
+            auth.replace_version('http://localhost:35357/v3', 'v2.0'))
+
+    def test_version_no_trailing_path_solidus(self):
+        self.assertEqual(
+            'http://localhost:35357/v2.0/',
+            auth.replace_version('http://localhost:35357/v3/', 'v2.0'))
+
+    def test_version_trailing_path(self):
+        self.assertEqual(
+            'http://localhost:35357/v2.0/uuid',
+            auth.replace_version('http://localhost:35357/v3/uuid', 'v2.0'))
+
+    def test_version_trailing_path_solidus(self):
+        self.assertEqual(
+            'http://localhost:35357/v2.0/uuid/',
+            auth.replace_version('http://localhost:35357/v3/uuid/', 'v2.0'))
+
+    def test_no_version_base(self):
+        self.assertEqual(
+            'http://localhost:35357/v2.0',
+            auth.replace_version('http://localhost:35357', 'v2.0'))
+
+    def test_no_version_base_solidus(self):
+        self.assertEqual(
+            'http://localhost:35357/v2.0',
+            auth.replace_version('http://localhost:35357/', 'v2.0'))
+
+    def test_no_version_path(self):
+        self.assertEqual(
+            'http://localhost/identity/v2.0',
+            auth.replace_version('http://localhost/identity', 'v2.0'))
+
+    def test_no_version_path_solidus(self):
+        self.assertEqual(
+            'http://localhost/identity/v2.0',
+            auth.replace_version('http://localhost/identity/', 'v2.0'))
+
+    def test_path_version(self):
+        self.assertEqual(
+            'http://localhost/identity/v2.0',
+            auth.replace_version('http://localhost/identity/v3', 'v2.0'))
+
+    def test_path_version_solidus(self):
+        self.assertEqual(
+            'http://localhost/identity/v2.0/',
+            auth.replace_version('http://localhost/identity/v3/', 'v2.0'))
+
+    def test_path_version_trailing_path(self):
+        self.assertEqual(
+            'http://localhost/identity/v2.0/uuid',
+            auth.replace_version('http://localhost/identity/v3/uuid', 'v2.0'))
+
+    def test_path_version_trailing_path_solidus(self):
+        self.assertEqual(
+            'http://localhost/identity/v2.0/uuid/',
+            auth.replace_version('http://localhost/identity/v3/uuid/', 'v2.0'))
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/lib/test_rest_client.py b/tempest/tests/lib/test_rest_client.py
index 6aff305..90651b0 100644
--- a/tempest/tests/lib/test_rest_client.py
+++ b/tempest/tests/lib/test_rest_client.py
@@ -15,16 +15,17 @@
 import copy
 import json
 
-import httplib2
 import jsonschema
 from oslotest import mockpatch
 import six
 
+from tempest.lib.common import http
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions
 from tempest.tests.lib import base
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib import fake_http
+import tempest.tests.utils as utils
 
 
 class BaseRestClientTestClass(base.TestCase):
@@ -36,7 +37,9 @@
         self.fake_auth_provider = fake_auth_provider.FakeAuthProvider()
         self.rest_client = rest_client.RestClient(
             self.fake_auth_provider, None, None)
-        self.stubs.Set(httplib2.Http, 'request', self.fake_http.request)
+        self.stubs.Set(http.ClosingHttp, 'request',
+                       self.fake_http.request)
+
         self.useFixture(mockpatch.PatchObject(self.rest_client,
                                               '_log_request'))
 
@@ -291,7 +294,9 @@
         if absolute_limit is False:
             resp_dict.update({'retry-after': 120})
             resp_body.update({'overLimit': {'message': 'fake_message'}})
-        resp = httplib2.Response(resp_dict)
+        resp = fake_http.fake_http_response(headers=resp_dict,
+                                            status=int(r_code),
+                                            body=json.dumps(resp_body))
         data = {
             "method": "fake_method",
             "url": "fake_url",
@@ -511,11 +516,20 @@
     def test_wait_for_resource_deletion_not_deleted(self):
         self.patch('time.sleep')
         # Set timeout to be very quick to force exception faster
-        self.rest_client.build_timeout = 1
+        timeout = 1
+        self.rest_client.build_timeout = timeout
+
+        time_mock = self.patch('time.time')
+        time_mock.side_effect = utils.generate_timeout_series(timeout)
+
         self.assertRaises(exceptions.TimeoutException,
                           self.rest_client.wait_for_resource_deletion,
                           '1234')
 
+        # time.time() should be called twice, first to start the timer
+        # and then to compute the timedelta
+        self.assertEqual(2, time_mock.call_count)
+
     def test_wait_for_deletion_with_unimplemented_deleted_method(self):
         self.rest_client.is_resource_deleted = self.original_deleted_method
         self.assertRaises(NotImplementedError,
diff --git a/tempest/tests/lib/test_ssh.py b/tempest/tests/lib/test_ssh.py
index 7a4fc09..f6efd47 100644
--- a/tempest/tests/lib/test_ssh.py
+++ b/tempest/tests/lib/test_ssh.py
@@ -14,7 +14,6 @@
 
 from io import StringIO
 import socket
-import time
 
 import mock
 import six
@@ -23,6 +22,7 @@
 from tempest.lib.common import ssh
 from tempest.lib import exceptions
 from tempest.tests.lib import base
+import tempest.tests.utils as utils
 
 
 class TestSshClient(base.TestCase):
@@ -79,7 +79,8 @@
         self.assertEqual(expected_connect, client_mock.connect.mock_calls)
         self.assertEqual(0, s_mock.call_count)
 
-    def test_get_ssh_connection_two_attemps(self):
+    @mock.patch('time.sleep')
+    def test_get_ssh_connection_two_attemps(self, sleep_mock):
         c_mock, aa_mock, client_mock = self._set_ssh_connection_mocks()
 
         c_mock.return_value = client_mock
@@ -89,15 +90,18 @@
         ]
 
         client = ssh.Client('localhost', 'root', timeout=1)
-        start_time = int(time.time())
         client._get_ssh_connection(sleep=1)
-        end_time = int(time.time())
-        self.assertLess((end_time - start_time), 4)
-        self.assertGreater((end_time - start_time), 1)
+        # We slept 2 seconds: because sleep is "1" and backoff is "1" too
+        sleep_mock.assert_called_once_with(2)
+        self.assertEqual(2, client_mock.connect.call_count)
 
     def test_get_ssh_connection_timeout(self):
         c_mock, aa_mock, client_mock = self._set_ssh_connection_mocks()
 
+        timeout = 2
+        time_mock = self.patch('time.time')
+        time_mock.side_effect = utils.generate_timeout_series(timeout + 1)
+
         c_mock.return_value = client_mock
         client_mock.connect.side_effect = [
             socket.error,
@@ -105,13 +109,16 @@
             socket.error,
         ]
 
-        client = ssh.Client('localhost', 'root', timeout=2)
-        start_time = int(time.time())
-        with testtools.ExpectedException(exceptions.SSHTimeout):
-            client._get_ssh_connection()
-        end_time = int(time.time())
-        self.assertLess((end_time - start_time), 5)
-        self.assertGreaterEqual((end_time - start_time), 2)
+        client = ssh.Client('localhost', 'root', timeout=timeout)
+        # We need to mock LOG here because LOG.info() calls time.time()
+        # in order to preprend a timestamp.
+        with mock.patch.object(ssh, 'LOG'):
+            self.assertRaises(exceptions.SSHTimeout,
+                              client._get_ssh_connection)
+
+        # time.time() should be called twice, first to start the timer
+        # and then to compute the timedelta
+        self.assertEqual(2, time_mock.call_count)
 
     @mock.patch('select.POLLIN', SELECT_POLLIN, create=True)
     def test_timeout_in_exec_command(self):
diff --git a/tempest/tests/negative/test_negative_auto_test.py b/tempest/tests/negative/test_negative_auto_test.py
index 7a127cd..c666bd3 100644
--- a/tempest/tests/negative/test_negative_auto_test.py
+++ b/tempest/tests/negative/test_negative_auto_test.py
@@ -15,8 +15,8 @@
 
 from tempest import config
 import tempest.test as test
-from tempest.tests import base
 from tempest.tests import fake_config
+from tempest.tests.lib import base
 
 
 class TestNegativeAutoTest(base.TestCase):
diff --git a/tempest/tests/negative/test_negative_generators.py b/tempest/tests/negative/test_negative_generators.py
index 78fd80d..e0d7f42 100644
--- a/tempest/tests/negative/test_negative_generators.py
+++ b/tempest/tests/negative/test_negative_generators.py
@@ -22,7 +22,7 @@
 from tempest.common.generator import base_generator
 from tempest.common.generator import negative_generator
 from tempest.common.generator import valid_generator
-from tempest.tests import base
+from tempest.tests.lib import base
 
 
 class TestNegativeBasicGenerator(base.TestCase):
diff --git a/tempest/tests/services/compute/base.py b/tempest/tests/services/compute/base.py
deleted file mode 100644
index a35a87c..0000000
--- a/tempest/tests/services/compute/base.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2015 Deutsche Telekom AG.  All rights reserved.
-#
-#    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 httplib2
-
-from oslo_serialization import jsonutils as json
-from oslotest import mockpatch
-
-from tempest.tests import base
-
-
-class BaseComputeServiceTest(base.TestCase):
-    def create_response(self, body, to_utf=False, status=200):
-        json_body = {}
-        if body:
-            json_body = json.dumps(body)
-            if to_utf:
-                json_body = json_body.encode('utf-8')
-        response = (httplib2.Response({'status': status}), json_body)
-        return response
-
-    def check_service_client_function(self, function, function2mock,
-                                      body, to_utf=False, status=200,
-                                      **kwargs):
-        mocked_response = self.create_response(body, to_utf, status)
-        self.useFixture(mockpatch.Patch(
-            function2mock, return_value=mocked_response))
-        if kwargs:
-            resp = function(**kwargs)
-        else:
-            resp = function()
-        self.assertEqual(body, resp)
diff --git a/tempest/tests/services/compute/test_keypairs_client.py b/tempest/tests/services/compute/test_keypairs_client.py
deleted file mode 100644
index e8f8280..0000000
--- a/tempest/tests/services/compute/test_keypairs_client.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright 2015 NEC Corporation.  All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import copy
-
-from tempest.services.compute.json import keypairs_client
-from tempest.tests.lib import fake_auth_provider
-from tempest.tests.services.compute import base
-
-
-class TestKeyPairsClient(base.BaseComputeServiceTest):
-
-    FAKE_KEYPAIR = {"keypair": {
-        "public_key": "ssh-rsa foo Generated-by-Nova",
-        "name": u'\u2740(*\xb4\u25e1`*)\u2740',
-        "user_id": "525d55f98980415ba98e634972fa4a10",
-        "fingerprint": "76:24:66:49:d7:ca:6e:5c:77:ea:8e:bb:9c:15:5f:98"
-        }}
-
-    def setUp(self):
-        super(TestKeyPairsClient, self).setUp()
-        fake_auth = fake_auth_provider.FakeAuthProvider()
-        self.client = keypairs_client.KeyPairsClient(
-            fake_auth, 'compute', 'regionOne')
-
-    def _test_list_keypairs(self, bytes_body=False):
-        self.check_service_client_function(
-            self.client.list_keypairs,
-            'tempest.lib.common.rest_client.RestClient.get',
-            {"keypairs": []},
-            bytes_body)
-
-    def test_list_keypairs_with_str_body(self):
-        self._test_list_keypairs()
-
-    def test_list_keypairs_with_bytes_body(self):
-        self._test_list_keypairs(bytes_body=True)
-
-    def _test_show_keypair(self, bytes_body=False):
-        fake_keypair = copy.deepcopy(self.FAKE_KEYPAIR)
-        fake_keypair["keypair"].update({
-            "deleted": False,
-            "created_at": "2015-07-22T04:53:52.000000",
-            "updated_at": None,
-            "deleted_at": None,
-            "id": 1
-            })
-
-        self.check_service_client_function(
-            self.client.show_keypair,
-            'tempest.lib.common.rest_client.RestClient.get',
-            fake_keypair,
-            bytes_body,
-            keypair_name="test")
-
-    def test_show_keypair_with_str_body(self):
-        self._test_show_keypair()
-
-    def test_show_keypair_with_bytes_body(self):
-        self._test_show_keypair(bytes_body=True)
-
-    def _test_create_keypair(self, bytes_body=False):
-        fake_keypair = copy.deepcopy(self.FAKE_KEYPAIR)
-        fake_keypair["keypair"].update({"private_key": "foo"})
-
-        self.check_service_client_function(
-            self.client.create_keypair,
-            'tempest.lib.common.rest_client.RestClient.post',
-            fake_keypair,
-            bytes_body,
-            name="test")
-
-    def test_create_keypair_with_str_body(self):
-        self._test_create_keypair()
-
-    def test_create_keypair_with_bytes_body(self):
-        self._test_create_keypair(bytes_body=True)
-
-    def test_delete_keypair(self):
-        self.check_service_client_function(
-            self.client.delete_keypair,
-            'tempest.lib.common.rest_client.RestClient.delete',
-            {}, status=202, keypair_name='test')
diff --git a/tempest/tests/stress/test_stress.py b/tempest/tests/stress/test_stress.py
index dfe0291..a35b4d7 100644
--- a/tempest/tests/stress/test_stress.py
+++ b/tempest/tests/stress/test_stress.py
@@ -18,7 +18,7 @@
 
 from oslo_log import log as logging
 from tempest.lib import exceptions
-from tempest.tests import base
+from tempest.tests.lib import base
 
 LOG = logging.getLogger(__name__)
 
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
new file mode 100644
index 0000000..9ffb7a1
--- /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 fake_config
+from tempest.tests.lib import base
+
+
+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)
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index 98b045a..4c9a3b7 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -22,8 +22,8 @@
 from tempest import config
 from tempest import exceptions
 from tempest import test
-from tempest.tests import base
 from tempest.tests import fake_config
+from tempest.tests.lib import base
 
 
 class BaseDecoratorsTest(base.TestCase):
@@ -201,7 +201,13 @@
         if expected_to_skip:
             self.assertRaises(testtools.TestCase.skipException, t.test_bar)
         else:
-            self.assertEqual(t.test_bar(), 0)
+            try:
+                self.assertEqual(t.test_bar(), 0)
+            except testtools.TestCase.skipException:
+                # We caught a skipException but we didn't expect to skip
+                # this test so raise a hard test failure instead.
+                raise testtools.TestCase.failureException(
+                    "Not supposed to skip")
 
     def test_requires_ext_decorator(self):
         self._test_requires_ext_helper(expected_to_skip=False,
@@ -213,7 +219,7 @@
                                        service='compute')
 
     def test_requires_ext_decorator_with_all_ext_enabled(self):
-        cfg.CONF.set_default('api_extensions', 'all',
+        cfg.CONF.set_default('api_extensions', ['all'],
                              group='compute-feature-enabled')
         self._test_requires_ext_helper(expected_to_skip=False,
                                        extension='random_ext',
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
index db9db34..fdbc2d2 100644
--- a/tempest/tests/test_glance_http.py
+++ b/tempest/tests/test_glance_http.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import socket
+
 import mock
 from oslotest import mockpatch
 import six
@@ -20,20 +22,15 @@
 
 from tempest.common import glance_http
 from tempest import exceptions
-from tempest.tests import base
 from tempest.tests import fake_auth_provider
-from tempest.tests import fake_http
+from tempest.tests.lib import base
+from tempest.tests.lib import fake_http
 
 
 class TestGlanceHTTPClient(base.TestCase):
 
     def setUp(self):
         super(TestGlanceHTTPClient, self).setUp()
-        self.fake_http = fake_http.fake_httplib2(return_type=200)
-        # NOTE(maurosr): using http here implies that we will be using httplib
-        # directly. With https glance_client would use an httpS version, but
-        # the real backend would still be httplib anyway and since we mock it
-        # that there is no reason to care.
         self.endpoint = 'http://fake_url.com'
         self.fake_auth = fake_auth_provider.FakeAuthProvider()
 
@@ -42,12 +39,12 @@
         self.useFixture(mockpatch.PatchObject(
             httplib.HTTPConnection,
             'request',
-            side_effect=self.fake_http.request(self.endpoint)[1]))
+            side_effect=b'fake_body'))
         self.client = glance_http.HTTPClient(self.fake_auth, {})
 
     def _set_response_fixture(self, header, status, resp_body):
-        resp = fake_http.fake_httplib(header, status=status,
-                                      body=six.StringIO(resp_body))
+        resp = fake_http.fake_http_response(header, status=status,
+                                            body=six.StringIO(resp_body))
         self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
                         'getresponse', return_value=resp))
         return resp
@@ -91,15 +88,29 @@
         self.assertEqual(httplib.HTTPConnection, conn_class)
 
     def test_get_connection_http(self):
-        self.assertTrue(isinstance(self.client._get_connection(),
-                                   httplib.HTTPConnection))
+        self.assertIsInstance(self.client._get_connection(),
+                              httplib.HTTPConnection)
 
     def test_get_connection_https(self):
         endpoint = 'https://fake_url.com'
         self.fake_auth.base_url = mock.MagicMock(return_value=endpoint)
         self.client = glance_http.HTTPClient(self.fake_auth, {})
-        self.assertTrue(isinstance(self.client._get_connection(),
-                                   glance_http.VerifiedHTTPSConnection))
+        self.assertIsInstance(self.client._get_connection(),
+                              glance_http.VerifiedHTTPSConnection)
+
+    def test_get_connection_ipv4_https(self):
+        endpoint = 'https://127.0.0.1'
+        self.fake_auth.base_url = mock.MagicMock(return_value=endpoint)
+        self.client = glance_http.HTTPClient(self.fake_auth, {})
+        self.assertIsInstance(self.client._get_connection(),
+                              glance_http.VerifiedHTTPSConnection)
+
+    def test_get_connection_ipv6_https(self):
+        endpoint = 'https://[::1]'
+        self.fake_auth.base_url = mock.MagicMock(return_value=endpoint)
+        self.client = glance_http.HTTPClient(self.fake_auth, {})
+        self.assertIsInstance(self.client._get_connection(),
+                              glance_http.VerifiedHTTPSConnection)
 
     def test_get_connection_url_not_fount(self):
         self.useFixture(mockpatch.PatchObject(self.client, 'connection_class',
@@ -146,10 +157,68 @@
         self.assertEqual(6, len(kwargs.keys()))
 
 
+class TestVerifiedHTTPSConnection(base.TestCase):
+
+    @mock.patch('socket.socket')
+    @mock.patch('tempest.common.glance_http.OpenSSLConnectionDelegator')
+    def test_connect_ipv4(self, mock_delegator, mock_socket):
+        connection = glance_http.VerifiedHTTPSConnection('127.0.0.1')
+        connection.connect()
+
+        mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM)
+        mock_delegator.assert_called_once_with(connection.context,
+                                               mock_socket.return_value)
+        mock_delegator.return_value.connect.assert_called_once_with(
+            (connection.host, 443))
+
+    @mock.patch('socket.socket')
+    @mock.patch('tempest.common.glance_http.OpenSSLConnectionDelegator')
+    def test_connect_ipv6(self, mock_delegator, mock_socket):
+        connection = glance_http.VerifiedHTTPSConnection('[::1]')
+        connection.connect()
+
+        mock_socket.assert_called_once_with(socket.AF_INET6,
+                                            socket.SOCK_STREAM)
+        mock_delegator.assert_called_once_with(connection.context,
+                                               mock_socket.return_value)
+        mock_delegator.return_value.connect.assert_called_once_with(
+            (connection.host, 443, 0, 0))
+
+    @mock.patch('tempest.common.glance_http.OpenSSLConnectionDelegator')
+    @mock.patch('socket.getaddrinfo',
+                side_effect=OSError('Gettaddrinfo failed'))
+    def test_connect_with_address_lookup_failure(self, mock_getaddrinfo,
+                                                 mock_delegator):
+        connection = glance_http.VerifiedHTTPSConnection('127.0.0.1')
+        self.assertRaises(exceptions.RestClientException, connection.connect)
+
+        mock_getaddrinfo.assert_called_once_with(
+            connection.host, connection.port, 0, socket.SOCK_STREAM)
+
+    @mock.patch('socket.socket')
+    @mock.patch('socket.getaddrinfo',
+                return_value=[(2, 1, 6, '', ('127.0.0.1', 443))])
+    @mock.patch('tempest.common.glance_http.OpenSSLConnectionDelegator')
+    def test_connect_with_socket_failure(self, mock_delegator,
+                                         mock_getaddrinfo,
+                                         mock_socket):
+        mock_delegator.return_value.connect.side_effect = \
+            OSError('Connect failed')
+
+        connection = glance_http.VerifiedHTTPSConnection('127.0.0.1')
+        self.assertRaises(exceptions.RestClientException, connection.connect)
+
+        mock_getaddrinfo.assert_called_once_with(
+            connection.host, connection.port, 0, socket.SOCK_STREAM)
+        mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM)
+        mock_delegator.return_value.connect.\
+            assert_called_once_with((connection.host, 443))
+
+
 class TestResponseBodyIterator(base.TestCase):
 
     def test_iter_default_chunk_size_64k(self):
-        resp = fake_http.fake_httplib({}, six.StringIO(
+        resp = fake_http.fake_http_response({}, six.StringIO(
             'X' * (glance_http.CHUNKSIZE + 1)))
         iterator = glance_http.ResponseBodyIterator(resp)
         chunks = list(iterator)
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index 55f00ef..6b3aa0d 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -13,7 +13,7 @@
 #    under the License.
 
 from tempest.hacking import checks
-from tempest.tests import base
+from tempest.tests.lib import base
 
 
 class HackingTestCase(base.TestCase):
@@ -147,3 +147,23 @@
             " @testtools.skipUnless(CONF.something, 'msg')"))))
         self.assertEqual(0, len(list(checks.no_testtools_skip_decorator(
             " @testtools.skipIf(CONF.something, 'msg')"))))
+
+    def test_dont_import_local_tempest_code_into_lib(self):
+        self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
+            "from tempest.common import waiters",
+            './tempest/common/compute.py'))))
+        self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
+            "from tempest import config",
+            './tempest/common/compute.py'))))
+        self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
+            "import tempest.exception",
+            './tempest/common/compute.py'))))
+        self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
+            "from tempest.common import waiters",
+            './tempest/lib/common/compute.py'))))
+        self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
+            "from tempest import config",
+            './tempest/lib/common/compute.py'))))
+        self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
+            "import tempest.exception",
+            './tempest/lib/common/compute.py'))))
diff --git a/tempest/tests/test_list_tests.py b/tempest/tests/test_list_tests.py
index 38d4c5c..69527b1 100644
--- a/tempest/tests/test_list_tests.py
+++ b/tempest/tests/test_list_tests.py
@@ -17,7 +17,7 @@
 import six
 import subprocess
 
-from tempest.tests import base
+from tempest.tests.lib import base
 
 
 class TestTestList(base.TestCase):
diff --git a/tempest/tests/test_microversions.py b/tempest/tests/test_microversions.py
index 6738641..1ac1232 100644
--- a/tempest/tests/test_microversions.py
+++ b/tempest/tests/test_microversions.py
@@ -17,9 +17,9 @@
 
 from tempest.api.compute import base as compute_base
 from tempest import config
-from tempest import exceptions
-from tempest.tests import base
+from tempest.lib import exceptions
 from tempest.tests import fake_config
+from tempest.tests.lib import base
 
 
 class VersionTestNoneTolatest(compute_base.BaseV2ComputeTest):
@@ -64,9 +64,9 @@
                       expected_pass_tests,
                       expected_skip_tests):
         cfg.CONF.set_default('min_microversion',
-                             cfg_min, group='compute-feature-enabled')
+                             cfg_min, group='compute')
         cfg.CONF.set_default('max_microversion',
-                             cfg_max, group='compute-feature-enabled')
+                             cfg_max, group='compute')
         try:
             for test_class in expected_pass_tests:
                 test_class.skip_checks()
@@ -138,16 +138,16 @@
 
     def test_config_invalid_version(self):
         cfg.CONF.set_default('min_microversion',
-                             '2.5', group='compute-feature-enabled')
+                             '2.5', group='compute')
         cfg.CONF.set_default('max_microversion',
-                             '2.1', group='compute-feature-enabled')
+                             '2.1', group='compute')
         self.assertRaises(exceptions.InvalidAPIVersionRange,
                           VersionTestNoneTolatest.skip_checks)
 
     def test_config_version_invalid_test_version(self):
         cfg.CONF.set_default('min_microversion',
-                             None, group='compute-feature-enabled')
+                             None, group='compute')
         cfg.CONF.set_default('max_microversion',
-                             '2.13', group='compute-feature-enabled')
+                             '2.13', group='compute')
         self.assertRaises(exceptions.InvalidAPIVersionRange,
                           InvalidVersionTest.skip_checks)
diff --git a/tempest/tests/test_negative_rest_client.py b/tempest/tests/test_negative_rest_client.py
index ce95739..a1b5f0e 100644
--- a/tempest/tests/test_negative_rest_client.py
+++ b/tempest/tests/test_negative_rest_client.py
@@ -15,15 +15,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import httplib2
+import mock
 from oslotest import mockpatch
 
 from tempest.common import negative_rest_client
 from tempest import config
-from tempest.tests import base
 from tempest.tests import fake_auth_provider
 from tempest.tests import fake_config
-from tempest.tests import fake_http
+from tempest.tests.lib import base
 
 
 class TestNegativeRestClient(base.TestCase):
@@ -31,59 +30,69 @@
     url = 'fake_endpoint'
 
     def setUp(self):
-        self.fake_http = fake_http.fake_httplib2()
         super(TestNegativeRestClient, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
         self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
-        self.stubs.Set(httplib2.Http, 'request', self.fake_http.request)
         self.negative_rest_client = negative_rest_client.NegativeRestClient(
             fake_auth_provider.FakeAuthProvider(), None)
         self.useFixture(mockpatch.PatchObject(self.negative_rest_client,
                                               '_log_request'))
 
-    def test_post(self):
+    @mock.patch('tempest.lib.common.rest_client.RestClient.post',
+                return_value=(mock.Mock(), mock.Mock()))
+    def test_post(self, mock_post):
         __, return_dict = self.negative_rest_client.send_request('POST',
                                                                  self.url,
                                                                  [], {})
-        self.assertEqual('POST', return_dict['method'])
+        mock_post.assert_called_once_with(self.url, {})
 
-    def test_get(self):
+    @mock.patch('tempest.lib.common.rest_client.RestClient.get',
+                return_value=(mock.Mock(), mock.Mock()))
+    def test_get(self, mock_get):
         __, return_dict = self.negative_rest_client.send_request('GET',
                                                                  self.url,
                                                                  [])
-        self.assertEqual('GET', return_dict['method'])
+        mock_get.assert_called_once_with(self.url)
 
-    def test_delete(self):
+    @mock.patch('tempest.lib.common.rest_client.RestClient.delete',
+                return_value=(mock.Mock(), mock.Mock()))
+    def test_delete(self, mock_delete):
         __, return_dict = self.negative_rest_client.send_request('DELETE',
                                                                  self.url,
                                                                  [])
-        self.assertEqual('DELETE', return_dict['method'])
+        mock_delete.assert_called_once_with(self.url)
 
-    def test_patch(self):
+    @mock.patch('tempest.lib.common.rest_client.RestClient.patch',
+                return_value=(mock.Mock(), mock.Mock()))
+    def test_patch(self, mock_patch):
         __, return_dict = self.negative_rest_client.send_request('PATCH',
                                                                  self.url,
                                                                  [], {})
-        self.assertEqual('PATCH', return_dict['method'])
+        mock_patch.assert_called_once_with(self.url, {})
 
-    def test_put(self):
+    @mock.patch('tempest.lib.common.rest_client.RestClient.put',
+                return_value=(mock.Mock(), mock.Mock()))
+    def test_put(self, mock_put):
         __, return_dict = self.negative_rest_client.send_request('PUT',
                                                                  self.url,
                                                                  [], {})
-        self.assertEqual('PUT', return_dict['method'])
+        mock_put.assert_called_once_with(self.url, {})
 
-    def test_head(self):
-        self.useFixture(mockpatch.PatchObject(self.negative_rest_client,
-                                              'response_checker'))
+    @mock.patch('tempest.lib.common.rest_client.RestClient.head',
+                return_value=(mock.Mock(), mock.Mock()))
+    def test_head(self, mock_head):
         __, return_dict = self.negative_rest_client.send_request('HEAD',
                                                                  self.url,
                                                                  [])
-        self.assertEqual('HEAD', return_dict['method'])
+        mock_head.assert_called_once_with(self.url)
 
-    def test_copy(self):
+    @mock.patch('tempest.lib.common.rest_client.RestClient.copy',
+                return_value=(mock.Mock(), mock.Mock()))
+    def test_copy(self, mock_copy):
         __, return_dict = self.negative_rest_client.send_request('COPY',
                                                                  self.url,
                                                                  [])
-        self.assertEqual('COPY', return_dict['method'])
+        mock_copy.assert_called_once_with(self.url)
 
     def test_other(self):
         self.assertRaises(AssertionError,
diff --git a/tempest/tests/test_tempest_plugin.py b/tempest/tests/test_tempest_plugin.py
index c07e98c..f66dfc8 100644
--- a/tempest/tests/test_tempest_plugin.py
+++ b/tempest/tests/test_tempest_plugin.py
@@ -14,8 +14,8 @@
 #    under the License.
 
 from tempest.test_discover import plugins
-from tempest.tests import base
 from tempest.tests import fake_tempest_plugin as fake_plugin
+from tempest.tests.lib import base
 
 
 class TestPluginDiscovery(base.TestCase):
diff --git a/tempest/tests/test_wrappers.py b/tempest/tests/test_wrappers.py
index a4ef699..edb9061 100644
--- a/tempest/tests/test_wrappers.py
+++ b/tempest/tests/test_wrappers.py
@@ -19,7 +19,7 @@
 
 import six
 
-from tempest.tests import base
+from tempest.tests.lib import base
 
 DEVNULL = open(os.devnull, 'wb')
 
diff --git a/tempest/tests/utils.py b/tempest/tests/utils.py
new file mode 100644
index 0000000..9c3049d
--- /dev/null
+++ b/tempest/tests/utils.py
@@ -0,0 +1,29 @@
+#    Copyright 2016 OpenStack Foundation
+#
+#    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.
+#
+
+
+def generate_timeout_series(timeout):
+    """Generate a series of times that exceeds the given timeout.
+
+    Yields a series of fake time.time() floating point numbers
+    such that the difference between each pair in the series just
+    exceeds the timeout value that is passed in.  Useful for
+    mocking time.time() in methods that otherwise wait for timeout
+    seconds.
+    """
+    iteration = 0
+    while True:
+        iteration += 1
+        yield (iteration * timeout) + iteration
diff --git a/test-requirements.txt b/test-requirements.txt
index be3a4f1..bb4b27f 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -6,7 +6,7 @@
 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD
 python-subunit>=0.0.18 # Apache-2.0/BSD
 oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
-reno>=0.1.1 # Apache2
+reno>=1.6.2 # Apache2
 mox>=0.5.3 # Apache-2.0
 mock>=1.2 # BSD
 coverage>=3.6 # Apache-2.0
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
new file mode 100644
index 0000000..03dbd9b
--- /dev/null
+++ b/tools/generate-tempest-plugins-list.py
@@ -0,0 +1,70 @@
+#! /usr/bin/env python
+
+# Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
+#
+# 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.
+
+# This script is intended to be run as part of a periodic proposal bot
+# job in OpenStack infrastructure.
+#
+# In order to function correctly, the environment in which the
+# script runs must have
+#   * network access to the review.openstack.org Gerrit API
+#     working directory
+#   * network access to https://git.openstack.org/cgit
+
+import json
+import re
+import requests
+
+url = 'https://review.openstack.org/projects/'
+
+# This is what a project looks like
+'''
+  "openstack-attic/akanda": {
+    "id": "openstack-attic%2Fakanda",
+    "state": "READ_ONLY"
+  },
+'''
+
+
+def is_in_openstack_namespace(proj):
+    return proj.startswith('openstack/')
+
+# Rather than returning a 404 for a nonexistent file, cgit delivers a
+# 0-byte response to a GET request.  It also does not provide a
+# Content-Length in a HEAD response, so the way we tell if a file exists
+# is to check the length of the entire GET response body.
+
+
+def has_tempest_plugin(proj):
+    r = requests.get(
+        "https://git.openstack.org/cgit/%s/plain/setup.cfg" % proj)
+    p = re.compile('^tempest\.test_plugins', re.M)
+    if p.findall(r.text):
+        return True
+    else:
+        False
+
+r = requests.get(url)
+# Gerrit prepends 4 garbage octets to the JSON, in order to counter
+# cross-site scripting attacks.  Therefore we must discard it so the
+# json library won't choke.
+projects = sorted(filter(is_in_openstack_namespace, json.loads(r.text[4:])))
+
+found_plugins = filter(has_tempest_plugin, projects)
+
+# Every element of the found_plugins list begins with "openstack/".
+# We drop those initial 10 octets when printing the list.
+for project in found_plugins:
+    print(project[10:])
diff --git a/tools/generate-tempest-plugins-list.sh b/tools/generate-tempest-plugins-list.sh
new file mode 100755
index 0000000..ecff508
--- /dev/null
+++ b/tools/generate-tempest-plugins-list.sh
@@ -0,0 +1,64 @@
+#!/bin/bash -ex
+
+# Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
+#
+# 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.
+
+# This script is intended to be run as a periodic proposal bot job
+# in OpenStack infrastructure, though you can run it as a one-off.
+#
+# In order to function correctly, the environment in which the
+# script runs must have
+#   * a writable doc/source directory relative to the current
+#     working directory
+#   AND ( (
+#   * git
+#   * all git repos meant to be searched for plugins cloned and
+#     at the desired level of up-to-datedness
+#   * the environment variable git_dir pointing to the location
+#   * of said git repositories
+#   ) OR (
+#   * network access to the review.openstack.org Gerrit API
+#     working directory
+#   * network access to https://git.openstack.org/cgit
+#   ))
+#
+# If a file named data/tempest-plugins-registry.header or
+# data/tempest-plugins-registry.footer is found relative to the
+# current working directory, it will be prepended or appended to
+# the generated reStructuredText plugins table respectively.
+
+(
+declare -A plugins
+
+if [[ -r data/tempest-plugins-registry.header ]]; then
+    cat data/tempest-plugins-registry.header
+fi
+
+sorted_plugins=$(python tools/generate-tempest-plugins-list.py)
+
+for k in ${sorted_plugins}; do
+    project=${k:0:28}
+    giturl="git://git.openstack.org/openstack/${k:0:26}"
+    printf "|%-28s|%-73s|\n" "${project}" "${giturl}"
+    printf "+----------------------------+-------------------------------------------------------------------------+\n"
+done
+
+if [[ -r data/tempest-plugins-registry.footer ]]; then
+    cat data/tempest-plugins-registry.footer
+fi
+) > doc/source/plugin-registry.rst
+
+if [[ -n ${1} ]]; then
+    cp doc/source/plugin-registry.rst ${1}/doc/source/plugin-registry.rst
+fi
diff --git a/tools/install_venv.py b/tools/install_venv.py
deleted file mode 100644
index d6d9c8e..0000000
--- a/tools/install_venv.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright 2010 United States Government as represented by the
-# Administrator of the National Aeronautics and Space Administration.
-# All Rights Reserved.
-#
-# Copyright 2010 OpenStack Foundation
-# Copyright 2013 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 os
-import sys
-
-import install_venv_common as install_venv  # noqa
-
-
-def print_help(venv, root):
-    help = """
-    OpenStack development environment setup is complete.
-
-    OpenStack development uses virtualenv to track and manage Python
-    dependencies while in development and testing.
-
-    To activate the OpenStack virtualenv for the extent of your current shell
-    session you can run:
-
-    $ source %s/bin/activate
-
-    Or, if you prefer, you can run commands in the virtualenv on a case by case
-    basis by running:
-
-    $ %s/tools/with_venv.sh <your command>
-
-    Also, make test will automatically use the virtualenv.
-    """
-    print(help % (venv, root))
-
-
-def main(argv):
-    root = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
-
-    if os.environ.get('TOOLS_PATH'):
-        root = os.environ['TOOLS_PATH']
-    venv = os.path.join(root, '.venv')
-    if os.environ.get('VENV'):
-        venv = os.environ['VENV']
-
-    pip_requires = os.path.join(root, 'requirements.txt')
-    test_requires = os.path.join(root, 'test-requirements.txt')
-    py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
-    project = 'Tempest'
-    install = install_venv.InstallVenv(root, venv, pip_requires, test_requires,
-                                       py_version, project)
-    options = install.parse_args(argv)
-    install.check_python_version()
-    install.check_dependencies()
-    install.create_virtualenv(no_site_packages=options.no_site_packages)
-    install.install_dependencies()
-    print_help(venv, root)
-
-if __name__ == '__main__':
-    main(sys.argv)
diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py
deleted file mode 100644
index e279159..0000000
--- a/tools/install_venv_common.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-# Copyright 2013 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.
-
-"""Provides methods needed by installation script for OpenStack development
-virtual environments.
-
-Since this script is used to bootstrap a virtualenv from the system's Python
-environment, it should be kept strictly compatible with Python 2.6.
-
-Synced in from openstack-common
-"""
-
-from __future__ import print_function
-
-import optparse
-import os
-import subprocess
-import sys
-
-
-class InstallVenv(object):
-
-    def __init__(self, root, venv, requirements,
-                 test_requirements, py_version,
-                 project):
-        self.root = root
-        self.venv = venv
-        self.requirements = requirements
-        self.test_requirements = test_requirements
-        self.py_version = py_version
-        self.project = project
-
-    def die(self, message, *args):
-        print(message % args, file=sys.stderr)
-        sys.exit(1)
-
-    def check_python_version(self):
-        if sys.version_info < (2, 6):
-            self.die("Need Python Version >= 2.6")
-
-    def run_command_with_code(self, cmd, redirect_output=True,
-                              check_exit_code=True):
-        """Runs a command in an out-of-process shell.
-
-        Returns the output of that command. Working directory is self.root.
-        """
-        if redirect_output:
-            stdout = subprocess.PIPE
-        else:
-            stdout = None
-
-        proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
-        output = proc.communicate()[0]
-        if check_exit_code and proc.returncode != 0:
-            self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
-        return (output, proc.returncode)
-
-    def run_command(self, cmd, redirect_output=True, check_exit_code=True):
-        return self.run_command_with_code(cmd, redirect_output,
-                                          check_exit_code)[0]
-
-    def get_distro(self):
-        if (os.path.exists('/etc/fedora-release') or
-                os.path.exists('/etc/redhat-release')):
-            return Fedora(
-                self.root, self.venv, self.requirements,
-                self.test_requirements, self.py_version, self.project)
-        else:
-            return Distro(
-                self.root, self.venv, self.requirements,
-                self.test_requirements, self.py_version, self.project)
-
-    def check_dependencies(self):
-        self.get_distro().install_virtualenv()
-
-    def create_virtualenv(self, no_site_packages=True):
-        """Creates the virtual environment and installs PIP.
-
-        Creates the virtual environment and installs PIP only into the
-        virtual environment.
-        """
-        if not os.path.isdir(self.venv):
-            print('Creating venv...', end=' ')
-            if no_site_packages:
-                self.run_command(['virtualenv', '-q', '--no-site-packages',
-                                 self.venv])
-            else:
-                self.run_command(['virtualenv', '-q', self.venv])
-            print('done.')
-        else:
-            print("venv already exists...")
-            pass
-
-    def pip_install(self, *args):
-        self.run_command(['tools/with_venv.sh',
-                         'pip', 'install', '--upgrade'] + list(args),
-                         redirect_output=False)
-
-    def install_dependencies(self):
-        print('Installing dependencies with pip (this can take a while)...')
-
-        # First things first, make sure our venv has the latest pip and
-        # setuptools and pbr
-        self.pip_install('pip>=1.4')
-        self.pip_install('setuptools')
-        self.pip_install('pbr')
-
-        self.pip_install('-r', self.requirements, '-r', self.test_requirements)
-
-    def parse_args(self, argv):
-        """Parses command-line arguments."""
-        parser = optparse.OptionParser()
-        parser.add_option('-n', '--no-site-packages',
-                          action='store_true',
-                          help="Do not inherit packages from global Python "
-                               "install.")
-        return parser.parse_args(argv[1:])[0]
-
-
-class Distro(InstallVenv):
-
-    def check_cmd(self, cmd):
-        return bool(self.run_command(['which', cmd],
-                    check_exit_code=False).strip())
-
-    def install_virtualenv(self):
-        if self.check_cmd('virtualenv'):
-            return
-
-        if self.check_cmd('easy_install'):
-            print('Installing virtualenv via easy_install...', end=' ')
-            if self.run_command(['easy_install', 'virtualenv']):
-                print('Succeeded')
-                return
-            else:
-                print('Failed')
-
-        self.die('ERROR: virtualenv not found.\n\n%s development'
-                 ' requires virtualenv, please install it using your'
-                 ' favorite package management tool' % self.project)
-
-
-class Fedora(Distro):
-    """This covers all Fedora-based distributions.
-
-    Includes: Fedora, RHEL, CentOS, Scientific Linux
-    """
-
-    def check_pkg(self, pkg):
-        return self.run_command_with_code(['rpm', '-q', pkg],
-                                          check_exit_code=False)[1] == 0
-
-    def install_virtualenv(self):
-        if self.check_cmd('virtualenv'):
-            return
-
-        if not self.check_pkg('python-virtualenv'):
-            self.die("Please install 'python-virtualenv'.")
-
-        super(Fedora, self).install_virtualenv()
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index a47e217..b554514 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -73,21 +73,23 @@
     DEF_RE = re.compile(r'\s*def (\w+)\(')
     bug_found = False
     results = []
-    lines = open(path, 'rb').readlines()
-    for x, line in enumerate(lines):
-        if not bug_found:
-            res = BUG_RE.match(line)
-            if res:
-                bug_no = int(res.group(1))
-                debug("Found bug skip %s on line %d", bug_no, x + 1)
-                bug_found = True
-        else:
-            res = DEF_RE.match(line)
-            if res:
-                method = res.group(1)
-                debug("Found test method %s skips for bug %d", method, bug_no)
-                results.append((method, bug_no))
-                bug_found = False
+    with open(path, 'rb') as content:
+        lines = content.readlines()
+        for x, line in enumerate(lines):
+            if not bug_found:
+                res = BUG_RE.match(line)
+                if res:
+                    bug_no = int(res.group(1))
+                    debug("Found bug skip %s on line %d", bug_no, x + 1)
+                    bug_found = True
+            else:
+                res = DEF_RE.match(line)
+                if res:
+                    method = res.group(1)
+                    debug("Found test method %s skips for bug %d",
+                          method, bug_no)
+                    results.append((method, bug_no))
+                    bug_found = False
     return results