Merge "Cleanup useless aliasing in test_container_acl"
diff --git a/.coveragerc b/.coveragerc
index 51482d3..449e62c 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,4 +1,4 @@
 [run]
 branch = True
 source = tempest
-omit = tempest/tests/*,tempest/scenario/test_*.py,tempest/api_schema/*,tempest/api/*
+omit = tempest/tests/*,tempest/scenario/test_*.py,tempest/api/*
diff --git a/.gitignore b/.gitignore
index d58b162..287db4c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
 AUTHORS
 ChangeLog
 *.pyc
+__pycache__/
+etc/accounts.yaml
 etc/tempest.conf
 etc/tempest.conf.sample
 etc/logging.conf
@@ -23,3 +25,7 @@
 !.coveragerc
 cover/
 doc/source/_static/tempest.conf.sample
+doc/source/plugin-registry.rst
+
+# Files created by releasenotes build
+releasenotes/build
diff --git a/HACKING.rst b/HACKING.rst
index 0962f80..a209b3f 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -16,8 +16,12 @@
 - [T107] Check that a service tag isn't in the module path
 - [T108] Check no hyphen at the end of rand_name() argument
 - [T109] Cannot use testtools.skip decorator; instead use
-         decorators.skip_because from tempest-lib
+         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
+- [T113] Check that tests use data_utils.rand_uuid() instead of uuid.uuid4()
+- [T114] Check that tempest.lib does not use tempest config
 - [N322] Method's default argument shouldn't be mutable
 
 Test Data/Configuration
@@ -129,6 +133,7 @@
 
 Set-up is split in a series of steps (setup stages), which can be overwritten
 by test classes. Set-up stages are:
+
 - `skip_checks`
 - `setup_credentials`
 - `setup_clients`
@@ -137,6 +142,7 @@
 Tear-down is also split in a series of steps (teardown stages), which are
 stacked for execution only if the corresponding setup stage had been
 reached during the setup phase. Tear-down stages are:
+
 - `clear_credentials` (defined in the base test class)
 - `resource_cleanup`
 
@@ -154,37 +160,42 @@
 
 Negative Tests
 --------------
-Newly added negative tests should use the negative test framework. First step
-is to create an interface description in a python file under
-`tempest/api_schema/request/`. These descriptions consists of two important
-sections for the test (one of those is mandatory):
+Error handling is an important aspect of API design and usage. Negative
+tests are a way to ensure that an application can gracefully handle
+invalid or unexpected input. However, as a black box integration test
+suite, Tempest is not suitable for handling all negative test cases, as
+the wide variety and complexity of negative tests can lead to long test
+runs and knowledge of internal implementation details. The bulk of
+negative testing should be handled with project function tests.
+All negative tests should be based on `API-WG guideline`_ . Such negative
+tests can block any changes from accurate failure code to invalid one.
 
- - A resource (part of the URL of the request): Resources needed for a test
-   must be created in `setUpClass` and registered with `set_resource` e.g.:
-   `cls.set_resource("server", server['id'])`
+.. _API-WG guideline: https://github.com/openstack/api-wg/blob/master/guidelines/http.rst#failure-code-clarifications
 
- - A json schema: defines properties for a request.
+If facing some gray area which is not clarified on the above guideline, propose
+a new guideline to the API-WG. With a proposal to the API-WG we will be able to
+build a consensus across all OpenStack projects and improve the quality and
+consistency of all the APIs.
 
-After that a test class must be added to automatically generate test scenarios
-out of the given interface description::
+In addition, we have some guidelines for additional negative tests.
 
-    load_tests = test.NegativeAutoTest.load_tests
+- About BadRequest(HTTP400) case: We can add a single negative tests of
+  BadRequest for each resource and method(POST, PUT).
+  Please don't implement more negative tests on the same combination of
+  resource and method even if API request parameters are different from
+  the existing test.
+- About NotFound(HTTP404) case: We can add a single negative tests of
+  NotFound for each resource and method(GET, PUT, DELETE, HEAD).
+  Please don't implement more negative tests on the same combination
+  of resource and method.
 
-    @test.SimpleNegativeAutoTest
-    class SampleTestNegativeTestJSON(<your base class>, test.NegativeAutoTest):
-        _service = 'compute'
-        _schema = <your schema file>
-
-The class decorator `SimpleNegativeAutoTest` will automatically generate test
-cases out of the given schema in the attribute `_schema`.
-
-All negative tests should be added into a separate negative test file.
-If such a file doesn't exist for the particular resource being tested a new
-test file should be added.
+The above guidelines don't cover all cases and we will grow these guidelines
+organically over time. Patches outside of the above guidelines are left up to
+the reviewers' discretion and if we face some conflicts between reviewers, we
+will expand the guideline based on our discussion and experience.
 
 Test skips because of Known Bugs
 --------------------------------
-
 If a test is broken because of a bug it is appropriate to skip the test until
 bug has been fixed. You should use the skip_because decorator so that
 Tempest's skip tracking tool can watch the bug status.
@@ -212,9 +223,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
@@ -229,29 +240,6 @@
   can be used to perform this. See AggregatesAdminTest in
   tempest.api.compute.admin for an example of using locking.
 
-Stress Tests in Tempest
------------------------
-Any tempest test case can be flagged as a stress test. With this flag it will
-be automatically discovery and used in the stress test runs. The stress test
-framework itself is a facility to spawn and control worker processes in order
-to find race conditions (see ``tempest/stress/`` for more information). Please
-note that these stress tests can't be used for benchmarking purposes since they
-don't measure any performance characteristics.
-
-Example::
-
-  @stresstest(class_setup_per='process')
-  def test_this_and_that(self):
-    ...
-
-This will flag the test ``test_this_and_that`` as a stress test. The parameter
-``class_setup_per`` gives control when the setUpClass function should be called.
-
-Good candidates for stress tests are:
-
-- Scenario tests
-- API tests that have a wide focus
-
 Sample Configuration File
 -------------------------
 The sample config file is autogenerated using a script. If any changes are made
@@ -330,9 +318,9 @@
         # The created server should be in the detailed list of all servers
         ...
 
-Tempest-lib includes a ``check-uuid`` tool that will test for the existence
+Tempest.lib includes a ``check-uuid`` tool that will test for the existence
 and uniqueness of idempotent_id metadata for every test. If you have
-tempest-lib installed you run the tool against Tempest by calling from the
+tempest installed you run the tool against Tempest by calling from the
 tempest repo::
 
     check-uuid
diff --git a/README.rst b/README.rst
index 71e185f..281516b 100644
--- a/README.rst
+++ b/README.rst
@@ -1,6 +1,19 @@
+========================
+Team and repository tags
+========================
+
+.. image:: http://governance.openstack.org/badges/tempest.svg
+    :target: http://governance.openstack.org/reference/tags/index.html
+    :remote:
+
+.. Change things from this point on
+
 Tempest - The OpenStack Integration Test Suite
 ==============================================
 
+The documentation for Tempest is officially hosted at:
+http://docs.openstack.org/developer/tempest/
+
 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
@@ -17,8 +30,7 @@
   discover features of a cloud incorrectly, and give people an
   incorrect assessment of their cloud. Explicit is always better.
 - Tempest uses OpenStack public interfaces. Tests in Tempest should
-  only touch public interfaces, API calls (native or 3rd party),
-  or libraries.
+  only touch public OpenStack APIs.
 - Tempest should not touch private or implementation specific
   interfaces. This means not directly going to the database, not
   directly hitting the hypervisors, not testing extensions not
@@ -50,27 +62,29 @@
 #. You first need to install Tempest. This is done with pip after you check out
    the Tempest repo::
 
-    $ git clone https://github.com/openstack/tempest/
+    $ git clone http://git.openstack.org/openstack/tempest
     $ pip install tempest/
 
    This can be done within a venv, but the assumption for this guide is that
    the Tempest cli entry point will be in your shell's PATH.
 
-#. Installing Tempest will create a /etc/tempest dir which will contain the
-   sample config file packaged with Tempest. The contents of /etc/tempest will
-   be copied to all local working dirs, so if there is any common configuration
-   you'd like to be shared between anyone setting up local Tempest working dirs
-   it's recommended that you copy or rename tempest.conf.sample to tempest.conf
-   and make those changes to that file in /etc/tempest
+#. Installing Tempest may create a /etc/tempest dir, however if one isn't
+   created you can create one or use ~/.tempest/etc or ~/.config/tempest in
+   place of /etc/tempest. If none of these dirs are created tempest will create
+   ~/.tempest/etc when it's needed. The contents of this dir will always
+   automatically be copied to all etc/ dirs in local workspaces as an initial
+   setup step. So if there is any common configuration you'd like to be shared
+   between local Tempest workspaces it's recommended that you pre-populate it
+   before running ``tempest init``.
 
-#. Setup a local working Tempest dir. This is done using the tempest init
+#. Setup a local Tempest workspace. This is done by using the tempest init
    command::
 
-    tempest init cloud-01
+    $ tempest init cloud-01
 
-   works the same as::
+   which also works the same as::
 
-    mkdir cloud-01 && cd cloud-01 && tempest init
+    $ mkdir cloud-01 && cd cloud-01 && tempest init
 
    This will create a new directory for running a single Tempest configuration.
    If you'd like to run Tempest against multiple OpenStack deployments the idea
@@ -81,19 +95,73 @@
    config files located in the etc/ subdir created by the ``tempest init``
    command. Tempest is expecting a tempest.conf file in etc/ so if only a
    sample exists you must rename or copy it to tempest.conf before making
-   any changes to it otherwise Tempest will not know how to load it.
+   any changes to it otherwise Tempest will not know how to load it. For
+   details on configuring tempest refer to the :ref:`tempest-configuration`.
 
 #. Once the configuration is done you're now ready to run Tempest. This can
-   be done with testr directly or any `testr`_ based test runner, like
-   `ostestr`_. For example, from the working dir running::
+   be done using the :ref:`tempest_run` command. This can be done by either
+   running::
 
-     $ ostestr --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))'
+    $ tempest run
+
+   from the Tempest workspace directory. Or you can use the ``--workspace``
+   argument to run in the workspace you created regarless of your current
+   working directory. For example::
+
+    $ tempest run --workspace cloud-01
+
+   There is also the option to use testr directly, or any `testr`_ based test
+   runner, like `ostestr`_. For example, from the workspace dir run::
+
+    $ ostestr --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))'
 
    will run the same set of tests as the default gate jobs.
 
 .. _testr: https://testrepository.readthedocs.org/en/latest/MANUAL.html
 .. _ostestr: http://docs.openstack.org/developer/os-testr/
 
+Library
+-------
+Tempest exposes a library interface. This interface is a stable interface and
+should be backwards compatible (including backwards compatibility with the
+old tempest-lib package, with the exception of the import). If you plan to
+directly consume tempest in your project you should only import code from the
+tempest library interface, other pieces of tempest do not have the same
+stable interface and there are no guarantees on the Python API unless otherwise
+stated.
+
+For more details refer to the library documentation here: :ref:`library`
+
+Release Versioning
+------------------
+`Tempest Release Notes <http://docs.openstack.org/releasenotes/tempest>`_
+shows what changes have been released on each version.
+
+Tempest's released versions are broken into 2 sets of information. Depending on
+how you intend to consume tempest you might need
+
+The version is a set of 3 numbers:
+
+X.Y.Z
+
+While this is almost `semver`_ like, the way versioning is handled is slightly
+different:
+
+X is used to represent the supported OpenStack releases for tempest tests
+in-tree, and to signify major feature changes to tempest. It's a monotonically
+increasing integer where each version either indicates a new supported OpenStack
+release, the drop of support for an OpenStack release (which will coincide with
+the upstream stable branch going EOL), or a major feature lands (or is removed)
+from tempest.
+
+Y.Z is used to represent library interface changes. This is treated the same
+way as minor and patch versions from `semver`_ but only for the library
+interface. When Y is incremented we've added functionality to the library
+interface and when Z is incremented it's a bug fix release for the library.
+Also note that both Y and Z are reset to 0 at each increment of X.
+
+.. _semver: http://semver.org/
+
 Configuration
 -------------
 
@@ -103,9 +171,9 @@
 of the configuration.
 
 You can generate a new sample tempest.conf file, run the following
-command from the top level of the Tempest directory:
+command from the top level of the Tempest directory::
 
-  tox -egenconfig
+    $ tox -egenconfig
 
 The most important pieces that are needed are the user ids, openstack
 endpoint, and basic flavors and images needed to run tests.
@@ -116,16 +184,15 @@
 Tempest also has a set of unit tests which test the Tempest code itself. These
 tests can be run by specifying the test discovery path::
 
-    $> OS_TEST_PATH=./tempest/tests testr run --parallel
+    $ OS_TEST_PATH=./tempest/tests testr run --parallel
 
 By setting OS_TEST_PATH to ./tempest/tests it specifies that test discover
 should only be run on the unit test directory. The default value of OS_TEST_PATH
 is OS_TEST_PATH=./tempest/test_discover which will only run test discover on the
 Tempest suite.
 
-Alternatively, you can use the run_tests.sh script which will create a venv and
-run the unit tests. There are also the py27 and py34 tox jobs which will run
-the unit tests with the corresponding version of python.
+Alternatively, there are the py27 and py34 tox jobs which will run the unit
+tests with the corresponding version of python.
 
 Python 2.6
 ----------
@@ -139,18 +206,18 @@
 from a remote system running python 2.7. (or deploy a cloud guest in your cloud
 that has python 2.7)
 
-Python 3.4
+Python 3.x
 ----------
 
 Starting during the Liberty release development cycle work began on enabling
 Tempest to run under both Python 2.7 and Python 3.4. Tempest strives to fully
-support running with Python 3.4. A gating unit test job was added to also run
-Tempest's unit tests under Python 3.4. This means that the Tempest code at
-least imports under Python 3.4 and things that have unit test coverage will
-work on Python 3.4. However, because large parts of Tempest are self-verifying
-there might be uncaught issues running on Python 3.4. So until there is a gating
-job which does a full Tempest run using Python 3.4 there isn't any guarantee
-that running Tempest under Python 3.4 is bug free.
+support running with Python 3.4 and newer. A gating unit test job was added to
+also run Tempest's unit tests under Python 3. This means that the Tempest
+code at least imports under Python 3.4 and things that have unit test coverage
+will work on Python 3.4. However, because large parts of Tempest are
+self-verifying there might be uncaught issues running on Python 3. So until
+there is a gating job which does a full Tempest run using Python 3 there
+isn't any guarantee that running Tempest under Python 3 is bug free.
 
 Legacy run method
 -----------------
@@ -167,9 +234,9 @@
 To start you need to create a configuration file. The easiest way to create a
 configuration file is to generate a sample in the ``etc/`` directory ::
 
-    $> cd $TEMPEST_ROOT_DIR
-    $> oslo-config-generator --config-file \
-        etc/config-generator.tempest.conf \
+    $ cd $TEMPEST_ROOT_DIR
+    $ oslo-config-generator --config-file \
+        tempest/cmd/config-generator.tempest.conf \
         --output-file etc/tempest.conf
 
 After that, open up the ``etc/tempest.conf`` file and edit the
@@ -190,21 +257,21 @@
 After setting up your configuration file, you can execute the set of Tempest
 tests by using ``testr`` ::
 
-    $> testr run --parallel
+    $ testr run --parallel
 
 To run one single test serially ::
 
-    $> testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
+    $ testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
 
 Alternatively, you can use the run_tempest.sh script which will create a venv
 and run the tests or use tox to do the same. Tox also contains several existing
 job configurations. For example::
 
-   $> tox -efull
+    $ tox -efull
 
 which will run the same set of tests as the OpenStack gate. (it's exactly how
 the gate invokes Tempest) Or::
 
-  $> tox -esmoke
+    $ tox -esmoke
 
 to run the tests tagged as smoke.
diff --git a/REVIEWING.rst b/REVIEWING.rst
index f7334ad..9b272bb 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
@@ -13,6 +13,13 @@
 it. Tests which aren't executed either because of configuration or skips should
 not be accepted.
 
+If a new test is added that depends on a new config option (like a feature
+flag), the commit message must reference a change in DevStack or DevStack-Gate
+that enables the execution of this newly introduced test. This reference could
+either be a `Cross-Repository Dependency <http://docs.openstack.org/infra/
+manual/developers.html#cross-repository-dependencies>`_ or a simple link
+to a Gerrit review.
+
 
 Unit Tests
 ----------
@@ -72,6 +79,25 @@
 scenario tests this is up to the reviewers discretion whether a docstring is
 required or not.
 
+Release Notes
+-------------
+Release notes are how we indicate to users and other consumers of Tempest what
+has changed in a given release. Since Tempest 10.0.0 we've been using `reno`_
+to manage and build the release notes. There are certain types of changes that
+require release notes and we should not approve them without including a release
+note. These include but aren't limited to, any addition, deprecation or removal
+from the lib interface, any change to configuration options (including
+deprecation), CLI additions or deprecations, major feature additions, and
+anything backwards incompatible or would require a user to take note or do
+something extra.
+
+.. _reno: http://docs.openstack.org/developer/reno/
+
+Deprecated Code
+---------------
+Sometimes we have some bugs in deprecated code. Basically, we leave it. Because
+we don't need to maintain it. However, if the bug is critical, we might need to
+fix it. When it will happen, we will deal with it on a case-by-case basis.
 
 When to approve
 ---------------
diff --git a/bindep.txt b/bindep.txt
new file mode 100644
index 0000000..6a28348
--- /dev/null
+++ b/bindep.txt
@@ -0,0 +1,11 @@
+# This file contains runtime (non-python) dependencies
+# More info at: http://docs.openstack.org/infra/bindep/readme.html
+
+libffi-dev [platform:dpkg]
+libffi-devel [platform:rpm]
+gcc [platform:rpm]
+gcc [platform:dpkg]
+python-dev [platform:dpkg]
+python-devel [platform:rpm]
+openssl-devel [platform:rpm]
+libssl-dev [platform:dpkg]
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/conf.py b/doc/source/conf.py
index 7e4503d..04ddfdf 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Tempest documentation build configuration file, created by
 # sphinx-quickstart on Tue May 21 17:43:32 2013.
 #
@@ -14,6 +12,18 @@
 import sys
 import os
 import subprocess
+import warnings
+
+# Build the plugin registry
+def build_plugin_registry(app):
+    root_dir = os.path.dirname(
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+    subprocess.call(['tools/generate-tempest-plugins-list.sh'], cwd=root_dir)
+
+def setup(app):
+    app.connect('builder-inited', build_plugin_registry)
+
+
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
@@ -34,7 +44,7 @@
               'oslo_config.sphinxconfiggen',
              ]
 
-config_generator_config_file = '../../etc/config-generator.tempest.conf'
+config_generator_config_file = '../../tempest/cmd/config-generator.tempest.conf'
 sample_config_basename = '_static/tempest'
 
 todo_include_todos = True
@@ -129,13 +139,17 @@
 # using the given strftime format.
 git_cmd = ["git", "log", "--pretty=format:'%ad, commit %h'", "--date=local",
    "-n1"]
-html_last_updated_fmt = subprocess.Popen(git_cmd,
-                                         stdout=subprocess.PIPE).\
-                                         communicate()[0]
+try:
+    html_last_updated_fmt = subprocess.Popen(git_cmd,
+                                             stdout=subprocess.PIPE).\
+                                             communicate()[0]
+except Exception:
+    warnings.warn('Cannot get last updated time from git repository. '
+                  'Not setting "html_last_updated_fmt".')
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+html_use_smartypants = False
 
 # Custom sidebar templates, maps document names to template names.
 #html_sidebars = {}
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index e428592..18269bf 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -9,23 +9,35 @@
 config file which explains the purpose of each individual option. You can see
 the sample config file here: :ref:`tempest-sampleconf`
 
-Auth/Credentials
+Test Credentials
 ----------------
 
-Tempest currently has two different ways in configuration to provide credentials
-to use when running Tempest. One is a traditional set of configuration options
-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.
+Tempest allows for configuring a set of admin credentials in the ``auth``
+section, via the following parameters:
 
-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
-with. You can specify the location of the file in the ``auth`` section in the
+ #. ``admin_username``
+ #. ``admin_password``
+ #. ``admin_project_name``
+ #. ``admin_domain_name``
+
+Admin credentials are not mandatory to run Tempest, but when provided they
+can be used to:
+
+- Run tests for admin APIs
+- Generate test credentials on the fly (see `Dynamic Credentials`_)
+
+When keystone uses a policy that requires domain scoped tokens for admin
+actions, the flag ``admin_domain_scope`` must be set to ``True``.
+The admin user configured, if any, must have a role assigned to the domain to
+be usable.
+
+Tempest allows for configuring pre-provisioned test credentials as well.
+This can be done using the accounts.yaml file (see
+`Pre-Provisioned Credentials`_). This file is used to specify an arbitrary
+number of users available to run tests with.
+You can specify the location of the file in the ``auth`` section in the
 tempest.conf file. To see the specific format used in the file please refer to
-the accounts.yaml.sample file included in Tempest.  Eventually the config
-options for providing credentials to Tempest will be deprecated and removed in
-favor of the accounts.yaml file.
+the accounts.yaml.sample file included in Tempest.
 
 Keystone Connection Info
 ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -49,23 +61,22 @@
 Credential Provider Mechanisms
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Tempest currently also has three different internal methods for providing
-authentication to tests: dynamic credentials, locking test accounts, and
-non-locking test accounts. Depending on which one is in use the configuration
-of Tempest is slightly different.
+Tempest currently has two different internal methods for providing authentication
+to tests: dynamic credentials and pre-provisioned credentials.
+Depending on which one is in use the configuration of Tempest is slightly different.
 
 Dynamic Credentials
 """""""""""""""""""
 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.
@@ -80,9 +91,18 @@
 by dynamic credentials. This option will not have any effect when Tempest is not
 configured to use dynamic credentials.
 
+When the ``admin_domain_scope`` option is set to ``True``, provisioned admin
+accounts will be assigned a role on domain configured in
+``default_credentials_domain_name``. This will make the accounts provisioned
+usable in a cloud where domain scoped tokens are required by keystone for
+admin operations. Note that the initial pre-provision admin accounts,
+configured in tempest.conf, must have a role on the same domain as well, for
+Dynamic Credentials to work.
 
-Pre-Provisioned Credentials (aka accounts.yaml or accounts file)
-""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+Pre-Provisioned Credentials
+"""""""""""""""""""""""""""
+
 For a long time using dynamic credentials was the only method available if you
 wanted to enable parallel execution of Tempest tests. However, this was
 insufficient for certain use cases because of the admin credentials requirement
@@ -112,46 +132,26 @@
  #. 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.
+unexpected failures in some tests. Also, ensure that these projects and users
+used do not have any pre-existing resources created. Tempest assumes all
+tenants it's using are empty and may sporadically fail if there are unexpected
+resources present.
 
+When the keystone in the target cloud requires domain scoped tokens to
+perform admin actions, all pre-provisioned admin users must have a role
+assigned on the domain where test accounts a provisioned.
+The option ``admin_domain_scope`` is used to tell tempest that domain scoped
+tokens shall be used. ``default_credentials_domain_name`` is the domain where
+test accounts are expected to be provisioned if no domain is specified.
 
-Legacy Credentials (aka credentials config options)
-"""""""""""""""""""""""""""""""""""""""""""""""""""
-**Starting in the Liberty release this mechanism was deprecated; it will be
-removed in a future release.**
+Note that if credentials are pre-provisioned via ``tempest account-generator``
+the role on the domain will be assigned automatically for you, as long as
+``admin_domain_scope`` as ``default_credentials_domain_name`` are configured
+properly in tempest.conf.
 
-When Tempest was refactored to allow for locking test accounts, the original
-non-tenant 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
-options in the ``identity`` section:
-
- #. ``username``
- #. ``password``
- #. ``tenant_name``
- #. ``admin_username``
- #. ``admin_password``
- #. ``admin_tenant_name``
- #. ``alt_username``
- #. ``alt_password``
- #. ``alt_tenant_name``
-
-And in the ``auth`` section:
-
- #. ``use_dynamic_credentials = False``
- #. Comment out ``test_accounts_file`` or keep it empty.
-
-It only makes sense to use this if parallel execution isn't needed, since
-Tempest won't be able to properly isolate tests using this. Additionally, using
-the traditional config options for credentials is not able to provide
-credentials to tests requiring specific roles on accounts. This is because the
-config options do not give sufficient flexibility to describe the roles assigned
-to a user for running the tests. There are additional limitations with regard to
-network configuration when using this credential provider mechanism - see the
-`Networking`_ section below.
+Pre-Provisioned Credentials are also known as accounts.yaml or accounts file.
 
 Compute
 -------
@@ -232,6 +232,8 @@
 
 Enabling Remote Access to Created Servers
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Network Creation/Usage for Servers
+""""""""""""""""""""""""""""""""""
 When Tempest creates servers for testing, some tests require being able to
 connect those servers. Depending on the configuration of the cloud, the methods
 for doing this can be different. In certain configurations it is required to
@@ -242,10 +244,10 @@
 a network when creating servers.
 
 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.
 
@@ -264,13 +266,13 @@
 
 
 Accounts File
-"""""""""""""
+'''''''''''''
 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
@@ -289,9 +291,9 @@
 
 
 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
@@ -314,6 +316,34 @@
 network available for the server creation, or use ``fixed_network_name`` to
 inform Tempest which network to use.
 
+SSH Connection Configuration
+""""""""""""""""""""""""""""
+There are also several different ways to actually establish a connection and
+authenticate/login on the server. After a server is booted with a provided
+network there are still details needed to know how to actually connect to
+the server. The ``validation`` group gathers all the options regarding
+connecting to and remotely accessing the created servers.
+
+To enable remote access to servers, there are 3 options at a minimum that are used:
+
+ #. ``run_validation``
+ #. ``connect_method``
+ #. ``auth_method``
+
+The ``run_validation`` is used to enable or disable ssh connectivity for
+all tests (with the exception of scenario tests which do not have a flag for
+enabling or disabling ssh) To enable ssh connectivity this needs be set to ``true``.
+
+The ``connect_method`` option is used to tell tempest what kind of IP to use for
+establishing a connection to the server. Two methods are available: ``fixed``
+and ``floating``, the later being set by default. If this is set to floating
+tempest will create a floating ip for the server before attempted to connect
+to it. The IP for the floating ip is what is used for the connection.
+
+For the ``auth_method`` option there is currently, only one valid option,
+``keypair``. With this set to ``keypair`` tempest will create an ssh keypair
+and use that for authenticating against the created server.
+
 Configuring Available Services
 ------------------------------
 OpenStack is really a constellation of several different projects which
@@ -360,11 +390,14 @@
     service catalog should be in a standard format (which is going to be
     standardized at the keystone level).
     Tempest expects URLs in the Service catalog in the following format:
-     * ``http://example.com:1234/<version-info>``
+
+    * ``http://example.com:1234/<version-info>``
+
     Examples:
-     * Good - ``http://example.com:1234/v2.0``
-     * Wouldn’t work -  ``http://example.com:1234/xyz/v2.0/``
-       (adding prefix/suffix around version etc)
+
+    * Good - ``http://example.com:1234/v2.0``
+    * Wouldn’t work -  ``http://example.com:1234/xyz/v2.0/``
+      (adding prefix/suffix around version etc)
 
 Service Feature Configuration
 -----------------------------
diff --git a/doc/source/field_guide/stress.rst b/doc/source/field_guide/stress.rst
deleted file mode 120000
index d39d0f8..0000000
--- a/doc/source/field_guide/stress.rst
+++ /dev/null
@@ -1 +0,0 @@
-../../../tempest/stress/README.rst
\ No newline at end of file
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 32e6e51..896cd98 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -2,15 +2,14 @@
 Tempest Testing Project
 =======================
 
-Contents:
+--------
+Overview
+--------
 
 .. toctree::
    :maxdepth: 2
 
    overview
-   HACKING
-   REVIEWING
-   plugin
 
 ------------
 Field Guides
@@ -25,9 +24,12 @@
    field_guide/index
    field_guide/api
    field_guide/scenario
-   field_guide/stress
    field_guide/unit_tests
 
+=========
+For users
+=========
+
 ---------------------------
 Tempest Configuration Guide
 ---------------------------
@@ -47,7 +49,44 @@
 
    account_generator
    cleanup
-   javelin
+   subunit_describe_calls
+   workspace
+   run
+
+==============
+For developers
+==============
+
+-----------
+Development
+-----------
+
+.. toctree::
+   :maxdepth: 2
+
+   HACKING
+   REVIEWING
+   microversion_testing
+   test-removal
+
+-------
+Plugins
+-------
+
+.. toctree::
+   :maxdepth: 2
+
+   plugin
+   plugin-registry
+
+-------
+Library
+-------
+
+.. toctree::
+   :maxdepth: 2
+
+   library
 
 ==================
 Indices and tables
diff --git a/doc/source/javelin.rst b/doc/source/javelin.rst
deleted file mode 100644
index 01090ca..0000000
--- a/doc/source/javelin.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-----------------------------------------------------------
-Javelin2 - How to check that resources survived an upgrade
-----------------------------------------------------------
-
-.. automodule:: tempest.cmd.javelin
diff --git a/doc/source/library.rst b/doc/source/library.rst
new file mode 100644
index 0000000..29248d1
--- /dev/null
+++ b/doc/source/library.rst
@@ -0,0 +1,70 @@
+.. _library:
+
+Tempest Library Documentation
+=============================
+
+Tempest provides a stable library interface that provides external tools or
+test suites an interface for reusing pieces of tempest code. Any public
+interface that lives in tempest/lib in the tempest repo is treated as a stable
+public interface and it should be safe to external consume that. Every effort
+goes into maintaining backwards compatibility with any change.
+The library is self contained and doesn't have any dependency on
+other tempest internals outside of lib (including no usage of tempest
+configuration).
+
+Stability
+---------
+Any code that lives in tempest/lib will be treated as a stable interface.
+This means that any public interface under the tempest/lib directory is
+expected to be a stable interface suitable for public consumption. However, for
+any interfaces outside of tempest/lib in the tempest tree (unless otherwise
+noted) or any private interfaces the same stability guarantees don't apply.
+
+Adding Interfaces
+'''''''''''''''''
+When adding an interface to tempest/lib we have to make sure there are no
+dependencies on any pieces of tempest outside of tempest/lib. This means if
+for example there is a dependency on the configuration file we need remove that.
+The other aspect when adding an interface is to make sure it's really an
+interface ready for external consumption and something we want to commit to
+supporting.
+
+Making changes
+''''''''''''''
+When making changes to tempest/lib you have to be conscious of the effect of
+any changes on external consumers. If your proposed changeset will change the
+default behaviour of any interface, or make something which previously worked
+not after your change, then it is not acceptable. Every effort needs to go into
+preserving backwards compatibility in changes.
+
+Reviewing
+'''''''''
+When reviewing a proposed change to tempest/lib code we need to be careful to
+ensure that we don't break backwards compatibility. For patches that change
+existing interfaces we have to be careful to make sure we don't break any
+external consumers. Some common red flags are:
+
+ * a change to an existing API requires a change outside the library directory
+   where the interface is being consumed
+ * a unit test has to be significantly changed to make the proposed change pass
+
+Testing
+'''''''
+When adding a new interface to the library we need to at a minimum have unit
+test coverage. A proposed change to add an interface to tempest/lib that
+doesn't have unit tests shouldn't be accepted. Ideally these unit tests will
+provide sufficient coverage to ensure a stable interface moving forward.
+
+Current Library APIs
+--------------------
+
+.. toctree::
+   :maxdepth: 2
+
+   library/cli
+   library/decorators
+   library/rest_client
+   library/utils
+   library/api_microversion_testing
+   library/auth
+   library/clients
diff --git a/doc/source/library/api_microversion_testing.rst b/doc/source/library/api_microversion_testing.rst
new file mode 100644
index 0000000..8be924d
--- /dev/null
+++ b/doc/source/library/api_microversion_testing.rst
@@ -0,0 +1,29 @@
+.. _api_microversion_testing:
+
+API Microversion Testing Support in Tempest
+===========================================
+
+---------------------------------------------
+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/library/auth.rst b/doc/source/library/auth.rst
new file mode 100644
index 0000000..e1d92ed
--- /dev/null
+++ b/doc/source/library/auth.rst
@@ -0,0 +1,11 @@
+.. _auth:
+
+Authentication Framework Usage
+==============================
+
+---------------
+The auth module
+---------------
+
+.. automodule:: tempest.lib.auth
+   :members:
diff --git a/doc/source/library/cli.rst b/doc/source/library/cli.rst
new file mode 100644
index 0000000..6bd7881
--- /dev/null
+++ b/doc/source/library/cli.rst
@@ -0,0 +1,18 @@
+.. _cli:
+
+CLI Testing Framework Usage
+===========================
+
+-------------------
+The cli.base module
+-------------------
+
+.. automodule:: tempest.lib.cli.base
+   :members:
+
+----------------------------
+The cli.output_parser module
+----------------------------
+
+.. automodule:: tempest.lib.cli.output_parser
+   :members:
diff --git a/doc/source/library/clients.rst b/doc/source/library/clients.rst
new file mode 100644
index 0000000..086cfc9
--- /dev/null
+++ b/doc/source/library/clients.rst
@@ -0,0 +1,24 @@
+.. _clients:
+
+Service Clients Usage
+=====================
+
+Tests make requests against APIs using service clients. Service clients are
+specializations of the ``RestClient`` class. The service clients that cover the
+APIs exposed by a service should be grouped in a service clients module.
+A service clients module is python module where all service clients are
+defined. If major API versions are available, submodules should be defined,
+one for each version.
+
+The ``ClientsFactory`` class helps initializing all clients of a specific
+service client module from a set of shared parameters.
+
+The ``ServiceClients`` class provides a convenient way to get access to all
+available service clients initialized with a provided set of credentials.
+
+------------------
+The clients module
+------------------
+
+.. automodule:: tempest.lib.services.clients
+   :members:
diff --git a/doc/source/library/decorators.rst b/doc/source/library/decorators.rst
new file mode 100644
index 0000000..a173967
--- /dev/null
+++ b/doc/source/library/decorators.rst
@@ -0,0 +1,13 @@
+.. _decorators:
+
+Decorators Usage Guide
+======================
+
+---------------------
+The decorators module
+---------------------
+
+.. automodule:: tempest.lib.decorators
+   :members:
+
+
diff --git a/doc/source/library/rest_client.rst b/doc/source/library/rest_client.rst
new file mode 100644
index 0000000..3045694
--- /dev/null
+++ b/doc/source/library/rest_client.rst
@@ -0,0 +1,11 @@
+.. _rest_client:
+
+Rest Client Usage
+=================
+
+----------------------
+The rest_client module
+----------------------
+
+.. automodule:: tempest.lib.common.rest_client
+   :members:
diff --git a/doc/source/library/utils.rst b/doc/source/library/utils.rst
new file mode 100644
index 0000000..bc2f79b
--- /dev/null
+++ b/doc/source/library/utils.rst
@@ -0,0 +1,11 @@
+.. _utils:
+
+Utils Usage
+===========
+
+---------------
+The misc module
+---------------
+
+.. automodule:: tempest.lib.common.utils.misc
+   :members:
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
new file mode 100644
index 0000000..6b87b4d
--- /dev/null
+++ b/doc/source/microversion_testing.rst
@@ -0,0 +1,245 @@
+===================================
+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:
+
+  .. code-block:: ini
+
+      [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:
+
+.. code-block:: python
+
+    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:
+
+.. code-block:: python
+
+    @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:
+
+.. code-block:: python
+
+      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:
+
+.. code-block:: python
+
+    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:
+
+.. code-block:: python
+
+      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:
+
+.. code-block:: python
+
+    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:
+
+.. code-block:: python
+
+    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
+
+ * `2.10`_
+
+ .. _2.10: http://docs.openstack.org/developer/nova/api_microversion_history.html#id9
+
+ * `2.20`_
+
+ .. _2.20: http://docs.openstack.org/developer/nova/api_microversion_history.html#id18
+
+ * `2.25`_
+
+ .. _2.25: http://docs.openstack.org/developer/nova/api_microversion_history.html#maximum-in-mitaka
+
+ * `2.32`_
+
+ .. _2.32: http://docs.openstack.org/developer/nova/api_microversion_history.html#id29
+
+ * `2.37`_
+
+ .. _2.37: http://docs.openstack.org/developer/nova/api_microversion_history.html#id34
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index 29653a6..6b30825 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -11,14 +11,26 @@
 =================
 
 Creating a plugin is fairly straightforward and doesn't require much additional
-effort on top of creating a test suite using tempest-lib. One thing to note with
+effort on top of creating a test suite using tempest.lib. One thing to note with
 doing this is that the interfaces exposed by tempest are not considered stable
 (with the exception of configuration variables which ever effort goes into
 ensuring backwards compatibility). You should not need to import anything from
-tempest itself except where explicitly noted. If there is an interface from
-tempest that you need to rely on in your plugin it likely needs to be migrated
-to tempest-lib. In that situation, file a bug, push a migration patch, etc. to
-expedite providing the interface in a reliable manner.
+tempest itself except where explicitly noted.
+
+Stable Tempest APIs plugins may use
+-----------------------------------
+
+As noted above, several tempest APIs are acceptable to use from plugins, while
+others are not. A list of stable APIs available to plugins is provided below:
+
+* tempest.lib.*
+* tempest.config
+* tempest.test_discover.plugins
+
+If there is an interface from tempest that you need to rely on in your plugin
+which is not listed above, it likely needs to be migrated to tempest.lib. In
+that situation, file a bug, push a migration patch, etc. to expedite providing
+the interface in a reliable manner.
 
 Plugin Cookiecutter
 -------------------
@@ -49,12 +61,45 @@
 to the "tempest.test_plugins" namespace.
 
 If you are using pbr this is fairly straightforward, in the setup.cfg just add
-something like the following::
+something like the following:
+
+.. code-block:: ini
 
   [entry_points]
   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
 ============
 
@@ -62,19 +107,17 @@
 your plugin you need to create a plugin class which tempest will load and call
 to get information when it needs. To simplify creating this tempest provides an
 abstract class that should be used as the parent for your plugin. To use this
-you would do something like the following::
+you would do something like the following:
+
+.. code-block:: python
 
   from tempest.test_discover import plugins
 
   class MyPlugin(plugins.TempestPlugin):
 
-Then you need to ensure you locally define all of the methods in the abstract
-class, you can refer to the api doc below for a reference of what that entails.
-
-Also, note eventually this abstract class will likely live in tempest-lib, when
-that migration occurs a deprecation shim will be added to tempest so as to not
-break any existing plugins. But, when that occurs migrating to using tempest-lib
-as the source for the abstract class will be prudent.
+Then you need to ensure you locally define all of the mandatory methods in the
+abstract class, you can refer to the api doc below for a reference of what that
+entails.
 
 Abstract Plugin Class
 ---------------------
@@ -126,6 +169,152 @@
 CONF object from tempest. This enables the plugin to add options to both
 existing sections and also create new configuration sections for new options.
 
+Service Clients
+---------------
+
+If a plugin defines a service client, it is beneficial for it to implement the
+``get_service_clients`` method in the plugin class. All service clients which
+are exposed via this interface will be automatically configured and be
+available in any instance of the service clients class, defined in
+``tempest.lib.services.clients.ServiceClients``. In case multiple plugins are
+installed, all service clients from all plugins will be registered, making it
+easy to write tests which rely on multiple APIs whose service clients are in
+different plugins.
+
+Example implementation of ``get_service_clients``:
+
+.. code-block:: python
+
+    def get_service_clients(self):
+        # Example implementation with two service clients
+        my_service1_config = config.service_client_config('my_service')
+        params_my_service1 = {
+            'name': 'my_service_v1',
+            'service_version': 'my_service.v1',
+            'module_path': 'plugin_tempest_tests.services.my_service.v1',
+            'client_names': ['API1Client', 'API2Client'],
+        }
+        params_my_service1.update(my_service_config)
+        my_service2_config = config.service_client_config('my_service')
+        params_my_service2 = {
+            'name': 'my_service_v2',
+            'service_version': 'my_service.v2',
+            'module_path': 'plugin_tempest_tests.services.my_service.v2',
+            'client_names': ['API1Client', 'API2Client'],
+        }
+        params_my_service2.update(my_service2_config)
+        return [params_my_service1, params_my_service2]
+
+Parameters:
+
+* **name**: Name of the attribute used to access the ``ClientsFactory`` from
+  the ``ServiceClients`` instance. See example below.
+* **service_version**: Tempest enforces a single implementation for each
+  service client. Available service clients are held in a ``ClientsRegistry``
+  singleton, and registered with ``service_version``, which means that
+  ``service_version`` must be unique and it should represent the service API
+  and version implemented by the service client.
+* **module_path**: Relative to the service client module, from the root of the
+  plugin.
+* **client_names**: Name of the classes that implement service clients in the
+  service clients module.
+
+Example usage of the service clients in tests:
+
+.. code-block:: python
+
+   # my_creds is instance of tempest.lib.auth.Credentials
+   # identity_uri is v2 or v3 depending on the configuration
+   from tempest.lib.services import clients
+
+   my_clients = clients.ServiceClients(my_creds, identity_uri)
+   my_service1_api1_client = my_clients.my_service_v1.API1Client()
+   my_service2_api1_client = my_clients.my_service_v2.API1Client(my_args='any')
+
+Automatic configuration and registration of service clients imposes some extra
+constraints on the structure of the configuration options exposed by the
+plugin.
+
+First ``service_version`` should be in the format `service_config[.version]`.
+The `.version` part is optional, and should only be used if there are multiple
+versions of the same API available. The `service_config` must match the name of
+a configuration options group defined by the plugin. Different versions of one
+API must share the same configuration group.
+
+Second the configuration options group `service_config` must contain the
+following options:
+
+* `catalog_type`: corresponds to `service` in the catalog
+* `endpoint_type`
+
+The following options will be honoured if defined, but they are not mandatory,
+as they do not necessarily apply to all service clients.
+
+* `region`: default to identity.region
+* `build_timeout` : default to compute.build_timeout
+* `build_interval`: default to compute.build_interval
+
+Third the service client classes should inherit from ``RestClient``, should
+accept generic keyword arguments, and should pass those arguments to the
+``__init__`` method of ``RestClient``. Extra arguments can be added. For
+instance:
+
+.. code-block:: python
+
+   class MyAPIClient(rest_client.RestClient):
+
+    def __init__(self, auth_provider, service, region,
+                 my_arg, my_arg2=True, **kwargs):
+        super(MyAPIClient, self).__init__(
+            auth_provider, service, region, **kwargs)
+        self.my_arg = my_arg
+        self.my_args2 = my_arg
+
+Finally the service client should be structured in a python module, so that all
+service client classes are importable from it. Each major API version should
+have its own module.
+
+The following folder and module structure is recommended for a single major
+API version::
+
+    plugin_dir/
+      services/
+        __init__.py
+        client_api_1.py
+        client_api_2.py
+
+The content of __init__.py module should be:
+
+.. code-block:: python
+
+   from client_api_1.py import API1Client
+   from client_api_2.py import API2Client
+
+   __all__ = ['API1Client', 'API2Client']
+
+The following folder and module structure is recommended for multiple major
+API version::
+
+    plugin_dir/
+      services/
+        v1/
+           __init__.py
+           client_api_1.py
+           client_api_2.py
+        v2/
+           __init__.py
+           client_api_1.py
+           client_api_2.py
+
+The content each of __init__.py module under vN should be:
+
+.. code-block:: python
+
+   from client_api_1.py import API1Client
+   from client_api_2.py import API2Client
+
+   __all__ = ['API1Client', 'API2Client']
+
 Using Plugins
 =============
 
diff --git a/doc/source/run.rst b/doc/source/run.rst
new file mode 100644
index 0000000..ce7f03e
--- /dev/null
+++ b/doc/source/run.rst
@@ -0,0 +1,7 @@
+.. _tempest_run:
+
+-----------
+Tempest Run
+-----------
+
+.. automodule:: tempest.cmd.run
diff --git a/doc/source/subunit_describe_calls.rst b/doc/source/subunit_describe_calls.rst
new file mode 100644
index 0000000..2bda50c
--- /dev/null
+++ b/doc/source/subunit_describe_calls.rst
@@ -0,0 +1,5 @@
+------------------------------
+Subunit Describe Calls Utility
+------------------------------
+
+.. automodule:: tempest.cmd.subunit_describe_calls
diff --git a/doc/source/test-removal.rst b/doc/source/test-removal.rst
new file mode 100644
index 0000000..79a5846
--- /dev/null
+++ b/doc/source/test-removal.rst
@@ -0,0 +1,168 @@
+Tempest Test Removal Procedure
+==============================
+
+Historically tempest was the only way of doing functional testing and
+integration testing in OpenStack. This was mostly only an artifact of tempest
+being the only proven pattern for doing this, not an artifact of a design
+decision. However, moving forward as functional testing is being spun up in
+each individual project we really only want tempest to be the integration test
+suite it was intended to be; testing the high level interactions between
+projects through REST API requests. In this model there are probably existing
+tests that aren't the best fit living in tempest. However, since tempest is
+largely still the only gating test suite in this space we can't carelessly rip
+out everything from the tree. This document outlines the procedure which was
+developed to ensure we minimize the risk for removing something of value from
+the tempest tree.
+
+This procedure might seem overly conservative and slow paced, but this is by
+design to try and ensure we don't remove something that is actually providing
+value. Having potential duplication between testing is not a big deal
+especially compared to the alternative of removing something which is actually
+providing value and is actively catching bugs, or blocking incorrect patches
+from landing.
+
+Proposing a test removal
+------------------------
+
+3 prong rule for removal
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+In the proposal etherpad we'll be looking for answers to 3 questions
+
+ #. The tests proposed for removal must have equiv. coverage in a different
+    project's test suite (whether this is another gating test project, or an in
+    tree functional test suite). For API tests preferably the other project will
+    have a similar source of friction in place to prevent breaking api changes
+    so that we don't regress and let breaking api changes slip through the
+    gate.
+ #. The test proposed for removal has a failure rate <  0.50% in the gate over
+    the past release (the value and interval will likely be adjusted in the
+    future)
+ #. There must not be an external user/consumer of tempest that depends on the
+    test proposed for removal
+
+The answers to 1 and 2 are easy to verify. For 1 just provide a link to the new
+test location. If you are linking to the tempest removal patch please also put
+a Depends-On in the commit message for the commit which moved the test into
+another repo.
+
+For prong 2 you can use OpenStack-Health:
+
+Using OpenStack-Health
+""""""""""""""""""""""
+
+Go to: http://status.openstack.org/openstack-health and then navigate to a per
+test page for six months. You'll end up with a page that will graph the success
+and failure rates on the bottom graph. For example, something like `this URL`_.
+
+.. _this URL: http://status.openstack.org/openstack-health/#/test/tempest.scenario.test_volume_boot_pattern.TestVolumeBootPatternV2.test_volume_boot_pattern?groupKey=project&resolutionKey=day&duration=P6M
+
+The Old Way using subunit2sql directly
+""""""""""""""""""""""""""""""""""""""
+
+SELECT * from tests where test_id like "%test_id%";
+(where $test_id is the full test_id, but truncated to the class because of
+setupClass or tearDownClass failures)
+
+You can access the infra mysql subunit2sql db w/ read-only permissions with:
+
+ * hostname: logstash.openstack.org
+ * username: query
+ * password: query
+ * db_name: subunit2sql
+
+For example if you were trying to remove the test with the id:
+tempest.api.compute.admin.test_flavors_negative.FlavorsAdminNegativeTestJSON.test_get_flavor_details_for_deleted_flavor
+you would run the following:
+
+ #. run: "mysql -u query -p -h logstash.openstack.org subunit2sql" to connect
+    to the subunit2sql db
+ #. run the query: MySQL [subunit2sql]> select * from tests where test_id like
+    "tempest.api.compute.admin.test_flavors_negative.FlavorsAdminNegativeTestJSON%";
+    which will return a table of all the tests in the class (but it will also
+    catch failures in setupClass and tearDownClass)
+ #. paste the output table with numbers and the mysql command you ran to
+    generate it into the etherpad.
+
+Eventually a cli interface will be created to make that a bit more friendly.
+Also a dashboard is in the works so we don't need to manually run the command.
+
+The intent of the 2nd prong is to verify that moving the test into a project
+specific testing is preventing bugs (assuming the tempest tests were catching
+issues) from bubbling up a layer into tempest jobs. If we're seeing failure
+rates above a certain threshold in the gate checks that means the functional
+testing isn't really being effective in catching that bug (and therefore
+blocking it from landing) and having the testing run in tempest still has
+value.
+
+However for the 3rd prong verification is a bit more subjective. The original
+intent of this prong was mostly for refstack/defcore and also for things that
+running on the stable branches. We don't want to remove any tests if that
+would break our api consistency checking between releases, or something that
+defcore/refstack is depending on being in tempest. It's worth pointing out
+that if a test is used in defcore as part of interop testing then it will
+probably have continuing value being in tempest as part of the
+integration/integrated tests in general. This is one area where some overlap
+is expected between testing in projects and tempest, which is not a bad thing.
+
+Discussing the 3rd prong
+""""""""""""""""""""""""
+
+There are 2 approaches to addressing the 3rd prong. Either it can be raised
+during a qa meeting during the tempest discussion. Please put it on the agenda
+well ahead of the scheduled meeting. Since the meeting time will be well known
+ahead of time anyone who depends on the tests will have ample time beforehand
+to outline any concerns on the before the meeting. To give ample time for
+people to respond to removal proposals please add things to the agenda by the
+Monday before the meeting.
+
+The other option is to raise the removal on the openstack-dev mailing list.
+(for example see: http://lists.openstack.org/pipermail/openstack-dev/2016-February/086218.html )
+This will raise the issue to the wider community and attract at least the same
+(most likely more) attention than discussing it during the irc meeting. The
+only downside is that it might take more time to get a response, given the
+nature of ML.
+
+Exceptions to this procedure
+----------------------------
+
+For the most part all tempest test removals have to go through this procedure
+there are a couple of exceptions though:
+
+ #. The class of testing has been decided to be outside the scope of tempest.
+ #. A revert for a patch which added a broken test, or testing which didn't
+    actually run in the gate (basically any revert for something which
+    shouldn't have been added)
+
+For the first exception type the only types of testing in tree which have been
+declared out of scope at this point are:
+
+ * The CLI tests (which should be completely removed at this point)
+ * Neutron Adv. Services testing (which should be completely removed at this
+   point)
+ * XML API Tests (which should be completely removed at this point)
+ * EC2 API/boto tests (which should be completely removed at this point)
+
+For tests that fit into this category the only criteria for removal is that
+there is equivalent testing elsewhere.
+
+Tempest Scope
+^^^^^^^^^^^^^
+
+Also starting in the liberty cycle tempest has defined a set of projects which
+are defined as in scope for direct testing in tempest. As of today that list
+is:
+
+ * Keystone
+ * Nova
+ * Glance
+ * Cinder
+ * Neutron
+ * Swift
+
+anything that lives in tempest which doesn't test one of these projects can be
+removed assuming there is equivalent testing elsewhere. Preferably using the
+`tempest plugin mechanism`_
+to maintain continuity after migrating the tests out of tempest.
+
+.. _tempest plugin mechanism: http://docs.openstack.org/developer/tempest/plugin.html
diff --git a/doc/source/workspace.rst b/doc/source/workspace.rst
new file mode 100644
index 0000000..41325b2
--- /dev/null
+++ b/doc/source/workspace.rst
@@ -0,0 +1,5 @@
+-----------------
+Tempest Workspace
+-----------------
+
+.. automodule:: tempest.cmd.workspace
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/etc/javelin-resources.yaml.sample b/etc/javelin-resources.yaml.sample
deleted file mode 100644
index fb270a4..0000000
--- a/etc/javelin-resources.yaml.sample
+++ /dev/null
@@ -1,65 +0,0 @@
-tenants:
-  - javelin
-  - discuss
-
-users:
-  - name: javelin
-    pass: gungnir
-    tenant: javelin
-  - name: javelin2
-    pass: gungnir2
-    tenant: discuss
-
-secgroups:
-  - name: secgroup1
-    owner: javelin
-    description: SecurityGroup1
-    rules:
-      - 'icmp -1 -1 0.0.0.0/0'
-      - 'tcp 22 22 0.0.0.0/0'
-  - name: secgroup2
-    owner: javelin2
-    description: SecurityGroup2
-    rules:
-      - 'tcp 80 80 0.0.0.0/0'
-
-images:
-  - name: cirros1
-    owner: javelin
-    imgdir: images
-    file: cirros.img
-    container_format: bare
-    disk_format: qcow2
-  - name: cirros2
-    owner: javelin2
-    imgdir: files/images/cirros-0.3.2-x86_64-uec
-    file: cirros-0.3.2-x86_64-blank.img
-    container_format: ami
-    disk_format: ami
-    aki: cirros-0.3.2-x86_64-vmlinuz
-    ari: cirros-0.3.2-x86_64-initrd
-
-networks:
-  - name: network1
-    owner: javelin
-  - name: network2
-    owner: javelin2
-
-subnets:
-  - name: net1-subnet1
-    range: 10.1.0.0/24
-    network: network1
-    owner: javelin
-  - name: net2-subnet2
-    range: 192.168.1.0/24
-    network: network2
-    owner: javelin2
-
-objects:
-  - container: container1
-    name: object1
-    owner: javelin
-    file: /etc/hosts
-    swift_role: Member
-
-telemetry: true
\ No newline at end of file
diff --git a/etc/logging.conf.sample b/etc/logging.conf.sample
index 36cd324..c131b07 100644
--- a/etc/logging.conf.sample
+++ b/etc/logging.conf.sample
@@ -1,5 +1,5 @@
 [loggers]
-keys=root,tempest_stress
+keys=root
 
 [handlers]
 keys=file,devel,syslog
@@ -11,11 +11,6 @@
 level=DEBUG
 handlers=file
 
-[logger_tempest_stress]
-level=DEBUG
-handlers=file,devel
-qualname=tempest.stress
-
 [handler_file]
 class=FileHandler
 level=DEBUG
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/10.0-supported-openstack-releases-b88db468695348f6.yaml b/releasenotes/notes/10.0-supported-openstack-releases-b88db468695348f6.yaml
new file mode 100644
index 0000000..217c2f6
--- /dev/null
+++ b/releasenotes/notes/10.0-supported-openstack-releases-b88db468695348f6.yaml
@@ -0,0 +1,13 @@
+---
+other:
+    - OpenStack Releases Supported at this time are the same as in the
+      previous release 9,
+        **Kilo** and
+        **Liberty**.
+
+
+      The release under current development as of this tag is Mitaka,
+      meaning that every Tempest commit is also tested against master during
+      the Mitaka cycle. However, this does not necessarily mean that using
+      Tempest as of this tag will work against a Mitaka (or future releases)
+      cloud.
diff --git a/releasenotes/notes/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml b/releasenotes/notes/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
new file mode 100644
index 0000000..0ed3130
--- /dev/null
+++ b/releasenotes/notes/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
@@ -0,0 +1,11 @@
+---
+prelude: |
+    This release includes the addition of the stable library interface for
+    tempest. This behaves just as tempest-lib did prior to this, but instead
+    it lives directly in the tempest project. For more information refer to
+    the `library docs`_.
+
+    .. _library docs: http://docs.openstack.org/developer/tempest/library.html#library
+
+features:
+  - Tempest library interface
diff --git a/releasenotes/notes/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml b/releasenotes/notes/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml
new file mode 100644
index 0000000..0bc9af5
--- /dev/null
+++ b/releasenotes/notes/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml
@@ -0,0 +1,3 @@
+---
+other:
+  - Start using reno for managing release notes.
diff --git a/releasenotes/notes/11.0.0-api-microversion-testing-support-2ceddd2255670932.yaml b/releasenotes/notes/11.0.0-api-microversion-testing-support-2ceddd2255670932.yaml
new file mode 100644
index 0000000..e98671a
--- /dev/null
+++ b/releasenotes/notes/11.0.0-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/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml b/releasenotes/notes/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml
new file mode 100644
index 0000000..de1b35e
--- /dev/null
+++ b/releasenotes/notes/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml
@@ -0,0 +1,3 @@
+---
+features:
+  - Compute Microversion testing support in Service Clients.
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/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml b/releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
new file mode 100644
index 0000000..9baf035
--- /dev/null
+++ b/releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
@@ -0,0 +1,12 @@
+---
+prelude: >
+    This release is marking the end of Kilo release support in Tempest
+other:
+    - OpenStack Releases Supported after this release are **Liberty**
+      and **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/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml b/releasenotes/notes/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml
new file mode 100644
index 0000000..07e3151
--- /dev/null
+++ b/releasenotes/notes/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - Adds a network version client for querying
+    Neutron's API version discovery URL ("GET /").
diff --git a/releasenotes/notes/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml b/releasenotes/notes/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml
new file mode 100644
index 0000000..297279f
--- /dev/null
+++ b/releasenotes/notes/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - Tempest library auth interface now supports scope. Scope allows to control
+    the scope of tokens requested via the identity API. Identity V2 supports
+    unscoped and project scoped tokens, but only the latter are implemented.
+    Identity V3 supports unscoped, project and domain scoped token, all three
+    are available.
\ No newline at end of file
diff --git a/releasenotes/notes/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml b/releasenotes/notes/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml
new file mode 100644
index 0000000..429bf52
--- /dev/null
+++ b/releasenotes/notes/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - Adds the tempest run command to the unified tempest CLI. This new command
+    is used for running tempest tests.
diff --git a/releasenotes/notes/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml b/releasenotes/notes/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml
new file mode 100644
index 0000000..9a1cef6
--- /dev/null
+++ b/releasenotes/notes/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - Adds tempest workspaces command and WorkspaceManager.
+    This is used to have a centralized repository for managing
+    different tempest configurations.
diff --git a/releasenotes/notes/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml b/releasenotes/notes/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml
new file mode 100644
index 0000000..092014e
--- /dev/null
+++ b/releasenotes/notes/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Adds subunit-describe-calls. A parser for subunit streams to determine what
+    REST API calls are made inside of a test and in what order they are called.
+
+      * Input can be piped in or a file can be specified
+      * Output is shortened for stdout, the output file has more information
diff --git a/releasenotes/notes/12.1.0-bug-1486834-7ebca15836ae27a9.yaml b/releasenotes/notes/12.1.0-bug-1486834-7ebca15836ae27a9.yaml
new file mode 100644
index 0000000..b2190f3
--- /dev/null
+++ b/releasenotes/notes/12.1.0-bug-1486834-7ebca15836ae27a9.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Tempest library auth interface now supports
+    filtering with catalog name.  Note that filtering by
+    name is only successful if a known service type is
+    provided.
diff --git a/releasenotes/notes/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml b/releasenotes/notes/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml
new file mode 100644
index 0000000..f9173a0
--- /dev/null
+++ b/releasenotes/notes/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml
@@ -0,0 +1,13 @@
+---
+features:
+  - |
+    Define identity service clients as libraries
+    The following identity service clients are defined as library interface,
+    so the other projects can use these modules as stable libraries without
+    any maintenance changes.
+
+      * endpoints_client(v2)
+      * roles_client(v2)
+      * services_client(v2)
+      * tenants_client(v2)
+      * users_client(v2)
diff --git a/releasenotes/notes/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml b/releasenotes/notes/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml
new file mode 100644
index 0000000..1fa4ddd
--- /dev/null
+++ b/releasenotes/notes/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml
@@ -0,0 +1,15 @@
+---
+features:
+  - |
+    Define image service clients as libraries
+    The following image service clients are defined as library interface,
+    so the other projects can use these modules as stable libraries
+    without any maintenance changes.
+
+      * image_members_client(v1)
+      * images_client(v1)
+      * image_members_client(v2)
+      * images_client(v2)
+      * namespaces_client(v2)
+      * resource_types_client(v2)
+      * schemas_client(v2)
diff --git a/releasenotes/notes/12.1.0-new-test-utils-module-adf34468c4d52719.yaml b/releasenotes/notes/12.1.0-new-test-utils-module-adf34468c4d52719.yaml
new file mode 100644
index 0000000..55df2b3
--- /dev/null
+++ b/releasenotes/notes/12.1.0-new-test-utils-module-adf34468c4d52719.yaml
@@ -0,0 +1,11 @@
+---
+features:
+  - A new `test_utils` module has been added to tempest.lib.common.utils. It
+    should hold any common utility functions that help writing Tempest tests.
+  - A new utility function called `call_and_ignore_notfound_exc` has been
+    added to the `test_utils` module. That function call another function
+    passed as parameter and ignore the NotFound exception if it raised.
+deprecations:
+  - tempest.lib.common.utils.misc.find_test_caller has been moved into the
+    tempest.lib.common.utils.test_utils module. Calling the find_test_caller
+    function with its old location is deprecated.
diff --git a/releasenotes/notes/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml b/releasenotes/notes/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml
new file mode 100644
index 0000000..4ee883f
--- /dev/null
+++ b/releasenotes/notes/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml
@@ -0,0 +1,11 @@
+---
+upgrade:
+  - The input scenarios functionality no longer exists in tempest. This caused
+    a large number of issues for limited benefit and was only used by a single
+    test, test_server_basic_ops. If you were using this functionality you'll
+    now have to do it manually with a script and/or tempest workspaces
+deprecations:
+  - All the options in the input-scenario group are now deprecated. These were
+    only used in tree by the now removed input scenarios functionality in
+    test_server_basic_ops. They were only deprecated because there could be
+    external consumers via plugins. They will be removed during the Ocata cycle.
diff --git a/releasenotes/notes/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml b/releasenotes/notes/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml
new file mode 100644
index 0000000..294f6d9
--- /dev/null
+++ b/releasenotes/notes/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+  - The integrated dashboard scenario test has been
+    removed and is now in a separate tempest plugin
+    tempest-horizon. The removed test coverage can be
+    used by installing tempest-horizon on the server
+    where you run tempest.
diff --git a/releasenotes/notes/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml b/releasenotes/notes/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
new file mode 100644
index 0000000..89b3f41
--- /dev/null
+++ b/releasenotes/notes/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+  - The deprecated legacy credential provider has been removed. The only way to
+    configure credentials in tempest now is to use the dynamic or preprovisioned
+    credential providers
diff --git a/releasenotes/notes/12.1.0-remove-trove-tests-666522e9113549f9.yaml b/releasenotes/notes/12.1.0-remove-trove-tests-666522e9113549f9.yaml
new file mode 100644
index 0000000..7a1fc36
--- /dev/null
+++ b/releasenotes/notes/12.1.0-remove-trove-tests-666522e9113549f9.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+  - All tests for the Trove project have been removed from tempest. They now
+    live as a tempest plugin in the trove project.
diff --git a/releasenotes/notes/12.1.0-routers-client-as-library-25a363379da351f6.yaml b/releasenotes/notes/12.1.0-routers-client-as-library-25a363379da351f6.yaml
new file mode 100644
index 0000000..35cf2c4
--- /dev/null
+++ b/releasenotes/notes/12.1.0-routers-client-as-library-25a363379da351f6.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - Define routers_client as stable library interface.
+    The routers_client module is defined as library interface,
+    so the other projects can use the module as stable library
+    without any maintenance changes.
diff --git a/releasenotes/notes/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml b/releasenotes/notes/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml
new file mode 100644
index 0000000..eb45523
--- /dev/null
+++ b/releasenotes/notes/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - The RestClient (in tempest.lib.common.rest_client) now supports POSTing
+    and PUTing data with chunked transfer encoding. Just pass an `iterable`
+    object as the `body` argument and set the `chunked` argument to `True`.
+  - A new generator called `chunkify` is added in
+    tempest.lib.common.utils.data_utils that yields fixed-size chunks (slices)
+    from a Python sequence.
+
diff --git a/releasenotes/notes/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml b/releasenotes/notes/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
new file mode 100644
index 0000000..eeda921
--- /dev/null
+++ b/releasenotes/notes/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
@@ -0,0 +1,12 @@
+---
+upgrade:
+  - The location on disk that the *tempest init* command looks for has changed.
+    Previously it would attempt to use python packaging's data files to guess
+    where setuptools/distutils were installing data files, which was incredibly
+    unreliable and depended on how you installed tempest and which versions of
+    setuptools, distutils, and python you had installed. Instead, now it will
+    use either /etc/tempest, $XDG_CONFIG_PATH/.config/tempest, or
+    ~/.tempest/etc (attempted in that order). If none of these exist it will
+    create an empty ~/.tempest/etc directory. If you were relying on the
+    previous behavior and none of these directories were being used you will
+    need to move the files to live in one of these directories.
diff --git a/releasenotes/notes/12.2.0-add-httptimeout-in-restclient-ax78061900e3f3d7.yaml b/releasenotes/notes/12.2.0-add-httptimeout-in-restclient-ax78061900e3f3d7.yaml
new file mode 100644
index 0000000..a360f8e
--- /dev/null
+++ b/releasenotes/notes/12.2.0-add-httptimeout-in-restclient-ax78061900e3f3d7.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - RestClient now supports setting timeout in urllib3.poolmanager.
+    Clients will use CONF.service_clients.http_timeout for timeout
+    value to wait for http request to response.
+  - KeystoneAuthProvider will accept http_timeout and will use it in
+    get_credentials.
diff --git a/releasenotes/notes/12.2.0-add-new-identity-clients-3c3afd674a395bde.yaml b/releasenotes/notes/12.2.0-add-new-identity-clients-3c3afd674a395bde.yaml
new file mode 100644
index 0000000..3ec8b56
--- /dev/null
+++ b/releasenotes/notes/12.2.0-add-new-identity-clients-3c3afd674a395bde.yaml
@@ -0,0 +1,13 @@
+---
+features:
+  - |
+    Define identity service clients as libraries.
+    The following identity service clients are defined as library interface,
+    so the other projects can use these modules as stable libraries without
+    any maintenance changes.
+
+     * endpoints_client(v3)
+     * policies_client (v3)
+     * regions_client(v3)
+     * services_client(v3)
+     * projects_client(v3)
diff --git a/releasenotes/notes/12.2.0-clients_module-16f3025f515bf9ec.yaml b/releasenotes/notes/12.2.0-clients_module-16f3025f515bf9ec.yaml
new file mode 100644
index 0000000..484d543
--- /dev/null
+++ b/releasenotes/notes/12.2.0-clients_module-16f3025f515bf9ec.yaml
@@ -0,0 +1,18 @@
+---
+features:
+  - The Tempest plugin interface contains a new optional method, which allows
+    plugins to declare and automatically register any service client defined
+    in the plugin.
+  - tempest.lib exposes a new stable interface, the clients module and
+    ServiceClients class, which provides a convinient way for plugin tests to
+    access service clients defined in Tempest as well as service clients
+    defined in all loaded plugins.
+    The new ServiceClients class only exposes for now the service clients
+    which are in tempest.lib, i.e. compute, network and image. The remaining
+    service clients (identity, volume and object-storage) will be added in
+    future updates.
+deprecations:
+  - The new clients module provides a stable alternative to tempest classes
+    manager.Manager and clients.Manager. manager.Manager only exists now
+    to smoothen the transition of plugins to the new interface, but it will
+    be removed shortly without further notice.
diff --git a/releasenotes/notes/12.2.0-nova_cert_default-90eb7c1e3cde624a.yaml b/releasenotes/notes/12.2.0-nova_cert_default-90eb7c1e3cde624a.yaml
new file mode 100644
index 0000000..cfe97c5
--- /dev/null
+++ b/releasenotes/notes/12.2.0-nova_cert_default-90eb7c1e3cde624a.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+
+  - The ``nova_cert`` option default is changed to ``False``. The nova
+    certification management APIs were a hold over from ec2, and are
+    not used by any other parts of nova. They are deprecated for
+    removal in nova after the newton release. This makes false a more
+    sensible default going forward.
\ No newline at end of file
diff --git a/releasenotes/notes/12.2.0-plugin-service-client-registration-00b19a2dd4935ba0.yaml b/releasenotes/notes/12.2.0-plugin-service-client-registration-00b19a2dd4935ba0.yaml
new file mode 100644
index 0000000..64f729a
--- /dev/null
+++ b/releasenotes/notes/12.2.0-plugin-service-client-registration-00b19a2dd4935ba0.yaml
@@ -0,0 +1,12 @@
+---
+features:
+  - A new optional interface `TempestPlugin.get_service_clients`
+    is available to plugins. It allows them to declare
+    any service client they implement. For now this is used by
+    tempest only, for auto-registration of service clients
+    in the new class `ServiceClients`.
+  - A new singleton class `clients.ClientsRegistry` is
+    available. It holds the service clients registration data
+    from all plugins. It is used by `ServiceClients` for
+    auto-registration of the service clients implemented
+    in plugins.
diff --git a/releasenotes/notes/12.2.0-remove-javelin-276f62d04f7e4a1d.yaml b/releasenotes/notes/12.2.0-remove-javelin-276f62d04f7e4a1d.yaml
new file mode 100644
index 0000000..8e893b8
--- /dev/null
+++ b/releasenotes/notes/12.2.0-remove-javelin-276f62d04f7e4a1d.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+  - The previously deprecated Javelin utility has been removed from Tempest.
+    As an alternative Ansible can be used to construct similar yaml workflows
+    to what Javelin used to provide.
diff --git a/releasenotes/notes/12.2.0-service_client_config-8a1d7b4de769c633.yaml b/releasenotes/notes/12.2.0-service_client_config-8a1d7b4de769c633.yaml
new file mode 100644
index 0000000..3e43f9a
--- /dev/null
+++ b/releasenotes/notes/12.2.0-service_client_config-8a1d7b4de769c633.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - A new helper method `service_client_config` has been added
+    to the stable module config.py that returns extracts from
+    configuration into a dictionary the configuration settings
+    relevant for the initisialisation of a service client.
diff --git a/releasenotes/notes/12.2.0-volume-clients-as-library-9a3444dd63c134b3.yaml b/releasenotes/notes/12.2.0-volume-clients-as-library-9a3444dd63c134b3.yaml
new file mode 100644
index 0000000..cf504ad
--- /dev/null
+++ b/releasenotes/notes/12.2.0-volume-clients-as-library-9a3444dd63c134b3.yaml
@@ -0,0 +1,18 @@
+---
+features:
+  - |
+    Define volume service clients as libraries
+    The following volume service clients are defined as library interface,
+    so the other projects can use these modules as stable libraries
+    without any maintenance changes.
+
+      * availability_zone_client(v1)
+      * availability_zone_client(v2)
+      * extensions_client(v1)
+      * extensions_client(v2)
+      * hosts_client(v1)
+      * hosts_client(v2)
+      * quotas_client(v1)
+      * quotas_client(v2)
+      * services_client(v1)
+      * services_client(v2)
diff --git a/releasenotes/notes/13.0.0-add-new-identity-clients-as-library-5f7ndha733nwdsn9.yaml b/releasenotes/notes/13.0.0-add-new-identity-clients-as-library-5f7ndha733nwdsn9.yaml
new file mode 100644
index 0000000..9e828f6
--- /dev/null
+++ b/releasenotes/notes/13.0.0-add-new-identity-clients-as-library-5f7ndha733nwdsn9.yaml
@@ -0,0 +1,15 @@
+---
+features:
+  - |
+    Define identity service clients as libraries.
+    Add new service clients to the library interface so the other projects can use these modules as stable libraries without
+    any maintenance changes.
+
+      * identity_client(v2)
+      * groups_client(v3)
+      * trusts_client(v3)
+      * users_client(v3)
+      * identity_client(v3)
+      * roles_client(v3)
+      * inherited_roles_client(v3)
+      * credentials_client(v3)
diff --git a/releasenotes/notes/13.0.0-add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml b/releasenotes/notes/13.0.0-add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml
new file mode 100644
index 0000000..9cfce0d
--- /dev/null
+++ b/releasenotes/notes/13.0.0-add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml
@@ -0,0 +1,16 @@
+---
+features:
+  - |
+    Define volume service clients as libraries.
+    The following volume service clients are defined as library interface,
+    so the other projects can use these modules as stable libraries without
+    any maintenance changes.
+
+    * backups_client
+    * encryption_types_client (v1)
+    * encryption_types_client (v2)
+    * qos_clients (v1)
+    * qos_clients (v2)
+    * snapshots_client (v1)
+    * snapshots_client (v2)
+
diff --git a/releasenotes/notes/13.0.0-deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml b/releasenotes/notes/13.0.0-deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml
new file mode 100644
index 0000000..0884cfa
--- /dev/null
+++ b/releasenotes/notes/13.0.0-deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml
@@ -0,0 +1,4 @@
+---
+deprecations:
+  - Oslo.utils provides same method get_ipv6_addr_by_EUI64,
+    so deprecate it in Newton and remove it in Ocata.
diff --git a/releasenotes/notes/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml b/releasenotes/notes/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml
new file mode 100644
index 0000000..543cf7b
--- /dev/null
+++ b/releasenotes/notes/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml
@@ -0,0 +1,5 @@
+---
+deprecations:
+  - The ``call_until_true`` function is moved from the ``tempest.test`` module
+   to the ``tempest.lib.common.utils.test_utils`` module. Backward
+   compatibilty is preserved until Ocata.
diff --git a/releasenotes/notes/13.0.0-start-of-newton-support-3ebb274f300f28eb.yaml b/releasenotes/notes/13.0.0-start-of-newton-support-3ebb274f300f28eb.yaml
new file mode 100644
index 0000000..b9b6fb5
--- /dev/null
+++ b/releasenotes/notes/13.0.0-start-of-newton-support-3ebb274f300f28eb.yaml
@@ -0,0 +1,13 @@
+---
+prelude: >
+    This release is marking the start of Newton release support in Tempest
+other:
+  - |
+    OpenStack releases supported at this time are **Liberty**, **Mitaka**,
+    and **Newton**.
+
+    The release under current development as of this tag is Ocata,
+    meaning that every Tempest commit is also tested against master during
+    the Ocata cycle. However, this does not necessarily mean that using
+    Tempest as of this tag will work against a Ocata (or future releases)
+    cloud.
diff --git a/releasenotes/notes/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml b/releasenotes/notes/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml
new file mode 100644
index 0000000..20f310d
--- /dev/null
+++ b/releasenotes/notes/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+  - the already depreacted tempest-cleanup standalone command has been
+    removed. The corresponding functionalities can be accessed through
+    the unified `tempest` command (`tempest cleanup`).
diff --git a/releasenotes/notes/13.0.0-volume-clients-as-library-660811011be29d1a.yaml b/releasenotes/notes/13.0.0-volume-clients-as-library-660811011be29d1a.yaml
new file mode 100644
index 0000000..9e9eff6
--- /dev/null
+++ b/releasenotes/notes/13.0.0-volume-clients-as-library-660811011be29d1a.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Define the v1 and v2 types_client clients for the volume service as
+    library interfaces, allowing other projects to use these modules as
+    stable libraries without maintenance changes.
diff --git a/releasenotes/notes/14.0.0-add-cred-provider-abstract-class-to-lib-70ff513221f8a871.yaml b/releasenotes/notes/14.0.0-add-cred-provider-abstract-class-to-lib-70ff513221f8a871.yaml
new file mode 100644
index 0000000..6f7a411
--- /dev/null
+++ b/releasenotes/notes/14.0.0-add-cred-provider-abstract-class-to-lib-70ff513221f8a871.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - The cred_provider abstract class which serves as the basis for both
+    of tempest's cred providers, pre-provisioned credentials and dynamic
+    credentials, is now a library interface. This provides the common signature
+    required for building a credential provider.
diff --git a/releasenotes/notes/14.0.0-add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml b/releasenotes/notes/14.0.0-add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml
new file mode 100644
index 0000000..432a6b1
--- /dev/null
+++ b/releasenotes/notes/14.0.0-add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - The cred_client module was added to tempest.lib. This module provides a
+    wrapper to the keystone services client which provides a uniform
+    interface that abstracts out the differences between keystone api versions.
diff --git a/releasenotes/notes/14.0.0-add-error-code-translation-to-versions-clients-acbc78292e24b014.yaml b/releasenotes/notes/14.0.0-add-error-code-translation-to-versions-clients-acbc78292e24b014.yaml
new file mode 100644
index 0000000..57bf47c
--- /dev/null
+++ b/releasenotes/notes/14.0.0-add-error-code-translation-to-versions-clients-acbc78292e24b014.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - Add an error translation to list_versions() of versions_client of both
+    compute and network. This can affect users who are expecting that these
+    clients return error status code instead of the exception. It is needed
+    to change the code for handling the exception like the other clients code.
diff --git a/releasenotes/notes/14.0.0-add-image-clients-af94564fb34ddca6.yaml b/releasenotes/notes/14.0.0-add-image-clients-af94564fb34ddca6.yaml
new file mode 100644
index 0000000..7e40fd4
--- /dev/null
+++ b/releasenotes/notes/14.0.0-add-image-clients-af94564fb34ddca6.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    As in the [doc]:
+    http://developer.openstack.org/api-ref/image/v2/metadefs-index.html,
+    there are some apis are not included, add them.
+
+      * namespace_properties_client(v2)
+
diff --git a/releasenotes/notes/14.0.0-add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml b/releasenotes/notes/14.0.0-add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
new file mode 100644
index 0000000..a1edcc5
--- /dev/null
+++ b/releasenotes/notes/14.0.0-add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
@@ -0,0 +1,6 @@
+---
+features:
+    - Define the identity service role_assignments_client as a library.
+      Add role_assignments_client to the library interface so the other
+      projects can use this module as a stable library without any
+      maintenance changes.
diff --git a/releasenotes/notes/14.0.0-add-service-provider-client-cbba77d424a30dd3.yaml b/releasenotes/notes/14.0.0-add-service-provider-client-cbba77d424a30dd3.yaml
new file mode 100644
index 0000000..b504c78
--- /dev/null
+++ b/releasenotes/notes/14.0.0-add-service-provider-client-cbba77d424a30dd3.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - A Neutron Service Providers client is added to deal with resources
+    of the '/service-providers' route.
diff --git a/releasenotes/notes/14.0.0-add-ssh-port-parameter-to-client-6d16c374ac4456c1.yaml b/releasenotes/notes/14.0.0-add-ssh-port-parameter-to-client-6d16c374ac4456c1.yaml
new file mode 100644
index 0000000..b2ad199
--- /dev/null
+++ b/releasenotes/notes/14.0.0-add-ssh-port-parameter-to-client-6d16c374ac4456c1.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - A new optional parameter `port` for ssh client (`tempest.lib.common.ssh.Client`)
+    to specify destination port for a host. The default value is 22.
diff --git a/releasenotes/notes/14.0.0-deprecate-nova-api-extensions-df16b02485dae203.yaml b/releasenotes/notes/14.0.0-deprecate-nova-api-extensions-df16b02485dae203.yaml
new file mode 100644
index 0000000..c2d9a9b
--- /dev/null
+++ b/releasenotes/notes/14.0.0-deprecate-nova-api-extensions-df16b02485dae203.yaml
@@ -0,0 +1,7 @@
+---
+deprecations:
+  - The *api_extensions* config option in the *compute-feature-enabled* group is
+    now deprecated. This option will be removed from tempest when all the
+    OpenStack releases supported by tempest no longer support the API extensions
+    mechanism. This was removed from Nova during the Newton cycle, so this will
+    be removed at the Mitaka EOL.
diff --git a/releasenotes/notes/14.0.0-move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml b/releasenotes/notes/14.0.0-move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml
new file mode 100644
index 0000000..9223ba5
--- /dev/null
+++ b/releasenotes/notes/14.0.0-move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml
@@ -0,0 +1,7 @@
+features:
+  - |
+    Define the Volume v3 service clients as library interfaces,
+    allowing other projects to use these modules as stable
+    libraries without maintenance changes.
+
+    * messages_client(v3)
diff --git a/releasenotes/notes/14.0.0-new-volume-limit-client-517c17d9090f4df4.yaml b/releasenotes/notes/14.0.0-new-volume-limit-client-517c17d9090f4df4.yaml
new file mode 100644
index 0000000..033e147
--- /dev/null
+++ b/releasenotes/notes/14.0.0-new-volume-limit-client-517c17d9090f4df4.yaml
@@ -0,0 +1,3 @@
+---
+features:
+  - The volume_limits client was added to tempest.lib.
diff --git a/releasenotes/notes/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml b/releasenotes/notes/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml
new file mode 100644
index 0000000..aa3a78e
--- /dev/null
+++ b/releasenotes/notes/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+  - The Stress tests framework and all the stress tests have been removed.
+
diff --git a/releasenotes/notes/14.0.0-remove-baremetal-tests-65186d9e15d5b8fb.yaml b/releasenotes/notes/14.0.0-remove-baremetal-tests-65186d9e15d5b8fb.yaml
new file mode 100644
index 0000000..ca2635e
--- /dev/null
+++ b/releasenotes/notes/14.0.0-remove-baremetal-tests-65186d9e15d5b8fb.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+  - All tests for the Ironic project have been removed from Tempest. Those
+    exist as a Tempest plugin in the Ironic project.
diff --git a/releasenotes/notes/14.0.0-remove-bootable-option-024f8944c056a3e0.yaml b/releasenotes/notes/14.0.0-remove-bootable-option-024f8944c056a3e0.yaml
new file mode 100644
index 0000000..e6e53af
--- /dev/null
+++ b/releasenotes/notes/14.0.0-remove-bootable-option-024f8944c056a3e0.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+  - The *bootable* config option in the *volume_feature_enabled* group is
+    removed because the corresponding feature os-set_bootable has been
+    implemented 2.5 years ago and all OpenStack versions which are supported
+    by Tempest should support the feature.
diff --git a/releasenotes/notes/14.0.0-remove-negative-test-generator-1653f4c0f86ccf75.yaml b/releasenotes/notes/14.0.0-remove-negative-test-generator-1653f4c0f86ccf75.yaml
new file mode 100644
index 0000000..a734d15
--- /dev/null
+++ b/releasenotes/notes/14.0.0-remove-negative-test-generator-1653f4c0f86ccf75.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+  - The Negative Tests Generator has been removed (it was not used by any
+    Tempest tests).
diff --git a/releasenotes/notes/14.0.0-remove-sahara-tests-1532c47c7df80e3a.yaml b/releasenotes/notes/14.0.0-remove-sahara-tests-1532c47c7df80e3a.yaml
new file mode 100644
index 0000000..b541cf9
--- /dev/null
+++ b/releasenotes/notes/14.0.0-remove-sahara-tests-1532c47c7df80e3a.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+  - All tests for the Sahara project have been removed from Tempest. They now
+    live as a Tempest plugin in the ``openstack/sahara-tests`` repository.
diff --git a/releasenotes/notes/14.0.0-volume-clients-as-library-309030c7a16e62ab.yaml b/releasenotes/notes/14.0.0-volume-clients-as-library-309030c7a16e62ab.yaml
new file mode 100644
index 0000000..6babd93
--- /dev/null
+++ b/releasenotes/notes/14.0.0-volume-clients-as-library-309030c7a16e62ab.yaml
@@ -0,0 +1,12 @@
+---
+features:
+  - |
+    Define volume service clients as libraries.
+    The following volume service clients are defined as library interface,
+    so the other projects can use these modules as stable libraries without
+    any maintenance changes.
+
+    * volumes_client(v1)
+    * volumes_client(v2)
+    * capabilities_client(v2)
+    * scheduler_stats_client(v2)
diff --git a/releasenotes/notes/add-identity-v3-clients-as-a-library-d34b4fdf376984ad.yaml b/releasenotes/notes/add-identity-v3-clients-as-a-library-d34b4fdf376984ad.yaml
new file mode 100644
index 0000000..1af1939
--- /dev/null
+++ b/releasenotes/notes/add-identity-v3-clients-as-a-library-d34b4fdf376984ad.yaml
@@ -0,0 +1,6 @@
+---
+features:
+    - Define the identity v3 service client domains_client as a library.
+      Add domains_client to the library interface so the other
+      projects can use this module as a stable library without any
+      maintenance changes.
diff --git a/releasenotes/notes/add-image-clients-tests-49dbc0a0a4281a77.yaml b/releasenotes/notes/add-image-clients-tests-49dbc0a0a4281a77.yaml
new file mode 100644
index 0000000..eaab1f0
--- /dev/null
+++ b/releasenotes/notes/add-image-clients-tests-49dbc0a0a4281a77.yaml
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    As in the [doc]:
+    http://developer.openstack.org/api-ref/image/v2/metadefs-index.html,
+    there are some apis are not included, add them.
+
+      * namespace_objects_client(v2)
+      * namespace_tags_client(v2)
+
diff --git a/releasenotes/notes/add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml b/releasenotes/notes/add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml
new file mode 100644
index 0000000..9a4e6b1
--- /dev/null
+++ b/releasenotes/notes/add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Define v2 snapshot_manage_client client for the volume service as
+    library interfaces, allowing other projects to use this module as
+    stable libraries without maintenance changes.
+
+    * snapshot_manage_client(v2)
diff --git a/releasenotes/notes/deprecate-allow_port_security_disabled-option-2d3d87f6bd11d03a.yaml b/releasenotes/notes/deprecate-allow_port_security_disabled-option-2d3d87f6bd11d03a.yaml
new file mode 100644
index 0000000..4acdc6d
--- /dev/null
+++ b/releasenotes/notes/deprecate-allow_port_security_disabled-option-2d3d87f6bd11d03a.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+  - The default value for the ``allow_port_security_disabled`` option in the
+    ``compute-feature-enabled`` section has been changed from ``False``
+    to ``True``.
+deprecations:
+  - The ``allow_port_security_disabled`` option in the
+   ``compute-feature-enabled`` section is now deprecated.
diff --git a/releasenotes/notes/deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml b/releasenotes/notes/deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml
new file mode 100644
index 0000000..c0a06d1
--- /dev/null
+++ b/releasenotes/notes/deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+  - The default value for the ``reseller`` option in the
+    ``identity-feature-enabled`` section has been changed from ``False``
+    to ``True``.
+deprecations:
+  - The ``reseller`` option in the ``identity-feature-enabled`` section is now
+    deprecated.
diff --git a/releasenotes/notes/deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml b/releasenotes/notes/deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml
new file mode 100644
index 0000000..c80f159
--- /dev/null
+++ b/releasenotes/notes/deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+  - The default value for the ``volume_services`` option in the
+    ``volume_feature_enabled`` section has been changed from ``False``
+    to ``True``.
+deprecations:
+  - The ``volume_services`` option in the ``volume_feature_enabled`` section
+    is now deprecated.
diff --git a/tempest/api/baremetal/__init__.py b/releasenotes/source/_static/.placeholder
similarity index 100%
copy from tempest/api/baremetal/__init__.py
copy to releasenotes/source/_static/.placeholder
diff --git a/tempest/api/database/flavors/__init__.py b/releasenotes/source/_templates/.placeholder
similarity index 100%
rename from tempest/api/database/flavors/__init__.py
rename to releasenotes/source/_templates/.placeholder
diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py
new file mode 100644
index 0000000..eec42cd
--- /dev/null
+++ b/releasenotes/source/conf.py
@@ -0,0 +1,279 @@
+# 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.
+
+# tempest Release Notes documentation build configuration file, created by
+# sphinx-quickstart on Tue Nov  3 17:40:50 2015.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    'oslosphinx',
+    'reno.sphinxext',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+# source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'tempest Release Notes'
+copyright = u'2016, tempest Developers'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+from tempest.version import version_info as tempest_version
+# The full version, including alpha/beta/rc tags.
+release = tempest_version.version_string_with_vcs()
+# The short X.Y version.
+version = tempest_version.canonical_version_string()
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+# language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+# today = ''
+# Else, today_fmt is used as the format for a strftime call.
+# today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+# default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+# add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+# add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+# show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+# modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+# keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+# html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+# html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+# html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+# html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+# html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+# html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+# html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+# html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+# html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+# html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+# html_additional_pages = {}
+
+# If false, no module index is generated.
+# html_domain_indices = True
+
+# If false, no index is generated.
+# html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+# html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+# html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+# html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+# html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+# html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+# html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'tempestReleaseNotesdoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+    # The paper size ('letterpaper' or 'a4paper').
+    # 'papersize': 'letterpaper',
+
+    # The font size ('10pt', '11pt' or '12pt').
+    # 'pointsize': '10pt',
+
+    # Additional stuff for the LaTeX preamble.
+    # 'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    ('index', 'olso.configReleaseNotes.tex',
+     u'olso.config Release Notes Documentation',
+     u'tempest Developers', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page
+# latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+# latex_use_parts = False
+
+# If true, show page references after internal links.
+# latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+# latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+# latex_appendices = []
+
+# If false, no module index is generated.
+# latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'olso.configreleasenotes',
+     u'tempest Release Notes Documentation',
+     [u'tempest Developers'], 1)
+]
+
+# If true, show URL addresses after external links.
+# man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    ('index', 'tempestReleaseNotes',
+     u'tempest Release Notes Documentation',
+     u'tempest Developers', 'olso.configReleaseNotes',
+     'An OpenStack library for parsing configuration options from the command'
+     ' line and configuration files.',
+     'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+# texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+# texinfo_no_detailmenu = False
+
+# -- Options for Internationalization output ------------------------------
+locale_dirs = ['locale/']
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
new file mode 100644
index 0000000..8eac1d0
--- /dev/null
+++ b/releasenotes/source/index.rst
@@ -0,0 +1,18 @@
+===========================
+ tempest Release Notes
+===========================
+
+ .. toctree::
+    :maxdepth: 1
+
+    unreleased
+    v13.0.0
+    v12.0.0
+    v11.0.0
+    v10.0.0
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`search`
diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst
new file mode 100644
index 0000000..875030f
--- /dev/null
+++ b/releasenotes/source/unreleased.rst
@@ -0,0 +1,5 @@
+============================
+Current Series Release Notes
+============================
+
+.. release-notes::
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/releasenotes/source/v12.0.0.rst b/releasenotes/source/v12.0.0.rst
new file mode 100644
index 0000000..0bc8343
--- /dev/null
+++ b/releasenotes/source/v12.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v12.0.0 Release Notes
+=====================
+
+.. release-notes:: 12.0.0 Release Notes
+   :version: 12.0.0
diff --git a/releasenotes/source/v13.0.0.rst b/releasenotes/source/v13.0.0.rst
new file mode 100644
index 0000000..39816e4
--- /dev/null
+++ b/releasenotes/source/v13.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v13.0.0 Release Notes
+=====================
+
+.. release-notes:: 13.0.0 Release Notes
+   :version: 13.0.0
diff --git a/requirements.txt b/requirements.txt
index 66e5696..d9a9ebb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,27 +1,25 @@
 # The order of packages is significant, because pip processes them in the order
 # 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
-anyjson>=0.3.3 # BSD
-httplib2>=0.7.5 # MIT
+pbr>=1.8 # Apache-2.0
+cliff>=2.3.0 # Apache-2.0
 jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
 testtools>=1.4.0 # MIT
-paramiko>=1.16.0 # LGPL
-netaddr!=0.7.16,>=0.7.12 # BSD
+paramiko>=2.0 # LGPLv2.1+
+netaddr!=0.7.16,>=0.7.13 # BSD
 testrepository>=0.0.18 # Apache-2.0/BSD
-pyOpenSSL>=0.14 # Apache-2.0
-oslo.concurrency>=2.3.0 # Apache-2.0
-oslo.config>=3.4.0 # Apache-2.0
-oslo.i18n>=2.1.0 # Apache-2.0
-oslo.log>=1.14.0 # Apache-2.0
+oslo.concurrency>=3.8.0 # Apache-2.0
+oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
+oslo.log>=3.11.0 # Apache-2.0
 oslo.serialization>=1.10.0 # Apache-2.0
-oslo.utils>=3.4.0 # Apache-2.0
+oslo.utils>=3.18.0 # Apache-2.0
 six>=1.9.0 # MIT
-iso8601>=0.1.9 # MIT
-fixtures>=1.3.1 # Apache-2.0/BSD
-testscenarios>=0.4 # Apache-2.0/BSD
-tempest-lib>=0.14.0 # Apache-2.0
-PyYAML>=3.1.0 # MIT
-stevedore>=1.5.0 # Apache-2.0
-PrettyTable<0.8,>=0.7 # BSD
+fixtures>=3.0.0 # Apache-2.0/BSD
+PyYAML>=3.10.0 # MIT
+python-subunit>=0.0.18 # Apache-2.0/BSD
+stevedore>=1.17.1 # Apache-2.0
+PrettyTable<0.8,>=0.7.1 # BSD
+os-testr>=0.8.0 # Apache-2.0
+urllib3>=1.15.1 # MIT
+debtcollector>=1.2.0 # Apache-2.0
+unittest2 # BSD
diff --git a/run_tempest.sh b/run_tempest.sh
index 8c8f25f..414146b 100755
--- a/run_tempest.sh
+++ b/run_tempest.sh
@@ -1,5 +1,7 @@
 #!/usr/bin/env bash
 
+echo "WARNING: This script is deprecated and will be removed in the near future. Please migrate to tempest run or another method of launching a test runner"
+
 function usage {
   echo "Usage: $0 [OPTION]..."
   echo "Run Tempest test suite"
@@ -103,22 +105,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..a856bb4 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -17,6 +17,44 @@
   echo "  -- [TESTROPTIONS]        After the first '--' you can pass arbitrary arguments to testr "
 }
 
+function deprecation_warning {
+  cat <<EOF
+-------------------------------------------------------------------------
+WARNING: run_tests.sh is deprecated and this script will be removed after
+the Newton release. All tests should be run through testr/ostestr or tox.
+
+To run style checks:
+
+ tox -e pep8
+
+To run python 2.7 unit tests
+
+ tox -e py27
+
+To run unit tests and generate coverage report
+
+ tox -e cover
+
+To run a subset of any of these tests:
+
+ tox -e py27 someregex
+
+ i.e.: tox -e py27 test_servers
+
+Additional tox targets are available in tox.ini. For more information
+see:
+http://docs.openstack.org/project-team-guide/project-setup/python.html
+
+NOTE: if you want to use testr to run tests, you can instead use:
+
+ OS_TEST_PATH=./tempest/tests testr run
+
+Documentation on using testr directly can be found at
+http://testrepository.readthedocs.org/en/latest/MANUAL.html
+-------------------------------------------------------------------------
+EOF
+}
+
 testrargs=""
 just_pep8=0
 venv=${VENV:-.venv}
@@ -32,6 +70,8 @@
 config_file=""
 update=0
 
+deprecation_warning
+
 if ! options=$(getopt -o VNnfuctphd -l virtual-env,no-virtual-env,no-site-packages,force,update,serial,coverage,pep8,help,debug -- "$@")
 then
     # parse error
@@ -114,22 +154,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/setup.cfg b/setup.cfg
index cc3a365..96313fd 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@
     README.rst
 author = OpenStack
 author-email = openstack-dev@lists.openstack.org
-home-page = http://www.openstack.org/
+home-page = http://docs.openstack.org/developer/tempest/
 classifier =
     Intended Audience :: Information Technology
     Intended Audience :: System Administrators
@@ -17,6 +17,7 @@
     Programming Language :: Python :: 2.7
     Programming Language :: Python :: 3
     Programming Language :: Python :: 3.4
+    Programming Language :: Python :: 3.5
 
 [files]
 packages =
@@ -27,18 +28,19 @@
 [entry_points]
 console_scripts =
     verify-tempest-config = tempest.cmd.verify_tempest_config:main
-    javelin2 = tempest.cmd.javelin:main
-    run-tempest-stress = tempest.cmd.run_stress:main
-    tempest-cleanup = tempest.cmd.cleanup:main
     tempest-account-generator = tempest.cmd.account_generator:main
     tempest = tempest.cmd.main:main
+    skip-tracker = tempest.lib.cmd.skip_tracker:main
+    check-uuid = tempest.lib.cmd.check_uuid:run
+    subunit-describe-calls = tempest.cmd.subunit_describe_calls:entry_point
 tempest.cm =
     account-generator = tempest.cmd.account_generator:TempestAccountGenerator
     init = tempest.cmd.init:TempestInit
     cleanup = tempest.cmd.cleanup:TempestCleanup
-    run-stress = tempest.cmd.run_stress:TempestRunStress
     list-plugins = tempest.cmd.list_plugins:TempestListPlugins
     verify-config = tempest.cmd.verify_tempest_config:TempestVerifyConfig
+    workspace = tempest.cmd.workspace:TempestWorkspace
+    run = tempest.cmd.run:TempestRun
 oslo.config.opts =
     tempest.config = tempest.config:list_opts
 
diff --git a/tempest/README.rst b/tempest/README.rst
index 113b191..0feec41 100644
--- a/tempest/README.rst
+++ b/tempest/README.rst
@@ -15,7 +15,6 @@
 | tempest/
 |    api/ - API tests
 |    scenario/ - complex scenario tests
-|    stress/ - stress tests
 
 Each of these directories contains different types of tests. What
 belongs in each directory, the rules and examples for good tests, are
@@ -26,10 +25,9 @@
 
 API tests are validation tests for the OpenStack API. They should not
 use the existing python clients for OpenStack, but should instead use
-the tempest implementations of clients. This allows us to test both
-XML and JSON. Having raw clients also lets us pass invalid JSON and
-XML to the APIs and see the results, something we could not get with
-the native clients.
+the tempest implementations of clients. Having raw clients let us
+pass invalid JSON to the APIs and see the results, something we could
+not get with the native clients.
 
 When it makes sense, API testing should be moved closer to the
 projects themselves, possibly as functional tests in their unit test
@@ -47,14 +45,6 @@
 but should instead use the tempest implementations of clients.
 
 
-:ref:`stress_field_guide`
--------------------------
-
-Stress tests are designed to stress an OpenStack environment by running a high
-workload against it and seeing what breaks. The stress test framework runs
-several test jobs in parallel and can run any existing test in Tempest as a
-stress job.
-
 :ref:`unit_tests_field_guide`
 -----------------------------
 
diff --git a/tempest/api/baremetal/README.rst b/tempest/api/baremetal/README.rst
deleted file mode 100644
index 759c937..0000000
--- a/tempest/api/baremetal/README.rst
+++ /dev/null
@@ -1,25 +0,0 @@
-Tempest Field Guide to Baremetal API tests
-==========================================
-
-
-What are these tests?
----------------------
-
-These tests stress the OpenStack baremetal provisioning API provided by
-Ironic.
-
-
-Why are these tests in tempest?
-------------------------------
-
-The purpose of these tests is to exercise the various APIs provided by Ironic
-for managing baremetal nodes.
-
-
-Scope of these tests
---------------------
-
-The baremetal API test perform basic CRUD operations on the Ironic node
-inventory.  They do not actually perform hardware provisioning. It is important
-to note that all Ironic API actions are admin operations meant to be used
-either by cloud operators or other OpenStack services (i.e., Nova).
diff --git a/tempest/api/baremetal/admin/__init__.py b/tempest/api/baremetal/admin/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/baremetal/admin/__init__.py
+++ /dev/null
diff --git a/tempest/api/baremetal/admin/base.py b/tempest/api/baremetal/admin/base.py
deleted file mode 100644
index 80b69b9..0000000
--- a/tempest/api/baremetal/admin/base.py
+++ /dev/null
@@ -1,205 +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 functools
-
-from tempest_lib import exceptions as lib_exc
-
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest import test
-
-CONF = config.CONF
-
-
-# NOTE(adam_g): The baremetal API tests exercise operations such as enroll
-# node, power on, power off, etc.  Testing against real drivers (ie, IPMI)
-# will require passing driver-specific data to Tempest (addresses,
-# credentials, etc).  Until then, only support testing against the fake driver,
-# which has no external dependencies.
-SUPPORTED_DRIVERS = ['fake']
-
-# NOTE(jroll): resources must be deleted in a specific order, this list
-# defines the resource types to clean up, and the correct order.
-RESOURCE_TYPES = ['port', 'node', 'chassis']
-
-
-def creates(resource):
-    """Decorator that adds resources to the appropriate cleanup list."""
-
-    def decorator(f):
-        @functools.wraps(f)
-        def wrapper(cls, *args, **kwargs):
-            resp, body = f(cls, *args, **kwargs)
-
-            if 'uuid' in body:
-                cls.created_objects[resource].add(body['uuid'])
-
-            return resp, body
-        return wrapper
-    return decorator
-
-
-class BaseBaremetalTest(test.BaseTestCase):
-    """Base class for Baremetal API tests."""
-
-    credentials = ['admin']
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaseBaremetalTest, cls).skip_checks()
-        if not CONF.service_available.ironic:
-            skip_msg = ('%s skipped as Ironic is not available' % cls.__name__)
-            raise cls.skipException(skip_msg)
-
-        if CONF.baremetal.driver not in SUPPORTED_DRIVERS:
-            skip_msg = ('%s skipped as Ironic driver %s is not supported for '
-                        'testing.' %
-                        (cls.__name__, CONF.baremetal.driver))
-            raise cls.skipException(skip_msg)
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseBaremetalTest, cls).setup_clients()
-        cls.client = cls.os_admin.baremetal_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(BaseBaremetalTest, cls).resource_setup()
-
-        cls.driver = CONF.baremetal.driver
-        cls.power_timeout = CONF.baremetal.power_timeout
-        cls.created_objects = {}
-        for resource in RESOURCE_TYPES:
-            cls.created_objects[resource] = set()
-
-    @classmethod
-    def resource_cleanup(cls):
-        """Ensure that all created objects get destroyed."""
-
-        try:
-            for resource in RESOURCE_TYPES:
-                uuids = cls.created_objects[resource]
-                delete_method = getattr(cls.client, 'delete_%s' % resource)
-                for u in uuids:
-                    delete_method(u, ignore_errors=lib_exc.NotFound)
-        finally:
-            super(BaseBaremetalTest, cls).resource_cleanup()
-
-    @classmethod
-    @creates('chassis')
-    def create_chassis(cls, description=None, expect_errors=False):
-        """Wrapper utility for creating test chassis.
-
-        :param description: A description of the chassis. if not supplied,
-            a random value will be generated.
-        :return: Created chassis.
-
-        """
-        description = description or data_utils.rand_name('test-chassis')
-        resp, body = cls.client.create_chassis(description=description)
-        return resp, body
-
-    @classmethod
-    @creates('node')
-    def create_node(cls, chassis_id, cpu_arch='x86', cpus=8, local_gb=10,
-                    memory_mb=4096):
-        """Wrapper utility for creating test baremetal nodes.
-
-        :param cpu_arch: CPU architecture of the node. Default: x86.
-        :param cpus: Number of CPUs. Default: 8.
-        :param local_gb: Disk size. Default: 10.
-        :param memory_mb: Available RAM. Default: 4096.
-        :return: Created node.
-
-        """
-        resp, body = cls.client.create_node(chassis_id, cpu_arch=cpu_arch,
-                                            cpus=cpus, local_gb=local_gb,
-                                            memory_mb=memory_mb,
-                                            driver=cls.driver)
-
-        return resp, body
-
-    @classmethod
-    @creates('port')
-    def create_port(cls, node_id, address, extra=None, uuid=None):
-        """Wrapper utility for creating test ports.
-
-        :param address: MAC address of the port.
-        :param extra: Meta data of the port. If not supplied, an empty
-            dictionary will be created.
-        :param uuid: UUID of the port.
-        :return: Created port.
-
-        """
-        extra = extra or {}
-        resp, body = cls.client.create_port(address=address, node_id=node_id,
-                                            extra=extra, uuid=uuid)
-
-        return resp, body
-
-    @classmethod
-    def delete_chassis(cls, chassis_id):
-        """Deletes a chassis having the specified UUID.
-
-        :param uuid: The unique identifier of the chassis.
-        :return: Server response.
-
-        """
-
-        resp, body = cls.client.delete_chassis(chassis_id)
-
-        if chassis_id in cls.created_objects['chassis']:
-            cls.created_objects['chassis'].remove(chassis_id)
-
-        return resp
-
-    @classmethod
-    def delete_node(cls, node_id):
-        """Deletes a node having the specified UUID.
-
-        :param uuid: The unique identifier of the node.
-        :return: Server response.
-
-        """
-
-        resp, body = cls.client.delete_node(node_id)
-
-        if node_id in cls.created_objects['node']:
-            cls.created_objects['node'].remove(node_id)
-
-        return resp
-
-    @classmethod
-    def delete_port(cls, port_id):
-        """Deletes a port having the specified UUID.
-
-        :param uuid: The unique identifier of the port.
-        :return: Server response.
-
-        """
-
-        resp, body = cls.client.delete_port(port_id)
-
-        if port_id in cls.created_objects['port']:
-            cls.created_objects['port'].remove(port_id)
-
-        return resp
-
-    def validate_self_link(self, resource, uuid, link):
-        """Check whether the given self link formatted correctly."""
-        expected_link = "{base}/{pref}/{res}/{uuid}".format(
-                        base=self.client.base_url,
-                        pref=self.client.uri_prefix,
-                        res=resource,
-                        uuid=uuid)
-        self.assertEqual(expected_link, link)
diff --git a/tempest/api/baremetal/admin/test_api_discovery.py b/tempest/api/baremetal/admin/test_api_discovery.py
deleted file mode 100644
index 41388ad..0000000
--- a/tempest/api/baremetal/admin/test_api_discovery.py
+++ /dev/null
@@ -1,42 +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.api.baremetal.admin import base
-from tempest import test
-
-
-class TestApiDiscovery(base.BaseBaremetalTest):
-    """Tests for API discovery features."""
-
-    @test.idempotent_id('a3c27e94-f56c-42c4-8600-d6790650b9c5')
-    def test_api_versions(self):
-        _, descr = self.client.get_api_description()
-        expected_versions = ('v1',)
-        versions = [version['id'] for version in descr['versions']]
-
-        for v in expected_versions:
-            self.assertIn(v, versions)
-
-    @test.idempotent_id('896283a6-488e-4f31-af78-6614286cbe0d')
-    def test_default_version(self):
-        _, descr = self.client.get_api_description()
-        default_version = descr['default_version']
-        self.assertEqual(default_version['id'], 'v1')
-
-    @test.idempotent_id('abc0b34d-e684-4546-9728-ab7a9ad9f174')
-    def test_version_1_resources(self):
-        _, descr = self.client.get_version_description(version='v1')
-        expected_resources = ('nodes', 'chassis',
-                              'ports', 'links', 'media_types')
-
-        for res in expected_resources:
-            self.assertIn(res, descr)
diff --git a/tempest/api/baremetal/admin/test_chassis.py b/tempest/api/baremetal/admin/test_chassis.py
deleted file mode 100644
index 29fc64c..0000000
--- a/tempest/api/baremetal/admin/test_chassis.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# -*- coding: utf-8 -*-
-#    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 tempest_lib import exceptions as lib_exc
-
-from tempest.api.baremetal.admin import base
-from tempest.common.utils import data_utils
-from tempest import test
-
-
-class TestChassis(base.BaseBaremetalTest):
-    """Tests for chassis."""
-
-    @classmethod
-    def resource_setup(cls):
-        super(TestChassis, cls).resource_setup()
-        _, cls.chassis = cls.create_chassis()
-
-    def _assertExpected(self, expected, actual):
-        # Check if not expected keys/values exists in actual response body
-        for key, value in six.iteritems(expected):
-            if key not in ('created_at', 'updated_at'):
-                self.assertIn(key, actual)
-                self.assertEqual(value, actual[key])
-
-    @test.idempotent_id('7c5a2e09-699c-44be-89ed-2bc189992d42')
-    def test_create_chassis(self):
-        descr = data_utils.rand_name('test-chassis')
-        _, chassis = self.create_chassis(description=descr)
-        self.assertEqual(chassis['description'], descr)
-
-    @test.idempotent_id('cabe9c6f-dc16-41a7-b6b9-0a90c212edd5')
-    def test_create_chassis_unicode_description(self):
-        # Use a unicode string for testing:
-        # 'We ♡ OpenStack in Ukraine'
-        descr = u'В Україні ♡ OpenStack!'
-        _, chassis = self.create_chassis(description=descr)
-        self.assertEqual(chassis['description'], descr)
-
-    @test.idempotent_id('c84644df-31c4-49db-a307-8942881f41c0')
-    def test_show_chassis(self):
-        _, chassis = self.client.show_chassis(self.chassis['uuid'])
-        self._assertExpected(self.chassis, chassis)
-
-    @test.idempotent_id('29c9cd3f-19b5-417b-9864-99512c3b33b3')
-    def test_list_chassis(self):
-        _, body = self.client.list_chassis()
-        self.assertIn(self.chassis['uuid'],
-                      [i['uuid'] for i in body['chassis']])
-
-    @test.idempotent_id('5ae649ad-22d1-4fe1-bbc6-97227d199fb3')
-    def test_delete_chassis(self):
-        _, body = self.create_chassis()
-        uuid = body['uuid']
-
-        self.delete_chassis(uuid)
-        self.assertRaises(lib_exc.NotFound, self.client.show_chassis, uuid)
-
-    @test.idempotent_id('cda8a41f-6be2-4cbf-840c-994b00a89b44')
-    def test_update_chassis(self):
-        _, body = self.create_chassis()
-        uuid = body['uuid']
-
-        new_description = data_utils.rand_name('new-description')
-        _, body = (self.client.update_chassis(uuid,
-                   description=new_description))
-        _, chassis = self.client.show_chassis(uuid)
-        self.assertEqual(chassis['description'], new_description)
-
-    @test.idempotent_id('76305e22-a4e2-4ab3-855c-f4e2368b9335')
-    def test_chassis_node_list(self):
-        _, node = self.create_node(self.chassis['uuid'])
-        _, body = self.client.list_chassis_nodes(self.chassis['uuid'])
-        self.assertIn(node['uuid'], [n['uuid'] for n in body['nodes']])
diff --git a/tempest/api/baremetal/admin/test_drivers.py b/tempest/api/baremetal/admin/test_drivers.py
deleted file mode 100644
index f08d7ab..0000000
--- a/tempest/api/baremetal/admin/test_drivers.py
+++ /dev/null
@@ -1,38 +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.
-
-from tempest.api.baremetal.admin import base
-from tempest import config
-from tempest import test
-
-CONF = config.CONF
-
-
-class TestDrivers(base.BaseBaremetalTest):
-    """Tests for drivers."""
-    @classmethod
-    def resource_setup(cls):
-        super(TestDrivers, cls).resource_setup()
-        cls.driver_name = CONF.baremetal.driver
-
-    @test.idempotent_id('5aed2790-7592-4655-9b16-99abcc2e6ec5')
-    def test_list_drivers(self):
-        _, drivers = self.client.list_drivers()
-        self.assertIn(self.driver_name,
-                      [d['name'] for d in drivers['drivers']])
-
-    @test.idempotent_id('fb3287a3-c4d7-44bf-ae9d-1eef906d78ce')
-    def test_show_driver(self):
-        _, driver = self.client.show_driver(self.driver_name)
-        self.assertEqual(self.driver_name, driver['name'])
diff --git a/tempest/api/baremetal/admin/test_nodes.py b/tempest/api/baremetal/admin/test_nodes.py
deleted file mode 100644
index b6dee18..0000000
--- a/tempest/api/baremetal/admin/test_nodes.py
+++ /dev/null
@@ -1,169 +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 six
-from tempest_lib import exceptions as lib_exc
-
-from tempest.api.baremetal.admin import base
-from tempest.common.utils import data_utils
-from tempest.common import waiters
-from tempest import test
-
-
-class TestNodes(base.BaseBaremetalTest):
-    """Tests for baremetal nodes."""
-
-    def setUp(self):
-        super(TestNodes, self).setUp()
-
-        _, self.chassis = self.create_chassis()
-        _, self.node = self.create_node(self.chassis['uuid'])
-
-    def _assertExpected(self, expected, actual):
-        # Check if not expected keys/values exists in actual response body
-        for key, value in six.iteritems(expected):
-            if key not in ('created_at', 'updated_at'):
-                self.assertIn(key, actual)
-                self.assertEqual(value, actual[key])
-
-    def _associate_node_with_instance(self):
-        self.client.set_node_power_state(self.node['uuid'], 'power off')
-        waiters.wait_for_bm_node_status(self.client, self.node['uuid'],
-                                        'power_state', 'power off')
-        instance_uuid = data_utils.rand_uuid()
-        self.client.update_node(self.node['uuid'],
-                                instance_uuid=instance_uuid)
-        self.addCleanup(self.client.update_node,
-                        uuid=self.node['uuid'], instance_uuid=None)
-        return instance_uuid
-
-    @test.idempotent_id('4e939eb2-8a69-4e84-8652-6fffcbc9db8f')
-    def test_create_node(self):
-        params = {'cpu_arch': 'x86_64',
-                  'cpus': '12',
-                  'local_gb': '10',
-                  'memory_mb': '1024'}
-
-        _, body = self.create_node(self.chassis['uuid'], **params)
-        self._assertExpected(params, body['properties'])
-
-    @test.idempotent_id('9ade60a4-505e-4259-9ec4-71352cbbaf47')
-    def test_delete_node(self):
-        _, node = self.create_node(self.chassis['uuid'])
-
-        self.delete_node(node['uuid'])
-
-        self.assertRaises(lib_exc.NotFound, self.client.show_node,
-                          node['uuid'])
-
-    @test.idempotent_id('55451300-057c-4ecf-8255-ba42a83d3a03')
-    def test_show_node(self):
-        _, loaded_node = self.client.show_node(self.node['uuid'])
-        self._assertExpected(self.node, loaded_node)
-
-    @test.idempotent_id('4ca123c4-160d-4d8d-a3f7-15feda812263')
-    def test_list_nodes(self):
-        _, body = self.client.list_nodes()
-        self.assertIn(self.node['uuid'],
-                      [i['uuid'] for i in body['nodes']])
-
-    @test.idempotent_id('85b1f6e0-57fd-424c-aeff-c3422920556f')
-    def test_list_nodes_association(self):
-        _, body = self.client.list_nodes(associated=True)
-        self.assertNotIn(self.node['uuid'],
-                         [n['uuid'] for n in body['nodes']])
-
-        self._associate_node_with_instance()
-
-        _, body = self.client.list_nodes(associated=True)
-        self.assertIn(self.node['uuid'], [n['uuid'] for n in body['nodes']])
-
-        _, body = self.client.list_nodes(associated=False)
-        self.assertNotIn(self.node['uuid'], [n['uuid'] for n in body['nodes']])
-
-    @test.idempotent_id('18c4ebd8-f83a-4df7-9653-9fb33a329730')
-    def test_node_port_list(self):
-        _, port = self.create_port(self.node['uuid'],
-                                   data_utils.rand_mac_address())
-        _, body = self.client.list_node_ports(self.node['uuid'])
-        self.assertIn(port['uuid'],
-                      [p['uuid'] for p in body['ports']])
-
-    @test.idempotent_id('72591acb-f215-49db-8395-710d14eb86ab')
-    def test_node_port_list_no_ports(self):
-        _, node = self.create_node(self.chassis['uuid'])
-        _, body = self.client.list_node_ports(node['uuid'])
-        self.assertEmpty(body['ports'])
-
-    @test.idempotent_id('4fed270a-677a-4d19-be87-fd38ae490320')
-    def test_update_node(self):
-        props = {'cpu_arch': 'x86_64',
-                 'cpus': '12',
-                 'local_gb': '10',
-                 'memory_mb': '128'}
-
-        _, node = self.create_node(self.chassis['uuid'], **props)
-
-        new_p = {'cpu_arch': 'x86',
-                 'cpus': '1',
-                 'local_gb': '10000',
-                 'memory_mb': '12300'}
-
-        _, body = self.client.update_node(node['uuid'], properties=new_p)
-        _, node = self.client.show_node(node['uuid'])
-        self._assertExpected(new_p, node['properties'])
-
-    @test.idempotent_id('cbf1f515-5f4b-4e49-945c-86bcaccfeb1d')
-    def test_validate_driver_interface(self):
-        _, body = self.client.validate_driver_interface(self.node['uuid'])
-        core_interfaces = ['power', 'deploy']
-        for interface in core_interfaces:
-            self.assertIn(interface, body)
-
-    @test.idempotent_id('5519371c-26a2-46e9-aa1a-f74226e9d71f')
-    def test_set_node_boot_device(self):
-        self.client.set_node_boot_device(self.node['uuid'], 'pxe')
-
-    @test.idempotent_id('9ea73775-f578-40b9-bc34-efc639c4f21f')
-    def test_get_node_boot_device(self):
-        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))
-
-    @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))
-
-    @test.idempotent_id('f63b6288-1137-4426-8cfe-0d5b7eb87c06')
-    def test_get_console(self):
-        _, body = self.client.get_console(self.node['uuid'])
-        con_info = ['console_enabled', 'console_info']
-        for key in con_info:
-            self.assertIn(key, body)
-
-    @test.idempotent_id('80504575-9b21-4670-92d1-143b948f9437')
-    def test_set_console_mode(self):
-        self.client.set_console_mode(self.node['uuid'], True)
-
-        _, body = self.client.get_console(self.node['uuid'])
-        self.assertEqual(True, body['console_enabled'])
-
-    @test.idempotent_id('b02a4f38-5e8b-44b2-aed2-a69a36ecfd69')
-    def test_get_node_by_instance_uuid(self):
-        instance_uuid = self._associate_node_with_instance()
-        _, body = self.client.show_node_by_instance_uuid(instance_uuid)
-        self.assertEqual(len(body['nodes']), 1)
-        self.assertIn(self.node['uuid'], [n['uuid'] for n in body['nodes']])
diff --git a/tempest/api/baremetal/admin/test_nodestates.py b/tempest/api/baremetal/admin/test_nodestates.py
deleted file mode 100644
index 1ffea25..0000000
--- a/tempest/api/baremetal/admin/test_nodestates.py
+++ /dev/null
@@ -1,59 +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.
-
-from oslo_utils import timeutils
-
-from tempest.api.baremetal.admin import base
-from tempest import exceptions
-from tempest import test
-
-
-class TestNodeStates(base.BaseBaremetalTest):
-    """Tests for baremetal NodeStates."""
-
-    @classmethod
-    def resource_setup(cls):
-        super(TestNodeStates, cls).resource_setup()
-        _, cls.chassis = cls.create_chassis()
-        _, cls.node = cls.create_node(cls.chassis['uuid'])
-
-    def _validate_power_state(self, node_uuid, power_state):
-        # Validate that power state is set within timeout
-        if power_state == 'rebooting':
-            power_state = 'power on'
-        start = timeutils.utcnow()
-        while timeutils.delta_seconds(
-                start, timeutils.utcnow()) < self.power_timeout:
-            _, node = self.client.show_node(node_uuid)
-            if node['power_state'] == power_state:
-                return
-        message = ('Failed to set power state within '
-                   'the required time: %s sec.' % self.power_timeout)
-        raise exceptions.TimeoutException(message)
-
-    @test.idempotent_id('cd8afa5e-3f57-4e43-8185-beb83d3c9015')
-    def test_list_nodestates(self):
-        _, nodestates = self.client.list_nodestates(self.node['uuid'])
-        for key in nodestates:
-            self.assertEqual(nodestates[key], self.node[key])
-
-    @test.idempotent_id('fc5b9320-0c98-4e5a-8848-877fe5a0322c')
-    def test_set_node_power_state(self):
-        _, node = self.create_node(self.chassis['uuid'])
-        states = ["power on", "rebooting", "power off"]
-        for state in states:
-            # Set power state
-            self.client.set_node_power_state(node['uuid'], state)
-            # Check power state after state is set
-            self._validate_power_state(node['uuid'], state)
diff --git a/tempest/api/baremetal/admin/test_ports.py b/tempest/api/baremetal/admin/test_ports.py
deleted file mode 100644
index 180b848..0000000
--- a/tempest/api/baremetal/admin/test_ports.py
+++ /dev/null
@@ -1,266 +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 six
-from tempest_lib import exceptions as lib_exc
-
-from tempest.api.baremetal.admin import base
-from tempest.common.utils import data_utils
-from tempest import test
-
-
-class TestPorts(base.BaseBaremetalTest):
-    """Tests for ports."""
-
-    def setUp(self):
-        super(TestPorts, self).setUp()
-
-        _, self.chassis = self.create_chassis()
-        _, self.node = self.create_node(self.chassis['uuid'])
-        _, self.port = self.create_port(self.node['uuid'],
-                                        data_utils.rand_mac_address())
-
-    def _assertExpected(self, expected, actual):
-        # Check if not expected keys/values exists in actual response body
-        for key, value in six.iteritems(expected):
-            if key not in ('created_at', 'updated_at'):
-                self.assertIn(key, actual)
-                self.assertEqual(value, actual[key])
-
-    @test.idempotent_id('83975898-2e50-42ed-b5f0-e510e36a0b56')
-    def test_create_port(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-
-        _, port = self.create_port(node_id=node_id, address=address)
-
-        _, body = self.client.show_port(port['uuid'])
-
-        self._assertExpected(port, body)
-
-    @test.idempotent_id('d1f6b249-4cf6-4fe6-9ed6-a6e84b1bf67b')
-    def test_create_port_specifying_uuid(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        uuid = data_utils.rand_uuid()
-
-        _, port = self.create_port(node_id=node_id,
-                                   address=address, uuid=uuid)
-
-        _, body = self.client.show_port(uuid)
-        self._assertExpected(port, body)
-
-    @test.idempotent_id('4a02c4b0-6573-42a4-a513-2e36ad485b62')
-    def test_create_port_with_extra(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        extra = {'str': 'value', 'int': 123, 'float': 0.123,
-                 'bool': True, 'list': [1, 2, 3], 'dict': {'foo': 'bar'}}
-
-        _, port = self.create_port(node_id=node_id, address=address,
-                                   extra=extra)
-
-        _, body = self.client.show_port(port['uuid'])
-        self._assertExpected(port, body)
-
-    @test.idempotent_id('1bf257a9-aea3-494e-89c0-63f657ab4fdd')
-    def test_delete_port(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        _, port = self.create_port(node_id=node_id, address=address)
-
-        self.delete_port(port['uuid'])
-
-        self.assertRaises(lib_exc.NotFound, self.client.show_port,
-                          port['uuid'])
-
-    @test.idempotent_id('9fa77ab5-ce59-4f05-baac-148904ba1597')
-    def test_show_port(self):
-        _, port = self.client.show_port(self.port['uuid'])
-        self._assertExpected(self.port, port)
-
-    @test.idempotent_id('7c1114ff-fc3f-47bb-bc2f-68f61620ba8b')
-    def test_show_port_by_address(self):
-        _, port = self.client.show_port_by_address(self.port['address'])
-        self._assertExpected(self.port, port['ports'][0])
-
-    @test.idempotent_id('bd773405-aea5-465d-b576-0ab1780069e5')
-    def test_show_port_with_links(self):
-        _, port = self.client.show_port(self.port['uuid'])
-        self.assertIn('links', port.keys())
-        self.assertEqual(2, len(port['links']))
-        self.assertIn(port['uuid'], port['links'][0]['href'])
-
-    @test.idempotent_id('b5e91854-5cd7-4a8e-bb35-3e0a1314606d')
-    def test_list_ports(self):
-        _, body = self.client.list_ports()
-        self.assertIn(self.port['uuid'],
-                      [i['uuid'] for i in body['ports']])
-        # Verify self links.
-        for port in body['ports']:
-            self.validate_self_link('ports', port['uuid'],
-                                    port['links'][0]['href'])
-
-    @test.idempotent_id('324a910e-2f80-4258-9087-062b5ae06240')
-    def test_list_with_limit(self):
-        _, body = self.client.list_ports(limit=3)
-
-        next_marker = body['ports'][-1]['uuid']
-        self.assertIn(next_marker, body['next'])
-
-    @test.idempotent_id('8a94b50f-9895-4a63-a574-7ecff86e5875')
-    def test_list_ports_details(self):
-        node_id = self.node['uuid']
-
-        uuids = [
-            self.create_port(node_id=node_id,
-                             address=data_utils.rand_mac_address())
-            [1]['uuid'] for i in range(0, 5)]
-
-        _, body = self.client.list_ports_detail()
-
-        ports_dict = dict((port['uuid'], port) for port in body['ports']
-                          if port['uuid'] in uuids)
-
-        for uuid in uuids:
-            self.assertIn(uuid, ports_dict)
-            port = ports_dict[uuid]
-            self.assertIn('extra', port)
-            self.assertIn('node_uuid', port)
-            # never expose the node_id
-            self.assertNotIn('node_id', port)
-            # Verify self link.
-            self.validate_self_link('ports', port['uuid'],
-                                    port['links'][0]['href'])
-
-    @test.idempotent_id('8a03f688-7d75-4ecd-8cbc-e06b8f346738')
-    def test_list_ports_details_with_address(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        self.create_port(node_id=node_id, address=address)
-        for i in range(0, 5):
-            self.create_port(node_id=node_id,
-                             address=data_utils.rand_mac_address())
-
-        _, body = self.client.list_ports_detail(address=address)
-        self.assertEqual(1, len(body['ports']))
-        self.assertEqual(address, body['ports'][0]['address'])
-
-    @test.idempotent_id('9c26298b-1bcb-47b7-9b9e-8bdd6e3c4aba')
-    def test_update_port_replace(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        extra = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
-
-        _, port = self.create_port(node_id=node_id, address=address,
-                                   extra=extra)
-
-        new_address = data_utils.rand_mac_address()
-        new_extra = {'key1': 'new-value1', 'key2': 'new-value2',
-                     'key3': 'new-value3'}
-
-        patch = [{'path': '/address',
-                  'op': 'replace',
-                  'value': new_address},
-                 {'path': '/extra/key1',
-                  'op': 'replace',
-                  'value': new_extra['key1']},
-                 {'path': '/extra/key2',
-                  'op': 'replace',
-                  'value': new_extra['key2']},
-                 {'path': '/extra/key3',
-                  'op': 'replace',
-                  'value': new_extra['key3']}]
-
-        self.client.update_port(port['uuid'], patch)
-
-        _, body = self.client.show_port(port['uuid'])
-        self.assertEqual(new_address, body['address'])
-        self.assertEqual(new_extra, body['extra'])
-
-    @test.idempotent_id('d7e7fece-6ed9-460a-9ebe-9267217e8580')
-    def test_update_port_remove(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        extra = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
-
-        _, port = self.create_port(node_id=node_id, address=address,
-                                   extra=extra)
-
-        # Removing one item from the collection
-        self.client.update_port(port['uuid'],
-                                [{'path': '/extra/key2',
-                                 'op': 'remove'}])
-        extra.pop('key2')
-        _, body = self.client.show_port(port['uuid'])
-        self.assertEqual(extra, body['extra'])
-
-        # Removing the collection
-        self.client.update_port(port['uuid'], [{'path': '/extra',
-                                               'op': 'remove'}])
-        _, body = self.client.show_port(port['uuid'])
-        self.assertEqual({}, body['extra'])
-
-        # Assert nothing else was changed
-        self.assertEqual(node_id, body['node_uuid'])
-        self.assertEqual(address, body['address'])
-
-    @test.idempotent_id('241288b3-e98a-400f-a4d7-d1f716146361')
-    def test_update_port_add(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-
-        _, port = self.create_port(node_id=node_id, address=address)
-
-        extra = {'key1': 'value1', 'key2': 'value2'}
-
-        patch = [{'path': '/extra/key1',
-                  'op': 'add',
-                  'value': extra['key1']},
-                 {'path': '/extra/key2',
-                  'op': 'add',
-                  'value': extra['key2']}]
-
-        self.client.update_port(port['uuid'], patch)
-
-        _, body = self.client.show_port(port['uuid'])
-        self.assertEqual(extra, body['extra'])
-
-    @test.idempotent_id('5309e897-0799-4649-a982-0179b04c3876')
-    def test_update_port_mixed_ops(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        extra = {'key1': 'value1', 'key2': 'value2'}
-
-        _, port = self.create_port(node_id=node_id, address=address,
-                                   extra=extra)
-
-        new_address = data_utils.rand_mac_address()
-        new_extra = {'key1': 0.123, 'key3': {'cat': 'meow'}}
-
-        patch = [{'path': '/address',
-                  'op': 'replace',
-                  'value': new_address},
-                 {'path': '/extra/key1',
-                  'op': 'replace',
-                  'value': new_extra['key1']},
-                 {'path': '/extra/key2',
-                  'op': 'remove'},
-                 {'path': '/extra/key3',
-                  'op': 'add',
-                  'value': new_extra['key3']}]
-
-        self.client.update_port(port['uuid'], patch)
-
-        _, body = self.client.show_port(port['uuid'])
-        self.assertEqual(new_address, body['address'])
-        self.assertEqual(new_extra, body['extra'])
diff --git a/tempest/api/baremetal/admin/test_ports_negative.py b/tempest/api/baremetal/admin/test_ports_negative.py
deleted file mode 100644
index 610758a..0000000
--- a/tempest/api/baremetal/admin/test_ports_negative.py
+++ /dev/null
@@ -1,339 +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_lib import exceptions as lib_exc
-
-from tempest.api.baremetal.admin import base
-from tempest.common.utils import data_utils
-from tempest import test
-
-
-class TestPortsNegative(base.BaseBaremetalTest):
-    """Negative tests for ports."""
-
-    def setUp(self):
-        super(TestPortsNegative, self).setUp()
-
-        _, self.chassis = self.create_chassis()
-        _, self.node = self.create_node(self.chassis['uuid'])
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('0a6ee1f7-d0d9-4069-8778-37f3aa07303a')
-    def test_create_port_malformed_mac(self):
-        node_id = self.node['uuid']
-        address = 'malformed:mac'
-
-        self.assertRaises(lib_exc.BadRequest,
-                          self.create_port, node_id=node_id, address=address)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('30277ee8-0c60-4f1d-b125-0e51c2f43369')
-    def test_create_port_nonexsistent_node_id(self):
-        node_id = str(data_utils.rand_uuid())
-        address = data_utils.rand_mac_address()
-        self.assertRaises(lib_exc.BadRequest, self.create_port,
-                          node_id=node_id, address=address)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('029190f6-43e1-40a3-b64a-65173ba653a3')
-    def test_show_port_malformed_uuid(self):
-        self.assertRaises(lib_exc.BadRequest, self.client.show_port,
-                          'malformed:uuid')
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('0d00e13d-e2e0-45b1-bcbc-55a6d90ca793')
-    def test_show_port_nonexistent_uuid(self):
-        self.assertRaises(lib_exc.NotFound, self.client.show_port,
-                          data_utils.rand_uuid())
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('4ad85266-31e9-4942-99ac-751897dc9e23')
-    def test_show_port_by_mac_not_allowed(self):
-        self.assertRaises(lib_exc.BadRequest, self.client.show_port,
-                          data_utils.rand_mac_address())
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('89a34380-3c61-4c32-955c-2cd9ce94da21')
-    def test_create_port_duplicated_port_uuid(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        uuid = data_utils.rand_uuid()
-
-        self.create_port(node_id=node_id, address=address, uuid=uuid)
-        self.assertRaises(lib_exc.Conflict, self.create_port, node_id=node_id,
-                          address=address, uuid=uuid)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('65e84917-733c-40ae-ae4b-96a4adff931c')
-    def test_create_port_no_mandatory_field_node_id(self):
-        address = data_utils.rand_mac_address()
-
-        self.assertRaises(lib_exc.BadRequest, self.create_port, node_id=None,
-                          address=address)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('bcea3476-7033-4183-acfe-e56a30809b46')
-    def test_create_port_no_mandatory_field_mac(self):
-        node_id = self.node['uuid']
-
-        self.assertRaises(lib_exc.BadRequest, self.create_port,
-                          node_id=node_id, address=None)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('2b51cd18-fb95-458b-9780-e6257787b649')
-    def test_create_port_malformed_port_uuid(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        uuid = 'malformed:uuid'
-
-        self.assertRaises(lib_exc.BadRequest, self.create_port,
-                          node_id=node_id, address=address, uuid=uuid)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('583a6856-6a30-4ac4-889f-14e2adff8105')
-    def test_create_port_malformed_node_id(self):
-        address = data_utils.rand_mac_address()
-        self.assertRaises(lib_exc.BadRequest, self.create_port,
-                          node_id='malformed:nodeid', address=address)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('e27f8b2e-42c6-4a43-a3cd-accff716bc5c')
-    def test_create_port_duplicated_mac(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        self.create_port(node_id=node_id, address=address)
-        self.assertRaises(lib_exc.Conflict,
-                          self.create_port, node_id=node_id,
-                          address=address)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('8907082d-ac5e-4be3-b05f-d072ede82020')
-    def test_update_port_by_mac_not_allowed(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        extra = {'key': 'value'}
-
-        self.create_port(node_id=node_id, address=address, extra=extra)
-
-        patch = [{'path': '/extra/key',
-                  'op': 'replace',
-                  'value': 'new-value'}]
-
-        self.assertRaises(lib_exc.BadRequest,
-                          self.client.update_port, address,
-                          patch)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('df1ac70c-db9f-41d9-90f1-78cd6b905718')
-    def test_update_port_nonexistent(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        extra = {'key': 'value'}
-
-        _, port = self.create_port(node_id=node_id, address=address,
-                                   extra=extra)
-        port_id = port['uuid']
-
-        _, body = self.client.delete_port(port_id)
-
-        patch = [{'path': '/extra/key',
-                  'op': 'replace',
-                  'value': 'new-value'}]
-        self.assertRaises(lib_exc.NotFound,
-                          self.client.update_port, port_id, patch)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('c701e315-aa52-41ea-817c-65c5ca8ca2a8')
-    def test_update_port_malformed_port_uuid(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-
-        self.create_port(node_id=node_id, address=address)
-
-        new_address = data_utils.rand_mac_address()
-        self.assertRaises(lib_exc.BadRequest, self.client.update_port,
-                          uuid='malformed:uuid',
-                          patch=[{'path': '/address', 'op': 'replace',
-                                  'value': new_address}])
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('f8f15803-34d6-45dc-b06f-e5e04bf1b38b')
-    def test_update_port_add_nonexistent_property(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-
-        _, port = self.create_port(node_id=node_id, address=address)
-        port_id = port['uuid']
-
-        self.assertRaises(lib_exc.BadRequest, self.client.update_port, port_id,
-                          [{'path': '/nonexistent', ' op': 'add',
-                            'value': 'value'}])
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('898ec904-38b1-4fcb-9584-1187d4263a2a')
-    def test_update_port_replace_node_id_with_malformed(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-
-        _, port = self.create_port(node_id=node_id, address=address)
-        port_id = port['uuid']
-
-        patch = [{'path': '/node_uuid',
-                  'op': 'replace',
-                  'value': 'malformed:node_uuid'}]
-        self.assertRaises(lib_exc.BadRequest,
-                          self.client.update_port, port_id, patch)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('2949f30f-5f59-43fa-a6d9-4eac578afab4')
-    def test_update_port_replace_mac_with_duplicated(self):
-        node_id = self.node['uuid']
-        address1 = data_utils.rand_mac_address()
-        address2 = data_utils.rand_mac_address()
-
-        _, port1 = self.create_port(node_id=node_id, address=address1)
-
-        _, port2 = self.create_port(node_id=node_id, address=address2)
-        port_id = port2['uuid']
-
-        patch = [{'path': '/address',
-                  'op': 'replace',
-                  'value': address1}]
-        self.assertRaises(lib_exc.Conflict,
-                          self.client.update_port, port_id, patch)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('97f6e048-6e4f-4eba-a09d-fbbc78b77a77')
-    def test_update_port_replace_node_id_with_nonexistent(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-
-        _, port = self.create_port(node_id=node_id, address=address)
-        port_id = port['uuid']
-
-        patch = [{'path': '/node_uuid',
-                  'op': 'replace',
-                  'value': data_utils.rand_uuid()}]
-        self.assertRaises(lib_exc.BadRequest,
-                          self.client.update_port, port_id, patch)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('375022c5-9e9e-4b11-9ca4-656729c0c9b2')
-    def test_update_port_replace_mac_with_malformed(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-
-        _, port = self.create_port(node_id=node_id, address=address)
-        port_id = port['uuid']
-
-        patch = [{'path': '/address',
-                  'op': 'replace',
-                  'value': 'malformed:mac'}]
-
-        self.assertRaises(lib_exc.BadRequest,
-                          self.client.update_port, port_id, patch)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('5722b853-03fc-4854-8308-2036a1b67d85')
-    def test_update_port_replace_nonexistent_property(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-
-        _, port = self.create_port(node_id=node_id, address=address)
-        port_id = port['uuid']
-
-        patch = [{'path': '/nonexistent', ' op': 'replace', 'value': 'value'}]
-
-        self.assertRaises(lib_exc.BadRequest,
-                          self.client.update_port, port_id, patch)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('ae2696ca-930a-4a7f-918f-30ae97c60f56')
-    def test_update_port_remove_mandatory_field_mac(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-
-        _, port = self.create_port(node_id=node_id, address=address)
-        port_id = port['uuid']
-
-        self.assertRaises(lib_exc.BadRequest, self.client.update_port, port_id,
-                          [{'path': '/address', 'op': 'remove'}])
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('5392c1f0-2071-4697-9064-ec2d63019018')
-    def test_update_port_remove_mandatory_field_port_uuid(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-
-        _, port = self.create_port(node_id=node_id, address=address)
-        port_id = port['uuid']
-
-        self.assertRaises(lib_exc.BadRequest, self.client.update_port, port_id,
-                          [{'path': '/uuid', 'op': 'remove'}])
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('06b50d82-802a-47ef-b079-0a3311cf85a2')
-    def test_update_port_remove_nonexistent_property(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-
-        _, port = self.create_port(node_id=node_id, address=address)
-        port_id = port['uuid']
-
-        self.assertRaises(lib_exc.BadRequest, self.client.update_port, port_id,
-                          [{'path': '/nonexistent', 'op': 'remove'}])
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('03d42391-2145-4a6c-95bf-63fe55eb64fd')
-    def test_delete_port_by_mac_not_allowed(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-
-        self.create_port(node_id=node_id, address=address)
-        self.assertRaises(lib_exc.BadRequest, self.client.delete_port, address)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('0629e002-818e-4763-b25b-ae5e07b1cb23')
-    def test_update_port_mixed_ops_integrity(self):
-        node_id = self.node['uuid']
-        address = data_utils.rand_mac_address()
-        extra = {'key1': 'value1', 'key2': 'value2'}
-
-        _, port = self.create_port(node_id=node_id, address=address,
-                                   extra=extra)
-        port_id = port['uuid']
-
-        new_address = data_utils.rand_mac_address()
-        new_extra = {'key1': 'new-value1', 'key3': 'new-value3'}
-
-        patch = [{'path': '/address',
-                  'op': 'replace',
-                  'value': new_address},
-                 {'path': '/extra/key1',
-                  'op': 'replace',
-                  'value': new_extra['key1']},
-                 {'path': '/extra/key2',
-                  'op': 'remove'},
-                 {'path': '/extra/key3',
-                  'op': 'add',
-                  'value': new_extra['key3']},
-                 {'path': '/nonexistent',
-                  'op': 'replace',
-                  'value': 'value'}]
-
-        self.assertRaises(lib_exc.BadRequest, self.client.update_port, port_id,
-                          patch)
-
-        # patch should not be applied
-        _, body = self.client.show_port(port_id)
-        self.assertEqual(address, body['address'])
-        self.assertEqual(extra, body['extra'])
diff --git a/tempest/api/compute/admin/test_agents.py b/tempest/api/compute/admin/test_agents.py
index 9f7bbae..1c37cad 100644
--- a/tempest/api/compute/admin/test_agents.py
+++ b/tempest/api/compute/admin/test_agents.py
@@ -12,15 +12,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_log import log
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest import test
 
-LOG = log.getLogger(__name__)
-
 
 class AgentsAdminTestJSON(base.BaseV2ComputeAdminTest):
     """Tests Agents API"""
@@ -30,25 +25,16 @@
         super(AgentsAdminTestJSON, cls).setup_clients()
         cls.client = cls.os_adm.agents_client
 
-    def setUp(self):
-        super(AgentsAdminTestJSON, self).setUp()
-        params = self._param_helper(
+    @classmethod
+    def resource_setup(cls):
+        super(AgentsAdminTestJSON, cls).resource_setup()
+        cls.params_agent = cls._param_helper(
             hypervisor='common', os='linux', architecture='x86_64',
             version='7.0', url='xxx://xxxx/xxx/xxx',
             md5hash='add6bb58e139be103324d04d82d8f545')
-        body = self.client.create_agent(**params)['agent']
-        self.agent_id = body['agent_id']
 
-    def tearDown(self):
-        try:
-            self.client.delete_agent(self.agent_id)
-        except lib_exc.NotFound:
-            pass
-        except Exception:
-            LOG.exception('Exception raised deleting agent %s', self.agent_id)
-        super(AgentsAdminTestJSON, self).tearDown()
-
-    def _param_helper(self, **kwargs):
+    @staticmethod
+    def _param_helper(**kwargs):
         rand_key = 'architecture'
         if rand_key in kwargs:
             # NOTE: The rand_name is for avoiding agent conflicts.
@@ -72,33 +58,43 @@
 
     @test.idempotent_id('dc9ffd51-1c50-4f0e-a820-ae6d2a568a9e')
     def test_update_agent(self):
-        # Update an agent.
+        # Create and update an agent.
+        body = self.client.create_agent(**self.params_agent)['agent']
+        self.addCleanup(self.client.delete_agent, body['agent_id'])
+        agent_id = body['agent_id']
         params = self._param_helper(
             version='8.0', url='xxx://xxxx/xxx/xxx2',
             md5hash='add6bb58e139be103324d04d82d8f547')
-        body = self.client.update_agent(self.agent_id, **params)['agent']
+        body = self.client.update_agent(agent_id, **params)['agent']
         for expected_item, value in params.items():
             self.assertEqual(value, body[expected_item])
 
     @test.idempotent_id('470e0b89-386f-407b-91fd-819737d0b335')
     def test_delete_agent(self):
-        # Delete an agent.
-        self.client.delete_agent(self.agent_id)
+        # Create an agent and delete it.
+        body = self.client.create_agent(**self.params_agent)['agent']
+        self.client.delete_agent(body['agent_id'])
 
         # Verify the list doesn't contain the deleted agent.
         agents = self.client.list_agents()['agents']
-        self.assertNotIn(self.agent_id, map(lambda x: x['agent_id'], agents))
+        self.assertNotIn(body['agent_id'], map(lambda x: x['agent_id'],
+                                               agents))
 
     @test.idempotent_id('6a326c69-654b-438a-80a3-34bcc454e138')
     def test_list_agents(self):
-        # List all agents.
+        # Create an agent and  list all agents.
+        body = self.client.create_agent(**self.params_agent)['agent']
+        self.addCleanup(self.client.delete_agent, body['agent_id'])
         agents = self.client.list_agents()['agents']
-        self.assertTrue(len(agents) > 0, 'Cannot get any agents.(%s)' % agents)
-        self.assertIn(self.agent_id, map(lambda x: x['agent_id'], agents))
+        self.assertGreater(len(agents), 0,
+                           'Cannot get any agents.(%s)' % agents)
+        self.assertIn(body['agent_id'], map(lambda x: x['agent_id'], agents))
 
     @test.idempotent_id('eabadde4-3cd7-4ec4-a4b5-5a936d2d4408')
     def test_list_agents_with_filter(self):
-        # List the agent builds by the filter.
+        # Create agents and list the agent builds by the filter.
+        body = self.client.create_agent(**self.params_agent)['agent']
+        self.addCleanup(self.client.delete_agent, body['agent_id'])
         params = self._param_helper(
             hypervisor='xen', os='linux', architecture='x86',
             version='7.0', url='xxx://xxxx/xxx/xxx1',
@@ -109,6 +105,8 @@
         agent_id_xen = agent_xen['agent_id']
         agents = (self.client.list_agents(hypervisor=agent_xen['hypervisor'])
                   ['agents'])
-        self.assertTrue(len(agents) > 0, 'Cannot get any agents.(%s)' % agents)
+        self.assertGreater(len(agents), 0,
+                           'Cannot get any agents.(%s)' % agents)
         self.assertIn(agent_id_xen, map(lambda x: x['agent_id'], agents))
-        self.assertNotIn(self.agent_id, map(lambda x: x['agent_id'], agents))
+        self.assertNotIn(body['agent_id'], map(lambda x: x['agent_id'],
+                                               agents))
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 1d83fec..1233a2b 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -13,19 +13,21 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
+import testtools
 
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
 from tempest.common.utils import data_utils
+from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest import test
 
+CONF = config.CONF
+
 
 class AggregatesAdminTestJSON(base.BaseV2ComputeAdminTest):
     """Tests Aggregates API that require admin privileges"""
 
-    _host_key = 'OS-EXT-SRV-ATTR:host'
-
     @classmethod
     def setup_clients(cls):
         super(AggregatesAdminTestJSON, cls).setup_clients()
@@ -37,18 +39,25 @@
         cls.aggregate_name_prefix = 'test_aggregate'
         cls.az_name_prefix = 'test_az'
 
-        hosts_all = cls.os_adm.hosts_client.list_hosts()['hosts']
-        hosts = map(lambda x: x['host_name'],
-                    filter(lambda y: y['service'] == 'compute', hosts_all))
-        cls.host = hosts[0]
+        cls.host = None
+        hypers = cls.os_adm.hypervisor_client.list_hypervisors(
+            detail=True)['hypervisors']
 
-    def _try_delete_aggregate(self, aggregate_id):
-        # delete aggregate, if it exists
-        try:
-            self.client.delete_aggregate(aggregate_id)
-        # if aggregate not found, it depict it was deleted in the test
-        except lib_exc.NotFound:
-            pass
+        if CONF.compute.hypervisor_type:
+            hypers = [hyper for hyper in hypers
+                      if (hyper['hypervisor_type'] ==
+                          CONF.compute.hypervisor_type)]
+
+        hosts_available = [hyper['service']['host'] for hyper in hypers
+                           if (hyper['state'] == 'up' and
+                               hyper['status'] == 'enabled')]
+        if hosts_available:
+            cls.host = hosts_available[0]
+        else:
+            msg = "no available compute node found"
+            if CONF.compute.hypervisor_type:
+                msg += " for hypervisor_type %s" % CONF.compute.hypervisor_type
+            raise testtools.TestCase.failureException(msg)
 
     @test.idempotent_id('0d148aa3-d54c-4317-aa8d-42040a475e20')
     def test_aggregate_create_delete(self):
@@ -56,7 +65,8 @@
         aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
         aggregate = (self.client.create_aggregate(name=aggregate_name)
                      ['aggregate'])
-        self.addCleanup(self._try_delete_aggregate, aggregate['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_aggregate, aggregate['id'])
         self.assertEqual(aggregate_name, aggregate['name'])
         self.assertIsNone(aggregate['availability_zone'])
 
@@ -70,7 +80,8 @@
         az_name = data_utils.rand_name(self.az_name_prefix)
         aggregate = self.client.create_aggregate(
             name=aggregate_name, availability_zone=az_name)['aggregate']
-        self.addCleanup(self._try_delete_aggregate, aggregate['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_aggregate, aggregate['id'])
         self.assertEqual(aggregate_name, aggregate['name'])
         self.assertEqual(az_name, aggregate['availability_zone'])
 
@@ -179,7 +190,7 @@
                         host=self.host)
 
         aggregates = self.client.list_aggregates()['aggregates']
-        aggs = filter(lambda x: x['id'] == aggregate['id'], aggregates)
+        aggs = [agg for agg in aggregates if agg['id'] == aggregate['id']]
         self.assertEqual(1, len(aggs))
         agg = aggs[0]
         self.assertEqual(aggregate_name, agg['name'])
@@ -215,10 +226,8 @@
         self.client.add_host(aggregate['id'], host=self.host)
         self.addCleanup(self.client.remove_host, aggregate['id'],
                         host=self.host)
-        server_name = data_utils.rand_name('test_server')
         admin_servers_client = self.os_adm.servers_client
-        server = self.create_test_server(name=server_name,
-                                         availability_zone=az_name,
+        server = self.create_test_server(availability_zone=az_name,
                                          wait_until='ACTIVE')
         body = admin_servers_client.show_server(server['id'])['server']
-        self.assertEqual(self.host, body[self._host_key])
+        self.assertEqual(self.host, body['OS-EXT-SRV-ATTR:host'])
diff --git a/tempest/api/compute/admin/test_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
index 181533b..85aa301 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -13,11 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -37,10 +36,17 @@
         cls.az_name_prefix = 'test_az'
 
         hosts_all = cls.os_adm.hosts_client.list_hosts()['hosts']
-        hosts = map(lambda x: x['host_name'],
-                    filter(lambda y: y['service'] == 'compute', hosts_all))
+        hosts = ([host['host_name']
+                 for host in hosts_all if host['service'] == 'compute'])
         cls.host = hosts[0]
 
+    def _create_test_aggregate(self):
+        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+        aggregate = (self.client.create_aggregate(name=aggregate_name)
+                     ['aggregate'])
+        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+        return aggregate
+
     @test.attr(type=['negative'])
     @test.idempotent_id('86a1cb14-da37-4a70-b056-903fd56dfe29')
     def test_aggregate_create_as_user(self):
@@ -71,24 +77,16 @@
     @test.idempotent_id('9c23a291-b0b1-487b-b464-132e061151b3')
     def test_aggregate_create_with_existent_aggregate_name(self):
         # creating an aggregate with existent aggregate name is forbidden
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = self.client.create_aggregate(name=aggregate_name)
-        self.addCleanup(self.client.delete_aggregate,
-                        aggregate['aggregate']['id'])
-
+        aggregate = self._create_test_aggregate()
         self.assertRaises(lib_exc.Conflict,
                           self.client.create_aggregate,
-                          name=aggregate_name)
+                          name=aggregate['name'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('cd6de795-c15d-45f1-8d9e-813c6bb72a3d')
     def test_aggregate_delete_as_user(self):
         # Regular user is not allowed to delete an aggregate.
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
+        aggregate = self._create_test_aggregate()
         self.assertRaises(lib_exc.Forbidden,
                           self.user_client.delete_aggregate,
                           aggregate['id'])
@@ -104,11 +102,7 @@
     @test.idempotent_id('557cad12-34c9-4ff4-95f0-22f0dfbaf7dc')
     def test_aggregate_get_details_as_user(self):
         # Regular user is not allowed to get aggregate details.
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
+        aggregate = self._create_test_aggregate()
         self.assertRaises(lib_exc.Forbidden,
                           self.user_client.show_aggregate,
                           aggregate['id'])
@@ -137,12 +131,7 @@
             non_exist_host = data_utils.rand_name('nonexist_host')
             if non_exist_host not in hosts:
                 break
-
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
+        aggregate = self._create_test_aggregate()
         self.assertRaises(lib_exc.NotFound, self.client.add_host,
                           aggregate['id'], host=non_exist_host)
 
@@ -150,11 +139,7 @@
     @test.idempotent_id('7324c334-bd13-4c93-8521-5877322c3d51')
     def test_aggregate_add_host_as_user(self):
         # Regular user is not allowed to add a host to an aggregate.
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
+        aggregate = self._create_test_aggregate()
         self.assertRaises(lib_exc.Forbidden,
                           self.user_client.add_host,
                           aggregate['id'], host=self.host)
@@ -163,10 +148,7 @@
     @test.idempotent_id('19dd44e1-c435-4ee1-a402-88c4f90b5950')
     def test_aggregate_add_existent_host(self):
         self.useFixture(fixtures.LockFixture('availability_zone'))
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+        aggregate = self._create_test_aggregate()
 
         self.client.add_host(aggregate['id'], host=self.host)
         self.addCleanup(self.client.remove_host, aggregate['id'],
@@ -180,10 +162,8 @@
     def test_aggregate_remove_host_as_user(self):
         # Regular user is not allowed to remove a host from an aggregate.
         self.useFixture(fixtures.LockFixture('availability_zone'))
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+        aggregate = self._create_test_aggregate()
+
         self.client.add_host(aggregate['id'], host=self.host)
         self.addCleanup(self.client.remove_host, aggregate['id'],
                         host=self.host)
@@ -195,11 +175,7 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('95d6a6fa-8da9-4426-84d0-eec0329f2e4d')
     def test_aggregate_remove_nonexistent_host(self):
-        non_exist_host = data_utils.rand_name('nonexist_host')
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+        aggregate = self._create_test_aggregate()
 
         self.assertRaises(lib_exc.NotFound, self.client.remove_host,
-                          aggregate['id'], host=non_exist_host)
+                          aggregate['id'], host='nonexist_host')
diff --git a/tempest/api/compute/admin/test_auto_allocate_network.py b/tempest/api/compute/admin/test_auto_allocate_network.py
new file mode 100644
index 0000000..08e4072
--- /dev/null
+++ b/tempest/api/compute/admin/test_auto_allocate_network.py
@@ -0,0 +1,205 @@
+# 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.
+
+from oslo_log import log
+
+from tempest.api.compute import base
+from tempest.common import compute
+from tempest.common import credentials_factory as credentials
+from tempest import config
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions as lib_excs
+from tempest import test
+
+CONF = config.CONF
+LOG = log.getLogger(__name__)
+
+
+# NOTE(mriedem): This is in the admin directory only because it requires
+# force_tenant_isolation=True, but doesn't extend BaseV2ComputeAdminTest
+# because it doesn't actually use any admin credentials in the tests.
+class AutoAllocateNetworkTest(base.BaseV2ComputeTest):
+    """Tests auto-allocating networks with the v2.37 microversion.
+
+    These tests rely on Neutron being enabled. Also, the tenant must not have
+    any network resources available to it so we can make sure that Nova
+    calls to Neutron to automatically allocate the network topology.
+    """
+
+    force_tenant_isolation = True
+
+    min_microversion = '2.37'
+    max_microversion = 'latest'
+
+    @classmethod
+    def skip_checks(cls):
+        super(AutoAllocateNetworkTest, cls).skip_checks()
+        identity_version = cls.get_identity_version()
+        if not credentials.is_admin_available(
+                identity_version=identity_version):
+            msg = "Missing Identity Admin API credentials in configuration."
+            raise cls.skipException(msg)
+        if not CONF.service_available.neutron:
+            raise cls.skipException('Neutron is required')
+        if not test.is_extension_enabled('auto-allocated-topology', 'network'):
+            raise cls.skipException(
+                'auto-allocated-topology extension is not available')
+
+    @classmethod
+    def setup_credentials(cls):
+        # Do not create network resources for these tests.
+        cls.set_network_resources()
+        super(AutoAllocateNetworkTest, cls).setup_credentials()
+
+    @classmethod
+    def setup_clients(cls):
+        super(AutoAllocateNetworkTest, cls).setup_clients()
+        cls.networks_client = cls.os.networks_client
+        cls.routers_client = cls.os.routers_client
+        cls.subnets_client = cls.os.subnets_client
+        cls.ports_client = cls.os.ports_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(AutoAllocateNetworkTest, cls).resource_setup()
+        # Sanity check that there are no networks available to the tenant.
+        # This is essentially what Nova does for getting available networks.
+        tenant_id = cls.networks_client.tenant_id
+        # (1) Retrieve non-public network list owned by the tenant.
+        search_opts = {'tenant_id': tenant_id, 'shared': False}
+        nets = cls.networks_client.list_networks(
+            **search_opts).get('networks', [])
+        if nets:
+            raise lib_excs.TempestException(
+                'Found tenant networks: %s' % nets)
+        # (2) Retrieve shared network list.
+        search_opts = {'shared': True}
+        nets = cls.networks_client.list_networks(
+            **search_opts).get('networks', [])
+        if nets:
+            raise lib_excs.TempestException(
+                'Found shared networks: %s' % nets)
+
+    @classmethod
+    def resource_cleanup(cls):
+        """Deletes any auto_allocated_network and it's associated resources."""
+
+        # Find the auto-allocated router for the tenant.
+        # This is a bit hacky since we don't have a great way to find the
+        # auto-allocated router given the private tenant network we have.
+        routers = cls.routers_client.list_routers().get('routers', [])
+        if len(routers) > 1:
+            # This indicates a race where nova is concurrently calling the
+            # neutron auto-allocated-topology API for multiple server builds
+            # at the same time (it's called from nova-compute when setting up
+            # networking for a server). Neutron will detect duplicates and
+            # automatically clean them up, but there is a window where the API
+            # can return multiple and we don't have a good way to filter those
+            # out right now, so we'll just handle them.
+            LOG.info('(%s) Found more than one router for tenant.',
+                     test_utils.find_test_caller())
+
+        # Let's just blindly remove any networks, duplicate or otherwise, that
+        # the test might have created even though Neutron will cleanup
+        # duplicate resources automatically (so ignore 404s).
+        networks = cls.networks_client.list_networks().get('networks', [])
+
+        for router in routers:
+            # Disassociate the subnets from the router. Because of the race
+            # mentioned above the subnets might not be associated with the
+            # router so ignore any 404.
+            for network in networks:
+                for subnet_id in network['subnets']:
+                    test_utils.call_and_ignore_notfound_exc(
+                        cls.routers_client.remove_router_interface,
+                        router['id'], subnet_id=subnet_id)
+
+            # Delete the router.
+            cls.routers_client.delete_router(router['id'])
+
+        for network in networks:
+            # Get and delete the ports for the given network.
+            ports = cls.ports_client.list_ports(
+                network_id=network['id']).get('ports', [])
+            for port in ports:
+                test_utils.call_and_ignore_notfound_exc(
+                    cls.ports_client.delete_port, port['id'])
+
+            # Delete the subnets.
+            for subnet_id in network['subnets']:
+                test_utils.call_and_ignore_notfound_exc(
+                    cls.subnets_client.delete_subnet, subnet_id)
+
+            # Delete the network.
+            test_utils.call_and_ignore_notfound_exc(
+                cls.networks_client.delete_network, network['id'])
+
+    @test.idempotent_id('5eb7b8fa-9c23-47a2-9d7d-02ed5809dd34')
+    def test_server_create_no_allocate(self):
+        """Tests that no networking is allocated for the server."""
+        # create the server with no networking
+        server, _ = compute.create_test_server(
+            self.os, networks='none', wait_until='ACTIVE')
+        self.addCleanup(self.delete_server, server['id'])
+        # get the server ips
+        addresses = self.servers_client.list_addresses(
+            server['id'])['addresses']
+        # assert that there is no networking
+        self.assertEqual({}, addresses)
+
+    @test.idempotent_id('2e6cf129-9e28-4e8a-aaaa-045ea826b2a6')
+    def test_server_multi_create_auto_allocate(self):
+        """Tests that networking is auto-allocated for multiple servers."""
+
+        # Create multiple servers with auto networking to make sure the
+        # automatic network allocation is atomic. Using a minimum of three
+        # servers is essential for this scenario because:
+        #
+        # - First request sees no networks for the tenant so it auto-allocates
+        #   one from Neutron, let's call that net1.
+        # - Second request sees no networks for the tenant so it auto-allocates
+        #   one from Neutron. Neutron creates net2 but sees it's a duplicate
+        #   so it queues net2 for deletion and returns net1 from the API and
+        #   Nova uses that for the second server request.
+        # - Third request sees net1 and net2 for the tenant and fails with a
+        #   NetworkAmbiguous 400 error.
+        _, servers = compute.create_test_server(
+            self.os, networks='auto', wait_until='ACTIVE',
+            min_count=3)
+        server_nets = set()
+        for server in servers:
+            self.addCleanup(self.delete_server, server['id'])
+            # get the server ips
+            addresses = self.servers_client.list_addresses(
+                server['id'])['addresses']
+            # assert that there is networking (should only be one)
+            self.assertEqual(1, len(addresses))
+            server_nets.add(list(addresses.keys())[0])
+        # all servers should be on the same network
+        self.assertEqual(1, len(server_nets))
+
+        # List the networks for the tenant; we filter on admin_state_up=True
+        # because the auto-allocated-topology code in Neutron won't set that
+        # to True until the network is ready and is returned from the API.
+        # Duplicate networks created from a race should have
+        # admin_state_up=False.
+        search_opts = {'tenant_id': self.networks_client.tenant_id,
+                       'shared': False,
+                       'admin_state_up': True}
+        nets = self.networks_client.list_networks(
+            **search_opts).get('networks', [])
+        self.assertEqual(1, len(nets))
+        # verify the single private tenant network is the one that the servers
+        # are using also
+        self.assertIn(nets[0]['name'], server_nets)
diff --git a/tempest/api/compute/admin/test_availability_zone.py b/tempest/api/compute/admin/test_availability_zone.py
index 5befa53..3470602 100644
--- a/tempest/api/compute/admin/test_availability_zone.py
+++ b/tempest/api/compute/admin/test_availability_zone.py
@@ -29,10 +29,10 @@
     def test_get_availability_zone_list(self):
         # List of availability zone
         availability_zone = self.client.list_availability_zones()
-        self.assertTrue(len(availability_zone['availabilityZoneInfo']) > 0)
+        self.assertGreater(len(availability_zone['availabilityZoneInfo']), 0)
 
     @test.idempotent_id('ef726c58-530f-44c2-968c-c7bed22d5b8c')
     def test_get_availability_zone_list_detail(self):
         # List of availability zones and available services
         availability_zone = self.client.list_availability_zones(detail=True)
-        self.assertTrue(len(availability_zone['availabilityZoneInfo']) > 0)
+        self.assertGreater(len(availability_zone['availabilityZoneInfo']), 0)
diff --git a/tempest/api/compute/admin/test_availability_zone_negative.py b/tempest/api/compute/admin/test_availability_zone_negative.py
index fe979d4..c60de1e 100644
--- a/tempest/api/compute/admin/test_availability_zone_negative.py
+++ b/tempest/api/compute/admin/test_availability_zone_negative.py
@@ -12,9 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
diff --git a/tempest/api/compute/admin/test_baremetal_nodes.py b/tempest/api/compute/admin/test_baremetal_nodes.py
index b764483..722d9a6 100644
--- a/tempest/api/compute/admin/test_baremetal_nodes.py
+++ b/tempest/api/compute/admin/test_baremetal_nodes.py
@@ -25,7 +25,7 @@
     @classmethod
     def resource_setup(cls):
         super(BaremetalNodesAdminTestJSON, cls).resource_setup()
-        if not CONF.service_available.ironic:
+        if not getattr(CONF.service_available, 'ironic', False):
             skip_msg = ('%s skipped as Ironic is not available' % cls.__name__)
             raise cls.skipException(skip_msg)
         cls.client = cls.os_adm.baremetal_nodes_client
diff --git a/tempest/api/compute/admin/test_fixed_ips_negative.py b/tempest/api/compute/admin/test_fixed_ips_negative.py
index 8d745c9..05bba21 100644
--- a/tempest/api/compute/admin/test_fixed_ips_negative.py
+++ b/tempest/api/compute/admin/test_fixed_ips_negative.py
@@ -12,10 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest import config
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 1ef8f67..fde5622 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -15,10 +15,9 @@
 
 import uuid
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -92,7 +91,7 @@
 
     @test.idempotent_id('94c9bb4e-2c2a-4f3c-bb1f-5f0daf918e6d')
     def test_create_flavor_with_uuid_id(self):
-        flavor_id = str(uuid.uuid4())
+        flavor_id = data_utils.rand_uuid()
         new_flavor_id = self._create_flavor(flavor_id)
         self.assertEqual(new_flavor_id, flavor_id)
 
@@ -162,6 +161,7 @@
         verify_flavor_response_extension(flavor)
 
         # Check if flavor is present in list
+        flag = False
         flavors = self.user_client.list_flavors(detail=True)['flavors']
         for flavor in flavors:
             if flavor['name'] == flavor_name:
diff --git a/tempest/api/compute/admin/test_flavors_access_negative.py b/tempest/api/compute/admin/test_flavors_access_negative.py
index 5070fd7..1b7eb12 100644
--- a/tempest/api/compute/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/admin/test_flavors_access_negative.py
@@ -13,12 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -146,4 +143,4 @@
         self.assertRaises(lib_exc.NotFound,
                           self.client.remove_flavor_access,
                           new_flavor['id'],
-                          str(uuid.uuid4()))
+                          data_utils.rand_uuid())
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
index 14646e8..7eb4bbf 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -14,10 +14,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -103,11 +102,10 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('440b9f3f-3c7f-4293-a106-0ceda350f8de')
     def test_flavor_unset_nonexistent_key(self):
-        nonexistent_key = data_utils.rand_name('flavor_key')
         self.assertRaises(lib_exc.NotFound,
                           self.client.unset_flavor_extra_spec,
                           self.flavor['id'],
-                          nonexistent_key)
+                          'nonexistent_key')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('329a7be3-54b2-48be-8052-bf2ce4afd898')
diff --git a/tempest/api/compute/admin/test_floating_ips_bulk.py b/tempest/api/compute/admin/test_floating_ips_bulk.py
index 456363c..a4695b0 100644
--- a/tempest/api/compute/admin/test_floating_ips_bulk.py
+++ b/tempest/api/compute/admin/test_floating_ips_bulk.py
@@ -17,7 +17,8 @@
 
 from tempest.api.compute import base
 from tempest import config
-from tempest import exceptions
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
 from tempest import test
 
 CONF = config.CONF
@@ -54,12 +55,6 @@
                 raise exceptions.InvalidConfiguration(msg)
         return
 
-    def _delete_floating_ips_bulk(self, ip_range):
-        try:
-            self.client.delete_floating_ips_bulk(ip_range)
-        except Exception:
-            pass
-
     @test.idempotent_id('2c8f145f-8012-4cb8-ac7e-95a587f0e4ab')
     @test.services('network')
     def test_create_list_delete_floating_ips_bulk(self):
@@ -73,7 +68,8 @@
                                                      pool,
                                                      interface)
                 ['floating_ips_bulk_create'])
-        self.addCleanup(self._delete_floating_ips_bulk, self.ip_range)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_floating_ips_bulk, self.ip_range)
         self.assertEqual(self.ip_range, body['ip_range'])
         ips_list = self.client.list_floating_ips_bulk()['floating_ip_info']
         self.assertNotEqual(0, len(ips_list))
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index f6ea3a4..3797b29 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -28,7 +28,7 @@
     @test.idempotent_id('9bfaf98d-e2cb-44b0-a07e-2558b2821e4f')
     def test_list_hosts(self):
         hosts = self.client.list_hosts()['hosts']
-        self.assertTrue(len(hosts) >= 2, str(hosts))
+        self.assertGreaterEqual(len(hosts), 2, str(hosts))
 
     @test.idempotent_id('5dc06f5b-d887-47a2-bb2a-67762ef3c6de')
     def test_list_hosts_with_zone(self):
@@ -36,7 +36,7 @@
         hosts = self.client.list_hosts()['hosts']
         host = hosts[0]
         hosts = self.client.list_hosts(zone=host['zone'])['hosts']
-        self.assertTrue(len(hosts) >= 1)
+        self.assertGreaterEqual(len(hosts), 1)
         self.assertIn(host, hosts)
 
     @test.idempotent_id('9af3c171-fbf4-4150-a624-22109733c2a6')
@@ -49,7 +49,7 @@
     @test.idempotent_id('c6ddbadb-c94e-4500-b12f-8ffc43843ff8')
     def test_list_hosts_with_nonexistent_zone(self):
         # If send the request with a nonexistent zone, the request will be
-        # successful and no hosts will be retured
+        # successful and no hosts will be returned
         hosts = self.client.list_hosts(zone='xxx')['hosts']
         self.assertEqual(0, len(hosts))
 
@@ -58,12 +58,12 @@
         hosts = self.client.list_hosts()['hosts']
 
         hosts = [host for host in hosts if host['service'] == 'compute']
-        self.assertTrue(len(hosts) >= 1)
+        self.assertGreaterEqual(len(hosts), 1)
 
         for host in hosts:
             hostname = host['host_name']
             resources = self.client.show_host(hostname)['host']
-            self.assertTrue(len(resources) >= 1)
+            self.assertGreaterEqual(len(resources), 1)
             host_resource = resources[0]['resource']
             self.assertIsNotNone(host_resource)
             self.assertIsNotNone(host_resource['cpu'])
diff --git a/tempest/api/compute/admin/test_hosts_negative.py b/tempest/api/compute/admin/test_hosts_negative.py
index 65ada4d..3821b22 100644
--- a/tempest/api/compute/admin/test_hosts_negative.py
+++ b/tempest/api/compute/admin/test_hosts_negative.py
@@ -12,10 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -28,11 +26,13 @@
         cls.client = cls.os_adm.hosts_client
         cls.non_admin_client = cls.os.hosts_client
 
-    def _get_host_name(self):
-        hosts = self.client.list_hosts()['hosts']
-        self.assertTrue(len(hosts) >= 1)
-        hostname = hosts[0]['host_name']
-        return hostname
+    @classmethod
+    def resource_setup(cls):
+        super(HostsAdminNegativeTestJSON, cls).resource_setup()
+        hosts = cls.client.list_hosts()['hosts']
+        if not hosts:
+            raise lib_exc.NotFound("no host found")
+        cls.hostname = hosts[0]['host_name']
 
     @test.attr(type=['negative'])
     @test.idempotent_id('dd032027-0210-4d9c-860e-69b1b8deed5f')
@@ -43,27 +43,22 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('e75b0a1a-041f-47a1-8b4a-b72a6ff36d3f')
     def test_show_host_detail_with_nonexistent_hostname(self):
-        nonexitent_hostname = data_utils.rand_name('rand_hostname')
         self.assertRaises(lib_exc.NotFound,
-                          self.client.show_host, nonexitent_hostname)
+                          self.client.show_host, 'nonexistent_hostname')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('19ebe09c-bfd4-4b7c-81a2-e2e0710f59cc')
     def test_show_host_detail_with_non_admin_user(self):
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.show_host,
-                          hostname)
+                          self.hostname)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('e40c72b1-0239-4ed6-ba21-81a184df1f7c')
     def test_update_host_with_non_admin_user(self):
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.update_host,
-                          hostname,
+                          self.hostname,
                           status='enable',
                           maintenance_mode='enable')
 
@@ -71,11 +66,9 @@
     @test.idempotent_id('fbe2bf3e-3246-4a95-a59f-94e4e298ec77')
     def test_update_host_with_invalid_status(self):
         # 'status' can only be 'enable' or 'disable'
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.BadRequest,
                           self.client.update_host,
-                          hostname,
+                          self.hostname,
                           status='invalid',
                           maintenance_mode='enable')
 
@@ -83,11 +76,9 @@
     @test.idempotent_id('ab1e230e-5e22-41a9-8699-82b9947915d4')
     def test_update_host_with_invalid_maintenance_mode(self):
         # 'maintenance_mode' can only be 'enable' or 'disable'
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.BadRequest,
                           self.client.update_host,
-                          hostname,
+                          self.hostname,
                           status='enable',
                           maintenance_mode='invalid')
 
@@ -95,73 +86,57 @@
     @test.idempotent_id('0cd85f75-6992-4a4a-b1bd-d11e37fd0eee')
     def test_update_host_without_param(self):
         # 'status' or 'maintenance_mode' needed for host update
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.BadRequest,
                           self.client.update_host,
-                          hostname)
+                          self.hostname)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('23c92146-2100-4d68-b2d6-c7ade970c9c1')
     def test_update_nonexistent_host(self):
-        nonexitent_hostname = data_utils.rand_name('rand_hostname')
-
         self.assertRaises(lib_exc.NotFound,
                           self.client.update_host,
-                          nonexitent_hostname,
+                          'nonexistent_hostname',
                           status='enable',
                           maintenance_mode='enable')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('0d981ac3-4320-4898-b674-82b61fbb60e4')
     def test_startup_nonexistent_host(self):
-        nonexitent_hostname = data_utils.rand_name('rand_hostname')
-
         self.assertRaises(lib_exc.NotFound,
                           self.client.startup_host,
-                          nonexitent_hostname)
+                          'nonexistent_hostname')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('9f4ebb7e-b2ae-4e5b-a38f-0fd1bb0ddfca')
     def test_startup_host_with_non_admin_user(self):
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.startup_host,
-                          hostname)
+                          self.hostname)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('9e637444-29cf-4244-88c8-831ae82c31b6')
     def test_shutdown_nonexistent_host(self):
-        nonexitent_hostname = data_utils.rand_name('rand_hostname')
-
         self.assertRaises(lib_exc.NotFound,
                           self.client.shutdown_host,
-                          nonexitent_hostname)
+                          'nonexistent_hostname')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('a803529c-7e3f-4d3c-a7d6-8e1c203d27f6')
     def test_shutdown_host_with_non_admin_user(self):
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.shutdown_host,
-                          hostname)
+                          self.hostname)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('f86bfd7b-0b13-4849-ae29-0322e83ee58b')
     def test_reboot_nonexistent_host(self):
-        nonexitent_hostname = data_utils.rand_name('rand_hostname')
-
         self.assertRaises(lib_exc.NotFound,
                           self.client.reboot_host,
-                          nonexitent_hostname)
+                          'nonexistent_hostname')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('02d79bb9-eb57-4612-abf6-2cb38897d2f8')
     def test_reboot_host_with_non_admin_user(self):
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.reboot_host,
-                          hostname)
+                          self.hostname)
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index 113ec40..1f043dc 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -31,7 +31,7 @@
         return hypers
 
     def assertHypervisors(self, hypers):
-        self.assertTrue(len(hypers) > 0, "No hypervisors found: %s" % hypers)
+        self.assertGreater(len(hypers), 0, "No hypervisors found: %s" % hypers)
 
     @test.idempotent_id('7f0ceacd-c64d-4e96-b8ee-d02943142cc5')
     def test_get_hypervisor_list(self):
@@ -52,7 +52,7 @@
         self.assertHypervisors(hypers)
 
         details = self.client.show_hypervisor(hypers[0]['id'])['hypervisor']
-        self.assertTrue(len(details) > 0)
+        self.assertGreater(len(details), 0)
         self.assertEqual(details['hypervisor_hostname'],
                          hypers[0]['hypervisor_hostname'])
 
@@ -65,14 +65,14 @@
         hostname = hypers[0]['hypervisor_hostname']
         hypervisors = (self.client.list_servers_on_hypervisor(hostname)
                        ['hypervisors'])
-        self.assertTrue(len(hypervisors) > 0)
+        self.assertGreater(len(hypervisors), 0)
 
     @test.idempotent_id('797e4f28-b6e0-454d-a548-80cc77c00816')
     def test_get_hypervisor_stats(self):
         # Verify the stats of the all hypervisor
         stats = (self.client.show_hypervisor_statistics()
                  ['hypervisor_statistics'])
-        self.assertTrue(len(stats) > 0)
+        self.assertGreater(len(stats), 0)
 
     @test.idempotent_id('91a50d7d-1c2b-4f24-b55a-a1fe20efca70')
     def test_get_hypervisor_uptime(self):
@@ -87,7 +87,7 @@
         ironic_only = True
         hypers_without_ironic = []
         for hyper in hypers:
-            details = (self.client.show_hypervisor(hypers[0]['id'])
+            details = (self.client.show_hypervisor(hyper['id'])
                        ['hypervisor'])
             if details['hypervisor_type'] != 'ironic':
                 hypers_without_ironic.append(hyper)
diff --git a/tempest/api/compute/admin/test_hypervisor_negative.py b/tempest/api/compute/admin/test_hypervisor_negative.py
index 0e8012a..0f35e14 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -13,12 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -39,7 +36,7 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('c136086a-0f67-4b2b-bc61-8482bd68989f')
     def test_show_nonexistent_hypervisor(self):
-        nonexistent_hypervisor_id = str(uuid.uuid4())
+        nonexistent_hypervisor_id = data_utils.rand_uuid()
 
         self.assertRaises(
             lib_exc.NotFound,
@@ -50,7 +47,7 @@
     @test.idempotent_id('51e663d0-6b89-4817-a465-20aca0667d03')
     def test_show_hypervisor_with_non_admin_user(self):
         hypers = self._list_hypervisors()
-        self.assertTrue(len(hypers) > 0)
+        self.assertGreater(len(hypers), 0)
 
         self.assertRaises(
             lib_exc.Forbidden,
@@ -61,7 +58,7 @@
     @test.idempotent_id('2a0a3938-832e-4859-95bf-1c57c236b924')
     def test_show_servers_with_non_admin_user(self):
         hypers = self._list_hypervisors()
-        self.assertTrue(len(hypers) > 0)
+        self.assertGreater(len(hypers), 0)
 
         self.assertRaises(
             lib_exc.Forbidden,
@@ -71,7 +68,7 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('02463d69-0ace-4d33-a4a8-93d7883a2bba')
     def test_show_servers_with_nonexistent_hypervisor(self):
-        nonexistent_hypervisor_id = str(uuid.uuid4())
+        nonexistent_hypervisor_id = data_utils.rand_uuid()
 
         self.assertRaises(
             lib_exc.NotFound,
@@ -88,7 +85,7 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('f60aa680-9a3a-4c7d-90e1-fae3a4891303')
     def test_get_nonexistent_hypervisor_uptime(self):
-        nonexistent_hypervisor_id = str(uuid.uuid4())
+        nonexistent_hypervisor_id = data_utils.rand_uuid()
 
         self.assertRaises(
             lib_exc.NotFound,
@@ -99,7 +96,7 @@
     @test.idempotent_id('6c3461f9-c04c-4e2a-bebb-71dc9cb47df2')
     def test_get_hypervisor_uptime_with_non_admin_user(self):
         hypers = self._list_hypervisors()
-        self.assertTrue(len(hypers) > 0)
+        self.assertGreater(len(hypers), 0)
 
         self.assertRaises(
             lib_exc.Forbidden,
@@ -125,18 +122,16 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('19a45cc1-1000-4055-b6d2-28e8b2ec4faa')
     def test_search_nonexistent_hypervisor(self):
-        nonexistent_hypervisor_name = data_utils.rand_name('test_hypervisor')
-
         self.assertRaises(
             lib_exc.NotFound,
             self.client.search_hypervisor,
-            nonexistent_hypervisor_name)
+            'nonexistent_hypervisor_name')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('5b6a6c79-5dc1-4fa5-9c58-9c8085948e74')
     def test_search_hypervisor_with_non_admin_user(self):
         hypers = self._list_hypervisors()
-        self.assertTrue(len(hypers) > 0)
+        self.assertGreater(len(hypers), 0)
 
         self.assertRaises(
             lib_exc.Forbidden,
diff --git a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
index eea3103..b908502 100644
--- a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
+++ b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
@@ -16,9 +16,9 @@
 import datetime
 
 from six.moves.urllib import parse as urllib
-from tempest_lib import exceptions as lib_exc
 
 from tempest.api.compute import base
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
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..9011433
--- /dev/null
+++ b/tempest/api/compute/admin/test_keypairs_v210.py
@@ -0,0 +1,75 @@
+# 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_keypair = self.create_keypair(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 3ad9305..ff84945 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -19,13 +19,15 @@
 from tempest.api.compute import base
 from tempest.common import waiters
 from tempest import config
+from tempest.lib import decorators
 from tempest import test
 
 CONF = config.CONF
 
 
 class LiveBlockMigrationTestJSON(base.BaseV2ComputeAdminTest):
-    _host_key = 'OS-EXT-SRV-ATTR:host'
+    max_microversion = '2.24'
+    block_migration = None
 
     @classmethod
     def skip_checks(cls):
@@ -60,29 +62,25 @@
         return body
 
     def _get_host_for_server(self, server_id):
-        return self._get_server_details(server_id)[self._host_key]
+        return self._get_server_details(server_id)['OS-EXT-SRV-ATTR:host']
 
-    def _migrate_server_to(self, server_id, dest_host, volume_backed):
-        block_migration = (CONF.compute_feature_enabled.
-                           block_migration_for_live_migration and
-                           not volume_backed)
-        body = self.admin_servers_client.live_migrate_server(
+    def _migrate_server_to(self, server_id, dest_host, volume_backed=False):
+        kwargs = dict()
+        block_migration = getattr(self, 'block_migration', None)
+        if self.block_migration is None:
+            kwargs['disk_over_commit'] = False
+            block_migration = (CONF.compute_feature_enabled.
+                               block_migration_for_live_migration and
+                               not volume_backed)
+        self.admin_servers_client.live_migrate_server(
             server_id, host=dest_host, block_migration=block_migration,
-            disk_over_commit=False)
-        return body
+            **kwargs)
 
     def _get_host_other_than(self, host):
         for target_host in self._get_compute_hostnames():
             if host != target_host:
                 return target_host
 
-    def _volume_clean_up(self, server_id, volume_id):
-        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')
-        self.volumes_client.delete_volume(volume_id)
-
     def _test_live_migration(self, state='ACTIVE', volume_backed=False):
         """Tests live migration between two hosts.
 
@@ -129,6 +127,7 @@
     def test_live_block_migration_paused(self):
         self._test_live_migration(state='PAUSED')
 
+    @decorators.skip_because(bug="1524898")
     @test.idempotent_id('5071cf17-3004-4257-ae61-73a84e28badd')
     @test.services('volume')
     def test_volume_backed_live_migration(self):
@@ -142,23 +141,23 @@
                       block_migrate_cinder_iscsi,
                       'Block Live migration not configured for iSCSI')
     def test_iscsi_volume(self):
-        server_id = self.create_test_server(wait_until="ACTIVE")['id']
+        server = self.create_test_server(wait_until="ACTIVE")
+        server_id = server['id']
         actual_host = self._get_host_for_server(server_id)
         target_host = self._get_host_other_than(actual_host)
 
-        volume = self.volumes_client.create_volume(
-            display_name='test')['volume']
-
-        self.volumes_client.wait_for_volume_status(volume['id'],
-                                                   'available')
-        self.addCleanup(self._volume_clean_up, server_id, volume['id'])
+        volume = self.create_volume()
 
         # 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')
+        self.attach_volume(server, volume, device='/dev/xvdb')
 
         self._migrate_server_to(server_id, target_host)
         waiters.wait_for_server_status(self.servers_client,
                                        server_id, 'ACTIVE')
         self.assertEqual(target_host, self._get_host_for_server(server_id))
+
+
+class LiveAutoBlockMigrationV225TestJSON(LiveBlockMigrationTestJSON):
+    min_microversion = '2.25'
+    max_microversion = 'latest'
+    block_migration = 'auto'
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index f81d665..2dd2e89 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -15,8 +15,10 @@
 import testtools
 
 from tempest.api.compute import base
+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
 
 CONF = config.CONF
@@ -28,6 +30,8 @@
     def setup_clients(cls):
         super(MigrationsAdminTest, cls).setup_clients()
         cls.client = cls.os_adm.migrations_client
+        cls.flavors_admin_client = cls.os_adm.flavors_client
+        cls.admin_servers_client = cls.os_adm.servers_client
 
     @test.idempotent_id('75c0b83d-72a0-4cf8-a153-631e83e7d53f')
     def test_list_migrations(self):
@@ -42,14 +46,95 @@
         server = self.create_test_server(wait_until="ACTIVE")
         server_id = server['id']
 
-        self.servers_client.resize_server(server_id, self.flavor_ref_alt)
-        waiters.wait_for_server_status(self.servers_client,
-                                       server_id, 'VERIFY_RESIZE')
-        self.servers_client.confirm_resize_server(server_id)
-        waiters.wait_for_server_status(self.servers_client,
-                                       server_id, 'ACTIVE')
+        self.resize_server(server_id, self.flavor_ref_alt)
 
         body = self.client.list_migrations()['migrations']
 
         instance_uuids = [x['instance_uuid'] for x in body]
         self.assertIn(server_id, instance_uuids)
+
+    def _flavor_clean_up(self, flavor_id):
+        try:
+            self.flavors_admin_client.delete_flavor(flavor_id)
+            self.flavors_admin_client.wait_for_resource_deletion(flavor_id)
+        except exceptions.NotFound:
+            pass
+
+    @test.idempotent_id('33f1fec3-ba18-4470-8e4e-1d888e7c3593')
+    @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+                          'Resize not available.')
+    def test_resize_server_revert_deleted_flavor(self):
+        # Tests that we can revert the resize on an instance whose original
+        # flavor has been deleted.
+
+        # First we have to create a flavor that we can delete so make a copy
+        # of the normal flavor from which we'd create a server.
+        flavor = self.flavors_admin_client.show_flavor(
+            self.flavor_ref)['flavor']
+        flavor = self.flavors_admin_client.create_flavor(
+            name=data_utils.rand_name('test_resize_flavor_'),
+            ram=flavor['ram'],
+            disk=flavor['disk'],
+            vcpus=flavor['vcpus']
+        )['flavor']
+        self.addCleanup(self._flavor_clean_up, flavor['id'])
+
+        # Now boot a server with the copied flavor.
+        server = self.create_test_server(
+            wait_until='ACTIVE', flavor=flavor['id'])
+
+        # Delete the flavor we used to boot the instance.
+        self._flavor_clean_up(flavor['id'])
+
+        # Now resize the server and wait for it to go into verify state.
+        self.servers_client.resize_server(server['id'], self.flavor_ref_alt)
+        waiters.wait_for_server_status(self.servers_client, server['id'],
+                                       'VERIFY_RESIZE')
+
+        # Now revert the resize, it should be OK even though the original
+        # flavor used to boot the server was deleted.
+        self.servers_client.revert_resize_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client, server['id'],
+                                       'ACTIVE')
+
+        server = self.servers_client.show_server(server['id'])['server']
+        self.assertEqual(flavor['id'], server['flavor']['id'])
+
+    def _test_cold_migrate_server(self, revert=False):
+        if CONF.compute.min_compute_nodes < 2:
+            msg = "Less than 2 compute nodes, skipping multinode tests."
+            raise self.skipException(msg)
+
+        server = self.create_test_server(wait_until="ACTIVE")
+        src_host = self.admin_servers_client.show_server(
+            server['id'])['server']['OS-EXT-SRV-ATTR:host']
+
+        self.admin_servers_client.migrate_server(server['id'])
+
+        waiters.wait_for_server_status(self.servers_client,
+                                       server['id'], 'VERIFY_RESIZE')
+
+        if revert:
+            self.servers_client.revert_resize_server(server['id'])
+            assert_func = self.assertEqual
+        else:
+            self.servers_client.confirm_resize_server(server['id'])
+            assert_func = self.assertNotEqual
+
+        waiters.wait_for_server_status(self.servers_client,
+                                       server['id'], 'ACTIVE')
+        dst_host = self.admin_servers_client.show_server(
+            server['id'])['server']['OS-EXT-SRV-ATTR:host']
+        assert_func(src_host, dst_host)
+
+    @test.idempotent_id('4bf0be52-3b6f-4746-9a27-3143636fe30d')
+    @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
+                          'Cold migration not available.')
+    def test_cold_migration(self):
+        self._test_cold_migrate_server(revert=False)
+
+    @test.idempotent_id('caa1aa8b-f4ef-4374-be0d-95f001c2ac2d')
+    @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
+                          'Cold migration not available.')
+    def test_revert_cold_migration(self):
+        self._test_cold_migrate_server(revert=True)
diff --git a/tempest/api/compute/admin/test_networks.py b/tempest/api/compute/admin/test_networks.py
index e5c8790..d8f688d 100644
--- a/tempest/api/compute/admin/test_networks.py
+++ b/tempest/api/compute/admin/test_networks.py
@@ -42,8 +42,12 @@
                              "{0} networks with label {1}".format(
                                  len(configured_network),
                                  CONF.compute.fixed_network_name))
+        elif CONF.network.public_network_id:
+            configured_network = [x for x in networks if x['id'] ==
+                                  CONF.network.public_network_id]
         else:
-            configured_network = networks
+            raise self.skipException(
+                "Environment has no known-for-sure existing network.")
         configured_network = configured_network[0]
         network = (self.client.show_network(configured_network['id'])
                    ['network'])
@@ -57,5 +61,5 @@
             configured_network = CONF.compute.fixed_network_name
             self.assertIn(configured_network, [x['label'] for x in networks])
         else:
-            network_name = map(lambda x: x['label'], networks)
-            self.assertGreaterEqual(len(network_name), 1)
+            network_labels = [x['label'] for x in networks]
+            self.assertGreaterEqual(len(network_labels), 1)
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 2907e26..33b9bef 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from oslo_log import log as logging
-import six
 from testtools import matchers
 
 from tempest.api.compute import base
@@ -74,7 +73,8 @@
                          'ram': 10240, 'floating_ips': 20, 'fixed_ips': 10,
                          'key_pairs': 200, 'injected_file_path_bytes': 512,
                          'instances': 20, 'security_group_rules': 20,
-                         'cores': 2, 'security_groups': 20}
+                         'cores': 2, 'security_groups': 20,
+                         'server_groups': 20, 'server_group_members': 20}
         # Update limits for all quota resources
         quota_set = self.adm_client.update_quota_set(
             self.demo_tenant_id,
@@ -82,13 +82,6 @@
             **new_quota_set)['quota_set']
 
         default_quota_set.pop('id')
-        # NOTE(PhilDay) The following is safe as we're not updating these
-        # two quota values yet.  Once the Nova change to add these is merged
-        # and the client updated to support them this can be removed
-        if 'server_groups' in default_quota_set:
-            default_quota_set.pop('server_groups')
-        if 'server_group_members' in default_quota_set:
-            default_quota_set.pop('server_group_members')
         self.addCleanup(self.adm_client.update_quota_set,
                         self.demo_tenant_id, **default_quota_set)
         for quota in new_quota_set:
@@ -117,8 +110,6 @@
                                                password=password,
                                                project=project,
                                                email=email)
-        if 'user' in user:
-            user = user['user']
         user_id = user['id']
         self.addCleanup(self.identity_utils.delete_user, user_id)
 
@@ -183,9 +174,9 @@
         # restore the defaults when the test is done
         self.addCleanup(self._restore_default_quotas, body.copy())
         # increment all of the values for updating the default quota class
-        for quota, default in six.iteritems(body):
+        for quota, default in body.items():
             # NOTE(sdague): we need to increment a lot, otherwise
-            # there is a real chance that we go from -1 (unlimitted)
+            # there is a real chance that we go from -1 (unlimited)
             # to a very small number which causes issues.
             body[quota] = default + 100
         LOG.debug("update limits for the default quota class set")
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index bdbfde4..015e14d 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -12,12 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import decorators
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
@@ -41,6 +40,17 @@
         # tenant most of them should be skipped if we can't do that
         cls.demo_tenant_id = cls.client.tenant_id
 
+    def _update_quota(self, quota_item, quota_value):
+        quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
+                     ['quota_set'])
+        default_quota_value = quota_set[quota_item]
+
+        self.adm_client.update_quota_set(self.demo_tenant_id,
+                                         force=True,
+                                         **{quota_item: quota_value})
+        self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
+                        **{quota_item: default_quota_value})
+
     @test.attr(type=['negative'])
     @test.idempotent_id('733abfe8-166e-47bb-8363-23dbd7ff3476')
     def test_update_quota_normal_user(self):
@@ -55,17 +65,7 @@
     @test.idempotent_id('91058876-9947-4807-9f22-f6eb17140d9b')
     def test_create_server_when_cpu_quota_is_full(self):
         # Disallow server creation when tenant's vcpu quota is full
-        quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
-                     ['quota_set'])
-        default_vcpu_quota = quota_set['cores']
-        vcpu_quota = 0  # Set the quota to zero to conserve resources
-
-        self.adm_client.update_quota_set(self.demo_tenant_id,
-                                         force=True,
-                                         cores=vcpu_quota)
-
-        self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
-                        cores=default_vcpu_quota)
+        self._update_quota('cores', 0)
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.create_test_server)
 
@@ -73,17 +73,7 @@
     @test.idempotent_id('6fdd7012-584d-4327-a61c-49122e0d5864')
     def test_create_server_when_memory_quota_is_full(self):
         # Disallow server creation when tenant's memory quota is full
-        quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
-                     ['quota_set'])
-        default_mem_quota = quota_set['ram']
-        mem_quota = 0  # Set the quota to zero to conserve resources
-
-        self.adm_client.update_quota_set(self.demo_tenant_id,
-                                         force=True,
-                                         ram=mem_quota)
-
-        self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
-                        ram=default_mem_quota)
+        self._update_quota('ram', 0)
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.create_test_server)
 
@@ -91,16 +81,7 @@
     @test.idempotent_id('7c6be468-0274-449a-81c3-ac1c32ee0161')
     def test_create_server_when_instances_quota_is_full(self):
         # Once instances quota limit is reached, disallow server creation
-        quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
-                     ['quota_set'])
-        default_instances_quota = quota_set['instances']
-        instances_quota = 0  # Set quota to zero to disallow server creation
-
-        self.adm_client.update_quota_set(self.demo_tenant_id,
-                                         force=True,
-                                         instances=instances_quota)
-        self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
-                        instances=default_instances_quota)
+        self._update_quota('instances', 0)
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.create_test_server)
 
@@ -110,22 +91,10 @@
     @test.services('network')
     def test_security_groups_exceed_limit(self):
         # Negative test: Creation Security Groups over limit should FAIL
-
-        quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
-                     ['quota_set'])
-        default_sg_quota = quota_set['security_groups']
-
         # Set the quota to number of used security groups
         sg_quota = self.limits_client.show_limits()['limits']['absolute'][
             'totalSecurityGroupsUsed']
-
-        self.adm_client.update_quota_set(self.demo_tenant_id,
-                                         force=True,
-                                         security_groups=sg_quota)
-
-        self.addCleanup(self.adm_client.update_quota_set,
-                        self.demo_tenant_id,
-                        security_groups=default_sg_quota)
+        self._update_quota('security_groups', sg_quota)
 
         # Check we cannot create anymore
         # A 403 Forbidden or 413 Overlimit (old behaviour) exception
@@ -142,19 +111,7 @@
     def test_security_groups_rules_exceed_limit(self):
         # Negative test: Creation of Security Group Rules should FAIL
         # when we reach limit maxSecurityGroupRules
-
-        quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
-                     ['quota_set'])
-        default_sg_rules_quota = quota_set['security_group_rules']
-        sg_rules_quota = 0  # Set the quota to zero to conserve resources
-
-        self.adm_client.update_quota_set(self.demo_tenant_id,
-                                         force=True,
-                                         security_group_rules=sg_rules_quota)
-
-        self.addCleanup(self.adm_client.update_quota_set,
-                        self.demo_tenant_id,
-                        security_group_rules=default_sg_rules_quota)
+        self._update_quota('security_group_rules', 0)
 
         s_name = data_utils.rand_name('securitygroup')
         s_description = data_utils.rand_name('description')
diff --git a/tempest/api/compute/admin/test_security_group_default_rules.py b/tempest/api/compute/admin/test_security_group_default_rules.py
index 74f3caa..ce350b6 100644
--- a/tempest/api/compute/admin/test_security_group_default_rules.py
+++ b/tempest/api/compute/admin/test_security_group_default_rules.py
@@ -12,11 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
 import testtools
 
 from tempest.api.compute import base
 from tempest import config
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
diff --git a/tempest/api/compute/admin/test_security_groups.py b/tempest/api/compute/admin/test_security_groups.py
index 1494745..e329869 100644
--- a/tempest/api/compute/admin/test_security_groups.py
+++ b/tempest/api/compute/admin/test_security_groups.py
@@ -15,11 +15,8 @@
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
-from tempest import config
 from tempest import test
 
-CONF = config.CONF
-
 
 class SecurityGroupsTestAdminJSON(base.BaseV2ComputeAdminTest):
 
@@ -65,7 +62,7 @@
         # Fetch all security groups based on 'all_tenants' search filter
         fetched_list = self.adm_client.list_security_groups(
             all_tenants='true')['security_groups']
-        sec_group_id_list = map(lambda sg: sg['id'], fetched_list)
+        sec_group_id_list = [sg['id'] for sg in fetched_list]
         # Now check if all created Security Groups are present in fetched list
         for sec_group in security_group_list:
             self.assertIn(sec_group['id'], sec_group_id_list)
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 49c7318..0f19850 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -12,49 +12,38 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import decorators
-
 from tempest.api.compute import base
 from tempest.common import compute
 from tempest.common import fixed_network
 from tempest.common.utils import data_utils
 from tempest.common import waiters
+from tempest.lib import decorators
 from tempest import test
 
 
 class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
     """Tests Servers API using admin privileges"""
 
-    _host_key = 'OS-EXT-SRV-ATTR:host'
-
     @classmethod
     def setup_clients(cls):
         super(ServersAdminTestJSON, cls).setup_clients()
         cls.client = cls.os_adm.servers_client
         cls.non_admin_client = cls.servers_client
-        cls.flavors_client = cls.os_adm.flavors_client
 
     @classmethod
     def resource_setup(cls):
         super(ServersAdminTestJSON, cls).resource_setup()
 
-        cls.s1_name = data_utils.rand_name('server')
+        cls.s1_name = data_utils.rand_name(cls.__name__ + '-server')
         server = cls.create_test_server(name=cls.s1_name,
                                         wait_until='ACTIVE')
         cls.s1_id = server['id']
 
-        cls.s2_name = data_utils.rand_name('server')
+        cls.s2_name = data_utils.rand_name(cls.__name__ + '-server')
         server = cls.create_test_server(name=cls.s2_name,
                                         wait_until='ACTIVE')
         cls.s2_id = server['id']
 
-    @test.idempotent_id('51717b38-bdc1-458b-b636-1cf82d99f62f')
-    def test_list_servers_by_admin(self):
-        # Listing servers by admin user returns empty list by default
-        body = self.client.list_servers(detail=True)
-        servers = body['servers']
-        self.assertEqual([], servers)
-
     @test.idempotent_id('06f960bb-15bb-48dc-873d-f96e89be7870')
     def test_list_servers_filter_by_error_status(self):
         # Filter the list of servers by server error status
@@ -71,6 +60,26 @@
         self.assertIn(self.s1_id, map(lambda x: x['id'], servers))
         self.assertNotIn(self.s2_id, map(lambda x: x['id'], servers))
 
+    @test.idempotent_id('d56e9540-73ed-45e0-9b88-98fc419087eb')
+    def test_list_servers_detailed_filter_by_invalid_status(self):
+        params = {'status': 'invalid_status'}
+        body = self.client.list_servers(detail=True, **params)
+        servers = body['servers']
+        self.assertEqual([], servers)
+
+    @test.idempotent_id('51717b38-bdc1-458b-b636-1cf82d99f62f')
+    def test_list_servers_by_admin(self):
+        # Listing servers by admin user returns a list which doesn't
+        # contain the other tenants' server by default
+        body = self.client.list_servers(detail=True)
+        servers = body['servers']
+
+        # This case is for the test environments which contain
+        # the existing servers before testing
+        servers_name = [server['name'] for server in servers]
+        self.assertNotIn(self.s1_name, servers_name)
+        self.assertNotIn(self.s2_name, servers_name)
+
     @test.idempotent_id('9f5579ae-19b4-4985-a091-2a5d56106580')
     def test_list_servers_by_admin_with_all_tenants(self):
         # Listing servers by admin user with all tenants parameter
@@ -78,7 +87,7 @@
         params = {'all_tenants': ''}
         body = self.client.list_servers(detail=True, **params)
         servers = body['servers']
-        servers_name = map(lambda x: x['name'], servers)
+        servers_name = [server['name'] for server in servers]
 
         self.assertIn(self.s1_name, servers_name)
         self.assertIn(self.s2_name, servers_name)
@@ -92,19 +101,31 @@
         params = {'tenant_id': tenant_id}
         body = self.client.list_servers(detail=True, **params)
         servers = body['servers']
-        self.assertEqual([], servers)
+        servers_name = [x['name'] for x in servers]
+        self.assertNotIn(self.s1_name, servers_name)
+        self.assertNotIn(self.s2_name, servers_name)
 
-        # List the admin tenant which has no servers
+        # List the primary tenant with all_tenants is specified
+        params = {'all_tenants': '', 'tenant_id': tenant_id}
+        body = self.client.list_servers(detail=True, **params)
+        servers = body['servers']
+        servers_name = [x['name'] for x in servers]
+        self.assertIn(self.s1_name, servers_name)
+        self.assertIn(self.s2_name, servers_name)
+
+        # List the admin tenant shouldn't get servers created by other tenants
         admin_tenant_id = self.client.tenant_id
         params = {'all_tenants': '', 'tenant_id': admin_tenant_id}
         body = self.client.list_servers(detail=True, **params)
         servers = body['servers']
-        self.assertEqual([], servers)
+        servers_name = [x['name'] for x in servers]
+        self.assertNotIn(self.s1_name, servers_name)
+        self.assertNotIn(self.s2_name, servers_name)
 
     @test.idempotent_id('86c7a8f7-50cf-43a9-9bac-5b985317134f')
     def test_list_servers_filter_by_exist_host(self):
         # Filter the list of servers by existent host
-        name = data_utils.rand_name('server')
+        name = data_utils.rand_name(self.__class__.__name__ + '-server')
         network = self.get_tenant_network()
         network_kwargs = fixed_network.set_networks_kwarg(network)
         # We need to create the server as an admin, so we can't use
@@ -115,7 +136,7 @@
         self.addCleanup(self.client.delete_server, test_server['id'])
         server = self.client.show_server(test_server['id'])['server']
         self.assertEqual(server['status'], 'ACTIVE')
-        hostname = server[self._host_key]
+        hostname = server['OS-EXT-SRV-ATTR:host']
         params = {'host': hostname}
         body = self.client.list_servers(**params)
         servers = body['servers']
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 23b8a6c..206260f 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -12,9 +12,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
 import testtools
 
 from tempest.api.compute import base
@@ -22,6 +19,7 @@
 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
@@ -36,13 +34,14 @@
         cls.client = cls.os_adm.servers_client
         cls.non_adm_client = cls.servers_client
         cls.flavors_client = cls.os_adm.flavors_client
+        cls.quotas_client = cls.os_adm.quotas_client
 
     @classmethod
     def resource_setup(cls):
         super(ServersAdminNegativeTestJSON, cls).resource_setup()
         cls.tenant_id = cls.client.tenant_id
 
-        cls.s1_name = data_utils.rand_name('server')
+        cls.s1_name = data_utils.rand_name(cls.__name__ + '-server')
         server = cls.create_test_server(name=cls.s1_name,
                                         wait_until='ACTIVE')
         cls.s1_id = server['id']
@@ -66,11 +65,15 @@
         self.useFixture(fixtures.LockFixture('compute_quotas'))
         flavor_name = data_utils.rand_name("flavor")
         flavor_id = self._get_unused_flavor_id()
-        quota_set = (self.quotas_client.show_default_quota_set(self.tenant_id)
-                     ['quota_set'])
-        ram = int(quota_set['ram']) + 1
-        vcpus = 8
-        disk = 10
+        quota_set = self.quotas_client.show_quota_set(
+            self.tenant_id)['quota_set']
+        ram = int(quota_set['ram'])
+        if ram == -1:
+            raise self.skipException("ram quota set is -1,"
+                                     " cannot test overlimit")
+        ram += 1
+        vcpus = 1
+        disk = 5
         flavor_ref = self.flavors_client.create_flavor(name=flavor_name,
                                                        ram=ram, vcpus=vcpus,
                                                        disk=disk,
@@ -90,11 +93,15 @@
         self.useFixture(fixtures.LockFixture('compute_quotas'))
         flavor_name = data_utils.rand_name("flavor")
         flavor_id = self._get_unused_flavor_id()
+        quota_set = self.quotas_client.show_quota_set(
+            self.tenant_id)['quota_set']
+        vcpus = int(quota_set['cores'])
+        if vcpus == -1:
+            raise self.skipException("cores quota set is -1,"
+                                     " cannot test overlimit")
+        vcpus += 1
         ram = 512
-        quota_set = (self.quotas_client.show_default_quota_set(self.tenant_id)
-                     ['quota_set'])
-        vcpus = int(quota_set['cores']) + 1
-        disk = 10
+        disk = 5
         flavor_ref = self.flavors_client.create_flavor(name=flavor_name,
                                                        ram=ram, vcpus=vcpus,
                                                        disk=disk,
@@ -139,7 +146,7 @@
         # migrate a non existent server
         self.assertRaises(lib_exc.NotFound,
                           self.client.migrate_server,
-                          str(uuid.uuid4()))
+                          data_utils.rand_uuid())
 
     @test.idempotent_id('b0b17f83-d14e-4fc4-8f31-bcc9f3cfa629')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
diff --git a/tempest/api/compute/admin/test_servers_on_multinodes.py b/tempest/api/compute/admin/test_servers_on_multinodes.py
index 814a876..1bbde98 100644
--- a/tempest/api/compute/admin/test_servers_on_multinodes.py
+++ b/tempest/api/compute/admin/test_servers_on_multinodes.py
@@ -12,6 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
 from tempest import config
 from tempest import test
@@ -34,6 +36,9 @@
             server_id)['server']['OS-EXT-SRV-ATTR:host']
 
     @test.idempotent_id('26a9d5df-6890-45f2-abc4-a659290cb130')
+    @testtools.skipUnless(
+        test.is_scheduler_filter_enabled("SameHostFilter"),
+        'SameHostFilter is not available.')
     def test_create_servers_on_same_host(self):
         server01 = self.create_test_server(wait_until='ACTIVE')['id']
 
@@ -45,6 +50,9 @@
         self.assertEqual(host01, host02)
 
     @test.idempotent_id('cc7ca884-6e3e-42a3-a92f-c522fcf25e8e')
+    @testtools.skipUnless(
+        test.is_scheduler_filter_enabled("DifferentHostFilter"),
+        'DifferentHostFilter is not available.')
     def test_create_servers_on_different_hosts(self):
         server01 = self.create_test_server(wait_until='ACTIVE')['id']
 
@@ -56,6 +64,9 @@
         self.assertNotEqual(host01, host02)
 
     @test.idempotent_id('7869cc84-d661-4e14-9f00-c18cdc89cf57')
+    @testtools.skipUnless(
+        test.is_scheduler_filter_enabled("DifferentHostFilter"),
+        'DifferentHostFilter is not available.')
     def test_create_servers_on_different_hosts_with_list_of_servers(self):
         server01 = self.create_test_server(wait_until='ACTIVE')['id']
 
diff --git a/tempest/api/compute/admin/test_services_negative.py b/tempest/api/compute/admin/test_services_negative.py
index e57401a..710cac4 100644
--- a/tempest/api/compute/admin/test_services_negative.py
+++ b/tempest/api/compute/admin/test_services_negative.py
@@ -12,9 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
index e5c17ca..dbc22e0 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage.py
@@ -16,8 +16,9 @@
 import datetime
 
 from tempest.api.compute import base
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions as e
 from tempest import test
-from tempest_lib import exceptions as e
 
 # Time that waits for until returning valid response
 # TODO(takmatsu): Ideally this value would come from configuration.
@@ -59,7 +60,9 @@
                 return True
             except e.InvalidHTTPResponseBody:
                 return False
-        test.call_until_true(is_valid, duration, 1)
+        self.assertEqual(test_utils.call_until_true(is_valid, duration, 1),
+                         True, "%s not return valid response in %s secs" % (
+                             func.__name__, duration))
         return self.resp
 
     @test.idempotent_id('062c8ae9-9912-4249-8b51-e38d664e926e')
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
index e9b4ad4..315116e 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 import datetime
-from tempest_lib import exceptions as lib_exc
 
 from tempest.api.compute import base
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
new file mode 100644
index 0000000..f603abd
--- /dev/null
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -0,0 +1,75 @@
+#    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 import base
+from tempest.common import waiters
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class TestVolumeSwap(base.BaseV2ComputeAdminTest):
+    """The test suite for swapping of volume with admin user.
+
+    The following is the scenario outline:
+    1. Create a volume "volume1" with non-admin.
+    2. Create a volume "volume2" with non-admin.
+    3. Boot an instance "instance1" with non-admin.
+    4. Attach "volume1" to "instance1" with non-admin.
+    5. Swap volume from "volume1" to "volume2" as admin.
+    6. Check the swap volume is successful and "volume2"
+       is attached to "instance1" and "volume1" is in available state.
+    """
+
+    @classmethod
+    def skip_checks(cls):
+        super(TestVolumeSwap, cls).skip_checks()
+        if not CONF.compute_feature_enabled.swap_volume:
+            raise cls.skipException("Swapping volumes is not supported.")
+
+    @classmethod
+    def setup_clients(cls):
+        super(TestVolumeSwap, cls).setup_clients()
+        # We need the admin client for performing the update (swap) volume call
+        cls.servers_admin_client = cls.os_adm.servers_client
+
+    @test.idempotent_id('1769f00d-a693-4d67-a631-6a3496773813')
+    @test.services('volume')
+    def test_volume_swap(self):
+        # Create two volumes.
+        # NOTE(gmann): Volumes are created before server creation so that
+        # volumes cleanup can happen successfully irrespective of which volume
+        # is attached to server.
+        volume1 = self.create_volume()
+        volume2 = self.create_volume()
+        # Boot server
+        server = self.create_test_server(wait_until='ACTIVE')
+        # Attach "volume1" to server
+        self.attach_volume(server, volume1)
+        # Swap volume from "volume1" to "volume2"
+        self.servers_admin_client.update_attached_volume(
+            server['id'], volume1['id'], volumeId=volume2['id'])
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume1['id'], 'available')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume2['id'], 'in-use')
+        self.addCleanup(self.servers_client.detach_volume,
+                        server['id'], volume2['id'])
+        # Verify "volume2" is attached to the server
+        vol_attachments = self.servers_client.list_volume_attachments(
+            server['id'])['volumeAttachments']
+        self.assertEqual(1, len(vol_attachments))
+        self.assertIn(volume2['id'], vol_attachments[0]['volumeId'])
+
+        # TODO(mriedem): Test swapping back from volume2 to volume1 after
+        # nova bug 1490236 is fixed.
diff --git a/tempest/api/compute/admin/test_volumes_negative.py b/tempest/api/compute/admin/test_volumes_negative.py
new file mode 100644
index 0000000..26b8742
--- /dev/null
+++ b/tempest/api/compute/admin/test_volumes_negative.py
@@ -0,0 +1,62 @@
+# 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 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
+
+
+class VolumesAdminNegativeTest(base.BaseV2ComputeAdminTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesAdminNegativeTest, cls).skip_checks()
+        if not CONF.service_available.cinder:
+            skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
+
+    @classmethod
+    def setup_clients(cls):
+        super(VolumesAdminNegativeTest, cls).setup_clients()
+        cls.servers_admin_client = cls.os_adm.servers_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumesAdminNegativeTest, cls).resource_setup()
+        cls.server = cls.create_test_server(wait_until='ACTIVE')
+
+    @test.idempotent_id('309b5ecd-0585-4a7e-a36f-d2b2bf55259d')
+    def test_update_attached_volume_with_nonexistent_volume_in_uri(self):
+        volume = self.create_volume()
+        nonexistent_volume = data_utils.rand_uuid()
+        self.assertRaises(lib_exc.NotFound,
+                          self.servers_admin_client.update_attached_volume,
+                          self.server['id'], nonexistent_volume,
+                          volumeId=volume['id'])
+
+    @test.related_bug('1629110', status_code=400)
+    @test.idempotent_id('7dcac15a-b107-46d3-a5f6-cb863f4e454a')
+    def test_update_attached_volume_with_nonexistent_volume_in_body(self):
+        volume = self.create_volume()
+        self.attach_volume(self.server, volume)
+
+        nonexistent_volume = data_utils.rand_uuid()
+        self.assertRaises(lib_exc.BadRequest,
+                          self.servers_admin_client.update_attached_volume,
+                          self.server['id'], volume['id'],
+                          volumeId=nonexistent_volume)
diff --git a/tempest/api/compute/api_microversion_fixture.py b/tempest/api/compute/api_microversion_fixture.py
new file mode 100644
index 0000000..695af52
--- /dev/null
+++ b/tempest/api/compute/api_microversion_fixture.py
@@ -0,0 +1,31 @@
+# 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 fixtures
+
+from tempest.lib.services.compute import base_compute_client
+
+
+class APIMicroversionFixture(fixtures.Fixture):
+
+    def __init__(self, compute_microversion):
+        self.compute_microversion = compute_microversion
+
+    def _setUp(self):
+        super(APIMicroversionFixture, self)._setUp()
+        base_compute_client.COMPUTE_MICROVERSION = self.compute_microversion
+        self.addCleanup(self._reset_compute_microversion)
+
+    def _reset_compute_microversion(self):
+        base_compute_client.COMPUTE_MICROVERSION = None
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 0856983..d77ea90 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -16,14 +16,16 @@
 import time
 
 from oslo_log import log as logging
-from tempest_lib import exceptions as lib_exc
 
-from tempest.common import api_version_utils
+from tempest.api.compute import api_microversion_fixture
 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.common.utils import test_utils
+from tempest.lib import exceptions as lib_exc
 import tempest.test
 
 CONF = config.CONF
@@ -46,8 +48,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,
@@ -56,13 +58,6 @@
     @classmethod
     def setup_credentials(cls):
         cls.set_network_resources()
-        cls.request_microversion = (
-            api_version_utils.select_request_microversion(
-                cls.min_microversion,
-                CONF.compute_feature_enabled.min_microversion))
-        if cls.request_microversion:
-            cls.services_microversion = {
-                CONF.compute.catalog_type: cls.request_microversion}
         super(BaseV2ComputeTest, cls).setup_credentials()
 
     @classmethod
@@ -80,7 +75,6 @@
             cls.os.compute_security_group_rules_client)
         cls.security_groups_client = cls.os.compute_security_groups_client
         cls.quotas_client = cls.os.quotas_client
-        cls.quota_classes_client = cls.os.quota_classes_client
         cls.compute_networks_client = cls.os.compute_networks_client
         cls.limits_client = cls.os.limits_client
         cls.volumes_extensions_client = cls.os.volumes_extensions_client
@@ -108,6 +102,10 @@
     @classmethod
     def resource_setup(cls):
         super(BaseV2ComputeTest, cls).resource_setup()
+        cls.request_microversion = (
+            api_version_utils.select_request_microversion(
+                cls.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
@@ -121,6 +119,7 @@
         cls.images = []
         cls.security_groups = []
         cls.server_groups = []
+        cls.volumes = []
 
     @classmethod
     def resource_cleanup(cls):
@@ -128,6 +127,7 @@
         cls.clear_servers()
         cls.clear_security_groups()
         cls.clear_server_groups()
+        cls.clear_volumes()
         super(BaseV2ComputeTest, cls).resource_cleanup()
 
     @classmethod
@@ -136,21 +136,18 @@
             server['id'] for server in cls.servers))
         for server in cls.servers:
             try:
-                cls.servers_client.delete_server(server['id'])
-            except lib_exc.NotFound:
-                # Something else already cleaned up the server, nothing to be
-                # worried about
-                pass
+                test_utils.call_and_ignore_notfound_exc(
+                    cls.servers_client.delete_server, server['id'])
             except Exception:
-                LOG.exception('Deleting server %s failed' % server['id'])
+                LOG.exception('Deleting server %s failed', server['id'])
 
         for server in cls.servers:
             try:
                 waiters.wait_for_server_termination(cls.servers_client,
                                                     server['id'])
             except Exception:
-                LOG.exception('Waiting for deletion of server %s failed'
-                              % server['id'])
+                LOG.exception('Waiting for deletion of server %s failed',
+                              server['id'])
 
     @classmethod
     def server_check_teardown(cls):
@@ -179,12 +176,10 @@
         LOG.debug('Clearing images: %s', ','.join(cls.images))
         for image_id in cls.images:
             try:
-                cls.compute_images_client.delete_image(image_id)
-            except lib_exc.NotFound:
-                # The image may have already been deleted which is OK.
-                pass
+                test_utils.call_and_ignore_notfound_exc(
+                    cls.compute_images_client.delete_image, image_id)
             except Exception:
-                LOG.exception('Exception raised deleting image %s' % image_id)
+                LOG.exception('Exception raised deleting image %s', image_id)
 
     @classmethod
     def clear_security_groups(cls):
@@ -192,10 +187,8 @@
             str(sg['id']) for sg in cls.security_groups))
         for sg in cls.security_groups:
             try:
-                cls.security_groups_client.delete_security_group(sg['id'])
-            except lib_exc.NotFound:
-                # The security group may have already been deleted which is OK.
-                pass
+                test_utils.call_and_ignore_notfound_exc(
+                    cls.security_groups_client.delete_security_group, sg['id'])
             except Exception as exc:
                 LOG.info('Exception raised deleting security group %s',
                          sg['id'])
@@ -206,10 +199,10 @@
         LOG.debug('Clearing server groups: %s', ','.join(cls.server_groups))
         for server_group_id in cls.server_groups:
             try:
-                cls.server_groups_client.delete_server_group(server_group_id)
-            except lib_exc.NotFound:
-                # The server-group may have already been deleted which is OK.
-                pass
+                test_utils.call_and_ignore_notfound_exc(
+                    cls.server_groups_client.delete_server_group,
+                    server_group_id
+                )
             except Exception:
                 LOG.exception('Exception raised deleting server-group %s',
                               server_group_id)
@@ -227,6 +220,8 @@
         :param validatable: Whether the server will be pingable or sshable.
         :param volume_backed: Whether the instance is volume backed or not.
         """
+        if 'name' not in kwargs:
+            kwargs['name'] = data_utils.rand_name(cls.__name__ + "-server")
         tenant_network = cls.get_tenant_network()
         body, servers = compute.create_test_server(
             cls.os,
@@ -288,7 +283,7 @@
             volumes_client.wait_for_resource_deletion(volume_id)
         except lib_exc.NotFound:
             LOG.warning("Unable to delete volume '%s' since it was not found. "
-                        "Maybe it was already deleted?" % volume_id)
+                        "Maybe it was already deleted?", volume_id)
 
     @classmethod
     def prepare_instance_network(cls):
@@ -323,12 +318,7 @@
     def rebuild_server(cls, server_id, validatable=False, **kwargs):
         # Destroy an existing server and creates a new one
         if server_id:
-            try:
-                cls.servers_client.delete_server(server_id)
-                waiters.wait_for_server_termination(cls.servers_client,
-                                                    server_id)
-            except Exception:
-                LOG.exception('Failed to delete server %s' % server_id)
+            cls.delete_server(server_id)
 
         cls.password = data_utils.rand_password()
         server = cls.create_test_server(
@@ -346,7 +336,16 @@
             waiters.wait_for_server_termination(cls.servers_client,
                                                 server_id)
         except Exception:
-            LOG.exception('Failed to delete server %s' % server_id)
+            LOG.exception('Failed to delete server %s', server_id)
+
+    @classmethod
+    def resize_server(cls, server_id, new_flavor_id, **kwargs):
+        """resize and confirm_resize an server, waits for it to be ACTIVE."""
+        cls.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
+        waiters.wait_for_server_status(cls.servers_client, server_id,
+                                       'VERIFY_RESIZE')
+        cls.servers_client.confirm_resize_server(server_id)
+        waiters.wait_for_server_status(cls.servers_client, server_id, 'ACTIVE')
 
     @classmethod
     def delete_volume(cls, volume_id):
@@ -367,9 +366,81 @@
             for address in addresses:
                 if address['version'] == CONF.validation.ip_version_for_ssh:
                     return address['addr']
-            raise exceptions.ServerUnreachable()
+            raise exceptions.ServerUnreachable(server_id=server['id'])
         else:
-            raise exceptions.InvalidConfiguration()
+            raise lib_exc.InvalidConfiguration()
+
+    def setUp(self):
+        super(BaseV2ComputeTest, self).setUp()
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            self.request_microversion))
+
+    @classmethod
+    def create_volume(cls, image_ref=None, **kwargs):
+        """Create a volume and wait for it to become 'available'.
+
+        :param image_ref: Specify an image id to create a bootable volume.
+        :**kwargs: other parameters to create volume.
+        :returns: The available volume.
+        """
+        if 'size' not in kwargs:
+            kwargs['size'] = CONF.volume.volume_size
+        if 'display_name' not in kwargs:
+            vol_name = data_utils.rand_name(cls.__name__ + '-volume')
+            kwargs['display_name'] = vol_name
+        if image_ref is not None:
+            kwargs['imageRef'] = image_ref
+        volume = cls.volumes_client.create_volume(**kwargs)['volume']
+        cls.volumes.append(volume)
+        waiters.wait_for_volume_status(cls.volumes_client,
+                                       volume['id'], 'available')
+        return volume
+
+    @classmethod
+    def clear_volumes(cls):
+        LOG.debug('Clearing volumes: %s', ','.join(
+            volume['id'] for volume in cls.volumes))
+        for volume in cls.volumes:
+            try:
+                test_utils.call_and_ignore_notfound_exc(
+                    cls.volumes_client.delete_volume, volume['id'])
+            except Exception:
+                LOG.exception('Deleting volume %s failed', volume['id'])
+
+        for volume in cls.volumes:
+            try:
+                cls.volumes_client.wait_for_resource_deletion(volume['id'])
+            except Exception:
+                LOG.exception('Waiting for deletion of volume %s failed',
+                              volume['id'])
+
+    def attach_volume(self, server, volume, device=None):
+        """Attaches volume to server and waits for 'in-use' volume status.
+
+        The volume will be detached when the test tears down.
+
+        :param server: The server to which the volume will be attached.
+        :param volume: The volume to attach.
+        :param device: Optional mountpoint for the attached volume. Note that
+            this is not guaranteed for all hypervisors and is not recommended.
+        """
+        attach_kwargs = dict(volumeId=volume['id'])
+        if device:
+            attach_kwargs['device'] = device
+        self.servers_client.attach_volume(
+            server['id'], **attach_kwargs)
+        # On teardown detach the volume and wait for it to be available. This
+        # is so we don't error out when trying to delete the volume during
+        # teardown.
+        self.addCleanup(waiters.wait_for_volume_status,
+                        self.volumes_client, volume['id'], 'available')
+        # Ignore 404s on detach in case the server is deleted or the volume
+        # is already detached.
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.servers_client.detach_volume,
+                        server['id'], volume['id'])
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume['id'], 'in-use')
 
 
 class BaseV2ComputeAdminTest(BaseV2ComputeTest):
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
deleted file mode 100644
index 83f8e19..0000000
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2013 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 tempest.api.compute import base
-from tempest.api_schema.request.compute.v2 import flavors
-from tempest import config
-from tempest import test
-
-
-CONF = config.CONF
-
-load_tests = test.NegativeAutoTest.load_tests
-
-
-@test.SimpleNegativeAutoTest
-class FlavorsListWithDetailsNegativeTestJSON(base.BaseV2ComputeTest,
-                                             test.NegativeAutoTest):
-    _service = CONF.compute.catalog_type
-    _schema = flavors.flavor_list
-
-
-@test.SimpleNegativeAutoTest
-class FlavorDetailsNegativeTestJSON(base.BaseV2ComputeTest,
-                                    test.NegativeAutoTest):
-    _service = CONF.compute.catalog_type
-    _schema = flavors.flavors_details
-
-    @classmethod
-    def resource_setup(cls):
-        super(FlavorDetailsNegativeTestJSON, cls).resource_setup()
-        cls.set_resource("flavor", cls.flavor_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 5b90641..dcb2d2c 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -13,13 +13,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 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.common.utils import test_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
+CONF = config.CONF
+
 
 class FloatingIPsTestJSON(base.BaseFloatingIPsTest):
     server_id = None
@@ -39,7 +40,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']
 
@@ -50,20 +52,13 @@
             cls.client.delete_floating_ip(cls.floating_ip_id)
         super(FloatingIPsTestJSON, cls).resource_cleanup()
 
-    def _try_delete_floating_ip(self, floating_ip_id):
-        # delete floating ip, if it exists
-        try:
-            self.client.delete_floating_ip(floating_ip_id)
-        # if not found, it depicts it was deleted in the test
-        except lib_exc.NotFound:
-            pass
-
     @test.idempotent_id('f7bfb946-297e-41b8-9e8c-aba8e9bb5194')
     @test.services('network')
     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)
@@ -79,8 +74,10 @@
         # 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']
-        self.addCleanup(self._try_delete_floating_ip, floating_ip_body['id'])
+        floating_ip_body = self.client.create_floating_ip(
+            pool=CONF.network.floating_network_name)['floating_ip']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_floating_ip, floating_ip_body['id'])
         # Deleting the floating IP from the project
         self.client.delete_floating_ip(floating_ip_body['id'])
         # Check it was really deleted.
@@ -113,10 +110,7 @@
         # positive test:Association of an already associated floating IP
         # to specific server should change the association of the Floating IP
         # Create server so as to use for Multiple association
-        new_name = data_utils.rand_name('floating_server')
-        body = self.create_test_server(name=new_name)
-        waiters.wait_for_server_status(self.servers_client,
-                                       body['id'], 'ACTIVE')
+        body = self.create_test_server(wait_until='ACTIVE')
         self.new_server_id = body['id']
         self.addCleanup(self.servers_client.delete_server, self.new_server_id)
 
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
index 0223c0d..31cf39c 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -13,20 +13,16 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute.floating_ips 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
 
 
 class FloatingIPsNegativeTestJSON(base.BaseFloatingIPsTest):
-    server_id = None
 
     @classmethod
     def setup_clients(cls):
@@ -41,15 +37,14 @@
         server = cls.create_test_server(wait_until='ACTIVE')
         cls.server_id = server['id']
         # Generating a nonexistent floatingIP id
-        cls.floating_ip_ids = []
         body = cls.client.list_floating_ips()['floating_ips']
-        for i in range(len(body)):
-            cls.floating_ip_ids.append(body[i]['id'])
+        floating_ip_ids = [floating_ip['id'] for floating_ip in body]
         while True:
-            cls.non_exist_id = data_utils.rand_int_id(start=999)
             if CONF.service_available.neutron:
-                cls.non_exist_id = str(uuid.uuid4())
-            if cls.non_exist_id not in cls.floating_ip_ids:
+                cls.non_exist_id = data_utils.rand_uuid()
+            else:
+                cls.non_exist_id = data_utils.rand_int_id(start=999)
+            if cls.non_exist_id not in floating_ip_ids:
                 break
 
     @test.attr(type=['negative'])
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..222bf18 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,14 +34,15 @@
         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'])
 
     @classmethod
     def resource_cleanup(cls):
-        for i in range(3):
-            cls.client.delete_floating_ip(cls.floating_ip_id[i])
+        for f_id in cls.floating_ip_id:
+            cls.client.delete_floating_ip(f_id)
         super(FloatingIPDetailsTestJSON, cls).resource_cleanup()
 
     @test.idempotent_id('16db31c3-fb85-40c9-bbe2-8cf7b67ff99f')
@@ -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/floating_ips/test_list_floating_ips_negative.py b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
index 75b6b55..ea56ae9 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
@@ -13,13 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 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
@@ -40,7 +37,7 @@
         # of non-existent floating IP
         # Creating a non-existent floatingIP id
         if CONF.service_available.neutron:
-            non_exist_id = str(uuid.uuid4())
+            non_exist_id = data_utils.rand_uuid()
         else:
             non_exist_id = data_utils.rand_int_id(start=999)
         self.assertRaises(lib_exc.NotFound,
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index 0724566..26d4efe 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -16,9 +16,11 @@
 import six
 
 from tempest.api.compute import base
+from tempest.common import image as common_image
 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
 
 CONF = config.CONF
@@ -36,7 +38,17 @@
     @classmethod
     def setup_clients(cls):
         super(ImagesMetadataTestJSON, cls).setup_clients()
-        cls.glance_client = cls.os.image_client
+        # Check if glance v1 is available to determine which client to use. We
+        # prefer glance v1 for the compute API tests since the compute image
+        # API proxy was written for glance v1.
+        if CONF.image_feature_enabled.api_v1:
+            cls.glance_client = cls.os.image_client
+        elif CONF.image_feature_enabled.api_v2:
+            cls.glance_client = cls.os.image_client_v2
+        else:
+            raise exceptions.InvalidConfiguration(
+                'Either api_v1 or api_v2 must be True in '
+                '[image-feature-enabled].')
         cls.client = cls.compute_images_client
 
     @classmethod
@@ -44,15 +56,26 @@
         super(ImagesMetadataTestJSON, cls).resource_setup()
         cls.image_id = None
 
-        name = data_utils.rand_name('image')
-        body = cls.glance_client.create_image(name=name,
-                                              container_format='bare',
-                                              disk_format='raw',
-                                              is_public=False)['image']
+        params = {
+            'name': data_utils.rand_name('image'),
+            'container_format': 'bare',
+            'disk_format': 'raw'
+        }
+        if CONF.image_feature_enabled.api_v1:
+            params.update({'is_public': False})
+            params = {'headers': common_image.image_meta_to_headers(**params)}
+        else:
+            params.update({'visibility': 'private'})
+
+        body = cls.glance_client.create_image(**params)
+        body = body['image'] if 'image' in body else body
         cls.image_id = body['id']
         cls.images.append(cls.image_id)
-        image_file = six.StringIO(('*' * 1024))
-        cls.glance_client.update_image(cls.image_id, data=image_file)
+        image_file = six.BytesIO((b'*' * 1024))
+        if CONF.image_feature_enabled.api_v1:
+            cls.glance_client.update_image(cls.image_id, data=image_file)
+        else:
+            cls.glance_client.store_image_file(cls.image_id, data=image_file)
         waiters.wait_for_image_status(cls.client, cls.image_id, 'ACTIVE')
 
     def setUp(self):
diff --git a/tempest/api/compute/images/test_image_metadata_negative.py b/tempest/api/compute/images/test_image_metadata_negative.py
index 85d137b..489bfbc 100644
--- a/tempest/api/compute/images/test_image_metadata_negative.py
+++ b/tempest/api/compute/images/test_image_metadata_negative.py
@@ -13,18 +13,17 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
-class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
+class ImagesMetadataNegativeTestJSON(base.BaseV2ComputeTest):
 
     @classmethod
     def setup_clients(cls):
-        super(ImagesMetadataTestJSON, cls).setup_clients()
+        super(ImagesMetadataNegativeTestJSON, cls).setup_clients()
         cls.client = cls.compute_images_client
 
     @test.attr(type=['negative'])
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index 150e8af..a06f4a7 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -38,17 +38,17 @@
     def setup_clients(cls):
         super(ImagesTestJSON, cls).setup_clients()
         cls.client = cls.compute_images_client
-        cls.servers_client = cls.servers_client
 
     @test.idempotent_id('aa06b52b-2db5-4807-b218-9441f75d74e3')
     def test_delete_saving_image(self):
-        snapshot_name = data_utils.rand_name('test-snap')
         server = self.create_test_server(wait_until='ACTIVE')
         self.addCleanup(self.servers_client.delete_server, server['id'])
         image = self.create_image_from_server(server['id'],
-                                              name=snapshot_name,
                                               wait_until='SAVING')
         self.client.delete_image(image['id'])
+        msg = ('The image with ID {image_id} failed to be deleted'
+               .format(image_id=image['id']))
+        self.assertTrue(self.client.is_resource_deleted(image['id']), msg)
 
     @test.idempotent_id('aaacd1d0-55a2-4ce8-818a-b5439df8adc9')
     def test_create_image_from_stopped_server(self):
diff --git a/tempest/api/compute/images/test_images_negative.py b/tempest/api/compute/images/test_images_negative.py
index 8706566..6c97072 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/test_images_negative.py
@@ -12,12 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute 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
@@ -40,7 +39,6 @@
     def setup_clients(cls):
         super(ImagesNegativeTestJSON, cls).setup_clients()
         cls.client = cls.compute_images_client
-        cls.servers_client = cls.servers_client
 
     @test.attr(type=['negative'])
     @test.idempotent_id('6cd5a89d-5b47-46a7-93bc-3916f0d84973')
@@ -48,27 +46,23 @@
         # An image should not be created if the server instance is removed
         server = self.create_test_server(wait_until='ACTIVE')
 
-        # Delete server before trying to create server
+        # Delete server before trying to create image
         self.servers_client.delete_server(server['id'])
         waiters.wait_for_server_termination(self.servers_client, server['id'])
         # Create a new image after server is deleted
-        name = data_utils.rand_name('image')
         meta = {'image_type': 'test'}
         self.assertRaises(lib_exc.NotFound,
                           self.create_image_from_server,
-                          server['id'], name=name, meta=meta)
+                          server['id'], meta=meta)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('82c5b0c4-9dbd-463c-872b-20c4755aae7f')
     def test_create_image_from_invalid_server(self):
         # An image should not be created with invalid server id
         # Create a new image with invalid server id
-        name = data_utils.rand_name('image')
         meta = {'image_type': 'test'}
-        resp = {}
-        resp['status'] = None
         self.assertRaises(lib_exc.NotFound, self.create_image_from_server,
-                          '!@$%^&*()', name=name, meta=meta)
+                          data_utils.rand_name('invalid'), meta=meta)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('ec176029-73dc-4037-8d72-2e4ff60cf538')
@@ -93,14 +87,14 @@
     def test_delete_image_with_invalid_image_id(self):
         # An image should not be deleted with invalid image id
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
-                          '!@$^&*()')
+                          data_utils.rand_name('invalid'))
 
     @test.attr(type=['negative'])
     @test.idempotent_id('137aef61-39f7-44a1-8ddf-0adf82511701')
     def test_delete_non_existent_image(self):
         # Return an error while trying to delete a non-existent image
 
-        non_existent_image_id = '11a22b9-12a9-5555-cc11-00ab112223fa'
+        non_existent_image_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
                           non_existent_image_id)
 
@@ -114,9 +108,9 @@
     @test.idempotent_id('924540c3-f1f1-444c-8f58-718958b6724e')
     def test_delete_image_non_hex_string_id(self):
         # Return an error while trying to delete an image with non hex id
-        image_id = '11a22b9-120q-5555-cc11-00ab112223gj'
+        invalid_image_id = data_utils.rand_uuid()[:-1] + "j"
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
-                          image_id)
+                          invalid_image_id)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('68e2c175-bd26-4407-ac0f-4ea9ce2139ea')
@@ -126,7 +120,8 @@
 
     @test.attr(type=['negative'])
     @test.idempotent_id('b340030d-82cd-4066-a314-c72fb7c59277')
-    def test_delete_image_id_is_over_35_character_limit(self):
+    def test_delete_image_with_id_over_character_limit(self):
         # Return an error while trying to delete image with id over limit
+        invalid_image_id = data_utils.rand_uuid() + "1"
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
-                          '11a22b9-12a9-5555-cc11-00ab112223fa-3fac')
+                          invalid_image_id)
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index 7b978ab..7768596 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -13,40 +13,18 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_log import log as logging
-
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest import test
 
 CONF = config.CONF
-LOG = logging.getLogger(__name__)
 
 
 class ImagesOneServerTestJSON(base.BaseV2ComputeTest):
 
-    def tearDown(self):
-        """Terminate test instances created after a test is executed."""
-        self.server_check_teardown()
-        super(ImagesOneServerTestJSON, self).tearDown()
-
-    def setUp(self):
-        # NOTE(afazekas): Normally we use the same server with all test cases,
-        # but if it has an issue, we build a new one
-        super(ImagesOneServerTestJSON, self).setUp()
-        # Check if the server is in a clean state after test
-        try:
-            waiters.wait_for_server_status(self.servers_client,
-                                           self.server_id, 'ACTIVE')
-        except Exception:
-            LOG.exception('server %s timed out to become ACTIVE. rebuilding'
-                          % self.server_id)
-            # Rebuild server if cannot reach the ACTIVE state
-            # Usually it means the server had a serious accident
-            self.__class__.server_id = self.rebuild_server(self.server_id)
-
     @classmethod
     def skip_checks(cls):
         super(ImagesOneServerTestJSON, cls).skip_checks()
@@ -64,25 +42,22 @@
         super(ImagesOneServerTestJSON, cls).setup_clients()
         cls.client = cls.compute_images_client
 
-    @classmethod
-    def resource_setup(cls):
-        super(ImagesOneServerTestJSON, cls).resource_setup()
-        server = cls.create_test_server(wait_until='ACTIVE')
-        cls.server_id = server['id']
-
     def _get_default_flavor_disk_size(self, flavor_id):
         flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
         return flavor['disk']
 
     @test.idempotent_id('3731d080-d4c5-4872-b41a-64d0d0021314')
     def test_create_delete_image(self):
+        server_id = self.create_test_server(wait_until='ACTIVE')['id']
 
         # Create a new image
         name = data_utils.rand_name('image')
         meta = {'image_type': 'test'}
-        body = self.client.create_image(self.server_id, name=name,
+        body = self.client.create_image(server_id, name=name,
                                         metadata=meta)
         image_id = data_utils.parse_image_id(body.response['location'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_image, image_id)
         waiters.wait_for_image_status(self.client, image_id, 'ACTIVE')
 
         # Verify the image was created correctly
@@ -106,13 +81,15 @@
 
     @test.idempotent_id('3b7c6fe4-dfe7-477c-9243-b06359db51e6')
     def test_create_image_specify_multibyte_character_image_name(self):
+        server_id = self.create_test_server(wait_until='ACTIVE')['id']
+
         # prefix character is:
         # http://www.fileformat.info/info/unicode/char/1F4A9/index.htm
 
         # We use a string with 3 byte utf-8 character due to bug
         # #1370954 in glance which will 500 if mysql is used as the
         # backend and it attempts to store a 4 byte utf-8 character
-        utf8_name = data_utils.rand_name('\xe2\x82\xa1')
-        body = self.client.create_image(self.server_id, name=utf8_name)
+        utf8_name = data_utils.rand_name(b'\xe2\x82\xa1'.decode('utf-8'))
+        body = self.client.create_image(server_id, name=utf8_name)
         image_id = data_utils.parse_image_id(body.response['location'])
         self.addCleanup(self.client.delete_image, image_id)
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 2fc9ef8..cd71de7 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -15,12 +15,12 @@
 #    under the License.
 
 from oslo_log import log as logging
-from tempest_lib import exceptions as lib_exc
 
 from tempest.api.compute 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
@@ -47,8 +47,8 @@
             waiters.wait_for_server_status(self.servers_client, self.server_id,
                                            'ACTIVE')
         except Exception:
-            LOG.exception('server %s timed out to become ACTIVE. rebuilding'
-                          % self.server_id)
+            LOG.exception('server %s timed out to become ACTIVE. rebuilding',
+                          self.server_id)
             # Rebuild server if cannot reach the ACTIVE state
             # Usually it means the server had a serious accident
             self._reset_server()
@@ -69,11 +69,6 @@
             raise cls.skipException(skip_msg)
 
     @classmethod
-    def setup_credentials(cls):
-        cls.prepare_instance_network()
-        super(ImagesOneServerNegativeTestJSON, cls).setup_credentials()
-
-    @classmethod
     def setup_clients(cls):
         super(ImagesOneServerNegativeTestJSON, cls).setup_clients()
         cls.client = cls.compute_images_client
@@ -98,9 +93,9 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('3d24d11f-5366-4536-bd28-cff32b748eca')
     def test_create_image_specify_metadata_over_limits(self):
-        # Return an error when creating image with meta data over 256 chars
+        # Return an error when creating image with meta data over 255 chars
         snapshot_name = data_utils.rand_name('test-snap')
-        meta = {'a' * 260: 'b' * 260}
+        meta = {'a' * 256: 'b' * 256}
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
                           self.server_id, name=snapshot_name, metadata=meta)
 
@@ -123,10 +118,10 @@
 
     @test.attr(type=['negative'])
     @test.idempotent_id('084f0cbc-500a-4963-8a4e-312905862581')
-    def test_create_image_specify_name_over_256_chars(self):
-        # Return an error if snapshot name over 256 characters is passed
+    def test_create_image_specify_name_over_character_limit(self):
+        # Return an error if snapshot name over 255 characters is passed
 
-        snapshot_name = data_utils.rand_name('a' * 260)
+        snapshot_name = ('a' * 256)
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
                           self.server_id, name=snapshot_name)
 
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index af840cc..a9c2f7a 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -19,9 +19,11 @@
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import image as common_image
 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
 
 CONF = config.CONF
@@ -40,25 +42,47 @@
     def setup_clients(cls):
         super(ListImageFiltersTestJSON, cls).setup_clients()
         cls.client = cls.compute_images_client
-        cls.glance_client = cls.os.image_client
+        # Check if glance v1 is available to determine which client to use. We
+        # prefer glance v1 for the compute API tests since the compute image
+        # API proxy was written for glance v1.
+        if CONF.image_feature_enabled.api_v1:
+            cls.glance_client = cls.os.image_client
+        elif CONF.image_feature_enabled.api_v2:
+            cls.glance_client = cls.os.image_client_v2
+        else:
+            raise exceptions.InvalidConfiguration(
+                'Either api_v1 or api_v2 must be True in '
+                '[image-feature-enabled].')
 
     @classmethod
     def resource_setup(cls):
         super(ListImageFiltersTestJSON, cls).resource_setup()
 
         def _create_image():
-            name = data_utils.rand_name('image')
-            body = cls.glance_client.create_image(name=name,
-                                                  container_format='bare',
-                                                  disk_format='raw',
-                                                  is_public=False)['image']
+            params = {
+                'name': data_utils.rand_name(cls.__name__ + '-image'),
+                'container_format': 'bare',
+                'disk_format': 'raw'
+            }
+            if CONF.image_feature_enabled.api_v1:
+                params.update({'is_public': False})
+                params = {'headers':
+                          common_image.image_meta_to_headers(**params)}
+            else:
+                params.update({'visibility': 'private'})
+
+            body = cls.glance_client.create_image(**params)
+            body = body['image'] if 'image' in body else body
             image_id = body['id']
             cls.images.append(image_id)
             # Wait 1 second between creation and upload to ensure a delta
             # between created_at and updated_at.
             time.sleep(1)
-            image_file = six.StringIO(('*' * 1024))
-            cls.glance_client.update_image(image_id, data=image_file)
+            image_file = six.BytesIO((b'*' * 1024))
+            if CONF.image_feature_enabled.api_v1:
+                cls.glance_client.update_image(image_id, data=image_file)
+            else:
+                cls.glance_client.store_image_file(image_id, data=image_file)
             waiters.wait_for_image_status(cls.client, image_id, 'ACTIVE')
             body = cls.client.show_image(image_id)['image']
             return body
diff --git a/tempest/api/compute/images/test_list_image_filters_negative.py b/tempest/api/compute/images/test_list_image_filters_negative.py
index 34d26e2..2689f88 100644
--- a/tempest/api/compute/images/test_list_image_filters_negative.py
+++ b/tempest/api/compute/images/test_list_image_filters_negative.py
@@ -12,11 +12,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 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
diff --git a/tempest/api/compute/keypairs/base.py b/tempest/api/compute/keypairs/base.py
index ebfb724..21f504b 100644
--- a/tempest/api/compute/keypairs/base.py
+++ b/tempest/api/compute/keypairs/base.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common.utils import data_utils
 
 
 class BaseKeypairTest(base.BaseV2ComputeTest):
@@ -24,15 +25,24 @@
         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=None,
+                       pub_key=None, keypair_type=None,
+                       user_id=None):
+        if keypair_name is None:
+            keypair_name = data_utils.rand_name(
+                self.__class__.__name__ + '-keypair')
         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/keypairs/test_keypairs.py b/tempest/api/compute/keypairs/test_keypairs.py
index be6f615..8acc25a 100644
--- a/tempest/api/compute/keypairs/test_keypairs.py
+++ b/tempest/api/compute/keypairs/test_keypairs.py
@@ -27,8 +27,7 @@
         # Create 3 keypairs
         key_list = list()
         for i in range(3):
-            k_name = data_utils.rand_name('keypair')
-            keypair = self._create_keypair(k_name)
+            keypair = self.create_keypair()
             # Need to pop these keys so that our compare doesn't fail later,
             # as the keypair dicts from list API doesn't have them.
             keypair.pop('private_key')
@@ -51,20 +50,20 @@
     def test_keypair_create_delete(self):
         # Keypair should be created, verified and deleted
         k_name = data_utils.rand_name('keypair')
-        keypair = self._create_keypair(k_name)
+        keypair = self.create_keypair(k_name)
         private_key = keypair['private_key']
         key_name = keypair['name']
         self.assertEqual(key_name, k_name,
                          "The created keypair name is not equal "
                          "to the requested name")
-        self.assertTrue(private_key is not None,
-                        "Field private_key is empty or not found.")
+        self.assertIsNotNone(private_key,
+                             "Field private_key is empty or not found.")
 
     @test.idempotent_id('a4233d5d-52d8-47cc-9a25-e1864527e3df')
     def test_get_keypair_detail(self):
         # Keypair should be created, Got details by name and deleted
         k_name = data_utils.rand_name('keypair')
-        self._create_keypair(k_name)
+        self.create_keypair(k_name)
         keypair_detail = self.client.show_keypair(k_name)['keypair']
         self.assertIn('name', keypair_detail)
         self.assertIn('public_key', keypair_detail)
@@ -72,8 +71,8 @@
                          "The created keypair name is not equal "
                          "to requested name")
         public_key = keypair_detail['public_key']
-        self.assertTrue(public_key is not None,
-                        "Field public_key is empty or not found.")
+        self.assertIsNotNone(public_key,
+                             "Field public_key is empty or not found.")
 
     @test.idempotent_id('39c90c6a-304a-49dd-95ec-2366129def05')
     def test_keypair_create_with_pub_key(self):
@@ -88,8 +87,8 @@
                    "LOeB1kYMOBaiUPLQTWXR3JpckqFIQwhIH0zoHlJvZE8hh90"
                    "XcPojYN56tI0OlrGqojbediJYD0rUsJu4weZpbn8vilb3JuDY+jws"
                    "snSA8wzBx3A/8y9Pp1B nova@ubuntu")
-        keypair = self._create_keypair(k_name, pub_key)
-        self.assertFalse('private_key' in keypair,
+        keypair = self.create_keypair(k_name, pub_key)
+        self.assertNotIn('private_key', keypair,
                          "Field private_key is not empty!")
         key_name = keypair['name']
         self.assertEqual(key_name, k_name,
diff --git a/tempest/api/compute/keypairs/test_keypairs_negative.py b/tempest/api/compute/keypairs/test_keypairs_negative.py
index 0ab78fb..f5ffa19 100644
--- a/tempest/api/compute/keypairs/test_keypairs_negative.py
+++ b/tempest/api/compute/keypairs/test_keypairs_negative.py
@@ -14,10 +14,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute.keypairs import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -26,10 +25,9 @@
     @test.idempotent_id('29cca892-46ae-4d48-bc32-8fe7e731eb81')
     def test_keypair_create_with_invalid_pub_key(self):
         # Keypair should not be created with a non RSA public key
-        k_name = data_utils.rand_name('keypair')
         pub_key = "ssh-rsa JUNK nova@ubuntu"
         self.assertRaises(lib_exc.BadRequest,
-                          self._create_keypair, k_name, pub_key)
+                          self.create_keypair, pub_key=pub_key)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('7cc32e47-4c42-489d-9623-c5e2cb5a2fa5')
@@ -43,19 +41,17 @@
     @test.idempotent_id('dade320e-69ca-42a9-ba4a-345300f127e0')
     def test_create_keypair_with_empty_public_key(self):
         # Keypair should not be created with an empty public key
-        k_name = data_utils.rand_name("keypair")
         pub_key = ' '
-        self.assertRaises(lib_exc.BadRequest, self._create_keypair,
-                          k_name, pub_key)
+        self.assertRaises(lib_exc.BadRequest, self.create_keypair,
+                          pub_key=pub_key)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('fc100c19-2926-4b9c-8fdc-d0589ee2f9ff')
     def test_create_keypair_when_public_key_bits_exceeds_maximum(self):
         # Keypair should not be created when public key bits are too long
-        k_name = data_utils.rand_name("keypair")
         pub_key = 'ssh-rsa ' + 'A' * 2048 + ' openstack@ubuntu'
-        self.assertRaises(lib_exc.BadRequest, self._create_keypair,
-                          k_name, pub_key)
+        self.assertRaises(lib_exc.BadRequest, self.create_keypair,
+                          pub_key=pub_key)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('0359a7f1-f002-4682-8073-0c91e4011b7c')
@@ -64,7 +60,7 @@
         k_name = data_utils.rand_name('keypair')
         self.client.create_keypair(name=k_name)
         # Now try the same keyname to create another key
-        self.assertRaises(lib_exc.Conflict, self._create_keypair,
+        self.assertRaises(lib_exc.Conflict, self.create_keypair,
                           k_name)
         self.client.delete_keypair(k_name)
 
@@ -72,7 +68,7 @@
     @test.idempotent_id('1398abe1-4a84-45fb-9294-89f514daff00')
     def test_create_keypair_with_empty_name_string(self):
         # Keypairs with name being an empty string should not be created
-        self.assertRaises(lib_exc.BadRequest, self._create_keypair,
+        self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           '')
 
     @test.attr(type=['negative'])
@@ -80,7 +76,7 @@
     def test_create_keypair_with_long_keynames(self):
         # Keypairs with name longer than 255 chars should not be created
         k_name = 'keypair-'.ljust(260, '0')
-        self.assertRaises(lib_exc.BadRequest, self._create_keypair,
+        self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           k_name)
 
     @test.attr(type=['negative'])
@@ -88,5 +84,5 @@
     def test_create_keypair_invalid_name(self):
         # Keypairs with name being an invalid name should not be created
         k_name = 'key_/.\@:'
-        self.assertRaises(lib_exc.BadRequest, self._create_keypair,
+        self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           k_name)
diff --git a/tempest/api/compute/keypairs/test_keypairs_v22.py b/tempest/api/compute/keypairs/test_keypairs_v22.py
index 997ef9b..4bd1a40 100644
--- a/tempest/api/compute/keypairs/test_keypairs_v22.py
+++ b/tempest/api/compute/keypairs/test_keypairs_v22.py
@@ -28,7 +28,7 @@
 
     def _test_keypairs_create_list_show(self, keypair_type=None):
         k_name = data_utils.rand_name('keypair')
-        keypair = self._create_keypair(k_name, keypair_type=keypair_type)
+        keypair = self.create_keypair(k_name, keypair_type=keypair_type)
         # Verify whether 'type' is present in keypair create response of
         # version 2.2 and it is with default value 'ssh'.
         self._check_keypair_type(keypair, keypair_type)
diff --git a/tempest/api/compute/limits/test_absolute_limits.py b/tempest/api/compute/limits/test_absolute_limits.py
index 69811f4..6cc722c 100644
--- a/tempest/api/compute/limits/test_absolute_limits.py
+++ b/tempest/api/compute/limits/test_absolute_limits.py
@@ -35,9 +35,10 @@
                              'maxTotalFloatingIps', 'maxSecurityGroups',
                              'maxSecurityGroupRules', 'maxTotalInstances',
                              'maxTotalKeypairs', 'maxTotalRAMSize',
+                             'maxServerGroups', 'maxServerGroupMembers',
                              'totalCoresUsed', 'totalFloatingIpsUsed',
                              'totalSecurityGroupsUsed', 'totalInstancesUsed',
-                             'totalRAMUsed']
+                             'totalRAMUsed', 'totalServerGroupsUsed']
         # check whether all expected elements exist
         missing_elements =\
             [ele for ele in expected_elements if ele not in absolute_limits]
diff --git a/tempest/api/compute/limits/test_absolute_limits_negative.py b/tempest/api/compute/limits/test_absolute_limits_negative.py
index 773bf23..66bc241 100644
--- a/tempest/api/compute/limits/test_absolute_limits_negative.py
+++ b/tempest/api/compute/limits/test_absolute_limits_negative.py
@@ -13,10 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
diff --git a/tempest/api/compute/security_groups/base.py b/tempest/api/compute/security_groups/base.py
index f70f6d3..cb18775 100644
--- a/tempest/api/compute/security_groups/base.py
+++ b/tempest/api/compute/security_groups/base.py
@@ -14,6 +14,11 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
 
 
 class BaseSecurityGroupsTest(base.BaseV2ComputeTest):
@@ -23,3 +28,11 @@
         # A network and a subnet will be created for these tests
         cls.set_network_resources(network=True, subnet=True)
         super(BaseSecurityGroupsTest, cls).setup_credentials()
+
+    @staticmethod
+    def generate_random_security_group_id():
+        if (CONF.service_available.neutron and
+            test.is_extension_enabled('security-group', 'network')):
+            return data_utils.rand_uuid()
+        else:
+            return data_utils.rand_int_id(start=999)
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index 38c294b..60caa19 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -43,7 +43,6 @@
         group = {}
         ip_range = {}
         cls.expected = {
-            'id': None,
             'parent_group_id': None,
             'ip_protocol': cls.ip_protocol,
             'from_port': from_port,
@@ -54,8 +53,6 @@
 
     def _check_expected_response(self, actual_rule):
         for key in self.expected:
-            if key == 'id':
-                continue
             self.assertEqual(self.expected[key], actual_rule[key],
                              "Miss-matched key is %s" % key)
 
diff --git a/tempest/api/compute/security_groups/test_security_group_rules_negative.py b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
index 816038a..78c19ca 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules_negative.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
@@ -13,29 +13,17 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute.security_groups 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
-
-
-def not_existing_id():
-    if CONF.service_available.neutron:
-        return data_utils.rand_uuid()
-    else:
-        return data_utils.rand_int_id(start=999)
-
 
 class SecurityGroupRulesNegativeTestJSON(base.BaseSecurityGroupsTest):
 
     @classmethod
     def setup_clients(cls):
         super(SecurityGroupRulesNegativeTestJSON, cls).setup_clients()
-        cls.client = cls.security_groups_client
         cls.rules_client = cls.security_group_rules_client
 
     @test.attr(type=['negative'])
@@ -45,7 +33,7 @@
         # Negative test: Creation of Security Group rule should FAIL
         # with non existent Parent group id
         # Adding rules to the non existent Security Group id
-        parent_group_id = not_existing_id()
+        parent_group_id = self.generate_random_security_group_id()
         ip_protocol = 'tcp'
         from_port = 22
         to_port = 22
@@ -180,7 +168,7 @@
     def test_delete_security_group_rule_with_non_existent_id(self):
         # Negative test: Deletion of Security Group rule should be FAIL
         # with non existent id
-        non_existent_rule_id = not_existing_id()
+        non_existent_rule_id = self.generate_random_security_group_id()
         self.assertRaises(lib_exc.NotFound,
                           self.rules_client.delete_security_group_rule,
                           non_existent_rule_id)
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 81a02be..4184afa 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -13,11 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute.security_groups import base
 from tempest.common.utils import data_utils
 from tempest.common import waiters
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -95,11 +94,8 @@
 
         # Create server and add the security group created
         # above to the server we just created
-        server_name = data_utils.rand_name('server')
-        server = self.create_test_server(name=server_name)
+        server = self.create_test_server(wait_until='ACTIVE')
         server_id = server['id']
-        waiters.wait_for_server_status(self.servers_client, server_id,
-                                       'ACTIVE')
         self.servers_client.add_security_group(server_id, name=sg['name'])
 
         # Check that we are not able to delete the security
diff --git a/tempest/api/compute/security_groups/test_security_groups_negative.py b/tempest/api/compute/security_groups/test_security_groups_negative.py
index 120d327..bcada1e 100644
--- a/tempest/api/compute/security_groups/test_security_groups_negative.py
+++ b/tempest/api/compute/security_groups/test_security_groups_negative.py
@@ -13,13 +13,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import decorators
-from tempest_lib import exceptions as lib_exc
 import testtools
 
 from tempest.api.compute.security_groups import base
 from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
@@ -37,27 +37,13 @@
         super(SecurityGroupsNegativeTestJSON, cls).resource_setup()
         cls.neutron_available = CONF.service_available.neutron
 
-    def _generate_a_non_existent_security_group_id(self):
-        security_group_id = []
-        body = self.client.list_security_groups()['security_groups']
-        for i in range(len(body)):
-            security_group_id.append(body[i]['id'])
-        # Generate a non-existent security group id
-        while True:
-            non_exist_id = data_utils.rand_int_id(start=999)
-            if self.neutron_available:
-                non_exist_id = data_utils.rand_uuid()
-            if non_exist_id not in security_group_id:
-                break
-        return non_exist_id
-
     @test.attr(type=['negative'])
     @test.idempotent_id('673eaec1-9b3e-48ed-bdf1-2786c1b9661c')
     @test.services('network')
     def test_security_group_get_nonexistent_group(self):
         # Negative test:Should not be able to GET the details
         # of non-existent Security Group
-        non_exist_id = self._generate_a_non_existent_security_group_id()
+        non_exist_id = self.generate_random_security_group_id()
         self.assertRaises(lib_exc.NotFound, self.client.show_security_group,
                           non_exist_id)
 
@@ -137,7 +123,7 @@
     @test.services('network')
     def test_delete_nonexistent_security_group(self):
         # Negative test:Deletion of a non-existent Security Group should fail
-        non_exist_id = self._generate_a_non_existent_security_group_id()
+        non_exist_id = self.generate_random_security_group_id()
         self.assertRaises(lib_exc.NotFound,
                           self.client.delete_security_group, non_exist_id)
 
@@ -202,7 +188,7 @@
     @test.services('network')
     def test_update_non_existent_security_group(self):
         # Update a non-existent Security Group should Fail
-        non_exist_id = self._generate_a_non_existent_security_group_id()
+        non_exist_id = self.generate_random_security_group_id()
         s_name = data_utils.rand_name('sg')
         s_description = data_utils.rand_name('description')
         self.assertRaises(lib_exc.NotFound,
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index be79163..1731bf3 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -15,11 +15,13 @@
 
 import time
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
+from tempest.common import compute
+from tempest.common.utils import net_utils
+from tempest.common import waiters
 from tempest import config
-from tempest import exceptions
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
@@ -44,32 +46,36 @@
     @classmethod
     def setup_clients(cls):
         super(AttachInterfacesTestJSON, cls).setup_clients()
-        cls.client = cls.os.interfaces_client
+        cls.subnets_client = cls.os.subnets_client
         cls.ports_client = cls.os.ports_client
 
-    def wait_for_interface_status(self, server, port_id, status):
-        """Waits for an interface to reach a given status."""
-        body = (self.client.show_interface(server, port_id)
-                ['interfaceAttachment'])
-        interface_status = body['port_state']
+    # TODO(mriedem): move this into a common waiters utility module
+    def wait_for_port_detach(self, port_id):
+        """Waits for the port's device_id to be unset.
+
+        :param port_id: The id of the port being detached.
+        :returns: The final port dict from the show_port response.
+        """
+        port = self.ports_client.show_port(port_id)['port']
+        device_id = port['device_id']
         start = int(time.time())
 
-        while(interface_status != status):
+        # NOTE(mriedem): Nova updates the port's device_id to '' rather than
+        # None, but it's not contractual so handle Falsey either way.
+        while device_id:
             time.sleep(self.build_interval)
-            body = (self.client.show_interface(server, port_id)
-                    ['interfaceAttachment'])
-            interface_status = body['port_state']
+            port = self.ports_client.show_port(port_id)['port']
+            device_id = port['device_id']
 
             timed_out = int(time.time()) - start >= self.build_timeout
 
-            if interface_status != status and timed_out:
-                message = ('Interface %s failed to reach %s status '
-                           '(current %s) within the required time (%s s).' %
-                           (port_id, status, interface_status,
-                            self.build_timeout))
-                raise exceptions.TimeoutException(message)
+            if device_id and timed_out:
+                message = ('Port %s failed to detach (device_id %s) within '
+                           'the required time (%s s).' %
+                           (port_id, device_id, self.build_timeout))
+                raise lib_exc.TimeoutException(message)
 
-        return body
+        return port
 
     def _check_interface(self, iface, port_id=None, network_id=None,
                          fixed_ip=None, mac_addr=None):
@@ -85,27 +91,27 @@
 
     def _create_server_get_interfaces(self):
         server = self.create_test_server(wait_until='ACTIVE')
-        ifs = (self.client.list_interfaces(server['id'])
+        ifs = (self.interfaces_client.list_interfaces(server['id'])
                ['interfaceAttachments'])
-        body = self.wait_for_interface_status(
-            server['id'], ifs[0]['port_id'], 'ACTIVE')
+        body = waiters.wait_for_interface_status(
+            self.interfaces_client, server['id'], ifs[0]['port_id'], 'ACTIVE')
         ifs[0]['port_state'] = body['port_state']
         return server, ifs
 
     def _test_create_interface(self, server):
-        iface = (self.client.create_interface(server['id'])
+        iface = (self.interfaces_client.create_interface(server['id'])
                  ['interfaceAttachment'])
-        iface = self.wait_for_interface_status(
-            server['id'], iface['port_id'], 'ACTIVE')
+        iface = waiters.wait_for_interface_status(
+            self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
         self._check_interface(iface)
         return iface
 
     def _test_create_interface_by_network_id(self, server, ifs):
         network_id = ifs[0]['net_id']
-        iface = self.client.create_interface(
+        iface = self.interfaces_client.create_interface(
             server['id'], net_id=network_id)['interfaceAttachment']
-        iface = self.wait_for_interface_status(
-            server['id'], iface['port_id'], 'ACTIVE')
+        iface = waiters.wait_for_interface_status(
+            self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
         self._check_interface(iface, network_id=network_id)
         return iface
 
@@ -114,16 +120,35 @@
         port = self.ports_client.create_port(network_id=network_id)
         port_id = port['port']['id']
         self.addCleanup(self.ports_client.delete_port, port_id)
-        iface = self.client.create_interface(
+        iface = self.interfaces_client.create_interface(
             server['id'], port_id=port_id)['interfaceAttachment']
-        iface = self.wait_for_interface_status(
-            server['id'], iface['port_id'], 'ACTIVE')
+        iface = waiters.wait_for_interface_status(
+            self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
         self._check_interface(iface, port_id=port_id)
         return iface
 
+    def _test_create_interface_by_fixed_ips(self, server, ifs):
+        network_id = ifs[0]['net_id']
+        subnet_id = ifs[0]['fixed_ips'][0]['subnet_id']
+        ip_list = net_utils.get_unused_ip_addresses(self.ports_client,
+                                                    self.subnets_client,
+                                                    network_id,
+                                                    subnet_id,
+                                                    1)
+
+        fixed_ips = [{'ip_address': ip_list[0]}]
+        iface = self.interfaces_client.create_interface(
+            server['id'], net_id=network_id,
+            fixed_ips=fixed_ips)['interfaceAttachment']
+        self.addCleanup(self.ports_client.delete_port, iface['port_id'])
+        iface = waiters.wait_for_interface_status(
+            self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
+        self._check_interface(iface, fixed_ip=ip_list[0])
+        return iface
+
     def _test_show_interface(self, server, ifs):
         iface = ifs[0]
-        _iface = self.client.show_interface(
+        _iface = self.interfaces_client.show_interface(
             server['id'], iface['port_id'])['interfaceAttachment']
         self._check_interface(iface, port_id=_iface['port_id'],
                               network_id=_iface['net_id'],
@@ -133,20 +158,20 @@
     def _test_delete_interface(self, server, ifs):
         # NOTE(danms): delete not the first or last, but one in the middle
         iface = ifs[1]
-        self.client.delete_interface(server['id'], iface['port_id'])
-        _ifs = (self.client.list_interfaces(server['id'])
+        self.interfaces_client.delete_interface(server['id'], iface['port_id'])
+        _ifs = (self.interfaces_client.list_interfaces(server['id'])
                 ['interfaceAttachments'])
         start = int(time.time())
 
         while len(ifs) == len(_ifs):
             time.sleep(self.build_interval)
-            _ifs = (self.client.list_interfaces(server['id'])
+            _ifs = (self.interfaces_client.list_interfaces(server['id'])
                     ['interfaceAttachments'])
             timed_out = int(time.time()) - start >= self.build_timeout
             if len(ifs) == len(_ifs) and timed_out:
                 message = ('Failed to delete interface within '
                            'the required time: %s sec.' % self.build_timeout)
-                raise exceptions.TimeoutException(message)
+                raise lib_exc.TimeoutException(message)
 
         self.assertNotIn(iface['port_id'], [i['port_id'] for i in _ifs])
         return _ifs
@@ -164,7 +189,7 @@
     def test_create_list_show_delete_interfaces(self):
         server, ifs = self._create_server_get_interfaces()
         interface_count = len(ifs)
-        self.assertTrue(interface_count > 0)
+        self.assertGreater(interface_count, 0)
         self._check_interface(ifs[0])
 
         try:
@@ -183,7 +208,10 @@
         iface = self._test_create_interface_by_port_id(server, ifs)
         ifs.append(iface)
 
-        _ifs = (self.client.list_interfaces(server['id'])
+        iface = self._test_create_interface_by_fixed_ips(server, ifs)
+        ifs.append(iface)
+
+        _ifs = (self.interfaces_client.list_interfaces(server['id'])
                 ['interfaceAttachments'])
         self._compare_iface_list(ifs, _ifs)
 
@@ -199,7 +227,7 @@
         # Add and Remove the fixed IP to server.
         server, ifs = self._create_server_get_interfaces()
         interface_count = len(ifs)
-        self.assertTrue(interface_count > 0)
+        self.assertGreater(interface_count, 0)
         self._check_interface(ifs[0])
         network_id = ifs[0]['net_id']
         self.servers_client.add_fixed_ip(server['id'], networkId=network_id)
@@ -216,3 +244,39 @@
             if fixed_ip is not None:
                 break
         self.servers_client.remove_fixed_ip(server['id'], address=fixed_ip)
+
+    @decorators.skip_because(bug='1607714')
+    @test.idempotent_id('2f3a0127-95c7-4977-92d2-bc5aec602fb4')
+    def test_reassign_port_between_servers(self):
+        """Tests the following:
+
+        1. Create a port in Neutron.
+        2. Create two servers in Nova.
+        3. Attach the port to the first server.
+        4. Detach the port from the first server.
+        5. Attach the port to the second server.
+        6. Detach the port from the second server.
+        """
+        network = self.get_tenant_network()
+        network_id = network['id']
+        port = self.ports_client.create_port(network_id=network_id)
+        port_id = port['port']['id']
+        self.addCleanup(self.ports_client.delete_port, port_id)
+
+        # create two servers
+        _, servers = compute.create_test_server(
+            self.os, tenant_network=network, wait_until='ACTIVE', min_count=2)
+        # add our cleanups for the servers since we bypassed the base class
+        for server in servers:
+            self.addCleanup(self.delete_server, server['id'])
+
+        for server in servers:
+            # attach the port to the server
+            iface = self.interfaces_client.create_interface(
+                server['id'], port_id=port_id)['interfaceAttachment']
+            self._check_interface(iface, port_id=port_id)
+
+            # detach the port from the server; this is a cast in the compute
+            # API so we have to poll the port until the device_id is unset.
+            self.interfaces_client.delete_interface(server['id'], port_id)
+            self.wait_for_port_detach(port_id)
diff --git a/tempest/api/compute/servers/test_availability_zone.py b/tempest/api/compute/servers/test_availability_zone.py
index 76da317..00df86b 100644
--- a/tempest/api/compute/servers/test_availability_zone.py
+++ b/tempest/api/compute/servers/test_availability_zone.py
@@ -29,4 +29,4 @@
     def test_get_availability_zone_list_with_non_admin_user(self):
         # List of availability zone with non-administrator user
         availability_zone = self.client.list_availability_zones()
-        self.assertTrue(len(availability_zone['availabilityZoneInfo']) > 0)
+        self.assertGreater(len(availability_zone['availabilityZoneInfo']), 0)
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index f719bfc..2dcacb7 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -19,7 +19,6 @@
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest.common.utils.linux import remote_client
-from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -38,7 +37,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
 
@@ -49,7 +47,7 @@
         cls.meta = {'hello': 'world'}
         cls.accessIPv4 = '1.1.1.1'
         cls.accessIPv6 = '0000:0000:0000:0000:0000:babe:220.12.22.2'
-        cls.name = data_utils.rand_name('server')
+        cls.name = data_utils.rand_name(cls.__name__ + '-server')
         cls.password = data_utils.rand_password()
         disk_config = cls.disk_config
         cls.server_initial = cls.create_test_server(
@@ -120,7 +118,9 @@
             self.get_server_ip(self.server),
             self.ssh_user,
             self.password,
-            self.validation_resources['keypair']['private_key'])
+            self.validation_resources['keypair']['private_key'],
+            server=self.server,
+            servers_client=self.client)
         self.assertEqual(flavor['vcpus'], linux_client.get_number_of_vcpus())
 
     @test.idempotent_id('ac1ad47f-984b-4441-9274-c9079b7a0666')
@@ -132,20 +132,21 @@
             self.get_server_ip(self.server),
             self.ssh_user,
             self.password,
-            self.validation_resources['keypair']['private_key'])
-        self.assertTrue(linux_client.hostname_equals_servername(self.name))
+            self.validation_resources['keypair']['private_key'],
+            server=self.server,
+            servers_client=self.client)
+        hostname = linux_client.get_hostname()
+        msg = ('Failed while verifying servername equals hostname. Expected '
+               'hostname "%s" but got "%s".' % (self.name, hostname))
+        self.assertEqual(self.name.lower(), hostname, msg)
 
     @test.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
+    @testtools.skipUnless(
+        test.is_scheduler_filter_enabled("ServerGroupAffinityFilter"),
+        'ServerGroupAffinityFilter is not available.')
     def test_create_server_with_scheduler_hint_group(self):
         # Create a server with the scheduler hint "group".
-        name = data_utils.rand_name('server_group')
-        policies = ['affinity']
-        body = self.server_groups_client.create_server_group(
-            name=name, policies=policies)['server_group']
-        group_id = body['id']
-        self.addCleanup(self.server_groups_client.delete_server_group,
-                        group_id)
-
+        group_id = self.create_test_server_group()['id']
         hints = {'group': group_id}
         server = self.create_test_server(scheduler_hints=hints,
                                          wait_until='ACTIVE')
@@ -176,12 +177,7 @@
         # when trying to delete the subnet. The tear down in the base class
         # will try to delete the server and get a 404 but it's ignored so
         # we're OK.
-        def cleanup_server():
-            self.client.delete_server(server_multi_nics['id'])
-            waiters.wait_for_server_termination(self.client,
-                                                server_multi_nics['id'])
-
-        self.addCleanup(cleanup_server)
+        self.addCleanup(self.delete_server, server_multi_nics['id'])
 
         addresses = (self.client.list_addresses(server_multi_nics['id'])
                      ['addresses'])
@@ -201,10 +197,6 @@
     @test.idempotent_id('1678d144-ed74-43f8-8e57-ab10dbf9b3c2')
     @testtools.skipUnless(CONF.service_available.neutron,
                           'Neutron service must be available.')
-    # The below skipUnless should be removed once Kilo-eol happens.
-    @testtools.skipUnless(CONF.compute_feature_enabled.
-                          allow_duplicate_networks,
-                          'Duplicate networks must be allowed')
     def test_verify_duplicate_network_nics(self):
         # Verify that server creation does not fail when more than one nic
         # is created on the same network.
@@ -217,13 +209,7 @@
 
         server_multi_nics = self.create_test_server(
             networks=networks, wait_until='ACTIVE')
-
-        def cleanup_server():
-            self.client.delete_server(server_multi_nics['id'])
-            waiters.wait_for_server_termination(self.client,
-                                                server_multi_nics['id'])
-
-        self.addCleanup(cleanup_server)
+        self.addCleanup(self.delete_server, server_multi_nics['id'])
 
         addresses = (self.client.list_addresses(server_multi_nics['id'])
                      ['addresses'])
@@ -266,37 +252,19 @@
         flavor_base = self.flavors_client.show_flavor(
             self.flavor_ref)['flavor']
 
-        def create_flavor_with_extra_specs():
-            flavor_with_eph_disk_name = data_utils.rand_name('eph_flavor')
-            flavor_with_eph_disk_id = data_utils.rand_int_id(start=1000)
+        def create_flavor_with_ephemeral(ephem_disk):
+            flavor_id = data_utils.rand_int_id(start=1000)
+            name = 'flavor_with_ephemeral_%s' % ephem_disk
+            flavor_name = data_utils.rand_name(name)
 
             ram = flavor_base['ram']
             vcpus = flavor_base['vcpus']
             disk = flavor_base['disk']
 
-            # Create a flavor with extra specs
-            flavor = (self.flavor_client.
-                      create_flavor(name=flavor_with_eph_disk_name,
-                                    ram=ram, vcpus=vcpus, disk=disk,
-                                    id=flavor_with_eph_disk_id,
-                                    ephemeral=1))['flavor']
-            self.addCleanup(flavor_clean_up, flavor['id'])
-
-            return flavor['id']
-
-        def create_flavor_without_extra_specs():
-            flavor_no_eph_disk_name = data_utils.rand_name('no_eph_flavor')
-            flavor_no_eph_disk_id = data_utils.rand_int_id(start=1000)
-
-            ram = flavor_base['ram']
-            vcpus = flavor_base['vcpus']
-            disk = flavor_base['disk']
-
-            # Create a flavor without extra specs
-            flavor = (self.flavor_client.
-                      create_flavor(name=flavor_no_eph_disk_name,
-                                    ram=ram, vcpus=vcpus, disk=disk,
-                                    id=flavor_no_eph_disk_id))['flavor']
+            # Create a flavor with ephemeral disk
+            flavor = self.flavor_client.create_flavor(
+                name=flavor_name, ram=ram, vcpus=vcpus, disk=disk,
+                id=flavor_id, ephemeral=ephem_disk)['flavor']
             self.addCleanup(flavor_clean_up, flavor['id'])
 
             return flavor['id']
@@ -305,8 +273,8 @@
             self.flavor_client.delete_flavor(flavor_id)
             self.flavor_client.wait_for_resource_deletion(flavor_id)
 
-        flavor_with_eph_disk_id = create_flavor_with_extra_specs()
-        flavor_no_eph_disk_id = create_flavor_without_extra_specs()
+        flavor_with_eph_disk_id = create_flavor_with_ephemeral(ephem_disk=1)
+        flavor_no_eph_disk_id = create_flavor_with_ephemeral(ephem_disk=0)
 
         admin_pass = self.image_ssh_password
 
@@ -316,15 +284,17 @@
             adminPass=admin_pass,
             flavor=flavor_no_eph_disk_id)
 
-        # Get partition number of server without extra specs.
+        # Get partition number of server without ephemeral disk.
         server_no_eph_disk = self.client.show_server(
             server_no_eph_disk['id'])['server']
         linux_client = remote_client.RemoteClient(
             self.get_server_ip(server_no_eph_disk),
             self.ssh_user,
             admin_pass,
-            self.validation_resources['keypair']['private_key'])
-        partition_num = len(linux_client.get_partitions().split('\n'))
+            self.validation_resources['keypair']['private_key'],
+            server=server_no_eph_disk,
+            servers_client=self.client)
+        disks_num = len(linux_client.get_disks().split('\n'))
 
         # Explicit server deletion necessary for Juno compatibility
         self.client.delete_server(server_no_eph_disk['id'])
@@ -341,9 +311,11 @@
             self.get_server_ip(server_with_eph_disk),
             self.ssh_user,
             admin_pass,
-            self.validation_resources['keypair']['private_key'])
-        partition_num_emph = len(linux_client.get_partitions().split('\n'))
-        self.assertEqual(partition_num + 1, partition_num_emph)
+            self.validation_resources['keypair']['private_key'],
+            server=server_with_eph_disk,
+            servers_client=self.client)
+        disks_num_eph = len(linux_client.get_disks().split('\n'))
+        self.assertEqual(disks_num + 1, disks_num_eph)
 
 
 class ServersTestManualDisk(ServersTestJSON):
diff --git a/tempest/api/compute/servers/test_delete_server.py b/tempest/api/compute/servers/test_delete_server.py
index 6796bb5..07f46c5 100644
--- a/tempest/api/compute/servers/test_delete_server.py
+++ b/tempest/api/compute/servers/test_delete_server.py
@@ -16,6 +16,7 @@
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import compute
 from tempest.common import waiters
 from tempest import config
 from tempest import test
@@ -84,16 +85,8 @@
     def test_delete_server_while_in_shelved_state(self):
         # Delete a server while it's VM state is Shelved
         server = self.create_test_server(wait_until='ACTIVE')
-        self.client.shelve_server(server['id'])
+        compute.shelve_server(self.client, server['id'])
 
-        offload_time = CONF.compute.shelved_offload_time
-        if offload_time >= 0:
-            waiters.wait_for_server_status(self.client, server['id'],
-                                           'SHELVED_OFFLOADED',
-                                           extra_timeout=offload_time)
-        else:
-            waiters.wait_for_server_status(self.client, server['id'],
-                                           'SHELVED')
         self.client.delete_server(server['id'])
         waiters.wait_for_server_termination(self.client, server['id'])
 
@@ -113,24 +106,15 @@
     @test.services('volume')
     def test_delete_server_while_in_attached_volume(self):
         # Delete a server while a volume is attached to it
-        volumes_client = self.volumes_extensions_client
         device = '/dev/%s' % CONF.compute.volume_device_name
         server = self.create_test_server(wait_until='ACTIVE')
 
-        volume = (volumes_client.create_volume(size=CONF.volume.volume_size)
-                  ['volume'])
-        self.addCleanup(volumes_client.delete_volume, volume['id'])
-        waiters.wait_for_volume_status(volumes_client,
-                                       volume['id'], 'available')
-        self.client.attach_volume(server['id'],
-                                  volumeId=volume['id'],
-                                  device=device)
-        waiters.wait_for_volume_status(volumes_client,
-                                       volume['id'], 'in-use')
+        volume = self.create_volume()
+        self.attach_volume(server, volume, device=device)
 
         self.client.delete_server(server['id'])
         waiters.wait_for_server_termination(self.client, server['id'])
-        waiters.wait_for_volume_status(volumes_client,
+        waiters.wait_for_volume_status(self.volumes_client,
                                        volume['id'], 'available')
 
 
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
new file mode 100644
index 0000000..1d502be
--- /dev/null
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -0,0 +1,263 @@
+# Copyright (C) 2016, Red Hat, Inc.
+#
+#    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 json
+
+from oslo_log import log as logging
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest.common.utils.linux import remote_client
+from tempest import config
+from tempest.lib import exceptions
+from tempest import test
+
+
+CONF = config.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+class DeviceTaggingTest(base.BaseV2ComputeTest):
+
+    min_microversion = '2.32'
+    max_microversion = 'latest'
+
+    @classmethod
+    def skip_checks(cls):
+        super(DeviceTaggingTest, cls).skip_checks()
+        if not CONF.service_available.neutron:
+            raise cls.skipException('Neutron is required')
+        if not CONF.validation.run_validation:
+            raise cls.skipException('Validation must be enabled')
+        if (not CONF.compute_feature_enabled.config_drive
+            and not CONF.compute_feature_enabled.metadata_service):
+            raise cls.skipException('One of metadata or config drive must be '
+                                    'enabled')
+
+    @classmethod
+    def setup_clients(cls):
+        super(DeviceTaggingTest, cls).setup_clients()
+        cls.networks_client = cls.os.networks_client
+        cls.ports_client = cls.os.ports_client
+        cls.subnets_client = cls.os.subnets_client
+        cls.interfaces_client = cls.os.interfaces_client
+
+    @classmethod
+    def setup_credentials(cls):
+        cls.set_network_resources(network=True, subnet=True, router=True,
+                                  dhcp=True)
+        super(DeviceTaggingTest, cls).setup_credentials()
+
+    @classmethod
+    def resource_setup(cls):
+        cls.set_validation_resources()
+        super(DeviceTaggingTest, cls).resource_setup()
+
+    def verify_device_metadata(self, md_json):
+        md_dict = json.loads(md_json)
+        for d in md_dict['devices']:
+            if d['type'] == 'nic':
+                if d['mac'] == self.port1['mac_address']:
+                    self.assertEqual(d['tags'], ['port-1'])
+                if d['mac'] == self.port2['mac_address']:
+                    self.assertEqual(d['tags'], ['port-2'])
+                if d['mac'] == self.net_2_100_mac:
+                    self.assertEqual(d['tags'], ['net-2-100'])
+                if d['mac'] == self.net_2_200_mac:
+                    self.assertEqual(d['tags'], ['net-2-200'])
+
+        found_devices = [d['tags'][0] for d in md_dict['devices']]
+        self.assertItemsEqual(found_devices, ['port-1', 'port-2', 'net-1',
+                                              'net-2-100', 'net-2-200',
+                                              'boot', 'other'])
+
+    @test.idempotent_id('a2e65a6c-66f1-4442-aaa8-498c31778d96')
+    @test.services('network', 'volume', 'image')
+    def test_device_tagging(self):
+        # Create volumes
+        # The create_volume methods waits for the volumes to be available and
+        # the base class will clean them up on tearDown.
+        boot_volume = self.create_volume(CONF.compute.image_ref)
+        other_volume = self.create_volume()
+        untagged_volume = self.create_volume()
+
+        # Create networks
+        net1 = self.networks_client.create_network(
+            name=data_utils.rand_name('device-tagging-net1'))['network']
+        self.addCleanup(self.networks_client.delete_network, net1['id'])
+
+        net2 = self.networks_client.create_network(
+            name=data_utils.rand_name('device-tagging-net2'))['network']
+        self.addCleanup(self.networks_client.delete_network, net2['id'])
+
+        # Create subnets
+        subnet1 = self.subnets_client.create_subnet(
+            network_id=net1['id'],
+            cidr='10.1.1.0/24',
+            ip_version=4)['subnet']
+        self.addCleanup(self.subnets_client.delete_subnet, subnet1['id'])
+
+        subnet2 = self.subnets_client.create_subnet(
+            network_id=net2['id'],
+            cidr='10.2.2.0/24',
+            ip_version=4)['subnet']
+        self.addCleanup(self.subnets_client.delete_subnet, subnet2['id'])
+
+        # Create ports
+        self.port1 = self.ports_client.create_port(
+            network_id=net1['id'],
+            fixed_ips=[{'subnet_id': subnet1['id']}])['port']
+        self.addCleanup(self.ports_client.delete_port, self.port1['id'])
+
+        self.port2 = self.ports_client.create_port(
+            network_id=net1['id'],
+            fixed_ips=[{'subnet_id': subnet1['id']}])['port']
+        self.addCleanup(self.ports_client.delete_port, self.port2['id'])
+
+        # Create server
+        admin_pass = data_utils.rand_password()
+        config_drive_enabled = CONF.compute_feature_enabled.config_drive
+
+        server = self.create_test_server(
+            validatable=True,
+            config_drive=config_drive_enabled,
+            adminPass=admin_pass,
+            name=data_utils.rand_name('device-tagging-server'),
+            networks=[
+                # Validation network for ssh
+                {
+                    'uuid': self.get_tenant_network()['id']
+                },
+                # Different tags for different ports
+                {
+                    'port': self.port1['id'],
+                    'tag': 'port-1'
+                },
+                {
+                    'port': self.port2['id'],
+                    'tag': 'port-2'
+                },
+                # Two nics on same net, one tagged one not
+                {
+                    'uuid': net1['id'],
+                    'tag': 'net-1'
+                },
+                {
+                    'uuid': net1['id']
+                },
+                # Two nics on same net, different IP
+                {
+                    'uuid': net2['id'],
+                    'fixed_ip': '10.2.2.100',
+                    'tag': 'net-2-100'
+                },
+                {
+                    'uuid': net2['id'],
+                    'fixed_ip': '10.2.2.200',
+                    'tag': 'net-2-200'
+                }
+            ],
+            block_device_mapping_v2=[
+                # Boot volume
+                {
+                    'uuid': boot_volume['id'],
+                    'source_type': 'volume',
+                    'destination_type': 'volume',
+                    'boot_index': 0,
+                    'tag': 'boot'
+                },
+                # Other volume
+                {
+                    'uuid': other_volume['id'],
+                    'source_type': 'volume',
+                    'destination_type': 'volume',
+                    'boot_index': 1,
+                    'tag': 'other'
+                },
+                # Untagged volume
+                {
+                    'uuid': untagged_volume['id'],
+                    'source_type': 'volume',
+                    'destination_type': 'volume',
+                    'boot_index': 2
+                }
+            ])
+
+        self.addCleanup(self.delete_server, server['id'])
+
+        self.ssh_client = remote_client.RemoteClient(
+            self.get_server_ip(server),
+            CONF.validation.image_ssh_user,
+            admin_pass,
+            self.validation_resources['keypair']['private_key'],
+            server=server,
+            servers_client=self.servers_client)
+
+        # Find the MAC addresses of our fixed IPs
+        self.net_2_100_mac = None
+        self.net_2_200_mac = None
+        ifaces = self.interfaces_client.list_interfaces(server['id'])
+        for iface in ifaces['interfaceAttachments']:
+            if 'fixed_ips' in iface:
+                for ip in iface['fixed_ips']:
+                    if ip['ip_address'] == '10.2.2.100':
+                        self.net_2_100_mac = iface['mac_addr']
+                    if ip['ip_address'] == '10.2.2.200':
+                        self.net_2_200_mac = iface['mac_addr']
+        # Make sure we have the MACs we need, there's no reason for some to be
+        # missing
+        self.assertTrue(self.net_2_100_mac)
+        self.assertTrue(self.net_2_200_mac)
+
+        # Verify metadata from metadata service
+        if CONF.compute_feature_enabled.metadata_service:
+            md_url = 'http://169.254.169.254/openstack/latest/meta_data.json'
+            LOG.info('Attempting to verify tagged devices in server %s via '
+                     'the metadata service: %s', server['id'], md_url)
+
+            def get_and_verify_metadata():
+                try:
+                    self.ssh_client.exec_command('curl -V')
+                except exceptions.SSHExecCommandFailed:
+                    if not CONF.compute_feature_enabled.config_drive:
+                        raise self.skipException('curl not found in guest '
+                                                 'and config drive is '
+                                                 'disabled')
+                    LOG.warning('curl was not found in the guest, device '
+                                'tagging metadata was not checked in the '
+                                'metadata API')
+                    return True
+                cmd = 'curl %s' % md_url
+                md_json = self.ssh_client.exec_command(cmd)
+                self.verify_device_metadata(md_json)
+                return True
+
+            if not test.call_until_true(get_and_verify_metadata,
+                                        CONF.compute.build_timeout,
+                                        CONF.compute.build_interval):
+                raise exceptions.TimeoutException('Timeout while verifying '
+                                                  'metadata on server.')
+
+        # Verify metadata on config drive
+        if CONF.compute_feature_enabled.config_drive:
+            cmd_blkid = 'blkid -t LABEL=config-2 -o device'
+            LOG.info('Attempting to verify tagged devices in server %s via '
+                     'the config drive.', server['id'])
+            dev_name = self.ssh_client.exec_command(cmd_blkid)
+            dev_name = dev_name.rstrip()
+            self.ssh_client.exec_command('sudo mount %s /mnt' % dev_name)
+            cmd_md = 'sudo cat /mnt/openstack/latest/meta_data.json'
+            md_json = self.ssh_client.exec_command(cmd_md)
+            self.verify_device_metadata(md_json)
diff --git a/tempest/api/compute/servers/test_disk_config.py b/tempest/api/compute/servers/test_disk_config.py
index 617cdd5..ff8ea6e 100644
--- a/tempest/api/compute/servers/test_disk_config.py
+++ b/tempest/api/compute/servers/test_disk_config.py
@@ -37,17 +37,11 @@
         super(ServerDiskConfigTestJSON, cls).setup_clients()
         cls.client = cls.os.servers_client
 
-    @classmethod
-    def resource_setup(cls):
-        super(ServerDiskConfigTestJSON, cls).resource_setup()
-        server = cls.create_test_server(wait_until='ACTIVE')
-        cls.server_id = server['id']
-
-    def _update_server_with_disk_config(self, disk_config):
-        server = self.client.show_server(self.server_id)['server']
+    def _update_server_with_disk_config(self, server_id, disk_config):
+        server = self.client.show_server(server_id)['server']
         if disk_config != server['OS-DCF:diskConfig']:
             server = self.client.update_server(
-                self.server_id, disk_config=disk_config)['server']
+                server_id, disk_config=disk_config)['server']
             waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
             server = self.client.show_server(server['id'])['server']
             self.assertEqual(disk_config, server['OS-DCF:diskConfig'])
@@ -55,9 +49,12 @@
     @test.idempotent_id('bef56b09-2e8c-4883-a370-4950812f430e')
     def test_rebuild_server_with_manual_disk_config(self):
         # A server should be rebuilt using the manual disk config option
-        self._update_server_with_disk_config(disk_config='AUTO')
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.addCleanup(self.client.delete_server, server['id'])
+        self._update_server_with_disk_config(server['id'],
+                                             disk_config='AUTO')
 
-        server = self.client.rebuild_server(self.server_id,
+        server = self.client.rebuild_server(server['id'],
                                             self.image_ref_alt,
                                             disk_config='MANUAL')['server']
 
@@ -71,9 +68,12 @@
     @test.idempotent_id('9c9fae77-4feb-402f-8450-bf1c8b609713')
     def test_rebuild_server_with_auto_disk_config(self):
         # A server should be rebuilt using the auto disk config option
-        self._update_server_with_disk_config(disk_config='MANUAL')
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.addCleanup(self.client.delete_server, server['id'])
+        self._update_server_with_disk_config(server['id'],
+                                             disk_config='MANUAL')
 
-        server = self.client.rebuild_server(self.server_id,
+        server = self.client.rebuild_server(server['id'],
                                             self.image_ref_alt,
                                             disk_config='AUTO')['server']
 
@@ -84,31 +84,20 @@
         server = self.client.show_server(server['id'])['server']
         self.assertEqual('AUTO', server['OS-DCF:diskConfig'])
 
-    def _get_alternative_flavor(self):
-        server = self.client.show_server(self.server_id)['server']
-
-        if server['flavor']['id'] == self.flavor_ref:
-            return self.flavor_ref_alt
-        else:
-            return self.flavor_ref
-
     @test.idempotent_id('414e7e93-45b5-44bc-8e03-55159c6bfc97')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize not available.')
     def test_resize_server_from_manual_to_auto(self):
         # A server should be resized from manual to auto disk config
-        self._update_server_with_disk_config(disk_config='MANUAL')
-
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.addCleanup(self.client.delete_server, server['id'])
+        self._update_server_with_disk_config(server['id'],
+                                             disk_config='MANUAL')
         # Resize with auto option
-        flavor_id = self._get_alternative_flavor()
-        self.client.resize_server(self.server_id, flavor_id,
-                                  disk_config='AUTO')
-        waiters.wait_for_server_status(self.client, self.server_id,
-                                       'VERIFY_RESIZE')
-        self.client.confirm_resize_server(self.server_id)
-        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        self.resize_server(server['id'], self.flavor_ref_alt,
+                           disk_config='AUTO')
 
-        server = self.client.show_server(self.server_id)['server']
+        server = self.client.show_server(server['id'])['server']
         self.assertEqual('AUTO', server['OS-DCF:diskConfig'])
 
     @test.idempotent_id('693d16f3-556c-489a-8bac-3d0ca2490bad')
@@ -116,27 +105,27 @@
                           'Resize not available.')
     def test_resize_server_from_auto_to_manual(self):
         # A server should be resized from auto to manual disk config
-        self._update_server_with_disk_config(disk_config='AUTO')
-
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.addCleanup(self.client.delete_server, server['id'])
+        self._update_server_with_disk_config(server['id'],
+                                             disk_config='AUTO')
         # Resize with manual option
-        flavor_id = self._get_alternative_flavor()
-        self.client.resize_server(self.server_id, flavor_id,
-                                  disk_config='MANUAL')
-        waiters.wait_for_server_status(self.client, self.server_id,
-                                       'VERIFY_RESIZE')
-        self.client.confirm_resize_server(self.server_id)
-        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        self.resize_server(server['id'], self.flavor_ref_alt,
+                           disk_config='MANUAL')
 
-        server = self.client.show_server(self.server_id)['server']
+        server = self.client.show_server(server['id'])['server']
         self.assertEqual('MANUAL', server['OS-DCF:diskConfig'])
 
     @test.idempotent_id('5ef18867-358d-4de9-b3c9-94d4ba35742f')
     def test_update_server_from_auto_to_manual(self):
         # A server should be updated from auto to manual disk config
-        self._update_server_with_disk_config(disk_config='AUTO')
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.addCleanup(self.client.delete_server, server['id'])
+        self._update_server_with_disk_config(server['id'],
+                                             disk_config='AUTO')
 
         # Update the disk_config attribute to manual
-        server = self.client.update_server(self.server_id,
+        server = self.client.update_server(server['id'],
                                            disk_config='MANUAL')['server']
         waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
 
diff --git a/tempest/api/compute/servers/test_instance_actions.py b/tempest/api/compute/servers/test_instance_actions.py
index 1367629..e50881f 100644
--- a/tempest/api/compute/servers/test_instance_actions.py
+++ b/tempest/api/compute/servers/test_instance_actions.py
@@ -28,26 +28,50 @@
     @classmethod
     def resource_setup(cls):
         super(InstanceActionsTestJSON, cls).resource_setup()
-        server = cls.create_test_server(wait_until='ACTIVE')
-        cls.request_id = server.response['x-compute-request-id']
-        cls.server_id = server['id']
+        cls.server = cls.create_test_server(wait_until='ACTIVE')
+        cls.request_id = cls.server.response['x-compute-request-id']
 
     @test.idempotent_id('77ca5cc5-9990-45e0-ab98-1de8fead201a')
     def test_list_instance_actions(self):
         # List actions of the provided server
-        self.client.reboot_server(self.server_id, type='HARD')
-        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        self.client.reboot_server(self.server['id'], type='HARD')
+        waiters.wait_for_server_status(self.client,
+                                       self.server['id'], 'ACTIVE')
 
-        body = (self.client.list_instance_actions(self.server_id)
+        body = (self.client.list_instance_actions(self.server['id'])
                 ['instanceActions'])
-        self.assertTrue(len(body) == 2, str(body))
-        self.assertTrue(any([i for i in body if i['action'] == 'create']))
-        self.assertTrue(any([i for i in body if i['action'] == 'reboot']))
+        self.assertEqual(len(body), 2, str(body))
+        self.assertEqual(sorted([i['action'] for i in body]),
+                         ['create', 'reboot'])
 
     @test.idempotent_id('aacc71ca-1d70-4aa5-bbf6-0ff71470e43c')
     def test_get_instance_action(self):
         # Get the action details of the provided server
         body = self.client.show_instance_action(
-            self.server_id, self.request_id)['instanceAction']
-        self.assertEqual(self.server_id, body['instance_uuid'])
+            self.server['id'], self.request_id)['instanceAction']
+        self.assertEqual(self.server['id'], body['instance_uuid'])
         self.assertEqual('create', body['action'])
+
+
+class InstanceActionsV221TestJSON(base.BaseV2ComputeTest):
+
+    min_microversion = '2.21'
+    max_microversion = 'latest'
+
+    @classmethod
+    def setup_clients(cls):
+        super(InstanceActionsV221TestJSON, cls).setup_clients()
+        cls.client = cls.servers_client
+
+    @test.idempotent_id('0a0f85d4-10fa-41f6-bf80-a54fb4aa2ae1')
+    def test_get_list_deleted_instance_actions(self):
+
+        # List actions of the deleted server
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.client.delete_server(server['id'])
+        waiters.wait_for_server_termination(self.client, server['id'])
+        body = (self.client.list_instance_actions(server['id'])
+                ['instanceActions'])
+        self.assertEqual(len(body), 2, str(body))
+        self.assertEqual(sorted([i['action'] for i in body]),
+                         ['create', 'delete'])
diff --git a/tempest/api/compute/servers/test_instance_actions_negative.py b/tempest/api/compute/servers/test_instance_actions_negative.py
index ac66d05..33fed08 100644
--- a/tempest/api/compute/servers/test_instance_actions_negative.py
+++ b/tempest/api/compute/servers/test_instance_actions_negative.py
@@ -13,10 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -30,8 +29,7 @@
     @classmethod
     def resource_setup(cls):
         super(InstanceActionsNegativeTestJSON, cls).resource_setup()
-        server = cls.create_test_server(wait_until='ACTIVE')
-        cls.server_id = server['id']
+        cls.server = cls.create_test_server(wait_until='ACTIVE')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('67e1fce6-7ec2-45c6-92d4-0a8f1a632910')
@@ -47,4 +45,4 @@
     def test_get_instance_action_invalid_request(self):
         # Get the action details of the provided server with invalid request
         self.assertRaises(lib_exc.NotFound, self.client.show_instance_action,
-                          self.server_id, '999')
+                          self.server['id'], '999')
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 37f322f..611d5a2 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -13,19 +13,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import decorators
-from tempest_lib import exceptions as lib_exc
-
 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
-from tempest import config
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
-CONF = config.CONF
-
 
 class ListServerFiltersTestJSON(base.BaseV2ComputeTest):
 
@@ -90,7 +85,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}
@@ -124,8 +119,8 @@
         self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
 
     @test.idempotent_id('ca78e20e-fddb-4ce6-b7f7-bcbf8605e66e')
-    def test_list_servers_filter_by_server_status(self):
-        # Filter the list of servers by server status
+    def test_list_servers_filter_by_active_status(self):
+        # Filter the list of servers by server active status
         params = {'status': 'active'}
         body = self.client.list_servers(**params)
         servers = body['servers']
@@ -175,7 +170,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}
@@ -276,14 +271,9 @@
             msg = 'fixed_network_name needs to be configured to run this test'
             raise self.skipException(msg)
         self.s1 = self.client.show_server(self.s1['id'])['server']
-        for addr_spec in self.s1['addresses'][self.fixed_network_name]:
-            ip = addr_spec['addr']
-            if addr_spec['version'] == 4:
-                params = {'ip': ip}
-                break
-        else:
-            msg = "Skipped until bug 1450859 is resolved"
-            raise self.skipException(msg)
+        # Get first ip address inspite of v4 or v6
+        addr_spec = self.s1['addresses'][self.fixed_network_name][0]
+        params = {'ip': addr_spec['addr']}
         body = self.client.list_servers(**params)
         servers = body['servers']
 
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index f205ddf..3e408d2 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -13,11 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from six import moves
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest.common import waiters
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -38,18 +36,13 @@
         # tearDownClass method of the super-class.
         cls.existing_fixtures = []
         cls.deleted_fixtures = []
-        for x in moves.xrange(2):
+        for x in range(2):
             srv = cls.create_test_server(wait_until='ACTIVE')
             cls.existing_fixtures.append(srv)
 
-        srv = cls.create_test_server()
+        srv = cls.create_test_server(wait_until='ACTIVE')
         cls.client.delete_server(srv['id'])
-        # We ignore errors on termination because the server may
-        # be put into ERROR status on a quick spawn, then delete,
-        # as the compute node expects the instance local status
-        # to be spawning, not deleted. See LP Bug#1061167
-        waiters.wait_for_server_termination(cls.client, srv['id'],
-                                            ignore_error=True)
+        waiters.wait_for_server_termination(cls.client, srv['id'])
         cls.deleted_fixtures.append(srv)
 
     @test.attr(type=['negative'])
@@ -68,8 +61,7 @@
     @test.idempotent_id('ff01387d-c7ad-47b4-ae9e-64fa214638fe')
     def test_list_servers_by_non_existing_image(self):
         # Listing servers for a non existing image returns empty list
-        non_existing_image = '1234abcd-zzz0-aaa9-ppp3-0987654abcde'
-        body = self.client.list_servers(image=non_existing_image)
+        body = self.client.list_servers(image='non_existing_image')
         servers = body['servers']
         self.assertEqual([], servers)
 
@@ -77,8 +69,7 @@
     @test.idempotent_id('5913660b-223b-44d4-a651-a0fbfd44ca75')
     def test_list_servers_by_non_existing_flavor(self):
         # Listing servers by non existing flavor returns empty list
-        non_existing_flavor = 1234
-        body = self.client.list_servers(flavor=non_existing_flavor)
+        body = self.client.list_servers(flavor='non_existing_flavor')
         servers = body['servers']
         self.assertEqual([], servers)
 
@@ -86,8 +77,7 @@
     @test.idempotent_id('e2c77c4a-000a-4af3-a0bd-629a328bde7c')
     def test_list_servers_by_non_existing_server_name(self):
         # Listing servers for a non existent server name returns empty list
-        non_existing_name = 'junk_server_1234'
-        body = self.client.list_servers(name=non_existing_name)
+        body = self.client.list_servers(name='non_existing_server_name')
         servers = body['servers']
         self.assertEqual([], servers)
 
@@ -95,8 +85,7 @@
     @test.idempotent_id('fcdf192d-0f74-4d89-911f-1ec002b822c4')
     def test_list_servers_status_non_existing(self):
         # Return an empty list when invalid status is specified
-        non_existing_status = 'BALONEY'
-        body = self.client.list_servers(status=non_existing_status)
+        body = self.client.list_servers(status='non_existing_status')
         servers = body['servers']
         self.assertEqual([], servers)
 
@@ -109,9 +98,12 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('d47c17fb-eebd-4287-8e95-f20a7e627b18')
     def test_list_servers_by_limits_greater_than_actual_count(self):
+        # Gather the complete list of servers in the project for reference
+        full_list = self.client.list_servers()['servers']
         # List servers by specifying a greater value for limit
-        body = self.client.list_servers(limit=100)
-        self.assertEqual(len(self.existing_fixtures), len(body['servers']))
+        limit = len(full_list) + 100
+        body = self.client.list_servers(limit=limit)
+        self.assertEqual(len(full_list), len(body['servers']))
 
     @test.attr(type=['negative'])
     @test.idempotent_id('679bc053-5e70-4514-9800-3dfab1a380a6')
diff --git a/tempest/api/compute/servers/test_multiple_create.py b/tempest/api/compute/servers/test_multiple_create.py
index eb1beb1..9fc30f9 100644
--- a/tempest/api/compute/servers/test_multiple_create.py
+++ b/tempest/api/compute/servers/test_multiple_create.py
@@ -14,32 +14,16 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest import test
 
 
 class MultipleCreateTestJSON(base.BaseV2ComputeTest):
-    _name = 'multiple-create-test'
-
-    def _generate_name(self):
-        return data_utils.rand_name(self._name)
-
-    def _create_multiple_servers(self, name=None, wait_until=None, **kwargs):
-        # NOTE: This is the right way to create_multiple servers and manage to
-        # get the created servers into the servers list to be cleaned up after
-        # all.
-        kwargs['name'] = name if name else self._generate_name()
-        if wait_until:
-            kwargs['wait_until'] = wait_until
-        body = self.create_test_server(**kwargs)
-
-        return body
 
     @test.idempotent_id('61e03386-89c3-449c-9bb1-a06f423fd9d1')
     def test_multiple_create(self):
-        body = self._create_multiple_servers(wait_until='ACTIVE',
-                                             min_count=1,
-                                             max_count=2)
+        body = self.create_test_server(wait_until='ACTIVE',
+                                       min_count=1,
+                                       max_count=2)
         # NOTE(maurosr): do status response check and also make sure that
         # reservation_id is not in the response body when the request send
         # contains return_reservation_id=False
@@ -47,8 +31,8 @@
 
     @test.idempotent_id('864777fb-2f1e-44e3-b5b9-3eb6fa84f2f7')
     def test_multiple_create_with_reservation_return(self):
-        body = self._create_multiple_servers(wait_until='ACTIVE',
-                                             min_count=1,
-                                             max_count=2,
-                                             return_reservation_id=True)
+        body = self.create_test_server(wait_until='ACTIVE',
+                                       min_count=1,
+                                       max_count=2,
+                                       return_reservation_id=True)
         self.assertIn('reservation_id', body)
diff --git a/tempest/api/compute/servers/test_multiple_create_negative.py b/tempest/api/compute/servers/test_multiple_create_negative.py
index 3d8a732..d9fb4ca 100644
--- a/tempest/api/compute/servers/test_multiple_create_negative.py
+++ b/tempest/api/compute/servers/test_multiple_create_negative.py
@@ -13,53 +13,39 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
 class MultipleCreateNegativeTestJSON(base.BaseV2ComputeTest):
-    _name = 'multiple-create-test'
-
-    def _generate_name(self):
-        return data_utils.rand_name(self._name)
-
-    def _create_multiple_servers(self, name=None, wait_until=None, **kwargs):
-        # This is the right way to create_multiple servers and manage to get
-        # the created servers into the servers list to be cleaned up after all.
-        kwargs['name'] = kwargs.get('name', self._generate_name())
-        body = self.create_test_server(**kwargs)
-
-        return body
 
     @test.attr(type=['negative'])
     @test.idempotent_id('daf29d8d-e928-4a01-9a8c-b129603f3fc0')
     def test_min_count_less_than_one(self):
         invalid_min_count = 0
-        self.assertRaises(lib_exc.BadRequest, self._create_multiple_servers,
+        self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           min_count=invalid_min_count)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('999aa722-d624-4423-b813-0d1ac9884d7a')
     def test_min_count_non_integer(self):
         invalid_min_count = 2.5
-        self.assertRaises(lib_exc.BadRequest, self._create_multiple_servers,
+        self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           min_count=invalid_min_count)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('a6f9c2ab-e060-4b82-b23c-4532cb9390ff')
     def test_max_count_less_than_one(self):
         invalid_max_count = 0
-        self.assertRaises(lib_exc.BadRequest, self._create_multiple_servers,
+        self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           max_count=invalid_max_count)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('9c5698d1-d7af-4c80-b971-9d403135eea2')
     def test_max_count_non_integer(self):
         invalid_max_count = 2.5
-        self.assertRaises(lib_exc.BadRequest, self._create_multiple_servers,
+        self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           max_count=invalid_max_count)
 
     @test.attr(type=['negative'])
@@ -67,6 +53,6 @@
     def test_max_count_less_than_min_count(self):
         min_count = 3
         max_count = 2
-        self.assertRaises(lib_exc.BadRequest, self._create_multiple_servers,
+        self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           min_count=min_count,
                           max_count=max_count)
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 66e85a6..0334eff 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -13,18 +13,18 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
-
+from oslo_log import log as logging
 from six.moves.urllib import parse as urlparse
-from tempest_lib import decorators
-from tempest_lib import exceptions as lib_exc
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import compute
 from tempest.common.utils import data_utils
 from tempest.common.utils.linux import remote_client
 from tempest.common import waiters
 from tempest import config
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
@@ -79,18 +79,25 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.change_password,
                           'Change password not available.')
     def test_change_server_password(self):
+        # Since this test messes with the password and makes the
+        # server unreachable, it should create its own server
+        newserver = self.create_test_server(
+            validatable=True,
+            wait_until='ACTIVE')
         # The server's password should be set to the provided password
         new_password = 'Newpass1234'
-        self.client.change_password(self.server_id, adminPass=new_password)
-        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        self.client.change_password(newserver['id'], adminPass=new_password)
+        waiters.wait_for_server_status(self.client, newserver['id'], 'ACTIVE')
 
         if CONF.validation.run_validation:
             # Verify that the user can authenticate with the new password
-            server = self.client.show_server(self.server_id)['server']
+            server = self.client.show_server(newserver['id'])['server']
             linux_client = remote_client.RemoteClient(
                 self.get_server_ip(server),
                 self.ssh_user,
-                new_password)
+                new_password,
+                server=server,
+                servers_client=self.client)
             linux_client.validate_authentication()
 
     def _test_reboot_server(self, reboot_type):
@@ -101,9 +108,15 @@
                 self.get_server_ip(server),
                 self.ssh_user,
                 self.password,
-                self.validation_resources['keypair']['private_key'])
+                self.validation_resources['keypair']['private_key'],
+                server=server,
+                servers_client=self.client)
             boot_time = linux_client.get_boot_time()
 
+            # NOTE: This sync is for avoiding the loss of pub key data
+            # in a server
+            linux_client.exec_command("sync")
+
         self.client.reboot_server(self.server_id, type=reboot_type)
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
@@ -113,10 +126,12 @@
                 self.get_server_ip(server),
                 self.ssh_user,
                 self.password,
-                self.validation_resources['keypair']['private_key'])
+                self.validation_resources['keypair']['private_key'],
+                server=server,
+                servers_client=self.client)
             new_boot_time = linux_client.get_boot_time()
-            self.assertTrue(new_boot_time > boot_time,
-                            '%s > %s' % (new_boot_time, boot_time))
+            self.assertGreater(new_boot_time, boot_time,
+                               '%s > %s' % (new_boot_time, boot_time))
 
     @test.attr(type='smoke')
     @test.idempotent_id('2cb1baf6-ac8d-4429-bf0d-ba8a0ba53e32')
@@ -143,7 +158,7 @@
     def test_rebuild_server(self):
         # The server should be rebuilt using the provided image and data
         meta = {'rebuild': 'server'}
-        new_name = data_utils.rand_name('server')
+        new_name = data_utils.rand_name(self.__class__.__name__ + '-server')
         password = 'rebuildPassw0rd'
         rebuilt_server = self.client.rebuild_server(
             self.server_id,
@@ -182,7 +197,9 @@
                 self.get_server_ip(rebuilt_server),
                 self.ssh_user,
                 password,
-                self.validation_resources['keypair']['private_key'])
+                self.validation_resources['keypair']['private_key'],
+                server=rebuilt_server,
+                servers_client=self.client)
             linux_client.validate_authentication()
 
     @test.idempotent_id('30449a88-5aff-4f9b-9866-6ee9b17f906d')
@@ -217,6 +234,25 @@
 
         self.client.start_server(self.server_id)
 
+    @test.idempotent_id('b68bd8d6-855d-4212-b59b-2e704044dace')
+    @test.services('volume')
+    def test_rebuild_server_with_volume_attached(self):
+        # create a new volume and attach it to the server
+        volume = self.create_volume()
+
+        server = self.client.show_server(self.server_id)['server']
+        self.attach_volume(server, volume)
+
+        # run general rebuild test
+        self.test_rebuild_server()
+
+        # make sure the volume is attached to the instance after rebuild
+        vol_after_rebuild = self.volumes_client.show_volume(volume['id'])
+        vol_after_rebuild = vol_after_rebuild['volume']
+        self.assertEqual('in-use', vol_after_rebuild['status'])
+        self.assertEqual(self.server_id,
+                         vol_after_rebuild['attachments'][0]['server_id'])
+
     def _test_resize_server_confirm(self, stop=False):
         # The server's RAM and disk space should be modified to that of
         # the provided flavor
@@ -227,6 +263,9 @@
                                            'SHUTOFF')
 
         self.client.resize_server(self.server_id, self.flavor_ref_alt)
+        # NOTE(jlk): Explicitly delete the server to get a new one for later
+        # tests. Avoids resize down race issues.
+        self.addCleanup(self.delete_server, self.server_id)
         waiters.wait_for_server_status(self.client, self.server_id,
                                        'VERIFY_RESIZE')
 
@@ -242,10 +281,6 @@
             # NOTE(mriedem): tearDown requires the server to be started.
             self.client.start_server(self.server_id)
 
-        # NOTE(jlk): Explicitly delete the server to get a new one for later
-        # tests. Avoids resize down race issues.
-        self.addCleanup(self.delete_server, self.server_id)
-
     @test.idempotent_id('1499262a-9328-4eda-9068-db1ac57498d2')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize not available.')
@@ -266,6 +301,9 @@
         # values after a resize is reverted
 
         self.client.resize_server(self.server_id, self.flavor_ref_alt)
+        # NOTE(zhufl): Explicitly delete the server to get a new one for later
+        # tests. Avoids resize down race issues.
+        self.addCleanup(self.delete_server, self.server_id)
         waiters.wait_for_server_status(self.client, self.server_id,
                                        'VERIFY_RESIZE')
 
@@ -282,6 +320,19 @@
     def test_create_backup(self):
         # Positive test:create backup successfully and rotate backups correctly
         # create the first and the second backup
+
+        # Check if glance v1 is available to determine which client to use. We
+        # prefer glance v1 for the compute API tests since the compute image
+        # API proxy was written for glance v1.
+        if CONF.image_feature_enabled.api_v1:
+            glance_client = self.os.image_client
+        elif CONF.image_feature_enabled.api_v2:
+            glance_client = self.os.image_client_v2
+        else:
+            raise lib_exc.InvalidConfiguration(
+                'Either api_v1 or api_v2 must be True in '
+                '[image-feature-enabled].')
+
         backup1 = data_utils.rand_name('backup-1')
         resp = self.client.create_backup(self.server_id,
                                          backup_type='daily',
@@ -293,17 +344,18 @@
         def _clean_oldest_backup(oldest_backup):
             if oldest_backup_exist:
                 try:
-                    self.os.image_client.delete_image(oldest_backup)
+                    glance_client.delete_image(oldest_backup)
                 except lib_exc.NotFound:
                     pass
                 else:
                     LOG.warning("Deletion of oldest backup %s should not have "
                                 "been successful as it should have been "
-                                "deleted during rotation." % oldest_backup)
+                                "deleted during rotation.", oldest_backup)
 
         image1_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(_clean_oldest_backup, image1_id)
-        self.os.image_client.wait_for_image_status(image1_id, 'active')
+        waiters.wait_for_image_status(glance_client,
+                                      image1_id, 'active')
 
         backup2 = data_utils.rand_name('backup-2')
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
@@ -312,8 +364,9 @@
                                          rotation=2,
                                          name=backup2).response
         image2_id = data_utils.parse_image_id(resp['location'])
-        self.addCleanup(self.os.image_client.delete_image, image2_id)
-        self.os.image_client.wait_for_image_status(image2_id, 'active')
+        self.addCleanup(glance_client.delete_image, image2_id)
+        waiters.wait_for_image_status(glance_client,
+                                      image2_id, 'active')
 
         # verify they have been created
         properties = {
@@ -321,12 +374,22 @@
             'backup_type': "daily",
             'instance_uuid': self.server_id,
         }
-        image_list = self.os.image_client.list_images(
-            detail=True,
-            properties=properties,
-            status='active',
-            sort_key='created_at',
-            sort_dir='asc')['images']
+        params = {
+            'status': 'active',
+            'sort_key': 'created_at',
+            'sort_dir': 'asc'
+        }
+        if CONF.image_feature_enabled.api_v1:
+            for key, value in properties.items():
+                params['property-%s' % key] = value
+            image_list = glance_client.list_images(
+                detail=True,
+                **params)['images']
+        else:
+            # Additional properties are flattened in glance v2.
+            params.update(properties)
+            image_list = glance_client.list_images(params)['images']
+
         self.assertEqual(2, len(image_list))
         self.assertEqual((backup1, backup2),
                          (image_list[0]['name'], image_list[1]['name']))
@@ -340,17 +403,16 @@
                                          rotation=2,
                                          name=backup3).response
         image3_id = data_utils.parse_image_id(resp['location'])
-        self.addCleanup(self.os.image_client.delete_image, image3_id)
+        self.addCleanup(glance_client.delete_image, image3_id)
         # the first back up should be deleted
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
-        self.os.image_client.wait_for_resource_deletion(image1_id)
+        glance_client.wait_for_resource_deletion(image1_id)
         oldest_backup_exist = False
-        image_list = self.os.image_client.list_images(
-            detail=True,
-            properties=properties,
-            status='active',
-            sort_key='created_at',
-            sort_dir='asc')['images']
+        if CONF.image_feature_enabled.api_v1:
+            image_list = glance_client.list_images(
+                detail=True, **params)['images']
+        else:
+            image_list = glance_client.list_images(params)['images']
         self.assertEqual(2, len(image_list),
                          'Unexpected number of images for '
                          'v2:test_create_backup; was the oldest backup not '
@@ -395,8 +457,8 @@
 
             # NOTE: This test tries to get full length console log, and the
             # length should be bigger than the one of test_get_console_output.
-            self.assertTrue(lines > 10, "Cannot get enough console log length."
-                                        " (lines: %s)" % lines)
+            self.assertGreater(lines, 10, "Cannot get enough console log "
+                                          "length. (lines: %s)" % lines)
 
         self.wait_for(_check_full_length_console_log)
 
@@ -440,20 +502,8 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
                           'Shelve is not available.')
     def test_shelve_unshelve_server(self):
-        self.client.shelve_server(self.server_id)
-
-        offload_time = CONF.compute.shelved_offload_time
-        if offload_time >= 0:
-            waiters.wait_for_server_status(self.client, self.server_id,
-                                           'SHELVED_OFFLOADED',
-                                           extra_timeout=offload_time)
-        else:
-            waiters.wait_for_server_status(self.client, self.server_id,
-                                           'SHELVED')
-
-            self.client.shelve_offload_server(self.server_id)
-            waiters.wait_for_server_status(self.client, self.server_id,
-                                           'SHELVED_OFFLOADED')
+        compute.shelve_server(self.client, self.server_id,
+                              force_shelve_offload=True)
 
         server = self.client.show_server(self.server_id)['server']
         image_name = server['name'] + '-shelved'
diff --git a/tempest/api/compute/servers/test_server_addresses.py b/tempest/api/compute/servers/test_server_addresses.py
index 864f38f..549ba03 100644
--- a/tempest/api/compute/servers/test_server_addresses.py
+++ b/tempest/api/compute/servers/test_server_addresses.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
-
 from tempest.api.compute import base
 from tempest import test
 
@@ -49,9 +47,9 @@
 
         # We do not know the exact network configuration, but an instance
         # should at least have a single public or private address
-        self.assertTrue(len(addresses) >= 1)
-        for network_name, network_addresses in six.iteritems(addresses):
-            self.assertTrue(len(network_addresses) >= 1)
+        self.assertGreaterEqual(len(addresses), 1)
+        for network_name, network_addresses in addresses.items():
+            self.assertGreaterEqual(len(network_addresses), 1)
             for address in network_addresses:
                 self.assertTrue(address['addr'])
                 self.assertTrue(address['version'])
diff --git a/tempest/api/compute/servers/test_server_addresses_negative.py b/tempest/api/compute/servers/test_server_addresses_negative.py
index 3503dc2..b4753e1 100644
--- a/tempest/api/compute/servers/test_server_addresses_negative.py
+++ b/tempest/api/compute/servers/test_server_addresses_negative.py
@@ -13,9 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
diff --git a/tempest/api/compute/servers/test_server_group.py b/tempest/api/compute/servers/test_server_group.py
index e32f6b0..bc49e7b 100644
--- a/tempest/api/compute/servers/test_server_group.py
+++ b/tempest/api/compute/servers/test_server_group.py
@@ -19,12 +19,13 @@
 
 
 class ServerGroupTestJSON(base.BaseV2ComputeTest):
-    """These tests check for the server-group APIs
+    """These tests check for the server-group APIs.
 
     They create/delete server-groups with different policies.
     policies = affinity/anti-affinity
     It also adds the tests for list and get details of server-groups
     """
+
     @classmethod
     def skip_checks(cls):
         super(ServerGroupTestJSON, cls).skip_checks()
@@ -40,12 +41,10 @@
     @classmethod
     def resource_setup(cls):
         super(ServerGroupTestJSON, cls).resource_setup()
-        server_group_name = data_utils.rand_name('server-group')
         cls.policy = ['affinity']
 
         cls.created_server_group = cls.create_test_server_group(
-            server_group_name,
-            cls.policy)
+            policy=cls.policy)
 
     def _create_server_group(self, name, policy):
         # create the test server-group with given policy
diff --git a/tempest/api/compute/servers/test_server_metadata.py b/tempest/api/compute/servers/test_server_metadata.py
index 9c07677..847b7a1 100644
--- a/tempest/api/compute/servers/test_server_metadata.py
+++ b/tempest/api/compute/servers/test_server_metadata.py
@@ -23,23 +23,21 @@
     def setup_clients(cls):
         super(ServerMetadataTestJSON, cls).setup_clients()
         cls.client = cls.servers_client
-        cls.quotas = cls.quotas_client
 
     @classmethod
     def resource_setup(cls):
         super(ServerMetadataTestJSON, cls).resource_setup()
-        server = cls.create_test_server(metadata={}, wait_until='ACTIVE')
-        cls.server_id = server['id']
+        cls.server = cls.create_test_server(metadata={}, wait_until='ACTIVE')
 
     def setUp(self):
         super(ServerMetadataTestJSON, self).setUp()
         meta = {'key1': 'value1', 'key2': 'value2'}
-        self.client.set_server_metadata(self.server_id, meta)['metadata']
+        self.client.set_server_metadata(self.server['id'], meta)['metadata']
 
     @test.idempotent_id('479da087-92b3-4dcf-aeb3-fd293b2d14ce')
     def test_list_server_metadata(self):
         # All metadata key/value pairs for a server should be returned
-        resp_metadata = (self.client.list_server_metadata(self.server_id)
+        resp_metadata = (self.client.list_server_metadata(self.server['id'])
                          ['metadata'])
 
         # Verify the expected metadata items are in the list
@@ -51,12 +49,12 @@
         # The server's metadata should be replaced with the provided values
         # Create a new set of metadata for the server
         req_metadata = {'meta2': 'data2', 'meta3': 'data3'}
-        self.client.set_server_metadata(self.server_id,
+        self.client.set_server_metadata(self.server['id'],
                                         req_metadata)['metadata']
 
         # Verify the expected values are correct, and that the
         # previous values have been removed
-        resp_metadata = (self.client.list_server_metadata(self.server_id)
+        resp_metadata = (self.client.list_server_metadata(self.server['id'])
                          ['metadata'])
         self.assertEqual(resp_metadata, req_metadata)
 
@@ -65,10 +63,10 @@
         # The server's metadata values should be updated to the
         # provided values
         meta = {'key1': 'alt1', 'key3': 'value3'}
-        self.client.update_server_metadata(self.server_id, meta)
+        self.client.update_server_metadata(self.server['id'], meta)
 
         # Verify the values have been updated to the proper values
-        resp_metadata = (self.client.list_server_metadata(self.server_id)
+        resp_metadata = (self.client.list_server_metadata(self.server['id'])
                          ['metadata'])
         expected = {'key1': 'alt1', 'key2': 'value2', 'key3': 'value3'}
         self.assertEqual(expected, resp_metadata)
@@ -78,8 +76,8 @@
         # The original metadata should not be lost if empty metadata body is
         # passed
         meta = {}
-        self.client.update_server_metadata(self.server_id, meta)
-        resp_metadata = (self.client.list_server_metadata(self.server_id)
+        self.client.update_server_metadata(self.server['id'], meta)
+        resp_metadata = (self.client.list_server_metadata(self.server['id'])
                          ['metadata'])
         expected = {'key1': 'value1', 'key2': 'value2'}
         self.assertEqual(expected, resp_metadata)
@@ -87,7 +85,7 @@
     @test.idempotent_id('3043c57d-7e0e-49a6-9a96-ad569c265e6a')
     def test_get_server_metadata_item(self):
         # The value for a specific metadata key should be returned
-        meta = self.client.show_server_metadata_item(self.server_id,
+        meta = self.client.show_server_metadata_item(self.server['id'],
                                                      'key2')['meta']
         self.assertEqual('value2', meta['key2'])
 
@@ -96,10 +94,10 @@
         # The item's value should be updated to the provided value
         # Update the metadata value
         meta = {'nova': 'alt'}
-        self.client.set_server_metadata_item(self.server_id, 'nova', meta)
+        self.client.set_server_metadata_item(self.server['id'], 'nova', meta)
 
         # Verify the meta item's value has been updated
-        resp_metadata = (self.client.list_server_metadata(self.server_id)
+        resp_metadata = (self.client.list_server_metadata(self.server['id'])
                          ['metadata'])
         expected = {'key1': 'value1', 'key2': 'value2', 'nova': 'alt'}
         self.assertEqual(expected, resp_metadata)
@@ -107,10 +105,10 @@
     @test.idempotent_id('127642d6-4c7b-4486-b7cd-07265a378658')
     def test_delete_server_metadata_item(self):
         # The metadata value/key pair should be deleted from the server
-        self.client.delete_server_metadata_item(self.server_id, 'key1')
+        self.client.delete_server_metadata_item(self.server['id'], 'key1')
 
         # Verify the metadata item has been removed
-        resp_metadata = (self.client.list_server_metadata(self.server_id)
+        resp_metadata = (self.client.list_server_metadata(self.server['id'])
                          ['metadata'])
         expected = {'key2': 'value2'}
         self.assertEqual(expected, resp_metadata)
diff --git a/tempest/api/compute/servers/test_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index 18d80be..62b8962 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -13,10 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -26,15 +25,12 @@
     def setup_clients(cls):
         super(ServerMetadataNegativeTestJSON, cls).setup_clients()
         cls.client = cls.servers_client
-        cls.quotas = cls.quotas_client
 
     @classmethod
     def resource_setup(cls):
         super(ServerMetadataNegativeTestJSON, cls).resource_setup()
         cls.tenant_id = cls.client.tenant_id
-        server = cls.create_test_server(metadata={}, wait_until='ACTIVE')
-
-        cls.server_id = server['id']
+        cls.server = cls.create_test_server(metadata={}, wait_until='ACTIVE')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('fe114a8f-3a57-4eff-9ee2-4e14628df049')
@@ -88,7 +84,7 @@
         meta = {'testkey': 'testvalue'}
         self.assertRaises(lib_exc.BadRequest,
                           self.client.set_server_metadata_item,
-                          self.server_id, 'key', meta)
+                          self.server['id'], 'key', meta)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('0df38c2a-3d4e-4db5-98d8-d4d9fa843a12')
@@ -119,7 +115,7 @@
         meta = {'': 'data1'}
         self.assertRaises(lib_exc.BadRequest,
                           self.client.update_server_metadata,
-                          self.server_id, meta=meta)
+                          self.server['id'], meta=meta)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('6bbd88e1-f8b3-424d-ba10-ae21c45ada8d')
@@ -137,7 +133,8 @@
         # A 403 Forbidden or 413 Overlimit (old behaviour) exception
         # will be raised while exceeding metadata items limit for
         # tenant.
-        quota_set = self.quotas.show_quota_set(self.tenant_id)['quota_set']
+        quota_set = self.quotas_client.show_quota_set(
+            self.tenant_id)['quota_set']
         quota_metadata = quota_set['metadata_items']
         if quota_metadata == -1:
             raise self.skipException("No limit for metadata_items")
@@ -147,14 +144,14 @@
             req_metadata['key' + str(num)] = 'val' + str(num)
         self.assertRaises((lib_exc.OverLimit, lib_exc.Forbidden),
                           self.client.set_server_metadata,
-                          self.server_id, req_metadata)
+                          self.server['id'], req_metadata)
 
         # A 403 Forbidden or 413 Overlimit (old behaviour) exception
         # will be raised while exceeding metadata items limit for
         # tenant.
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.client.update_server_metadata,
-                          self.server_id, req_metadata)
+                          self.server['id'], req_metadata)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('96100343-7fa9-40d8-80fa-d29ef588ce1c')
@@ -164,7 +161,7 @@
         meta = {'': 'data1'}
         self.assertRaises(lib_exc.BadRequest,
                           self.client.set_server_metadata,
-                          self.server_id, meta=meta)
+                          self.server['id'], meta=meta)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('64a91aee-9723-4863-be44-4c9d9f1e7d0e')
@@ -174,4 +171,4 @@
         meta = {'meta1': 'data1'}
         self.assertRaises(lib_exc.BadRequest,
                           self.client.set_server_metadata,
-                          self.server_id, meta=meta, no_metadata_field=True)
+                          self.server['id'], meta=meta, no_metadata_field=True)
diff --git a/tempest/api/compute/servers/test_server_personality.py b/tempest/api/compute/servers/test_server_personality.py
index dad8e90..ab291b4 100644
--- a/tempest/api/compute/servers/test_server_personality.py
+++ b/tempest/api/compute/servers/test_server_personality.py
@@ -13,14 +13,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import base64
-from tempest_lib.common.utils import data_utils
-from tempest_lib import exceptions as lib_exc
+from oslo_serialization import base64
 
 from tempest.api.compute import base
 from tempest.common.utils.linux import remote_client
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
@@ -55,7 +55,7 @@
         file_contents = 'This is a test file.'
         file_path = '/test.txt'
         personality = [{'path': file_path,
-                        'contents': base64.b64encode(file_contents)}]
+                        'contents': base64.encode_as_text(file_contents)}]
         password = data_utils.rand_password()
         created_server = self.create_test_server(personality=personality,
                                                  adminPass=password,
@@ -66,7 +66,9 @@
             linux_client = remote_client.RemoteClient(
                 self.get_server_ip(server),
                 self.ssh_user, password,
-                self.validation_resources['keypair']['private_key'])
+                self.validation_resources['keypair']['private_key'],
+                server=server,
+                servers_client=self.client)
             self.assertEqual(file_contents,
                              linux_client.exec_command(
                                  'sudo cat %s' % file_path))
@@ -77,7 +79,7 @@
         server_id = server['id']
         file_contents = 'Test server rebuild.'
         personality = [{'path': 'rebuild.txt',
-                        'contents': base64.b64encode(file_contents)}]
+                        'contents': base64.encode_as_text(file_contents)}]
         rebuilt_server = self.client.rebuild_server(server_id,
                                                     self.image_ref_alt,
                                                     personality=personality)
@@ -98,7 +100,8 @@
         for i in range(0, int(max_file_limit) + 1):
             path = 'etc/test' + str(i) + '.txt'
             personality.append({'path': path,
-                                'contents': base64.b64encode(file_contents)})
+                                'contents': base64.encode_as_text(
+                                    file_contents)})
         # A 403 Forbidden or 413 Overlimit (old behaviour) exception
         # will be raised when out of quota
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
@@ -115,10 +118,12 @@
             raise self.skipException("No limit for personality files")
         person = []
         for i in range(0, int(max_file_limit)):
-            path = '/etc/test' + str(i) + '.txt'
+            # NOTE(andreaf) The cirros disk image is blank before boot
+            # so we can only inject safely to /
+            path = '/test' + str(i) + '.txt'
             person.append({
                 'path': path,
-                'contents': base64.b64encode(file_contents),
+                'contents': base64.encode_as_text(file_contents + str(i)),
             })
         password = data_utils.rand_password()
         created_server = self.create_test_server(personality=person,
@@ -130,8 +135,10 @@
             linux_client = remote_client.RemoteClient(
                 self.get_server_ip(server),
                 self.ssh_user, password,
-                self.validation_resources['keypair']['private_key'])
+                self.validation_resources['keypair']['private_key'],
+                server=server,
+                servers_client=self.client)
             for i in person:
-                self.assertEqual(base64.b64decode(i['contents']),
+                self.assertEqual(base64.decode_as_text(i['contents']),
                                  linux_client.exec_command(
                                      'sudo cat %s' % i['path']))
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 12b824f..9834d02 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()
 
@@ -60,9 +61,6 @@
         waiters.wait_for_server_status(cls.servers_client, cls.server_id,
                                        'ACTIVE')
 
-    def setUp(self):
-        super(ServerRescueTestJSON, self).setUp()
-
     @classmethod
     def resource_cleanup(cls):
         # Deleting the floating IP which is created in this method
@@ -71,9 +69,6 @@
             cls.sg_id)
         super(ServerRescueTestJSON, cls).resource_cleanup()
 
-    def tearDown(self):
-        super(ServerRescueTestJSON, self).tearDown()
-
     def _unrescue(self, server_id):
         self.servers_client.unrescue_server(server_id)
         waiters.wait_for_server_status(self.servers_client, server_id,
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index 5afb4d1..41b648c 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -13,13 +13,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
 import testtools
 
 from tempest.api.compute 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
@@ -60,20 +60,6 @@
         waiters.wait_for_server_status(cls.servers_client,
                                        cls.server_id, 'ACTIVE')
 
-    def _create_volume(self):
-        volume = self.volumes_extensions_client.create_volume(
-            size=CONF.volume.volume_size, display_name=data_utils.rand_name(
-                self.__class__.__name__ + '_volume'))['volume']
-        self.addCleanup(self.delete_volume, volume['id'])
-        waiters.wait_for_volume_status(self.volumes_extensions_client,
-                                       volume['id'], 'available')
-        return volume
-
-    def _detach(self, server_id, volume_id):
-        self.servers_client.detach_volume(server_id, volume_id)
-        waiters.wait_for_volume_status(self.volumes_extensions_client,
-                                       volume_id, 'available')
-
     def _unrescue(self, server_id):
         self.servers_client.unrescue_server(server_id)
         waiters.wait_for_server_status(self.servers_client,
@@ -125,7 +111,7 @@
     @test.services('volume')
     @test.attr(type=['negative'])
     def test_rescued_vm_attach_volume(self):
-        volume = self._create_volume()
+        volume = self.create_volume()
 
         # Rescue the server
         self.servers_client.rescue_server(self.server_id,
@@ -145,14 +131,11 @@
     @test.services('volume')
     @test.attr(type=['negative'])
     def test_rescued_vm_detach_volume(self):
-        volume = self._create_volume()
+        volume = self.create_volume()
 
         # Attach the volume to the server
-        self.servers_client.attach_volume(self.server_id,
-                                          volumeId=volume['id'],
-                                          device='/dev/%s' % self.device)
-        waiters.wait_for_volume_status(self.volumes_extensions_client,
-                                       volume['id'], 'in-use')
+        server = self.servers_client.show_server(self.server_id)['server']
+        self.attach_volume(server, volume, device='/dev/%s' % self.device)
 
         # Rescue the server
         self.servers_client.rescue_server(self.server_id,
@@ -160,7 +143,6 @@
         waiters.wait_for_server_status(self.servers_client,
                                        self.server_id, 'RESCUE')
         # addCleanup is a LIFO queue
-        self.addCleanup(self._detach, self.server_id, volume['id'])
         self.addCleanup(self._unrescue, self.server_id)
 
         # Detach the volume from the server expecting failure
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index 2f79d47..5aeba4e 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -52,7 +52,8 @@
         # Creating a server with a name that already exists is allowed
 
         # TODO(sdague): clear out try, we do cleanup one layer up
-        server_name = data_utils.rand_name('server')
+        server_name = data_utils.rand_name(
+            self.__class__.__name__ + '-server')
         server = self.create_test_server(name=server_name,
                                          wait_until='ACTIVE')
         id1 = server['id']
@@ -80,7 +81,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 +96,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 +104,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 681b5db..2b4cee7 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -15,13 +15,14 @@
 
 import sys
 
-from tempest_lib import exceptions as lib_exc
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import compute
 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
@@ -29,8 +30,6 @@
 
 class ServersNegativeTestJSON(base.BaseV2ComputeTest):
 
-    credentials = ['primary', 'alt']
-
     def setUp(self):
         super(ServersNegativeTestJSON, self).setUp()
         try:
@@ -47,7 +46,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):
@@ -211,7 +209,7 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('c3e0fb12-07fc-4d76-a22e-37409887afe8')
     def test_create_server_name_length_exceeds_256(self):
-        # Create a server with name length exceeding 256 characters
+        # Create a server with name length exceeding 255 characters
 
         server_name = 'a' * 256
         self.assertRaises(lib_exc.BadRequest,
@@ -219,6 +217,26 @@
                           name=server_name)
 
     @test.attr(type=['negative'])
+    @test.related_bug('1651064', status_code=500)
+    @test.idempotent_id('12146ac1-d7df-4928-ad25-b1f99e5286cd')
+    def test_create_server_invalid_bdm_in_2nd_dict(self):
+        volume = self.create_volume()
+        bdm_1st = {"source_type": "image",
+                   "delete_on_termination": True,
+                   "boot_index": 0,
+                   "uuid": self.image_ref,
+                   "destination_type": "local"}
+        bdm_2nd = {"source_type": "volume",
+                   "uuid": volume["id"],
+                   "destination_type": "invalid"}
+        bdm = [bdm_1st, bdm_2nd]
+
+        self.assertRaises(lib_exc.BadRequest,
+                          self.create_test_server,
+                          image_id=self.image_ref,
+                          block_device_mapping_v2=bdm)
+
+    @test.attr(type=['negative'])
     @test.idempotent_id('4e72dc2d-44c5-4336-9667-f7972e95c402')
     def test_create_with_invalid_network_uuid(self):
         # Pass invalid network uuid while creating a server
@@ -255,7 +273,8 @@
         # Update name of a non-existent server
 
         nonexistent_server = data_utils.rand_uuid()
-        new_name = data_utils.rand_name('server') + '_updated'
+        new_name = data_utils.rand_name(
+            self.__class__.__name__ + '-server') + '_updated'
 
         self.assertRaises(lib_exc.NotFound, self.client.update_server,
                           nonexistent_server, name=new_name)
@@ -271,16 +290,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 +310,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
@@ -321,7 +322,7 @@
         # Pass a server ID that exceeds length limit to delete server
 
         self.assertRaises(lib_exc.NotFound, self.client.delete_server,
-                          sys.maxint + 1)
+                          sys.maxsize + 1)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('c5fa6041-80cd-483b-aa6d-4e45f19d093c')
@@ -474,18 +475,7 @@
     @test.attr(type=['negative'])
     def test_shelve_shelved_server(self):
         # shelve a shelved server.
-        self.client.shelve_server(self.server_id)
-
-        offload_time = CONF.compute.shelved_offload_time
-        if offload_time >= 0:
-            waiters.wait_for_server_status(self.client,
-                                           self.server_id,
-                                           'SHELVED_OFFLOADED',
-                                           extra_timeout=offload_time)
-        else:
-            waiters.wait_for_server_status(self.client,
-                                           self.server_id,
-                                           'SHELVED')
+        compute.shelve_server(self.client, self.server_id)
 
         server = self.client.show_server(self.server_id)['server']
         image_name = server['name'] + '-shelved'
@@ -519,3 +509,75 @@
         self.assertRaises(lib_exc.Conflict,
                           self.client.unshelve_server,
                           self.server_id)
+
+    @test.attr(type=['negative'])
+    @test.idempotent_id('74085be3-a370-4ca2-bc51-2d0e10e0f573')
+    @test.services('volume', 'image')
+    def test_create_server_from_non_bootable_volume(self):
+        # Create a volume
+        volume = self.create_volume()
+
+        # Update volume bootable status to false
+        self.volumes_client.set_bootable_volume(volume['id'],
+                                                bootable=False)
+
+        # Verify bootable flag was updated
+        nonbootable_vol = self.volumes_client.show_volume(
+            volume['id'])['volume']
+        self.assertEqual('false', nonbootable_vol['bootable'])
+
+        # Block device mapping
+        bd_map = [{'boot_index': '0',
+                   'uuid': volume['id'],
+                   'source_type': 'volume',
+                   'destination_type': 'volume',
+                   'delete_on_termination': False}]
+
+        # Try creating a server from non-bootable volume
+        self.assertRaises(lib_exc.BadRequest,
+                          self.create_test_server,
+                          image_id='',
+                          wait_until='ACTIVE',
+                          block_device_mapping_v2=bd_map)
+
+
+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 7aa6d34..08c34d3 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
-from tempest_lib import decorators
+import testtools
 
 from tempest.api.compute import base
 from tempest import config
+from tempest.lib import exceptions
 from tempest import test
 
 CONF = config.CONF
@@ -39,23 +40,28 @@
     @classmethod
     def resource_setup(cls):
         super(VirtualInterfacesTestJSON, cls).resource_setup()
-        server = cls.create_test_server(wait_until='ACTIVE')
-        cls.server_id = server['id']
+        cls.server = cls.create_test_server(wait_until='ACTIVE')
 
-    @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/servers/test_virtual_interfaces_negative.py b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
index 577a673..912b0a1 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces_negative.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
@@ -13,11 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -40,7 +38,7 @@
     def test_list_virtual_interfaces_invalid_server_id(self):
         # Negative test: Should not be able to GET virtual interfaces
         # for an invalid server_id
-        invalid_server_id = str(uuid.uuid4())
+        invalid_server_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound,
                           self.client.list_virtual_interfaces,
                           invalid_server_id)
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
deleted file mode 100644
index bf4396d..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_lib import exceptions as lib_exc
-
-from tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest import config
-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_extensions.py b/tempest/api/compute/test_extensions.py
index 6e57aff..d171cd5 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -42,7 +42,7 @@
             raise self.skipException('There are not any extensions configured')
         # Log extensions list
         extension_list = map(lambda x: x['alias'], extensions)
-        LOG.debug("Nova extensions: %s" % ','.join(extension_list))
+        LOG.debug("Nova extensions: %s", ','.join(extension_list))
 
     @test.idempotent_id('05762f39-bdfa-4cdb-9b46-b78f8e78e2fd')
     @test.requires_ext(extension='os-consoles', service='compute')
diff --git a/tempest/api/compute/test_live_block_migration_negative.py b/tempest/api/compute/test_live_block_migration_negative.py
index 2cd85f2..7853962 100644
--- a/tempest/api/compute/test_live_block_migration_negative.py
+++ b/tempest/api/compute/test_live_block_migration_negative.py
@@ -13,20 +13,17 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.compute 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 LiveBlockMigrationNegativeTestJSON(base.BaseV2ComputeAdminTest):
-    _host_key = 'OS-EXT-SRV-ATTR:host'
-
     @classmethod
     def skip_checks(cls):
         super(LiveBlockMigrationNegativeTestJSON, cls).skip_checks()
@@ -36,15 +33,13 @@
     @classmethod
     def setup_clients(cls):
         super(LiveBlockMigrationNegativeTestJSON, cls).setup_clients()
-        cls.admin_hosts_client = cls.os_adm.hosts_client
         cls.admin_servers_client = cls.os_adm.servers_client
 
     def _migrate_server_to(self, server_id, dest_host):
         bmflm = CONF.compute_feature_enabled.block_migration_for_live_migration
-        body = self.admin_servers_client.live_migrate_server(
+        self.admin_servers_client.live_migrate_server(
             server_id, host=dest_host, block_migration=bmflm,
             disk_over_commit=False)
-        return body
 
     @test.attr(type=['negative'])
     @test.idempotent_id('7fb7856e-ae92-44c9-861a-af62d7830bcb')
@@ -52,9 +47,8 @@
         # Migrating to an invalid host should not change the status
         target_host = data_utils.rand_name('host')
         server = self.create_test_server(wait_until="ACTIVE")
-        server_id = server['id']
 
         self.assertRaises(lib_exc.BadRequest, self._migrate_server_to,
-                          server_id, target_host)
-        waiters.wait_for_server_status(self.servers_client, server_id,
+                          server['id'], target_host)
+        waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'ACTIVE')
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 43f4c97..b9e0c35 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -48,7 +48,8 @@
                                      'fixed_ips', 'key_pairs',
                                      'injected_file_path_bytes',
                                      'instances', 'security_group_rules',
-                                     'cores', 'security_groups'))
+                                     'cores', 'security_groups',
+                                     'server_group_members', 'server_groups'))
 
     @test.idempotent_id('f1ef0a97-dbbb-4cca-adc5-c9fbc4f76107')
     def test_get_quotas(self):
@@ -79,8 +80,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..fa465af 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -13,22 +13,24 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log as logging
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import compute
 from tempest.common.utils.linux import remote_client
 from tempest.common import waiters
 from tempest import config
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
 
+LOG = logging.getLogger(__name__)
+
 
 class AttachVolumeTestJSON(base.BaseV2ComputeTest):
-
-    def __init__(self, *args, **kwargs):
-        super(AttachVolumeTestJSON, self).__init__(*args, **kwargs)
-        self.attachment = None
+    max_microversion = '2.19'
 
     @classmethod
     def skip_checks(cls):
@@ -45,108 +47,242 @@
     @classmethod
     def resource_setup(cls):
         cls.set_validation_resources()
-
         super(AttachVolumeTestJSON, cls).resource_setup()
         cls.device = CONF.compute.volume_device_name
 
-    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')
-
-    def _delete_volume(self):
-        # Delete the created Volumes
-        if self.volume:
-            self.volumes_client.delete_volume(self.volume['id'])
-            self.volumes_client.wait_for_resource_deletion(self.volume['id'])
-            self.volume = None
-
-    def _create_and_attach(self):
+    def _create_server(self):
         # Start a server and wait for it to become ready
-        self.admin_pass = self.image_ssh_password
-        self.server = self.create_test_server(
+        server = self.create_test_server(
             validatable=True,
             wait_until='ACTIVE',
-            adminPass=self.admin_pass)
-
+            adminPass=self.image_ssh_password)
         # Record addresses so that we can ssh later
-        self.server['addresses'] = self.servers_client.list_addresses(
-            self.server['id'])['addresses']
+        server['addresses'] = self.servers_client.list_addresses(
+            server['id'])['addresses']
+        return server
 
-        # Create a volume and wait for it to become ready
-        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')
+    def _detach_volume(self, server_id, volume_id):
+        try:
+            self.servers_client.detach_volume(server_id, volume_id)
+            waiters.wait_for_volume_status(self.volumes_client,
+                                           volume_id, 'available')
+        except lib_exc.NotFound:
+            LOG.warning("Unable to detach volume %s from server %s "
+                        "possibly it was already detached", volume_id,
+                        server_id)
 
+    def _attach_volume(self, server_id, volume_id, device=None):
         # 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')
+        kwargs = {'volumeId': volume_id}
+        if device:
+            kwargs.update({'device': '/dev/%s' % device})
+        attachment = self.servers_client.attach_volume(
+            server_id, **kwargs)['volumeAttachment']
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume_id, 'in-use')
+        self.addCleanup(self._detach_volume, server_id,
+                        volume_id)
 
-        self.addCleanup(self._detach, self.server['id'], self.volume['id'])
+        return attachment
 
     @test.idempotent_id('52e9045a-e90d-4c0d-9087-79d657faffff')
-    @testtools.skipUnless(CONF.validation.run_validation,
-                          'SSH required for this test')
     def test_attach_detach_volume(self):
         # Stop and Start a server with an attached volume, ensuring that
         # the volume remains attached.
-        self._create_and_attach()
+        server = self._create_server()
+        volume = self.create_volume()
+        attachment = self._attach_volume(server['id'], volume['id'],
+                                         device=self.device)
 
-        self.servers_client.stop_server(self.server['id'])
-        waiters.wait_for_server_status(self.servers_client, self.server['id'],
+        self.servers_client.stop_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'SHUTOFF')
 
-        self.servers_client.start_server(self.server['id'])
-        waiters.wait_for_server_status(self.servers_client, self.server['id'],
+        self.servers_client.start_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'ACTIVE')
 
-        linux_client = remote_client.RemoteClient(
-            self.get_server_ip(self.server),
-            self.image_ssh_user,
-            self.admin_pass,
-            self.validation_resources['keypair']['private_key'])
+        if CONF.validation.run_validation:
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(server),
+                self.image_ssh_user,
+                self.image_ssh_password,
+                self.validation_resources['keypair']['private_key'],
+                server=server,
+                servers_client=self.servers_client)
 
-        partitions = linux_client.get_partitions()
-        self.assertIn(self.device, partitions)
+            disks = linux_client.get_disks()
+            device_name_to_match = '\n' + self.device + ' '
+            self.assertIn(device_name_to_match, disks)
 
-        self._detach(self.server['id'], self.volume['id'])
-        self.attachment = None
-        self.servers_client.stop_server(self.server['id'])
-        waiters.wait_for_server_status(self.servers_client, self.server['id'],
+        self._detach_volume(server['id'], attachment['volumeId'])
+        self.servers_client.stop_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'SHUTOFF')
 
-        self.servers_client.start_server(self.server['id'])
-        waiters.wait_for_server_status(self.servers_client, self.server['id'],
+        self.servers_client.start_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'ACTIVE')
 
-        linux_client = remote_client.RemoteClient(
-            self.get_server_ip(self.server),
-            self.image_ssh_user,
-            self.admin_pass,
-            self.validation_resources['keypair']['private_key'])
+        if CONF.validation.run_validation:
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(server),
+                self.image_ssh_user,
+                self.image_ssh_password,
+                self.validation_resources['keypair']['private_key'],
+                server=server,
+                servers_client=self.servers_client)
 
-        partitions = linux_client.get_partitions()
-        self.assertNotIn(self.device, partitions)
+            disks = linux_client.get_disks()
+            self.assertNotIn(device_name_to_match, disks)
 
     @test.idempotent_id('7fa563fe-f0f7-43eb-9e22-a1ece036b513')
     def test_list_get_volume_attachments(self):
-        # Create Server, Volume and attach that Volume to Server
-        self._create_and_attach()
-        # List Volume attachment of the server
+        # List volume attachment of the server
+        server = self._create_server()
+        volume = self.create_volume()
+        attachment = self._attach_volume(server['id'], volume['id'],
+                                         device=self.device)
         body = self.servers_client.list_volume_attachments(
-            self.server['id'])['volumeAttachments']
+            server['id'])['volumeAttachments']
         self.assertEqual(1, len(body))
-        self.assertIn(self.attachment, body)
+        self.assertIn(attachment, body)
 
-        # Get Volume attachment of the server
+        # Get volume attachment of the server
         body = self.servers_client.show_volume_attachment(
-            self.server['id'],
-            self.attachment['id'])['volumeAttachment']
-        self.assertEqual(self.server['id'], body['serverId'])
-        self.assertEqual(self.volume['id'], body['volumeId'])
-        self.assertEqual(self.attachment['id'], body['id'])
+            server['id'],
+            attachment['id'])['volumeAttachment']
+        self.assertEqual(server['id'], body['serverId'])
+        self.assertEqual(volume['id'], body['volumeId'])
+        self.assertEqual(attachment['id'], body['id'])
+
+    @test.idempotent_id('757d488b-a951-4bc7-b3cd-f417028da08a')
+    def test_list_get_two_volume_attachments(self):
+        # NOTE: This test is using the volume device auto-assignment
+        # without specifying the device ("/dev/sdb", etc). The feature
+        # is supported since Nova Liberty release or later. So this should
+        # be skipped on older clouds.
+        server = self._create_server()
+        volume_1st = self.create_volume()
+        volume_2nd = self.create_volume()
+        attachment_1st = self._attach_volume(server['id'], volume_1st['id'])
+        attachment_2nd = self._attach_volume(server['id'], volume_2nd['id'])
+
+        body = self.servers_client.list_volume_attachments(
+            server['id'])['volumeAttachments']
+        self.assertEqual(2, len(body))
+
+        body = self.servers_client.show_volume_attachment(
+            server['id'],
+            attachment_1st['id'])['volumeAttachment']
+        self.assertEqual(server['id'], body['serverId'])
+        self.assertEqual(attachment_1st['volumeId'], body['volumeId'])
+        self.assertEqual(attachment_1st['id'], body['id'])
+
+        body = self.servers_client.show_volume_attachment(
+            server['id'],
+            attachment_2nd['id'])['volumeAttachment']
+        self.assertEqual(server['id'], body['serverId'])
+        self.assertEqual(attachment_2nd['volumeId'], body['volumeId'])
+        self.assertEqual(attachment_2nd['id'], body['id'])
+
+
+class AttachVolumeShelveTestJSON(AttachVolumeTestJSON):
+    """Testing volume with shelved instance.
+
+    This test checks the attaching and detaching volumes from
+    a shelved or shelved offload instance.
+    """
+
+    min_microversion = '2.20'
+    max_microversion = 'latest'
+
+    def _count_volumes(self, server):
+        # Count number of volumes on an instance
+        volumes = 0
+        if CONF.validation.run_validation:
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(server),
+                self.image_ssh_user,
+                self.image_ssh_password,
+                self.validation_resources['keypair']['private_key'],
+                server=server,
+                servers_client=self.servers_client)
+
+            command = 'grep -c -E [vs]d.$ /proc/partitions'
+            volumes = int(linux_client.exec_command(command).strip())
+        return volumes
+
+    def _shelve_server(self, server):
+        # NOTE(andreaf) If we are going to shelve a server, we should
+        # check first whether the server is ssh-able. Otherwise we
+        # won't be able to distinguish failures introduced by shelve
+        # from pre-existing ones. Also it's good to wait for cloud-init
+        # to be done and sshd server to be running before shelving to
+        # avoid breaking the VM
+        if CONF.validation.run_validation:
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(server),
+                self.image_ssh_user,
+                self.image_ssh_password,
+                self.validation_resources['keypair']['private_key'],
+                server=server,
+                servers_client=self.servers_client)
+            linux_client.validate_authentication()
+
+        # If validation went ok, or it was skipped, shelve the server
+        compute.shelve_server(self.servers_client, server['id'])
+
+    def _unshelve_server_and_check_volumes(self, server, number_of_volumes):
+        # Unshelve the instance and check that there are expected volumes
+        self.servers_client.unshelve_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client,
+                                       server['id'],
+                                       'ACTIVE')
+        if CONF.validation.run_validation:
+            counted_volumes = self._count_volumes(server)
+            self.assertEqual(number_of_volumes, counted_volumes)
+
+    @test.idempotent_id('13a940b6-3474-4c3c-b03f-29b89112bfee')
+    @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
+                          'Shelve is not available.')
+    def test_attach_volume_shelved_or_offload_server(self):
+        # Create server, count number of volumes on it, shelve
+        # server and attach pre-created volume to shelved server
+        server = self._create_server()
+        volume = self.create_volume()
+        num_vol = self._count_volumes(server)
+        self._shelve_server(server)
+        attachment = self._attach_volume(server['id'], volume['id'],
+                                         device=self.device)
+
+        # Unshelve the instance and check that attached volume exists
+        self._unshelve_server_and_check_volumes(server, num_vol + 1)
+
+        # Get volume attachment of the server
+        volume_attachment = self.servers_client.show_volume_attachment(
+            server['id'],
+            attachment['id'])['volumeAttachment']
+        self.assertEqual(server['id'], volume_attachment['serverId'])
+        self.assertEqual(attachment['id'], volume_attachment['id'])
+        # Check the mountpoint is not None after unshelve server even in
+        # case of shelved_offloaded.
+        self.assertIsNotNone(volume_attachment['device'])
+
+    @test.idempotent_id('b54e86dd-a070-49c4-9c07-59ae6dae15aa')
+    @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
+                          'Shelve is not available.')
+    def test_detach_volume_shelved_or_offload_server(self):
+        # Count number of volumes on instance, shelve
+        # server and attach pre-created volume to shelved server
+        server = self._create_server()
+        volume = self.create_volume()
+        num_vol = self._count_volumes(server)
+        self._shelve_server(server)
+        self._attach_volume(server['id'], volume['id'], device=self.device)
+        # Detach the volume
+        self._detach_volume(server['id'], volume['id'])
+
+        # Unshelve the instance and check that we have the expected number of
+        # volume(s)
+        self._unshelve_server_and_check_volumes(server, num_vol)
diff --git a/tempest/api/compute/volumes/test_attach_volume_negative.py b/tempest/api/compute/volumes/test_attach_volume_negative.py
new file mode 100644
index 0000000..1f18bfe
--- /dev/null
+++ b/tempest/api/compute/volumes/test_attach_volume_negative.py
@@ -0,0 +1,42 @@
+# 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 import base
+from tempest import config
+from tempest.lib import exceptions as lib_exc
+from tempest import test
+
+CONF = config.CONF
+
+
+class AttachVolumeNegativeTest(base.BaseV2ComputeTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(AttachVolumeNegativeTest, cls).skip_checks()
+        if not CONF.service_available.cinder:
+            skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
+
+    @test.related_bug('1630783', status_code=500)
+    @test.idempotent_id('a313b5cd-fbd0-49cc-94de-870e99f763c7')
+    def test_delete_attached_volume(self):
+        server = self.create_test_server(wait_until='ACTIVE')
+        volume = self.create_volume()
+
+        path = "/dev/%s" % CONF.compute.volume_device_name
+        self.attach_volume(server, volume, device=path)
+
+        self.assertRaises(lib_exc.BadRequest,
+                          self.delete_volume, volume['id'])
diff --git a/tempest/api/compute/volumes/test_volume_snapshots.py b/tempest/api/compute/volumes/test_volume_snapshots.py
index f42d153..01718cc 100644
--- a/tempest/api/compute/volumes/test_volume_snapshots.py
+++ b/tempest/api/compute/volumes/test_volume_snapshots.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest.common import waiters
@@ -39,15 +41,13 @@
         cls.snapshots_client = cls.snapshots_extensions_client
 
     @test.idempotent_id('cd4ec87d-7825-450d-8040-6e2068f2da8f')
+    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+                          'Cinder volume snapshots are disabled')
     def test_volume_snapshot_create_get_list_delete(self):
-        v_name = data_utils.rand_name('Volume')
-        volume = self.volumes_client.create_volume(
-            size=CONF.volume.volume_size,
-            display_name=v_name)['volume']
+        volume = self.create_volume()
         self.addCleanup(self.delete_volume, volume['id'])
-        waiters.wait_for_volume_status(self.volumes_client, volume['id'],
-                                       'available')
-        s_name = data_utils.rand_name('Snapshot')
+
+        s_name = data_utils.rand_name(self.__class__.__name__ + '-Snapshot')
         # Create snapshot
         snapshot = self.snapshots_client.create_snapshot(
             volume_id=volume['id'],
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index 6074054..7549d4a 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -42,21 +42,20 @@
     @test.idempotent_id('f10f25eb-9775-4d9d-9cbe-1cf54dae9d5f')
     def test_volume_create_get_delete(self):
         # CREATE, GET, DELETE Volume
-        volume = None
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
         # Create volume
         volume = self.client.create_volume(size=CONF.volume.volume_size,
                                            display_name=v_name,
                                            metadata=metadata)['volume']
-        self.addCleanup(self.delete_volume, volume['id'])
         self.assertIn('id', volume)
+        self.addCleanup(self.delete_volume, volume['id'])
         self.assertIn('displayName', volume)
         self.assertEqual(volume['displayName'], v_name,
                          "The created volume name is not equal "
                          "to the requested name")
-        self.assertTrue(volume['id'] is not None,
-                        "Field volume id is empty or not found.")
+        self.assertIsNotNone(volume['id'],
+                             "Field volume id is empty or not found.")
         # Wait for Volume status to become ACTIVE
         waiters.wait_for_volume_status(self.client, volume['id'], 'available')
         # GET Volume
@@ -66,6 +65,10 @@
                          fetched_volume['displayName'],
                          'The fetched Volume is different '
                          'from the created Volume')
+        self.assertEqual(CONF.volume.volume_size,
+                         fetched_volume['size'],
+                         'The fetched volume size is different '
+                         'from the created Volume')
         self.assertEqual(volume['id'],
                          fetched_volume['id'],
                          'The fetched Volume is different '
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index 990e429..82cc653 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -14,8 +14,6 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -27,7 +25,7 @@
     # ensure that the backing file for the volume group that Nova uses
     # has space for at least 3 1G volumes!
     # If you are running a Devstack environment, ensure that the
-    # VOLUME_BACKING_FILE_SIZE is atleast 4G in your localrc
+    # VOLUME_BACKING_FILE_SIZE is at least 4G in your localrc
 
     @classmethod
     def skip_checks(cls):
@@ -48,39 +46,11 @@
         cls.volume_list = []
         cls.volume_id_list = []
         for i in range(3):
-            v_name = data_utils.rand_name('volume')
             metadata = {'Type': 'work'}
-            try:
-                volume = cls.client.create_volume(size=CONF.volume.volume_size,
-                                                  display_name=v_name,
-                                                  metadata=metadata)['volume']
-                waiters.wait_for_volume_status(cls.client,
-                                               volume['id'], 'available')
-                volume = cls.client.show_volume(volume['id'])['volume']
-                cls.volume_list.append(volume)
-                cls.volume_id_list.append(volume['id'])
-            except Exception:
-                if cls.volume_list:
-                    # We could not create all the volumes, though we were able
-                    # to create *some* of the volumes. This is typically
-                    # because the backing file size of the volume group is
-                    # too small. So, here, we clean up whatever we did manage
-                    # to create and raise a SkipTest
-                    for volume in cls.volume_list:
-                        cls.delete_volume(volume['id'])
-                    msg = ("Failed to create ALL necessary volumes to run "
-                           "test. This typically means that the backing file "
-                           "size of the nova-volumes group is too small to "
-                           "create the 3 volumes needed by this test case")
-                    raise cls.skipException(msg)
-                raise
-
-    @classmethod
-    def resource_cleanup(cls):
-        # Delete the created Volumes
-        for volume in cls.volume_list:
-            cls.delete_volume(volume['id'])
-        super(VolumesTestJSON, cls).resource_cleanup()
+            volume = cls.create_volume(metadata=metadata)
+            volume = cls.client.show_volume(volume['id'])['volume']
+            cls.volume_list.append(volume)
+            cls.volume_id_list.append(volume['id'])
 
     @test.idempotent_id('bc2dd1a0-15af-48e5-9990-f2e75a48325d')
     def test_volume_list(self):
diff --git a/tempest/api/compute/volumes/test_volumes_negative.py b/tempest/api/compute/volumes/test_volumes_negative.py
index 01a0baf..0e1fef2 100644
--- a/tempest/api/compute/volumes/test_volumes_negative.py
+++ b/tempest/api/compute/volumes/test_volumes_negative.py
@@ -13,13 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 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
@@ -46,7 +43,7 @@
         # Creating a nonexistent volume id
         # Trying to GET a non existent volume
         self.assertRaises(lib_exc.NotFound, self.client.show_volume,
-                          str(uuid.uuid4()))
+                          data_utils.rand_uuid())
 
     @test.attr(type=['negative'])
     @test.idempotent_id('54a34226-d910-4b00-9ef8-8683e6c55846')
@@ -55,24 +52,24 @@
         # Creating nonexistent volume id
         # Trying to DELETE a non existent volume
         self.assertRaises(lib_exc.NotFound, self.client.delete_volume,
-                          str(uuid.uuid4()))
+                          data_utils.rand_uuid())
 
     @test.attr(type=['negative'])
     @test.idempotent_id('5125ae14-152b-40a7-b3c5-eae15e9022ef')
     def test_create_volume_with_invalid_size(self):
         # Negative: Should not be able to create volume with invalid size
         # in request
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
         self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
                           size='#$%', display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('131cb3a1-75cc-4d40-b4c3-1317f64719b0')
-    def test_create_volume_with_out_passing_size(self):
+    def test_create_volume_without_passing_size(self):
         # Negative: Should not be able to create volume without passing size
         # in request
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
         self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
                           size='', display_name=v_name, metadata=metadata)
@@ -81,19 +78,12 @@
     @test.idempotent_id('8cce995e-0a83-479a-b94d-e1e40b8a09d1')
     def test_create_volume_with_size_zero(self):
         # Negative: Should not be able to create volume with size zero
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
         self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
                           size='0', display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
-    @test.idempotent_id('f01904f2-e975-4915-98ce-cb5fa27bde4f')
-    def test_get_invalid_volume_id(self):
-        # Negative: Should not be able to get volume with invalid id
-        self.assertRaises(lib_exc.NotFound,
-                          self.client.show_volume, '#$%%&^&^')
-
-    @test.attr(type=['negative'])
     @test.idempotent_id('62bab09a-4c03-4617-8cca-8572bc94af9b')
     def test_get_volume_without_passing_volume_id(self):
         # Negative: Should not be able to get volume when empty ID is passed
@@ -105,7 +95,8 @@
         # Negative: Should not be able to delete volume when invalid ID is
         # passed
         self.assertRaises(lib_exc.NotFound,
-                          self.client.delete_volume, '!@#$%^&*()')
+                          self.client.delete_volume,
+                          data_utils.rand_name('invalid'))
 
     @test.attr(type=['negative'])
     @test.idempotent_id('0d1417c5-4ae8-4c2c-adc5-5f0b864253e5')
diff --git a/tempest/api/data_processing/__init__.py b/tempest/api/data_processing/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/data_processing/__init__.py
+++ /dev/null
diff --git a/tempest/api/data_processing/base.py b/tempest/api/data_processing/base.py
deleted file mode 100644
index 8424db0..0000000
--- a/tempest/api/data_processing/base.py
+++ /dev/null
@@ -1,446 +0,0 @@
-# Copyright (c) 2014 Mirantis Inc.
-#
-#    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 collections import OrderedDict
-import copy
-
-import six
-from tempest_lib import exceptions as lib_exc
-
-from tempest import config
-from tempest import exceptions
-import tempest.test
-
-
-CONF = config.CONF
-
-"""Default templates.
-There should always be at least a master1 and a worker1 node
-group template."""
-BASE_VANILLA_DESC = {
-    'NODES': {
-        'master1': {
-            'count': 1,
-            'node_processes': ['namenode', 'resourcemanager',
-                               'hiveserver']
-        },
-        'master2': {
-            'count': 1,
-            'node_processes': ['oozie', 'historyserver',
-                               'secondarynamenode']
-        },
-        'worker1': {
-            'count': 1,
-            'node_processes': ['datanode', 'nodemanager'],
-            'node_configs': {
-                'MapReduce': {
-                    'yarn.app.mapreduce.am.resource.mb': 256,
-                    'yarn.app.mapreduce.am.command-opts': '-Xmx256m'
-                },
-                'YARN': {
-                    'yarn.scheduler.minimum-allocation-mb': 256,
-                    'yarn.scheduler.maximum-allocation-mb': 1024,
-                    'yarn.nodemanager.vmem-check-enabled': False
-                }
-            }
-        }
-    },
-    'cluster_configs': {
-        'HDFS': {
-            'dfs.replication': 1
-        }
-    }
-}
-
-BASE_SPARK_DESC = {
-    'NODES': {
-        'master1': {
-            'count': 1,
-            'node_processes': ['namenode', 'master']
-        },
-        'worker1': {
-            'count': 1,
-            'node_processes': ['datanode', 'slave']
-        }
-    },
-    'cluster_configs': {
-        'HDFS': {
-            'dfs.replication': 1
-        }
-    }
-}
-
-BASE_CDH_DESC = {
-    'NODES': {
-        'master1': {
-            'count': 1,
-            'node_processes': ['CLOUDERA_MANAGER']
-        },
-        'master2': {
-            'count': 1,
-            'node_processes': ['HDFS_NAMENODE',
-                               'YARN_RESOURCEMANAGER']
-        },
-        'master3': {
-            'count': 1,
-            'node_processes': ['OOZIE_SERVER', 'YARN_JOBHISTORY',
-                               'HDFS_SECONDARYNAMENODE',
-                               'HIVE_METASTORE', 'HIVE_SERVER2']
-        },
-        'worker1': {
-            'count': 1,
-            'node_processes': ['YARN_NODEMANAGER', 'HDFS_DATANODE']
-        }
-    },
-    'cluster_configs': {
-        'HDFS': {
-            'dfs_replication': 1
-        }
-    }
-}
-
-
-DEFAULT_TEMPLATES = {
-    'vanilla': OrderedDict([
-        ('2.6.0', copy.deepcopy(BASE_VANILLA_DESC)),
-        ('2.7.1', copy.deepcopy(BASE_VANILLA_DESC)),
-        ('1.2.1', {
-            'NODES': {
-                'master1': {
-                    'count': 1,
-                    'node_processes': ['namenode', 'jobtracker']
-                },
-                'worker1': {
-                    'count': 1,
-                    'node_processes': ['datanode', 'tasktracker'],
-                    'node_configs': {
-                        'HDFS': {
-                            'Data Node Heap Size': 1024
-                        },
-                        'MapReduce': {
-                            'Task Tracker Heap Size': 1024
-                        }
-                    }
-                }
-            },
-            'cluster_configs': {
-                'HDFS': {
-                    'dfs.replication': 1
-                },
-                'MapReduce': {
-                    'mapred.map.tasks.speculative.execution': False,
-                    'mapred.child.java.opts': '-Xmx500m'
-                },
-                'general': {
-                    'Enable Swift': False
-                }
-            }
-        })
-    ]),
-    'hdp': OrderedDict([
-        ('2.0.6', {
-            'NODES': {
-                'master1': {
-                    'count': 1,
-                    'node_processes': ['NAMENODE', 'SECONDARY_NAMENODE',
-                                       'ZOOKEEPER_SERVER', 'AMBARI_SERVER',
-                                       'HISTORYSERVER', 'RESOURCEMANAGER',
-                                       'GANGLIA_SERVER', 'NAGIOS_SERVER',
-                                       'OOZIE_SERVER']
-                },
-                'worker1': {
-                    'count': 1,
-                    'node_processes': ['HDFS_CLIENT', 'DATANODE',
-                                       'YARN_CLIENT', 'ZOOKEEPER_CLIENT',
-                                       'MAPREDUCE2_CLIENT', 'NODEMANAGER',
-                                       'PIG', 'OOZIE_CLIENT']
-                }
-            },
-            'cluster_configs': {
-                'HDFS': {
-                    'dfs.replication': 1
-                }
-            }
-        })
-    ]),
-    'spark': OrderedDict([
-        ('1.0.0', copy.deepcopy(BASE_SPARK_DESC)),
-        ('1.3.1', copy.deepcopy(BASE_SPARK_DESC))
-    ]),
-    'cdh': OrderedDict([
-        ('5.4.0', copy.deepcopy(BASE_CDH_DESC)),
-        ('5.3.0', copy.deepcopy(BASE_CDH_DESC)),
-        ('5', copy.deepcopy(BASE_CDH_DESC))
-    ]),
-}
-
-
-class BaseDataProcessingTest(tempest.test.BaseTestCase):
-
-    credentials = ['primary']
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaseDataProcessingTest, cls).skip_checks()
-        if not CONF.service_available.sahara:
-            raise cls.skipException('Sahara support is required')
-        cls.default_plugin = cls._get_default_plugin()
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseDataProcessingTest, cls).setup_clients()
-        cls.client = cls.os.data_processing_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(BaseDataProcessingTest, cls).resource_setup()
-
-        cls.default_version = cls._get_default_version()
-        if cls.default_plugin is not None and cls.default_version is None:
-            raise exceptions.InvalidConfiguration(
-                message="No known Sahara plugin version was found")
-        cls.flavor_ref = CONF.compute.flavor_ref
-
-        # add lists for watched resources
-        cls._node_group_templates = []
-        cls._cluster_templates = []
-        cls._data_sources = []
-        cls._job_binary_internals = []
-        cls._job_binaries = []
-        cls._jobs = []
-
-    @classmethod
-    def resource_cleanup(cls):
-        cls.cleanup_resources(getattr(cls, '_cluster_templates', []),
-                              cls.client.delete_cluster_template)
-        cls.cleanup_resources(getattr(cls, '_node_group_templates', []),
-                              cls.client.delete_node_group_template)
-        cls.cleanup_resources(getattr(cls, '_jobs', []), cls.client.delete_job)
-        cls.cleanup_resources(getattr(cls, '_job_binaries', []),
-                              cls.client.delete_job_binary)
-        cls.cleanup_resources(getattr(cls, '_job_binary_internals', []),
-                              cls.client.delete_job_binary_internal)
-        cls.cleanup_resources(getattr(cls, '_data_sources', []),
-                              cls.client.delete_data_source)
-        super(BaseDataProcessingTest, cls).resource_cleanup()
-
-    @staticmethod
-    def cleanup_resources(resource_id_list, method):
-        for resource_id in resource_id_list:
-            try:
-                method(resource_id)
-            except lib_exc.NotFound:
-                # ignore errors while auto removing created resource
-                pass
-
-    @classmethod
-    def create_node_group_template(cls, name, plugin_name, hadoop_version,
-                                   node_processes, flavor_id,
-                                   node_configs=None, **kwargs):
-        """Creates watched node group template with specified params.
-
-        It supports passing additional params using kwargs and returns created
-        object. All resources created in this method will be automatically
-        removed in tearDownClass method.
-        """
-        resp_body = cls.client.create_node_group_template(name, plugin_name,
-                                                          hadoop_version,
-                                                          node_processes,
-                                                          flavor_id,
-                                                          node_configs,
-                                                          **kwargs)
-        resp_body = resp_body['node_group_template']
-        # store id of created node group template
-        cls._node_group_templates.append(resp_body['id'])
-
-        return resp_body
-
-    @classmethod
-    def create_cluster_template(cls, name, plugin_name, hadoop_version,
-                                node_groups, cluster_configs=None, **kwargs):
-        """Creates watched cluster template with specified params.
-
-        It supports passing additional params using kwargs and returns created
-        object. All resources created in this method will be automatically
-        removed in tearDownClass method.
-        """
-        resp_body = cls.client.create_cluster_template(name, plugin_name,
-                                                       hadoop_version,
-                                                       node_groups,
-                                                       cluster_configs,
-                                                       **kwargs)
-        resp_body = resp_body['cluster_template']
-        # store id of created cluster template
-        cls._cluster_templates.append(resp_body['id'])
-
-        return resp_body
-
-    @classmethod
-    def create_data_source(cls, name, type, url, **kwargs):
-        """Creates watched data source with specified params.
-
-        It supports passing additional params using kwargs and returns created
-        object. All resources created in this method will be automatically
-        removed in tearDownClass method.
-        """
-        resp_body = cls.client.create_data_source(name, type, url, **kwargs)
-        resp_body = resp_body['data_source']
-        # store id of created data source
-        cls._data_sources.append(resp_body['id'])
-
-        return resp_body
-
-    @classmethod
-    def create_job_binary_internal(cls, name, data):
-        """Creates watched job binary internal with specified params.
-
-        It returns created object. All resources created in this method will
-        be automatically removed in tearDownClass method.
-        """
-        resp_body = cls.client.create_job_binary_internal(name, data)
-        resp_body = resp_body['job_binary_internal']
-        # store id of created job binary internal
-        cls._job_binary_internals.append(resp_body['id'])
-
-        return resp_body
-
-    @classmethod
-    def create_job_binary(cls, name, url, extra=None, **kwargs):
-        """Creates watched job binary with specified params.
-
-        It supports passing additional params using kwargs and returns created
-        object. All resources created in this method will be automatically
-        removed in tearDownClass method.
-        """
-        resp_body = cls.client.create_job_binary(name, url, extra, **kwargs)
-        resp_body = resp_body['job_binary']
-        # store id of created job binary
-        cls._job_binaries.append(resp_body['id'])
-
-        return resp_body
-
-    @classmethod
-    def create_job(cls, name, job_type, mains, libs=None, **kwargs):
-        """Creates watched job with specified params.
-
-        It supports passing additional params using kwargs and returns created
-        object. All resources created in this method will be automatically
-        removed in tearDownClass method.
-        """
-        resp_body = cls.client.create_job(name,
-                                          job_type, mains, libs, **kwargs)
-        resp_body = resp_body['job']
-        # store id of created job
-        cls._jobs.append(resp_body['id'])
-
-        return resp_body
-
-    @classmethod
-    def _get_default_plugin(cls):
-        """Returns the default plugin used for testing."""
-        if len(CONF.data_processing_feature_enabled.plugins) == 0:
-            return None
-
-        for plugin in CONF.data_processing_feature_enabled.plugins:
-            if plugin in DEFAULT_TEMPLATES.keys():
-                break
-        else:
-            plugin = ''
-        return plugin
-
-    @classmethod
-    def _get_default_version(cls):
-        """Returns the default plugin version used for testing.
-
-        This is gathered separately from the plugin to allow
-        the usage of plugin name in skip_checks. This method is
-        rather invoked into resource_setup, which allows API calls
-        and exceptions.
-        """
-        if not cls.default_plugin:
-            return None
-        plugin = cls.client.get_plugin(cls.default_plugin)['plugin']
-
-        for version in DEFAULT_TEMPLATES[cls.default_plugin].keys():
-            if version in plugin['versions']:
-                break
-        else:
-            version = None
-
-        return version
-
-    @classmethod
-    def get_node_group_template(cls, nodegroup='worker1'):
-        """Returns a node group template for the default plugin."""
-        try:
-            plugin_data = (
-                DEFAULT_TEMPLATES[cls.default_plugin][cls.default_version]
-            )
-            nodegroup_data = plugin_data['NODES'][nodegroup]
-            node_group_template = {
-                'description': 'Test node group template',
-                'plugin_name': cls.default_plugin,
-                'hadoop_version': cls.default_version,
-                'node_processes': nodegroup_data['node_processes'],
-                'flavor_id': cls.flavor_ref,
-                'node_configs': nodegroup_data.get('node_configs', {}),
-            }
-            return node_group_template
-        except (IndexError, KeyError):
-            return None
-
-    @classmethod
-    def get_cluster_template(cls, node_group_template_ids=None):
-        """Returns a cluster template for the default plugin.
-
-        node_group_template_defined contains the type and ID of pre-defined
-        node group templates that have to be used in the cluster template
-        (instead of dynamically defining them with 'node_processes').
-        """
-        if node_group_template_ids is None:
-            node_group_template_ids = {}
-        try:
-            plugin_data = (
-                DEFAULT_TEMPLATES[cls.default_plugin][cls.default_version]
-            )
-
-            all_node_groups = []
-            for ng_name, ng_data in six.iteritems(plugin_data['NODES']):
-                node_group = {
-                    'name': '%s-node' % (ng_name),
-                    'flavor_id': cls.flavor_ref,
-                    'count': ng_data['count']
-                }
-                if ng_name in node_group_template_ids.keys():
-                    # node group already defined, use it
-                    node_group['node_group_template_id'] = (
-                        node_group_template_ids[ng_name]
-                    )
-                else:
-                    # node_processes list defined on-the-fly
-                    node_group['node_processes'] = ng_data['node_processes']
-                if 'node_configs' in ng_data:
-                    node_group['node_configs'] = ng_data['node_configs']
-                all_node_groups.append(node_group)
-
-            cluster_template = {
-                'description': 'Test cluster template',
-                'plugin_name': cls.default_plugin,
-                'hadoop_version': cls.default_version,
-                'cluster_configs': plugin_data.get('cluster_configs', {}),
-                'node_groups': all_node_groups,
-            }
-            return cluster_template
-        except (IndexError, KeyError):
-            return None
diff --git a/tempest/api/data_processing/test_cluster_templates.py b/tempest/api/data_processing/test_cluster_templates.py
deleted file mode 100644
index dfd8e27..0000000
--- a/tempest/api/data_processing/test_cluster_templates.py
+++ /dev/null
@@ -1,124 +0,0 @@
-# Copyright (c) 2014 Mirantis Inc.
-#
-#    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.data_processing import base as dp_base
-from tempest.common.utils import data_utils
-from tempest import exceptions
-from tempest import test
-
-
-class ClusterTemplateTest(dp_base.BaseDataProcessingTest):
-    # Link to the API documentation is http://docs.openstack.org/developer/
-    # sahara/restapi/rest_api_v1.0.html#cluster-templates
-
-    @classmethod
-    def skip_checks(cls):
-        super(ClusterTemplateTest, cls).skip_checks()
-        if cls.default_plugin is None:
-            raise cls.skipException("No Sahara plugins configured")
-
-    @classmethod
-    def resource_setup(cls):
-        super(ClusterTemplateTest, cls).resource_setup()
-
-        # pre-define a node group templates
-        node_group_template_w = cls.get_node_group_template('worker1')
-        if node_group_template_w is None:
-            raise exceptions.InvalidConfiguration(
-                message="No known Sahara plugin was found")
-
-        node_group_template_w['name'] = data_utils.rand_name(
-            'sahara-ng-template')
-        resp_body = cls.create_node_group_template(**node_group_template_w)
-        node_group_template_id = resp_body['id']
-        configured_node_group_templates = {'worker1': node_group_template_id}
-
-        cls.full_cluster_template = cls.get_cluster_template(
-            configured_node_group_templates)
-
-        # create cls.cluster_template variable to use for comparison to cluster
-        # template response body. The 'node_groups' field in the response body
-        # has some extra info that post body does not have. The 'node_groups'
-        # field in the response body is something like this
-        #
-        #   'node_groups': [
-        #       {
-        #           'count': 3,
-        #           'name': 'worker-node',
-        #           'volume_mount_prefix': '/volumes/disk',
-        #           'created_at': '2014-05-21 14:31:37',
-        #           'updated_at': None,
-        #           'floating_ip_pool': None,
-        #           ...
-        #       },
-        #       ...
-        #   ]
-        cls.cluster_template = cls.full_cluster_template.copy()
-        del cls.cluster_template['node_groups']
-
-    def _create_cluster_template(self, template_name=None):
-        """Creates Cluster Template with optional name specified.
-
-        It creates template, ensures template name and response body.
-        Returns id and name of created template.
-        """
-        if not template_name:
-            # generate random name if it's not specified
-            template_name = data_utils.rand_name('sahara-cluster-template')
-
-        # create cluster template
-        resp_body = self.create_cluster_template(template_name,
-                                                 **self.full_cluster_template)
-
-        # ensure that template created successfully
-        self.assertEqual(template_name, resp_body['name'])
-        self.assertDictContainsSubset(self.cluster_template, resp_body)
-
-        return resp_body['id'], template_name
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('3525f1f1-3f9c-407d-891a-a996237e728b')
-    def test_cluster_template_create(self):
-        self._create_cluster_template()
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('7a161882-e430-4840-a1c6-1d928201fab2')
-    def test_cluster_template_list(self):
-        template_info = self._create_cluster_template()
-
-        # check for cluster template in list
-        templates = self.client.list_cluster_templates()['cluster_templates']
-        templates_info = [(template['id'], template['name'])
-                          for template in templates]
-        self.assertIn(template_info, templates_info)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('2b75fe22-f731-4b0f-84f1-89ab25f86637')
-    def test_cluster_template_get(self):
-        template_id, template_name = self._create_cluster_template()
-
-        # check cluster template fetch by id
-        template = self.client.get_cluster_template(template_id)
-        template = template['cluster_template']
-        self.assertEqual(template_name, template['name'])
-        self.assertDictContainsSubset(self.cluster_template, template)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('ff1fd989-171c-4dd7-91fd-9fbc71b09675')
-    def test_cluster_template_delete(self):
-        template_id, _ = self._create_cluster_template()
-
-        # delete the cluster template by id
-        self.client.delete_cluster_template(template_id)
-        # TODO(ylobankov): check that cluster template is really deleted
diff --git a/tempest/api/data_processing/test_data_sources.py b/tempest/api/data_processing/test_data_sources.py
deleted file mode 100644
index 67d09a0..0000000
--- a/tempest/api/data_processing/test_data_sources.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# Copyright (c) 2014 Mirantis Inc.
-#
-#    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.data_processing import base as dp_base
-from tempest.common.utils import data_utils
-from tempest import test
-
-
-class DataSourceTest(dp_base.BaseDataProcessingTest):
-    @classmethod
-    def resource_setup(cls):
-        super(DataSourceTest, cls).resource_setup()
-        cls.swift_data_source_with_creds = {
-            'url': 'swift://sahara-container.sahara/input-source',
-            'description': 'Test data source',
-            'credentials': {
-                'user': cls.os.credentials.username,
-                'password': cls.os.credentials.password
-            },
-            'type': 'swift'
-        }
-        cls.swift_data_source = cls.swift_data_source_with_creds.copy()
-        del cls.swift_data_source['credentials']
-
-        cls.local_hdfs_data_source = {
-            'url': 'input-source',
-            'description': 'Test data source',
-            'type': 'hdfs'
-        }
-
-        cls.external_hdfs_data_source = {
-            'url': 'hdfs://172.18.168.2:8020/usr/hadoop/input-source',
-            'description': 'Test data source',
-            'type': 'hdfs'
-        }
-
-    def _create_data_source(self, source_body, source_name=None):
-        """Creates Data Source with optional name specified.
-
-        It creates a link to input-source file (it may not exist), ensures
-        source name and response body. Returns id and name of created source.
-        """
-        if not source_name:
-            # generate random name if it's not specified
-            source_name = data_utils.rand_name('sahara-data-source')
-
-        # create data source
-        resp_body = self.create_data_source(source_name, **source_body)
-
-        # ensure that source created successfully
-        self.assertEqual(source_name, resp_body['name'])
-        if source_body['type'] == 'swift':
-            source_body = self.swift_data_source
-        self.assertDictContainsSubset(source_body, resp_body)
-
-        return resp_body['id'], source_name
-
-    def _list_data_sources(self, source_info):
-        # check for data source in list
-        sources = self.client.list_data_sources()['data_sources']
-        sources_info = [(source['id'], source['name']) for source in sources]
-        self.assertIn(source_info, sources_info)
-
-    def _get_data_source(self, source_id, source_name, source_body):
-        # check data source fetch by id
-        source = self.client.get_data_source(source_id)['data_source']
-        self.assertEqual(source_name, source['name'])
-        self.assertDictContainsSubset(source_body, source)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('9e0e836d-c372-4fca-91b7-b66c3e9646c8')
-    def test_swift_data_source_create(self):
-        self._create_data_source(self.swift_data_source_with_creds)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('3cb87a4a-0534-4b97-9edc-8bbc822b68a0')
-    def test_swift_data_source_list(self):
-        source_info = (
-            self._create_data_source(self.swift_data_source_with_creds))
-        self._list_data_sources(source_info)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('fc07409b-6477-4cb3-9168-e633c46b227f')
-    def test_swift_data_source_get(self):
-        source_id, source_name = (
-            self._create_data_source(self.swift_data_source_with_creds))
-        self._get_data_source(source_id, source_name, self.swift_data_source)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('df53669c-0cd1-4cf7-b408-4cf215d8beb8')
-    def test_swift_data_source_delete(self):
-        source_id, _ = (
-            self._create_data_source(self.swift_data_source_with_creds))
-
-        # delete the data source by id
-        self.client.delete_data_source(source_id)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('88505d52-db01-4229-8f1d-a1137da5fe2d')
-    def test_local_hdfs_data_source_create(self):
-        self._create_data_source(self.local_hdfs_data_source)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('81d7d42a-d7f6-4d9b-b38c-0801a4dfe3c2')
-    def test_local_hdfs_data_source_list(self):
-        source_info = self._create_data_source(self.local_hdfs_data_source)
-        self._list_data_sources(source_info)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('ec0144c6-db1e-4169-bb06-7abae14a8443')
-    def test_local_hdfs_data_source_get(self):
-        source_id, source_name = (
-            self._create_data_source(self.local_hdfs_data_source))
-        self._get_data_source(
-            source_id, source_name, self.local_hdfs_data_source)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('e398308b-4230-4f86-ba10-9b0b60a59c8d')
-    def test_local_hdfs_data_source_delete(self):
-        source_id, _ = self._create_data_source(self.local_hdfs_data_source)
-
-        # delete the data source by id
-        self.client.delete_data_source(source_id)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('bfd91128-e642-4d95-a973-3e536962180c')
-    def test_external_hdfs_data_source_create(self):
-        self._create_data_source(self.external_hdfs_data_source)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('92e2be72-f7ab-499d-ae01-fb9943c90d8e')
-    def test_external_hdfs_data_source_list(self):
-        source_info = self._create_data_source(self.external_hdfs_data_source)
-        self._list_data_sources(source_info)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('a31edb1b-6bc6-4f42-871f-70cd243184ac')
-    def test_external_hdfs_data_source_get(self):
-        source_id, source_name = (
-            self._create_data_source(self.external_hdfs_data_source))
-        self._get_data_source(
-            source_id, source_name, self.external_hdfs_data_source)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('295924cd-a085-4b45-aea8-0707cdb2da7e')
-    def test_external_hdfs_data_source_delete(self):
-        source_id, _ = self._create_data_source(self.external_hdfs_data_source)
-
-        # delete the data source by id
-        self.client.delete_data_source(source_id)
diff --git a/tempest/api/data_processing/test_job_binaries.py b/tempest/api/data_processing/test_job_binaries.py
deleted file mode 100644
index a47ddbc..0000000
--- a/tempest/api/data_processing/test_job_binaries.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# Copyright (c) 2014 Mirantis Inc.
-#
-#    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.data_processing import base as dp_base
-from tempest.common.utils import data_utils
-from tempest import test
-
-
-class JobBinaryTest(dp_base.BaseDataProcessingTest):
-    # Link to the API documentation is http://docs.openstack.org/developer/
-    # sahara/restapi/rest_api_v1.1_EDP.html#job-binaries
-
-    @classmethod
-    def resource_setup(cls):
-        super(JobBinaryTest, cls).resource_setup()
-        cls.swift_job_binary_with_extra = {
-            'url': 'swift://sahara-container.sahara/example.jar',
-            'description': 'Test job binary',
-            'extra': {
-                'user': cls.os.credentials.username,
-                'password': cls.os.credentials.password
-            }
-        }
-        # Create extra cls.swift_job_binary variable to use for comparison to
-        # job binary response body because response body has no 'extra' field.
-        cls.swift_job_binary = cls.swift_job_binary_with_extra.copy()
-        del cls.swift_job_binary['extra']
-
-        name = data_utils.rand_name('sahara-internal-job-binary')
-        cls.job_binary_data = 'Some script may be data'
-        job_binary_internal = (
-            cls.create_job_binary_internal(name, cls.job_binary_data))
-        cls.internal_db_job_binary = {
-            'url': 'internal-db://%s' % job_binary_internal['id'],
-            'description': 'Test job binary',
-        }
-
-    def _create_job_binary(self, binary_body, binary_name=None):
-        """Creates Job Binary with optional name specified.
-
-        It creates a link to data (jar, pig files, etc.), ensures job binary
-        name and response body. Returns id and name of created job binary.
-        Data may not exist when using Swift as data storage.
-        In other cases data must exist in storage.
-        """
-        if not binary_name:
-            # generate random name if it's not specified
-            binary_name = data_utils.rand_name('sahara-job-binary')
-
-        # create job binary
-        resp_body = self.create_job_binary(binary_name, **binary_body)
-
-        # ensure that binary created successfully
-        self.assertEqual(binary_name, resp_body['name'])
-        if 'swift' in binary_body['url']:
-            binary_body = self.swift_job_binary
-        self.assertDictContainsSubset(binary_body, resp_body)
-
-        return resp_body['id'], binary_name
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('c00d43f8-4360-45f8-b280-af1a201b12d3')
-    def test_swift_job_binary_create(self):
-        self._create_job_binary(self.swift_job_binary_with_extra)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('f8809352-e79d-4748-9359-ce1efce89f2a')
-    def test_swift_job_binary_list(self):
-        binary_info = self._create_job_binary(self.swift_job_binary_with_extra)
-
-        # check for job binary in list
-        binaries = self.client.list_job_binaries()['binaries']
-        binaries_info = [(binary['id'], binary['name']) for binary in binaries]
-        self.assertIn(binary_info, binaries_info)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('2d4a670f-e8f1-413c-b5ac-50c1bfe9e1b1')
-    def test_swift_job_binary_get(self):
-        binary_id, binary_name = (
-            self._create_job_binary(self.swift_job_binary_with_extra))
-
-        # check job binary fetch by id
-        binary = self.client.get_job_binary(binary_id)['job_binary']
-        self.assertEqual(binary_name, binary['name'])
-        self.assertDictContainsSubset(self.swift_job_binary, binary)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('9b0e8f38-04f3-4616-b399-cfa7eb2677ed')
-    def test_swift_job_binary_delete(self):
-        binary_id, _ = (
-            self._create_job_binary(self.swift_job_binary_with_extra))
-
-        # delete the job binary by id
-        self.client.delete_job_binary(binary_id)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('63662f6d-8291-407e-a6fc-f654522ebab6')
-    def test_internal_db_job_binary_create(self):
-        self._create_job_binary(self.internal_db_job_binary)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('38731e7b-6d9d-4ffa-8fd1-193c453e88b1')
-    def test_internal_db_job_binary_list(self):
-        binary_info = self._create_job_binary(self.internal_db_job_binary)
-
-        # check for job binary in list
-        binaries = self.client.list_job_binaries()['binaries']
-        binaries_info = [(binary['id'], binary['name']) for binary in binaries]
-        self.assertIn(binary_info, binaries_info)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('1b32199b-c3f5-43e1-a37a-3797e57b7066')
-    def test_internal_db_job_binary_get(self):
-        binary_id, binary_name = (
-            self._create_job_binary(self.internal_db_job_binary))
-
-        # check job binary fetch by id
-        binary = self.client.get_job_binary(binary_id)['job_binary']
-        self.assertEqual(binary_name, binary['name'])
-        self.assertDictContainsSubset(self.internal_db_job_binary, binary)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('3c42b0c3-3e03-46a5-adf0-df0650271a4e')
-    def test_internal_db_job_binary_delete(self):
-        binary_id, _ = self._create_job_binary(self.internal_db_job_binary)
-
-        # delete the job binary by id
-        self.client.delete_job_binary(binary_id)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('d5d47659-7e2c-4ea7-b292-5b3e559e8587')
-    def test_job_binary_get_data(self):
-        binary_id, _ = self._create_job_binary(self.internal_db_job_binary)
-
-        # get data of job binary by id
-        _, data = self.client.get_job_binary_data(binary_id)
-        self.assertEqual(data, self.job_binary_data)
diff --git a/tempest/api/data_processing/test_job_binary_internals.py b/tempest/api/data_processing/test_job_binary_internals.py
deleted file mode 100644
index b4f0769..0000000
--- a/tempest/api/data_processing/test_job_binary_internals.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# Copyright (c) 2014 Mirantis Inc.
-#
-#    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.data_processing import base as dp_base
-from tempest.common.utils import data_utils
-from tempest import test
-
-
-class JobBinaryInternalTest(dp_base.BaseDataProcessingTest):
-    # Link to the API documentation is http://docs.openstack.org/developer/
-    # sahara/restapi/rest_api_v1.1_EDP.html#job-binary-internals
-
-    @classmethod
-    def resource_setup(cls):
-        super(JobBinaryInternalTest, cls).resource_setup()
-        cls.job_binary_internal_data = 'Some script may be data'
-
-    def _create_job_binary_internal(self, binary_name=None):
-        """Creates Job Binary Internal with optional name specified.
-
-        It puts data into Sahara database and ensures job binary internal name.
-        Returns id and name of created job binary internal.
-        """
-        if not binary_name:
-            # generate random name if it's not specified
-            binary_name = data_utils.rand_name('sahara-job-binary-internal')
-
-        # create job binary internal
-        resp_body = (
-            self.create_job_binary_internal(binary_name,
-                                            self.job_binary_internal_data))
-
-        # ensure that job binary internal created successfully
-        self.assertEqual(binary_name, resp_body['name'])
-
-        return resp_body['id'], binary_name
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('249c4dc2-946f-4939-83e6-212ddb6ea0be')
-    def test_job_binary_internal_create(self):
-        self._create_job_binary_internal()
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('1e3c2ecd-5673-499d-babe-4fe2fcdf64ee')
-    def test_job_binary_internal_list(self):
-        binary_info = self._create_job_binary_internal()
-
-        # check for job binary internal in list
-        binaries = self.client.list_job_binary_internals()['binaries']
-        binaries_info = [(binary['id'], binary['name']) for binary in binaries]
-        self.assertIn(binary_info, binaries_info)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('a2046a53-386c-43ab-be35-df54b19db776')
-    def test_job_binary_internal_get(self):
-        binary_id, binary_name = self._create_job_binary_internal()
-
-        # check job binary internal fetch by id
-        binary = self.client.get_job_binary_internal(binary_id)
-        self.assertEqual(binary_name, binary['job_binary_internal']['name'])
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('b3568c33-4eed-40d5-aae4-6ff3b2ac58f5')
-    def test_job_binary_internal_delete(self):
-        binary_id, _ = self._create_job_binary_internal()
-
-        # delete the job binary internal by id
-        self.client.delete_job_binary_internal(binary_id)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('8871f2b0-5782-4d66-9bb9-6f95bcb839ea')
-    def test_job_binary_internal_get_data(self):
-        binary_id, _ = self._create_job_binary_internal()
-
-        # get data of job binary internal by id
-        _, data = self.client.get_job_binary_internal_data(binary_id)
-        self.assertEqual(data, self.job_binary_internal_data)
diff --git a/tempest/api/data_processing/test_jobs.py b/tempest/api/data_processing/test_jobs.py
deleted file mode 100644
index 8503320..0000000
--- a/tempest/api/data_processing/test_jobs.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# Copyright (c) 2014 Mirantis Inc.
-#
-#    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.data_processing import base as dp_base
-from tempest.common.utils import data_utils
-from tempest import test
-
-
-class JobTest(dp_base.BaseDataProcessingTest):
-    # NOTE: Link to the API documentation: http://docs.openstack.org/developer/
-    # sahara/restapi/rest_api_v1.1_EDP.html#jobs
-
-    @classmethod
-    def resource_setup(cls):
-        super(JobTest, cls).resource_setup()
-        # create job binary
-        job_binary = {
-            'name': data_utils.rand_name('sahara-job-binary'),
-            'url': 'swift://sahara-container.sahara/example.jar',
-            'description': 'Test job binary',
-            'extra': {
-                'user': cls.os.credentials.username,
-                'password': cls.os.credentials.password
-            }
-        }
-        resp_body = cls.create_job_binary(**job_binary)
-        job_binary_id = resp_body['id']
-
-        cls.job = {
-            'job_type': 'Pig',
-            'mains': [job_binary_id]
-        }
-
-    def _create_job(self, job_name=None):
-        """Creates Job with optional name specified.
-
-        It creates job and ensures job name. Returns id and name of created
-        job.
-        """
-        if not job_name:
-            # generate random name if it's not specified
-            job_name = data_utils.rand_name('sahara-job')
-
-        # create job
-        resp_body = self.create_job(job_name, **self.job)
-
-        # ensure that job created successfully
-        self.assertEqual(job_name, resp_body['name'])
-
-        return resp_body['id'], job_name
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('8cf785ca-adf4-473d-8281-fb9a5efa3073')
-    def test_job_create(self):
-        self._create_job()
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('41e253fe-b02a-41a0-b186-5ff1f0463ba3')
-    def test_job_list(self):
-        job_info = self._create_job()
-
-        # check for job in list
-        jobs = self.client.list_jobs()['jobs']
-        jobs_info = [(job['id'], job['name']) for job in jobs]
-        self.assertIn(job_info, jobs_info)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('3faf17fa-bc94-4a60-b1c3-79e53674c16c')
-    def test_job_get(self):
-        job_id, job_name = self._create_job()
-
-        # check job fetch by id
-        job = self.client.get_job(job_id)['job']
-        self.assertEqual(job_name, job['name'])
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('dff85e62-7dda-4ad8-b1ee-850adecb0c6e')
-    def test_job_delete(self):
-        job_id, _ = self._create_job()
-
-        # delete the job by id
-        self.client.delete_job(job_id)
diff --git a/tempest/api/data_processing/test_node_group_templates.py b/tempest/api/data_processing/test_node_group_templates.py
deleted file mode 100644
index 388bb58..0000000
--- a/tempest/api/data_processing/test_node_group_templates.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# Copyright (c) 2014 Mirantis Inc.
-#
-#    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.data_processing import base as dp_base
-from tempest.common.utils import data_utils
-from tempest import test
-
-
-class NodeGroupTemplateTest(dp_base.BaseDataProcessingTest):
-
-    @classmethod
-    def skip_checks(cls):
-        super(NodeGroupTemplateTest, cls).skip_checks()
-        if cls.default_plugin is None:
-            raise cls.skipException("No Sahara plugins configured")
-
-    @classmethod
-    def resource_setup(cls):
-        super(NodeGroupTemplateTest, cls).resource_setup()
-
-    def _create_node_group_template(self, template_name=None):
-        """Creates Node Group Template with optional name specified.
-
-        It creates template, ensures template name and response body.
-        Returns id and name of created template.
-        """
-        self.node_group_template = self.get_node_group_template()
-        self.assertIsNotNone(self.node_group_template,
-                             "No known Sahara plugin was found")
-
-        if not template_name:
-            # generate random name if it's not specified
-            template_name = data_utils.rand_name('sahara-ng-template')
-
-        # create node group template
-        resp_body = self.create_node_group_template(template_name,
-                                                    **self.node_group_template)
-
-        # ensure that template created successfully
-        self.assertEqual(template_name, resp_body['name'])
-        self.assertDictContainsSubset(self.node_group_template, resp_body)
-
-        return resp_body['id'], template_name
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('63164051-e46d-4387-9741-302ef4791cbd')
-    def test_node_group_template_create(self):
-        self._create_node_group_template()
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('eb39801d-2612-45e5-88b1-b5d70b329185')
-    def test_node_group_template_list(self):
-        template_info = self._create_node_group_template()
-
-        # check for node group template in list
-        templates = self.client.list_node_group_templates()
-        templates = templates['node_group_templates']
-        templates_info = [(template['id'], template['name'])
-                          for template in templates]
-        self.assertIn(template_info, templates_info)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('6ee31539-a708-466f-9c26-4093ce09a836')
-    def test_node_group_template_get(self):
-        template_id, template_name = self._create_node_group_template()
-
-        # check node group template fetch by id
-        template = self.client.get_node_group_template(template_id)
-        template = template['node_group_template']
-        self.assertEqual(template_name, template['name'])
-        self.assertDictContainsSubset(self.node_group_template, template)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('f4f5cb82-708d-4031-81c4-b0618a706a2f')
-    def test_node_group_template_delete(self):
-        template_id, _ = self._create_node_group_template()
-
-        # delete the node group template by id
-        self.client.delete_node_group_template(template_id)
diff --git a/tempest/api/data_processing/test_plugins.py b/tempest/api/data_processing/test_plugins.py
deleted file mode 100644
index 14594e4..0000000
--- a/tempest/api/data_processing/test_plugins.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright (c) 2014 Mirantis Inc.
-#
-#    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.data_processing import base as dp_base
-from tempest import config
-from tempest import test
-
-CONF = config.CONF
-
-
-class PluginsTest(dp_base.BaseDataProcessingTest):
-    def _list_all_plugin_names(self):
-        """Returns all enabled plugin names.
-
-        It ensures main plugins availability.
-        """
-        plugins = self.client.list_plugins()['plugins']
-        plugins_names = [plugin['name'] for plugin in plugins]
-        for enabled_plugin in CONF.data_processing_feature_enabled.plugins:
-            self.assertIn(enabled_plugin, plugins_names)
-
-        return plugins_names
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('01a005a3-426c-4c0b-9617-d09475403e09')
-    def test_plugin_list(self):
-        self._list_all_plugin_names()
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('53cf6487-2cfb-4a6f-8671-97c542c6e901')
-    def test_plugin_get(self):
-        for plugin_name in self._list_all_plugin_names():
-            plugin = self.client.get_plugin(plugin_name)['plugin']
-            self.assertEqual(plugin_name, plugin['name'])
-
-            for plugin_version in plugin['versions']:
-                detailed_plugin = self.client.get_plugin(plugin_name,
-                                                         plugin_version)
-                detailed_plugin = detailed_plugin['plugin']
-                self.assertEqual(plugin_name, detailed_plugin['name'])
-
-                # check that required image tags contains name and version
-                image_tags = detailed_plugin['required_image_tags']
-                self.assertIn(plugin_name, image_tags)
-                self.assertIn(plugin_version, image_tags)
diff --git a/tempest/api/database/__init__.py b/tempest/api/database/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/database/__init__.py
+++ /dev/null
diff --git a/tempest/api/database/base.py b/tempest/api/database/base.py
deleted file mode 100644
index 01e05db..0000000
--- a/tempest/api/database/base.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright 2014 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 tempest import config
-import tempest.test
-
-CONF = config.CONF
-
-
-class BaseDatabaseTest(tempest.test.BaseTestCase):
-    """Base test case class for all Database API tests."""
-
-    credentials = ['primary']
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaseDatabaseTest, cls).skip_checks()
-        if not CONF.service_available.trove:
-            skip_msg = ("%s skipped as trove is not available" % cls.__name__)
-            raise cls.skipException(skip_msg)
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseDatabaseTest, cls).setup_clients()
-        cls.database_flavors_client = cls.os.database_flavors_client
-        cls.os_flavors_client = cls.os.flavors_client
-        cls.database_limits_client = cls.os.database_limits_client
-        cls.database_versions_client = cls.os.database_versions_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(BaseDatabaseTest, cls).resource_setup()
-
-        cls.catalog_type = CONF.database.catalog_type
-        cls.db_flavor_ref = CONF.database.db_flavor_ref
-        cls.db_current_version = CONF.database.db_current_version
diff --git a/tempest/api/database/flavors/test_flavors.py b/tempest/api/database/flavors/test_flavors.py
deleted file mode 100644
index f75b867..0000000
--- a/tempest/api/database/flavors/test_flavors.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright 2014 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 tempest.api.database import base
-from tempest import test
-
-
-class DatabaseFlavorsTest(base.BaseDatabaseTest):
-
-    @classmethod
-    def setup_clients(cls):
-        super(DatabaseFlavorsTest, cls).setup_clients()
-        cls.client = cls.database_flavors_client
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('c94b825e-0132-4686-8049-8a4a2bc09525')
-    def test_get_db_flavor(self):
-        # The expected flavor details should be returned
-        flavor = (self.client.show_db_flavor(self.db_flavor_ref)
-                  ['flavor'])
-        self.assertEqual(self.db_flavor_ref, str(flavor['id']))
-        self.assertIn('ram', flavor)
-        self.assertIn('links', flavor)
-        self.assertIn('name', flavor)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('685025d6-0cec-4673-8a8d-995cb8e0d3bb')
-    def test_list_db_flavors(self):
-        flavor = (self.client.show_db_flavor(self.db_flavor_ref)
-                  ['flavor'])
-        # List of all flavors should contain the expected flavor
-        flavors = self.client.list_db_flavors()['flavors']
-        self.assertIn(flavor, flavors)
-
-    def _check_values(self, names, db_flavor, os_flavor, in_db=True):
-        for name in names:
-            self.assertIn(name, os_flavor)
-            if in_db:
-                self.assertIn(name, db_flavor)
-                self.assertEqual(str(db_flavor[name]), str(os_flavor[name]),
-                                 "DB flavor differs from OS on '%s' value"
-                                 % name)
-            else:
-                self.assertNotIn(name, db_flavor)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('afb2667f-4ec2-4925-bcb7-313fdcffb80d')
-    @test.services('compute')
-    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)
-                      ['flavors'])
-        self.assertEqual(len(os_flavors), len(db_flavors),
-                         "OS flavors %s do not match DB flavors %s" %
-                         (os_flavors, db_flavors))
-        for os_flavor in os_flavors:
-            db_flavor =\
-                self.client.show_db_flavor(os_flavor['id'])['flavor']
-            self._check_values(['id', 'name', 'ram'], db_flavor, os_flavor)
-            self._check_values(['disk', 'vcpus', 'swap'], db_flavor, os_flavor,
-                               in_db=False)
diff --git a/tempest/api/database/flavors/test_flavors_negative.py b/tempest/api/database/flavors/test_flavors_negative.py
deleted file mode 100644
index 3dee96f..0000000
--- a/tempest/api/database/flavors/test_flavors_negative.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2014 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 tempest_lib import exceptions as lib_exc
-
-from tempest.api.database import base
-from tempest import test
-
-
-class DatabaseFlavorsNegativeTest(base.BaseDatabaseTest):
-
-    @classmethod
-    def setup_clients(cls):
-        super(DatabaseFlavorsNegativeTest, cls).setup_clients()
-        cls.client = cls.database_flavors_client
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('f8e7b721-373f-4a64-8e9c-5327e975af3e')
-    def test_get_non_existent_db_flavor(self):
-        # flavor details are not returned for non-existent flavors
-        self.assertRaises(lib_exc.NotFound,
-                          self.client.show_db_flavor, -1)
diff --git a/tempest/api/database/limits/__init__.py b/tempest/api/database/limits/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/database/limits/__init__.py
+++ /dev/null
diff --git a/tempest/api/database/limits/test_limits.py b/tempest/api/database/limits/test_limits.py
deleted file mode 100644
index ee51b1d..0000000
--- a/tempest/api/database/limits/test_limits.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2014 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 tempest.api.database import base
-from tempest import test
-
-
-class DatabaseLimitsTest(base.BaseDatabaseTest):
-
-    @classmethod
-    def resource_setup(cls):
-        super(DatabaseLimitsTest, cls).resource_setup()
-        cls.client = cls.database_limits_client
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('73024538-f316-4829-b3e9-b459290e137a')
-    def test_absolute_limits(self):
-        # Test to verify if all absolute limit parameters are
-        # present when verb is ABSOLUTE
-        limits = self.client.list_db_limits()['limits']
-        expected_abs_limits = ['max_backups', 'max_volumes',
-                               'max_instances', 'verb']
-        absolute_limit = [l for l in limits
-                          if l['verb'] == 'ABSOLUTE']
-        self.assertEqual(1, len(absolute_limit), "One ABSOLUTE limit "
-                         "verb is allowed. Fetched %s"
-                         % len(absolute_limit))
-        actual_abs_limits = absolute_limit[0].keys()
-        missing_abs_limit = set(expected_abs_limits) - set(actual_abs_limits)
-        self.assertEmpty(missing_abs_limit,
-                         "Failed to find the following absolute limit(s)"
-                         " in a fetched list: %s" %
-                         ', '.join(str(a) for a in missing_abs_limit))
diff --git a/tempest/api/database/versions/__init__.py b/tempest/api/database/versions/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/database/versions/__init__.py
+++ /dev/null
diff --git a/tempest/api/database/versions/test_versions.py b/tempest/api/database/versions/test_versions.py
deleted file mode 100644
index ae568b1..0000000
--- a/tempest/api/database/versions/test_versions.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2014 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 tempest.api.database import base
-from tempest import test
-
-
-class DatabaseVersionsTest(base.BaseDatabaseTest):
-
-    @classmethod
-    def setup_clients(cls):
-        super(DatabaseVersionsTest, cls).setup_clients()
-        cls.client = cls.database_versions_client
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('6952cd77-90cd-4dca-bb60-8e2c797940cf')
-    def test_list_db_versions(self):
-        versions = self.client.list_db_versions()['versions']
-        self.assertTrue(len(versions) > 0, "No database versions found")
-        # List of all versions should contain the current version, and there
-        # should only be one 'current' version
-        current_versions = list()
-        for version in versions:
-            if 'CURRENT' == version['status']:
-                current_versions.append(version['id'])
-        self.assertEqual(1, len(current_versions))
-        self.assertIn(self.db_current_version, current_versions)
diff --git a/tempest/api/identity/admin/v2/test_endpoints.py b/tempest/api/identity/admin/v2/test_endpoints.py
index df75d0a..651a316 100644
--- a/tempest/api/identity/admin/v2/test_endpoints.py
+++ b/tempest/api/identity/admin/v2/test_endpoints.py
@@ -28,7 +28,8 @@
         s_type = data_utils.rand_name('type')
         s_description = data_utils.rand_name('description')
         cls.service_data = cls.services_client.create_service(
-            s_name, s_type, description=s_description)['OS-KSADM:service']
+            name=s_name, type=s_type,
+            description=s_description)['OS-KSADM:service']
         cls.service_id = cls.service_data['id']
         cls.service_ids.append(cls.service_id)
         # Create endpoints so as to use for LIST and GET test cases
@@ -37,8 +38,8 @@
             region = data_utils.rand_name('region')
             url = data_utils.rand_url()
             endpoint = cls.endpoints_client.create_endpoint(
-                cls.service_id,
-                region,
+                service_id=cls.service_id,
+                region=region,
                 publicurl=url,
                 adminurl=url,
                 internalurl=url)['endpoint']
@@ -70,8 +71,8 @@
         region = data_utils.rand_name('region')
         url = data_utils.rand_url()
         endpoint = self.endpoints_client.create_endpoint(
-            self.service_id,
-            region,
+            service_id=self.service_id,
+            region=region,
             publicurl=url,
             adminurl=url,
             internalurl=url)['endpoint']
diff --git a/tempest/api/identity/admin/v2/test_roles.py b/tempest/api/identity/admin/v2/test_roles.py
index 5847129..d284aac 100644
--- a/tempest/api/identity/admin/v2/test_roles.py
+++ b/tempest/api/identity/admin/v2/test_roles.py
@@ -13,10 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from six import moves
-
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest import test
 
 
@@ -25,17 +24,22 @@
     @classmethod
     def resource_setup(cls):
         super(RolesTestJSON, cls).resource_setup()
-        for _ in moves.xrange(5):
+        cls.roles = list()
+        for _ in range(5):
             role_name = data_utils.rand_name(name='role')
             role = cls.roles_client.create_role(name=role_name)['role']
-            cls.data.roles.append(role)
+            cls.roles.append(role)
+
+    @classmethod
+    def resource_cleanup(cls):
+        super(RolesTestJSON, cls).resource_cleanup()
+        for role in cls.roles:
+            cls.roles_client.delete_role(role['id'])
 
     def _get_role_params(self):
-        self.data.setup_test_user()
-        self.data.setup_test_role()
-        user = self.get_user_by_name(self.data.user['name'])
-        tenant = self.get_tenant_by_name(self.data.tenant['name'])
-        role = self.get_role_by_name(self.data.role['name'])
+        user = self.setup_test_user()
+        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
+        role = self.setup_test_role()
         return (user, tenant, role)
 
     def assert_role_in_role_list(self, role, roles):
@@ -49,15 +53,17 @@
     def test_list_roles(self):
         """Return a list of all roles."""
         body = self.roles_client.list_roles()['roles']
-        found = [role for role in body if role in self.data.roles]
+        found = [role for role in body if role in self.roles]
         self.assertTrue(any(found))
-        self.assertEqual(len(found), len(self.data.roles))
+        self.assertEqual(len(found), len(self.roles))
 
     @test.idempotent_id('c62d909d-6c21-48c0-ae40-0a0760e6db5e')
     def test_role_create_delete(self):
         """Role should be created, verified, and deleted."""
         role_name = data_utils.rand_name(name='role-test')
         body = self.roles_client.create_role(name=role_name)['role']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.roles_client.delete_role, body['id'])
         self.assertEqual(role_name, body['name'])
 
         body = self.roles_client.list_roles()['roles']
@@ -73,9 +79,9 @@
     @test.idempotent_id('db6870bd-a6ed-43be-a9b1-2f10a5c9994f')
     def test_get_role_by_id(self):
         """Get a role by its id."""
-        self.data.setup_test_role()
-        role_id = self.data.role['id']
-        role_name = self.data.role['name']
+        role = self.setup_test_role()
+        role_id = role['id']
+        role_name = role['name']
         body = self.roles_client.show_role(role_id)['role']
         self.assertEqual(role_id, body['id'])
         self.assertEqual(role_name, body['name'])
@@ -84,28 +90,30 @@
     def test_assign_user_role(self):
         """Assign a role to a user on a tenant."""
         (user, tenant, role) = self._get_role_params()
-        self.roles_client.assign_user_role(tenant['id'], user['id'],
-                                           role['id'])
-        roles = self.roles_client.list_user_roles(tenant['id'],
-                                                  user['id'])['roles']
+        self.roles_client.create_user_role_on_project(tenant['id'],
+                                                      user['id'],
+                                                      role['id'])
+        roles = self.roles_client.list_user_roles_on_project(
+            tenant['id'], user['id'])['roles']
         self.assert_role_in_role_list(role, roles)
 
     @test.idempotent_id('f0b9292c-d3ba-4082-aa6c-440489beef69')
     def test_remove_user_role(self):
         """Remove a role assigned to a user on a tenant."""
         (user, tenant, role) = self._get_role_params()
-        user_role = self.roles_client.assign_user_role(tenant['id'],
-                                                       user['id'],
-                                                       role['id'])['role']
-        self.roles_client.delete_user_role(tenant['id'], user['id'],
-                                           user_role['id'])
+        user_role = self.roles_client.create_user_role_on_project(
+            tenant['id'], user['id'], role['id'])['role']
+        self.roles_client.delete_role_from_user_on_project(tenant['id'],
+                                                           user['id'],
+                                                           user_role['id'])
 
     @test.idempotent_id('262e1e3e-ed71-4edd-a0e5-d64e83d66d05')
     def test_list_user_roles(self):
         """List roles assigned to a user on tenant."""
         (user, tenant, role) = self._get_role_params()
-        self.roles_client.assign_user_role(tenant['id'], user['id'],
-                                           role['id'])
-        roles = self.roles_client.list_user_roles(tenant['id'],
-                                                  user['id'])['roles']
+        self.roles_client.create_user_role_on_project(tenant['id'],
+                                                      user['id'],
+                                                      role['id'])
+        roles = self.roles_client.list_user_roles_on_project(
+            tenant['id'], user['id'])['roles']
         self.assert_role_in_role_list(role, roles)
diff --git a/tempest/api/identity/admin/v2/test_roles_negative.py b/tempest/api/identity/admin/v2/test_roles_negative.py
index 23a1958..7116913 100644
--- a/tempest/api/identity/admin/v2/test_roles_negative.py
+++ b/tempest/api/identity/admin/v2/test_roles_negative.py
@@ -13,23 +13,18 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
 class RolesNegativeTestJSON(base.BaseIdentityV2AdminTest):
 
     def _get_role_params(self):
-        self.data.setup_test_user()
-        self.data.setup_test_role()
-        user = self.get_user_by_name(self.data.user['name'])
-        tenant = self.get_tenant_by_name(self.data.tenant['name'])
-        role = self.get_role_by_name(self.data.role['name'])
+        user = self.setup_test_user()
+        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
+        role = self.setup_test_role()
         return (user, tenant, role)
 
     @test.attr(type=['negative'])
@@ -92,7 +87,7 @@
         # Non-administrator user should not be able to delete role
         role_name = data_utils.rand_name(name='role')
         body = self.roles_client.create_role(name=role_name)['role']
-        self.data.roles.append(body)
+        self.addCleanup(self.roles_client.delete_role, body['id'])
         role_id = body.get('id')
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_roles_client.delete_role, role_id)
@@ -103,7 +98,7 @@
         # Request to delete role without a valid token should fail
         role_name = data_utils.rand_name(name='role')
         body = self.roles_client.create_role(name=role_name)['role']
-        self.data.roles.append(body)
+        self.addCleanup(self.roles_client.delete_role, body['id'])
         role_id = body.get('id')
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
@@ -116,7 +111,7 @@
     @test.idempotent_id('38373691-8551-453a-b074-4260ad8298ef')
     def test_delete_role_non_existent(self):
         # Attempt to delete a non existent role should fail
-        non_existent_role = str(uuid.uuid4().hex)
+        non_existent_role = data_utils.rand_uuid_hex()
         self.assertRaises(lib_exc.NotFound, self.roles_client.delete_role,
                           non_existent_role)
 
@@ -126,9 +121,10 @@
         # Non-administrator user should not be authorized to
         # assign a role to user
         (user, tenant, role) = self._get_role_params()
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_roles_client.assign_user_role,
-                          tenant['id'], user['id'], role['id'])
+        self.assertRaises(
+            lib_exc.Forbidden,
+            self.non_admin_roles_client.create_user_role_on_project,
+            tenant['id'], user['id'], role['id'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('f0d2683c-5603-4aee-95d7-21420e87cfd8')
@@ -137,9 +133,10 @@
         (user, tenant, role) = self._get_role_params()
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
-        self.assertRaises(lib_exc.Unauthorized,
-                          self.roles_client.assign_user_role, tenant['id'],
-                          user['id'], role['id'])
+        self.assertRaises(
+            lib_exc.Unauthorized,
+            self.roles_client.create_user_role_on_project, tenant['id'],
+            user['id'], role['id'])
         self.client.auth_provider.clear_auth()
 
     @test.attr(type=['negative'])
@@ -147,8 +144,9 @@
     def test_assign_user_role_for_non_existent_role(self):
         # Attempt to assign a non existent role to user should fail
         (user, tenant, role) = self._get_role_params()
-        non_existent_role = str(uuid.uuid4().hex)
-        self.assertRaises(lib_exc.NotFound, self.roles_client.assign_user_role,
+        non_existent_role = data_utils.rand_uuid_hex()
+        self.assertRaises(lib_exc.NotFound,
+                          self.roles_client.create_user_role_on_project,
                           tenant['id'], user['id'], non_existent_role)
 
     @test.attr(type=['negative'])
@@ -156,8 +154,9 @@
     def test_assign_user_role_for_non_existent_tenant(self):
         # Attempt to assign a role on a non existent tenant should fail
         (user, tenant, role) = self._get_role_params()
-        non_existent_tenant = str(uuid.uuid4().hex)
-        self.assertRaises(lib_exc.NotFound, self.roles_client.assign_user_role,
+        non_existent_tenant = data_utils.rand_uuid_hex()
+        self.assertRaises(lib_exc.NotFound,
+                          self.roles_client.create_user_role_on_project,
                           non_existent_tenant, user['id'], role['id'])
 
     @test.attr(type=['negative'])
@@ -165,9 +164,11 @@
     def test_assign_duplicate_user_role(self):
         # Duplicate user role should not get assigned
         (user, tenant, role) = self._get_role_params()
-        self.roles_client.assign_user_role(tenant['id'], user['id'],
-                                           role['id'])
-        self.assertRaises(lib_exc.Conflict, self.roles_client.assign_user_role,
+        self.roles_client.create_user_role_on_project(tenant['id'],
+                                                      user['id'],
+                                                      role['id'])
+        self.assertRaises(lib_exc.Conflict,
+                          self.roles_client.create_user_role_on_project,
                           tenant['id'], user['id'], role['id'])
 
     @test.attr(type=['negative'])
@@ -176,26 +177,27 @@
         # Non-administrator user should not be authorized to
         # remove a user's role
         (user, tenant, role) = self._get_role_params()
-        self.roles_client.assign_user_role(tenant['id'],
-                                           user['id'],
-                                           role['id'])
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_roles_client.delete_user_role,
-                          tenant['id'], user['id'], role['id'])
+        self.roles_client.create_user_role_on_project(tenant['id'],
+                                                      user['id'],
+                                                      role['id'])
+        self.assertRaises(
+            lib_exc.Forbidden,
+            self.non_admin_roles_client.delete_role_from_user_on_project,
+            tenant['id'], user['id'], role['id'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('cac81cf4-c1d2-47dc-90d3-f2b7eb572286')
     def test_remove_user_role_request_without_token(self):
         # Request to remove a user's role without a valid token
         (user, tenant, role) = self._get_role_params()
-        self.roles_client.assign_user_role(tenant['id'],
-                                           user['id'],
-                                           role['id'])
+        self.roles_client.create_user_role_on_project(tenant['id'],
+                                                      user['id'],
+                                                      role['id'])
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
         self.assertRaises(lib_exc.Unauthorized,
-                          self.roles_client.delete_user_role, tenant['id'],
-                          user['id'], role['id'])
+                          self.roles_client.delete_role_from_user_on_project,
+                          tenant['id'], user['id'], role['id'])
         self.client.auth_provider.clear_auth()
 
     @test.attr(type=['negative'])
@@ -203,11 +205,12 @@
     def test_remove_user_role_non_existent_role(self):
         # Attempt to delete a non existent role from a user should fail
         (user, tenant, role) = self._get_role_params()
-        self.roles_client.assign_user_role(tenant['id'],
-                                           user['id'],
-                                           role['id'])
-        non_existent_role = str(uuid.uuid4().hex)
-        self.assertRaises(lib_exc.NotFound, self.roles_client.delete_user_role,
+        self.roles_client.create_user_role_on_project(tenant['id'],
+                                                      user['id'],
+                                                      role['id'])
+        non_existent_role = data_utils.rand_uuid_hex()
+        self.assertRaises(lib_exc.NotFound,
+                          self.roles_client.delete_role_from_user_on_project,
                           tenant['id'], user['id'], non_existent_role)
 
     @test.attr(type=['negative'])
@@ -215,11 +218,12 @@
     def test_remove_user_role_non_existent_tenant(self):
         # Attempt to remove a role from a non existent tenant should fail
         (user, tenant, role) = self._get_role_params()
-        self.roles_client.assign_user_role(tenant['id'],
-                                           user['id'],
-                                           role['id'])
-        non_existent_tenant = str(uuid.uuid4().hex)
-        self.assertRaises(lib_exc.NotFound, self.roles_client.delete_user_role,
+        self.roles_client.create_user_role_on_project(tenant['id'],
+                                                      user['id'],
+                                                      role['id'])
+        non_existent_tenant = data_utils.rand_uuid_hex()
+        self.assertRaises(lib_exc.NotFound,
+                          self.roles_client.delete_role_from_user_on_project,
                           non_existent_tenant, user['id'], role['id'])
 
     @test.attr(type=['negative'])
@@ -228,11 +232,13 @@
         # Non-administrator user should not be authorized to list
         # a user's roles
         (user, tenant, role) = self._get_role_params()
-        self.roles_client.assign_user_role(tenant['id'], user['id'],
-                                           role['id'])
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_roles_client.list_user_roles,
-                          tenant['id'], user['id'])
+        self.roles_client.create_user_role_on_project(tenant['id'],
+                                                      user['id'],
+                                                      role['id'])
+        self.assertRaises(
+            lib_exc.Forbidden,
+            self.non_admin_roles_client.list_user_roles_on_project,
+            tenant['id'], user['id'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('682adfb2-fd5f-4b0a-a9ca-322e9bebb907')
@@ -243,7 +249,8 @@
         self.client.delete_token(token)
         try:
             self.assertRaises(lib_exc.Unauthorized,
-                              self.roles_client.list_user_roles, tenant['id'],
+                              self.roles_client.list_user_roles_on_project,
+                              tenant['id'],
                               user['id'])
         finally:
             self.client.auth_provider.clear_auth()
diff --git a/tempest/api/identity/admin/v2/test_services.py b/tempest/api/identity/admin/v2/test_services.py
index 5685922..7973a03 100644
--- a/tempest/api/identity/admin/v2/test_services.py
+++ b/tempest/api/identity/admin/v2/test_services.py
@@ -13,11 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from six import moves
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -35,18 +33,19 @@
         # GET Service
         # Creating a Service
         name = data_utils.rand_name('service')
-        type = data_utils.rand_name('type')
+        s_type = data_utils.rand_name('type')
         description = data_utils.rand_name('description')
         service_data = self.services_client.create_service(
-            name, type, description=description)['OS-KSADM:service']
-        self.assertFalse(service_data['id'] is None)
+            name=name, type=s_type,
+            description=description)['OS-KSADM:service']
+        self.assertIsNotNone(service_data['id'])
         self.addCleanup(self._del_service, service_data['id'])
         # Verifying response body of create service
         self.assertIn('id', service_data)
         self.assertIn('name', service_data)
         self.assertEqual(name, service_data['name'])
         self.assertIn('type', service_data)
-        self.assertEqual(type, service_data['type'])
+        self.assertEqual(s_type, service_data['type'])
         self.assertIn('description', service_data)
         self.assertEqual(description, service_data['description'])
         # Get service
@@ -68,29 +67,31 @@
     def test_create_service_without_description(self):
         # Create a service only with name and type
         name = data_utils.rand_name('service')
-        type = data_utils.rand_name('type')
-        service = self.services_client.create_service(name,
-                                                      type)['OS-KSADM:service']
+        s_type = data_utils.rand_name('type')
+        service = self.services_client.create_service(
+            name=name, type=s_type)['OS-KSADM:service']
         self.assertIn('id', service)
         self.addCleanup(self._del_service, service['id'])
         self.assertIn('name', service)
         self.assertEqual(name, service['name'])
         self.assertIn('type', service)
-        self.assertEqual(type, service['type'])
+        self.assertEqual(s_type, service['type'])
 
     @test.attr(type='smoke')
     @test.idempotent_id('34ea6489-012d-4a86-9038-1287cadd5eca')
     def test_list_services(self):
         # Create, List, Verify and Delete Services
         services = []
-        for _ in moves.xrange(3):
+        for _ in range(3):
             name = data_utils.rand_name('service')
-            type = data_utils.rand_name('type')
+            s_type = data_utils.rand_name('type')
             description = data_utils.rand_name('description')
+
             service = self.services_client.create_service(
-                name, type, description=description)['OS-KSADM:service']
+                name=name, type=s_type,
+                description=description)['OS-KSADM:service']
             services.append(service)
-        service_ids = map(lambda x: x['id'], services)
+        service_ids = [svc['id'] for svc in services]
 
         def delete_services():
             for service_id in service_ids:
diff --git a/tempest/api/identity/admin/v2/test_tenant_negative.py b/tempest/api/identity/admin/v2/test_tenant_negative.py
index a02dbc1..baa78e9 100644
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ b/tempest/api/identity/admin/v2/test_tenant_negative.py
@@ -13,12 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -46,8 +43,8 @@
     def test_tenant_delete_by_unauthorized_user(self):
         # Non-administrator user should not be able to delete a tenant
         tenant_name = data_utils.rand_name(name='tenant')
-        tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
-        self.data.tenants.append(tenant)
+        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
+        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_tenants_client.delete_tenant,
                           tenant['id'])
@@ -57,8 +54,8 @@
     def test_tenant_delete_request_without_token(self):
         # Request to delete a tenant without a valid token should fail
         tenant_name = data_utils.rand_name(name='tenant')
-        tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
-        self.data.tenants.append(tenant)
+        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
+        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
         self.assertRaises(lib_exc.Unauthorized,
@@ -71,22 +68,19 @@
     def test_delete_non_existent_tenant(self):
         # Attempt to delete a non existent tenant should fail
         self.assertRaises(lib_exc.NotFound, self.tenants_client.delete_tenant,
-                          str(uuid.uuid4().hex))
+                          data_utils.rand_uuid_hex())
 
     @test.attr(type=['negative'])
     @test.idempotent_id('af16f44b-a849-46cb-9f13-a751c388f739')
     def test_tenant_create_duplicate(self):
         # Tenant names should be unique
         tenant_name = data_utils.rand_name(name='tenant')
-        body = self.tenants_client.create_tenant(tenant_name)['tenant']
-        tenant = body
-        self.data.tenants.append(tenant)
+        body = self.tenants_client.create_tenant(name=tenant_name)['tenant']
         tenant1_id = body.get('id')
 
         self.addCleanup(self.tenants_client.delete_tenant, tenant1_id)
-        self.addCleanup(self.data.tenants.remove, tenant)
         self.assertRaises(lib_exc.Conflict, self.tenants_client.create_tenant,
-                          tenant_name)
+                          name=tenant_name)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('d26b278a-6389-4702-8d6e-5980d80137e0')
@@ -95,7 +89,7 @@
         tenant_name = data_utils.rand_name(name='tenant')
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_tenants_client.create_tenant,
-                          tenant_name)
+                          name=tenant_name)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('a3ee9d7e-6920-4dd5-9321-d4b2b7f0a638')
@@ -106,7 +100,7 @@
         self.client.delete_token(token)
         self.assertRaises(lib_exc.Unauthorized,
                           self.tenants_client.create_tenant,
-                          tenant_name)
+                          name=tenant_name)
         self.client.auth_provider.clear_auth()
 
     @test.attr(type=['negative'])
@@ -124,22 +118,22 @@
         tenant_name = 'a' * 65
         self.assertRaises(lib_exc.BadRequest,
                           self.tenants_client.create_tenant,
-                          tenant_name)
+                          name=tenant_name)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('bd20dc2a-9557-4db7-b755-f48d952ad706')
     def test_update_non_existent_tenant(self):
         # Attempt to update a non existent tenant should fail
         self.assertRaises(lib_exc.NotFound, self.tenants_client.update_tenant,
-                          str(uuid.uuid4().hex))
+                          data_utils.rand_uuid_hex())
 
     @test.attr(type=['negative'])
     @test.idempotent_id('41704dc5-c5f7-4f79-abfa-76e6fedc570b')
     def test_tenant_update_by_unauthorized_user(self):
         # Non-administrator user should not be able to update a tenant
         tenant_name = data_utils.rand_name(name='tenant')
-        tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
-        self.data.tenants.append(tenant)
+        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
+        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_tenants_client.update_tenant,
                           tenant['id'])
@@ -149,8 +143,8 @@
     def test_tenant_update_request_without_token(self):
         # Request to update a tenant without a valid token should fail
         tenant_name = data_utils.rand_name(name='tenant')
-        tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
-        self.data.tenants.append(tenant)
+        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
+        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
         self.assertRaises(lib_exc.Unauthorized,
diff --git a/tempest/api/identity/admin/v2/test_tenants.py b/tempest/api/identity/admin/v2/test_tenants.py
index 8d0b9b1..f4fad53 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -13,10 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from six import moves
-
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest import test
 
 
@@ -26,19 +25,21 @@
     def test_tenant_list_delete(self):
         # Create several tenants and delete them
         tenants = []
-        for _ in moves.xrange(3):
+        for _ in range(3):
             tenant_name = data_utils.rand_name(name='tenant-new')
-            tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
-            self.data.tenants.append(tenant)
+            tenant = self.tenants_client.create_tenant(
+                name=tenant_name)['tenant']
+            # Add the tenant to the cleanup list
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            self.tenants_client.delete_tenant, tenant['id'])
             tenants.append(tenant)
-        tenant_ids = map(lambda x: x['id'], tenants)
+        tenant_ids = [tn['id'] for tn in tenants]
         body = self.tenants_client.list_tenants()['tenants']
         found = [t for t in body if t['id'] in tenant_ids]
         self.assertEqual(len(found), len(tenants), 'Tenants not created')
 
         for tenant in tenants:
             self.tenants_client.delete_tenant(tenant['id'])
-            self.data.tenants.remove(tenant)
 
         body = self.tenants_client.list_tenants()['tenants']
         found = [tenant for tenant in body if tenant['id'] in tenant_ids]
@@ -49,10 +50,12 @@
         # Create tenant with a description
         tenant_name = data_utils.rand_name(name='tenant')
         tenant_desc = data_utils.rand_name(name='desc')
-        body = self.tenants_client.create_tenant(tenant_name,
+        body = self.tenants_client.create_tenant(name=tenant_name,
                                                  description=tenant_desc)
         tenant = body['tenant']
-        self.data.tenants.append(tenant)
+        # Add the tenant to the cleanup list
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.tenants_client.delete_tenant, tenant['id'])
         tenant_id = tenant['id']
         desc1 = tenant['description']
         self.assertEqual(desc1, tenant_desc, 'Description should have '
@@ -62,15 +65,17 @@
         self.assertEqual(desc2, tenant_desc, 'Description does not appear'
                          'to be set')
         self.tenants_client.delete_tenant(tenant_id)
-        self.data.tenants.remove(tenant)
 
     @test.idempotent_id('670bdddc-1cd7-41c7-b8e2-751cfb67df50')
     def test_tenant_create_enabled(self):
         # Create a tenant that is enabled
         tenant_name = data_utils.rand_name(name='tenant')
-        body = self.tenants_client.create_tenant(tenant_name, enabled=True)
+        body = self.tenants_client.create_tenant(name=tenant_name,
+                                                 enabled=True)
         tenant = body['tenant']
-        self.data.tenants.append(tenant)
+        # Add the tenant to the cleanup list
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.tenants_client.delete_tenant, tenant['id'])
         tenant_id = tenant['id']
         en1 = tenant['enabled']
         self.assertTrue(en1, 'Enable should be True in response')
@@ -78,15 +83,17 @@
         en2 = body['enabled']
         self.assertTrue(en2, 'Enable should be True in lookup')
         self.tenants_client.delete_tenant(tenant_id)
-        self.data.tenants.remove(tenant)
 
     @test.idempotent_id('3be22093-b30f-499d-b772-38340e5e16fb')
     def test_tenant_create_not_enabled(self):
         # Create a tenant that is not enabled
         tenant_name = data_utils.rand_name(name='tenant')
-        body = self.tenants_client.create_tenant(tenant_name, enabled=False)
+        body = self.tenants_client.create_tenant(name=tenant_name,
+                                                 enabled=False)
         tenant = body['tenant']
-        self.data.tenants.append(tenant)
+        # Add the tenant to the cleanup list
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.tenants_client.delete_tenant, tenant['id'])
         tenant_id = tenant['id']
         en1 = tenant['enabled']
         self.assertEqual('false', str(en1).lower(),
@@ -96,15 +103,16 @@
         self.assertEqual('false', str(en2).lower(),
                          'Enable should be False in lookup')
         self.tenants_client.delete_tenant(tenant_id)
-        self.data.tenants.remove(tenant)
 
     @test.idempotent_id('781f2266-d128-47f3-8bdb-f70970add238')
     def test_tenant_update_name(self):
         # Update name attribute of a tenant
         t_name1 = data_utils.rand_name(name='tenant')
-        body = self.tenants_client.create_tenant(t_name1)['tenant']
+        body = self.tenants_client.create_tenant(name=t_name1)['tenant']
         tenant = body
-        self.data.tenants.append(tenant)
+        # Add the tenant to the cleanup list
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.tenants_client.delete_tenant, tenant['id'])
 
         t_id = body['id']
         resp1_name = body['name']
@@ -122,16 +130,18 @@
         self.assertEqual(resp2_name, resp3_name)
 
         self.tenants_client.delete_tenant(t_id)
-        self.data.tenants.remove(tenant)
 
     @test.idempotent_id('859fcfe1-3a03-41ef-86f9-b19a47d1cd87')
     def test_tenant_update_desc(self):
         # Update description attribute of a tenant
         t_name = data_utils.rand_name(name='tenant')
         t_desc = data_utils.rand_name(name='desc')
-        body = self.tenants_client.create_tenant(t_name, description=t_desc)
+        body = self.tenants_client.create_tenant(name=t_name,
+                                                 description=t_desc)
         tenant = body['tenant']
-        self.data.tenants.append(tenant)
+        # Add the tenant to the cleanup list
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.tenants_client.delete_tenant, tenant['id'])
 
         t_id = tenant['id']
         resp1_desc = tenant['description']
@@ -150,16 +160,17 @@
         self.assertEqual(resp2_desc, resp3_desc)
 
         self.tenants_client.delete_tenant(t_id)
-        self.data.tenants.remove(tenant)
 
     @test.idempotent_id('8fc8981f-f12d-4c66-9972-2bdcf2bc2e1a')
     def test_tenant_update_enable(self):
         # Update the enabled attribute of a tenant
         t_name = data_utils.rand_name(name='tenant')
         t_en = False
-        body = self.tenants_client.create_tenant(t_name, enabled=t_en)
+        body = self.tenants_client.create_tenant(name=t_name, enabled=t_en)
         tenant = body['tenant']
-        self.data.tenants.append(tenant)
+        # Add the tenant to the cleanup list
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.tenants_client.delete_tenant, tenant['id'])
 
         t_id = tenant['id']
         resp1_en = tenant['enabled']
@@ -178,4 +189,3 @@
         self.assertEqual(resp2_en, resp3_en)
 
         self.tenants_client.delete_tenant(t_id)
-        self.data.tenants.remove(tenant)
diff --git a/tempest/api/identity/admin/v2/test_tokens.py b/tempest/api/identity/admin/v2/test_tokens.py
index ee04420..2f7e941 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -27,12 +27,16 @@
         user_password = data_utils.rand_password()
         # first:create a tenant
         tenant_name = data_utils.rand_name(name='tenant')
-        tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
-        self.data.tenants.append(tenant)
+        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
+        # Delete the tenant at the end of the test
+        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
         # second:create a user
-        user = self.users_client.create_user(user_name, user_password,
-                                             tenant['id'], '')['user']
-        self.data.users.append(user)
+        user = self.users_client.create_user(name=user_name,
+                                             password=user_password,
+                                             tenantId=tenant['id'],
+                                             email='')['user']
+        # Delete the user at the end of the test
+        self.addCleanup(self.users_client.delete_user, user['id'])
         # then get a token for the user
         body = self.token_client.auth(user_name,
                                       user_password,
@@ -62,30 +66,40 @@
         user_password = data_utils.rand_password()
         tenant_id = None  # No default tenant so will get unscoped token.
         email = ''
-        user = self.users_client.create_user(user_name, user_password,
-                                             tenant_id, email)['user']
-        self.data.users.append(user)
+        user = self.users_client.create_user(name=user_name,
+                                             password=user_password,
+                                             tenantId=tenant_id,
+                                             email=email)['user']
+        # Delete the user at the end of the test
+        self.addCleanup(self.users_client.delete_user, user['id'])
 
         # Create a couple tenants.
         tenant1_name = data_utils.rand_name(name='tenant')
-        tenant1 = self.tenants_client.create_tenant(tenant1_name)['tenant']
-        self.data.tenants.append(tenant1)
+        tenant1 = self.tenants_client.create_tenant(
+            name=tenant1_name)['tenant']
+        # Delete the tenant at the end of the test
+        self.addCleanup(self.tenants_client.delete_tenant, tenant1['id'])
 
         tenant2_name = data_utils.rand_name(name='tenant')
-        tenant2 = self.tenants_client.create_tenant(tenant2_name)['tenant']
-        self.data.tenants.append(tenant2)
+        tenant2 = self.tenants_client.create_tenant(
+            name=tenant2_name)['tenant']
+        # Delete the tenant at the end of the test
+        self.addCleanup(self.tenants_client.delete_tenant, tenant2['id'])
 
         # Create a role
         role_name = data_utils.rand_name(name='role')
         role = self.roles_client.create_role(name=role_name)['role']
-        self.data.roles.append(role)
+        # Delete the role at the end of the test
+        self.addCleanup(self.roles_client.delete_role, role['id'])
 
         # Grant the user the role on the tenants.
-        self.roles_client.assign_user_role(tenant1['id'], user['id'],
-                                           role['id'])
+        self.roles_client.create_user_role_on_project(tenant1['id'],
+                                                      user['id'],
+                                                      role['id'])
 
-        self.roles_client.assign_user_role(tenant2['id'], user['id'],
-                                           role['id'])
+        self.roles_client.create_user_role_on_project(tenant2['id'],
+                                                      user['id'],
+                                                      role['id'])
 
         # Get an unscoped token.
         body = self.token_client.auth(user_name, user_password)
diff --git a/tempest/api/identity/admin/v2/test_users.py b/tempest/api/identity/admin/v2/test_users.py
index 60c4e97..4a4b51a 100644
--- a/tempest/api/identity/admin/v2/test_users.py
+++ b/tempest/api/identity/admin/v2/test_users.py
@@ -13,10 +13,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import time
+
 from testtools import matchers
 
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest import test
 
 
@@ -33,23 +36,27 @@
     @test.idempotent_id('2d55a71e-da1d-4b43-9c03-d269fd93d905')
     def test_create_user(self):
         # Create a user
-        self.data.setup_test_tenant()
-        user = self.users_client.create_user(self.alt_user, self.alt_password,
-                                             self.data.tenant['id'],
-                                             self.alt_email)['user']
-        self.data.users.append(user)
+        tenant = self.setup_test_tenant()
+        user = self.users_client.create_user(name=self.alt_user,
+                                             password=self.alt_password,
+                                             tenantId=tenant['id'],
+                                             email=self.alt_email)['user']
+        # Delete the User at the end of the test
+        self.addCleanup(self.users_client.delete_user, user['id'])
         self.assertEqual(self.alt_user, user['name'])
 
     @test.idempotent_id('89d9fdb8-15c2-4304-a429-48715d0af33d')
     def test_create_user_with_enabled(self):
         # Create a user with enabled : False
-        self.data.setup_test_tenant()
+        tenant = self.setup_test_tenant()
         name = data_utils.rand_name('test_user')
-        user = self.users_client.create_user(name, self.alt_password,
-                                             self.data.tenant['id'],
-                                             self.alt_email,
+        user = self.users_client.create_user(name=name,
+                                             password=self.alt_password,
+                                             tenantId=tenant['id'],
+                                             email=self.alt_email,
                                              enabled=False)['user']
-        self.data.users.append(user)
+        # Delete the User at the end of the test
+        self.addCleanup(self.users_client.delete_user, user['id'])
         self.assertEqual(name, user['name'])
         self.assertEqual(False, user['enabled'])
         self.assertEqual(self.alt_email, user['email'])
@@ -58,10 +65,11 @@
     def test_update_user(self):
         # Test case to check if updating of user attributes is successful.
         test_user = data_utils.rand_name('test_user')
-        self.data.setup_test_tenant()
-        user = self.users_client.create_user(test_user, self.alt_password,
-                                             self.data.tenant['id'],
-                                             self.alt_email)['user']
+        tenant = self.setup_test_tenant()
+        user = self.users_client.create_user(name=test_user,
+                                             password=self.alt_password,
+                                             tenantId=tenant['id'],
+                                             email=self.alt_email)['user']
         # Delete the User at the end of this method
         self.addCleanup(self.users_client.delete_user, user['id'])
         # Updating user details with new values
@@ -84,73 +92,85 @@
     def test_delete_user(self):
         # Delete a user
         test_user = data_utils.rand_name('test_user')
-        self.data.setup_test_tenant()
-        user = self.users_client.create_user(test_user, self.alt_password,
-                                             self.data.tenant['id'],
-                                             self.alt_email)['user']
+        tenant = self.setup_test_tenant()
+        user = self.users_client.create_user(name=test_user,
+                                             password=self.alt_password,
+                                             tenantId=tenant['id'],
+                                             email=self.alt_email)['user']
+        # Delete the User at the end of the test
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.users_client.delete_user, user['id'])
         self.users_client.delete_user(user['id'])
 
     @test.idempotent_id('aca696c3-d645-4f45-b728-63646045beb1')
     def test_user_authentication(self):
         # Valid user's token is authenticated
-        self.data.setup_test_user()
+        password = data_utils.rand_password()
+        user = self.setup_test_user(password)
+        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
         # Get a token
-        self.token_client.auth(self.data.user['name'],
-                               self.data.user_password,
-                               self.data.tenant['name'])
+        self.token_client.auth(user['name'],
+                               password,
+                               tenant['name'])
         # Re-auth
-        self.token_client.auth(self.data.user['name'],
-                               self.data.user_password,
-                               self.data.tenant['name'])
+        self.token_client.auth(user['name'],
+                               password,
+                               tenant['name'])
 
     @test.idempotent_id('5d1fa498-4c2d-4732-a8fe-2b054598cfdd')
     def test_authentication_request_without_token(self):
         # Request for token authentication with a valid token in header
-        self.data.setup_test_user()
-        self.token_client.auth(self.data.user['name'],
-                               self.data.user_password,
-                               self.data.tenant['name'])
+        password = data_utils.rand_password()
+        user = self.setup_test_user(password)
+        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
+        self.token_client.auth(user['name'],
+                               password,
+                               tenant['name'])
         # Get the token of the current client
         token = self.client.auth_provider.get_token()
         # Delete the token from database
         self.client.delete_token(token)
         # Re-auth
-        self.token_client.auth(self.data.user['name'],
-                               self.data.user_password,
-                               self.data.tenant['name'])
+        self.token_client.auth(user['name'],
+                               password,
+                               tenant['name'])
         self.client.auth_provider.clear_auth()
 
     @test.idempotent_id('a149c02e-e5e0-4b89-809e-7e8faf33ccda')
     def test_get_users(self):
         # Get a list of users and find the test user
-        self.data.setup_test_user()
+        user = self.setup_test_user()
         users = self.users_client.list_users()['users']
         self.assertThat([u['name'] for u in users],
-                        matchers.Contains(self.data.user['name']),
-                        "Could not find %s" % self.data.user['name'])
+                        matchers.Contains(user['name']),
+                        "Could not find %s" % user['name'])
 
     @test.idempotent_id('6e317209-383a-4bed-9f10-075b7c82c79a')
     def test_list_users_for_tenant(self):
         # Return a list of all users for a tenant
-        self.data.setup_test_tenant()
+        tenant = self.setup_test_tenant()
         user_ids = list()
         fetched_user_ids = list()
         password1 = data_utils.rand_password()
         alt_tenant_user1 = data_utils.rand_name('tenant_user1')
-        user1 = self.users_client.create_user(alt_tenant_user1, password1,
-                                              self.data.tenant['id'],
-                                              'user1@123')['user']
+        user1 = self.users_client.create_user(name=alt_tenant_user1,
+                                              password=password1,
+                                              tenantId=tenant['id'],
+                                              email='user1@123')['user']
         user_ids.append(user1['id'])
-        self.data.users.append(user1)
+        # Delete the User at the end of the test
+        self.addCleanup(self.users_client.delete_user, user1['id'])
         password2 = data_utils.rand_password()
         alt_tenant_user2 = data_utils.rand_name('tenant_user2')
-        user2 = self.users_client.create_user(alt_tenant_user2, password2,
-                                              self.data.tenant['id'],
-                                              'user2@123')['user']
+        user2 = self.users_client.create_user(name=alt_tenant_user2,
+                                              password=password2,
+                                              tenantId=tenant['id'],
+                                              email='user2@123')['user']
         user_ids.append(user2['id'])
-        self.data.users.append(user2)
+        # Delete the User at the end of the test
+        self.addCleanup(self.users_client.delete_user, user2['id'])
         # List of users for the respective tenant ID
-        body = (self.tenants_client.list_tenant_users(self.data.tenant['id'])
+        body = (self.tenants_client.list_tenant_users(tenant['id'])
                 ['users'])
         for i in body:
             fetched_user_ids.append(i['id'])
@@ -164,31 +184,30 @@
     @test.idempotent_id('a8b54974-40e1-41c0-b812-50fc90827971')
     def test_list_users_with_roles_for_tenant(self):
         # Return list of users on tenant when roles are assigned to users
-        self.data.setup_test_user()
-        self.data.setup_test_role()
-        user = self.get_user_by_name(self.data.user['name'])
-        tenant = self.get_tenant_by_name(self.data.tenant['name'])
-        role = self.get_role_by_name(self.data.role['name'])
+        user = self.setup_test_user()
+        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
+        role = self.setup_test_role()
         # Assigning roles to two users
         user_ids = list()
         fetched_user_ids = list()
         user_ids.append(user['id'])
-        role = self.roles_client.assign_user_role(tenant['id'], user['id'],
-                                                  role['id'])['role']
+        role = self.roles_client.create_user_role_on_project(
+            tenant['id'], user['id'], role['id'])['role']
 
         alt_user2 = data_utils.rand_name('second_user')
         alt_password2 = data_utils.rand_password()
-        second_user = self.users_client.create_user(alt_user2, alt_password2,
-                                                    self.data.tenant['id'],
-                                                    'user2@123')['user']
+        second_user = self.users_client.create_user(
+            name=alt_user2,
+            password=alt_password2,
+            tenantId=tenant['id'],
+            email='user2@123')['user']
         user_ids.append(second_user['id'])
-        self.data.users.append(second_user)
-        role = self.roles_client.assign_user_role(tenant['id'],
-                                                  second_user['id'],
-                                                  role['id'])['role']
+        # Delete the User at the end of the test
+        self.addCleanup(self.users_client.delete_user, second_user['id'])
+        role = self.roles_client.create_user_role_on_project(
+            tenant['id'], second_user['id'], role['id'])['role']
         # List of users with roles for the respective tenant ID
-        body = (self.tenants_client.list_tenant_users(self.data.tenant['id'])
-                ['users'])
+        body = (self.tenants_client.list_tenant_users(tenant['id'])['users'])
         for i in body:
             fetched_user_ids.append(i['id'])
         # verifying the user Id in the list
@@ -201,15 +220,18 @@
     @test.idempotent_id('1aeb25ac-6ec5-4d8b-97cb-7ac3567a989f')
     def test_update_user_password(self):
         # Test case to check if updating of user password is successful.
-        self.data.setup_test_user()
+        user = self.setup_test_user()
+        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
         # Updating the user with new password
         new_pass = data_utils.rand_password()
         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
-        body = self.token_client.auth(self.data.user['name'], new_pass,
-                                      self.data.tenant['name'])
-        self.assertTrue('id' in body['token'])
+            user['id'], password=new_pass)['user']
+        self.assertEqual(update_user['id'], user['id'])
+        # 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(user['name'], new_pass,
+                                      tenant['name'])
+        self.assertIn('id', body['token'])
diff --git a/tempest/api/identity/admin/v2/test_users_negative.py b/tempest/api/identity/admin/v2/test_users_negative.py
index 0a5d0c9..597413e 100644
--- a/tempest/api/identity/admin/v2/test_users_negative.py
+++ b/tempest/api/identity/admin/v2/test_users_negative.py
@@ -13,12 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -35,81 +32,91 @@
     @test.idempotent_id('60a1f5fa-5744-4cdf-82bf-60b7de2d29a4')
     def test_create_user_by_unauthorized_user(self):
         # Non-administrator should not be authorized to create a user
-        self.data.setup_test_tenant()
+        tenant = self.setup_test_tenant()
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_users_client.create_user,
-                          self.alt_user, self.alt_password,
-                          self.data.tenant['id'],
-                          self.alt_email)
+                          name=self.alt_user, password=self.alt_password,
+                          tenantId=tenant['id'],
+                          email=self.alt_email)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('d80d0c2f-4514-4d1e-806d-0930dfc5a187')
     def test_create_user_with_empty_name(self):
         # User with an empty name should not be created
-        self.data.setup_test_tenant()
+        tenant = self.setup_test_tenant()
         self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
-                          '', self.alt_password, self.data.tenant['id'],
-                          self.alt_email)
+                          name='', password=self.alt_password,
+                          tenantId=tenant['id'],
+                          email=self.alt_email)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('7704b4f3-3b75-4b82-87cc-931d41c8f780')
     def test_create_user_with_name_length_over_255(self):
         # Length of user name filed should be restricted to 255 characters
-        self.data.setup_test_tenant()
+        tenant = self.setup_test_tenant()
         self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
-                          'a' * 256, self.alt_password,
-                          self.data.tenant['id'], self.alt_email)
+                          name='a' * 256, password=self.alt_password,
+                          tenantId=tenant['id'],
+                          email=self.alt_email)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('57ae8558-120c-4723-9308-3751474e7ecf')
     def test_create_user_with_duplicate_name(self):
         # Duplicate user should not be created
-        self.data.setup_test_user()
+        password = data_utils.rand_password()
+        user = self.setup_test_user(password)
+        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
         self.assertRaises(lib_exc.Conflict, self.users_client.create_user,
-                          self.data.user['name'], self.data.user_password,
-                          self.data.tenant['id'], self.data.user['email'])
+                          name=user['name'],
+                          password=password,
+                          tenantId=tenant['id'],
+                          email=user['email'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('0132cc22-7c4f-42e1-9e50-ac6aad31d59a')
     def test_create_user_for_non_existent_tenant(self):
         # Attempt to create a user in a non-existent tenant should fail
         self.assertRaises(lib_exc.NotFound, self.users_client.create_user,
-                          self.alt_user, self.alt_password, '49ffgg99999',
-                          self.alt_email)
+                          name=self.alt_user,
+                          password=self.alt_password,
+                          tenantId='49ffgg99999',
+                          email=self.alt_email)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('55bbb103-d1ae-437b-989b-bcdf8175c1f4')
     def test_create_user_request_without_a_token(self):
         # Request to create a user without a valid token should fail
-        self.data.setup_test_tenant()
+        tenant = self.setup_test_tenant()
         # Get the token of the current client
         token = self.client.auth_provider.get_token()
         # Delete the token from database
         self.client.delete_token(token)
-        self.assertRaises(lib_exc.Unauthorized, self.users_client.create_user,
-                          self.alt_user, self.alt_password,
-                          self.data.tenant['id'], self.alt_email)
 
         # Unset the token to allow further tests to generate a new token
-        self.client.auth_provider.clear_auth()
+        self.addCleanup(self.client.auth_provider.clear_auth)
+
+        self.assertRaises(lib_exc.Unauthorized, self.users_client.create_user,
+                          name=self.alt_user, password=self.alt_password,
+                          tenantId=tenant['id'],
+                          email=self.alt_email)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('23a2f3da-4a1a-41da-abdd-632328a861ad')
     def test_create_user_with_enabled_non_bool(self):
         # Attempt to create a user with valid enabled para should fail
-        self.data.setup_test_tenant()
+        tenant = self.setup_test_tenant()
         name = data_utils.rand_name('test_user')
         self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
-                          name, self.alt_password,
-                          self.data.tenant['id'],
-                          self.alt_email, enabled=3)
+                          name=name, password=self.alt_password,
+                          tenantId=tenant['id'],
+                          email=self.alt_email, enabled=3)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('3d07e294-27a0-4144-b780-a2a1bf6fee19')
     def test_update_user_for_non_existent_user(self):
         # Attempt to update a user non-existent user should fail
         user_name = data_utils.rand_name('user')
-        non_existent_id = str(uuid.uuid4())
+        non_existent_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.users_client.update_user,
                           non_existent_id, name=user_name)
 
@@ -122,17 +129,17 @@
         token = self.client.auth_provider.get_token()
         # Delete the token from database
         self.client.delete_token(token)
-        self.assertRaises(lib_exc.Unauthorized, self.users_client.update_user,
-                          self.alt_user)
 
         # Unset the token to allow further tests to generate a new token
-        self.client.auth_provider.clear_auth()
+        self.addCleanup(self.client.auth_provider.clear_auth)
+
+        self.assertRaises(lib_exc.Unauthorized, self.users_client.update_user,
+                          self.alt_user)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('424868d5-18a7-43e1-8903-a64f95ee3aac')
     def test_update_user_by_unauthorized_user(self):
         # Non-administrator should not be authorized to update user
-        self.data.setup_test_tenant()
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_users_client.update_user,
                           self.alt_user)
@@ -141,10 +148,10 @@
     @test.idempotent_id('d45195d5-33ed-41b9-a452-7d0d6a00f6e9')
     def test_delete_users_by_unauthorized_user(self):
         # Non-administrator user should not be authorized to delete a user
-        self.data.setup_test_user()
+        user = self.setup_test_user()
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_users_client.delete_user,
-                          self.data.user['id'])
+                          user['id'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('7cc82f7e-9998-4f89-abae-23df36495867')
@@ -162,67 +169,73 @@
         token = self.client.auth_provider.get_token()
         # Delete the token from database
         self.client.delete_token(token)
-        self.assertRaises(lib_exc.Unauthorized, self.users_client.delete_user,
-                          self.alt_user)
 
         # Unset the token to allow further tests to generate a new token
-        self.client.auth_provider.clear_auth()
+        self.addCleanup(self.client.auth_provider.clear_auth)
+
+        self.assertRaises(lib_exc.Unauthorized, self.users_client.delete_user,
+                          self.alt_user)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('593a4981-f6d4-460a-99a1-57a78bf20829')
     def test_authentication_for_disabled_user(self):
         # Disabled user's token should not get authenticated
-        self.data.setup_test_user()
-        self.disable_user(self.data.user['name'])
+        password = data_utils.rand_password()
+        user = self.setup_test_user(password)
+        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
+        self.disable_user(user['name'])
         self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
-                          self.data.user['name'],
-                          self.data.user_password,
-                          self.data.tenant['name'])
+                          user['name'],
+                          password,
+                          tenant['name'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('440a7a8d-9328-4b7b-83e0-d717010495e4')
     def test_authentication_when_tenant_is_disabled(self):
         # User's token for a disabled tenant should not be authenticated
-        self.data.setup_test_user()
-        self.disable_tenant(self.data.tenant['name'])
+        password = data_utils.rand_password()
+        user = self.setup_test_user(password)
+        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
+        self.disable_tenant(tenant['name'])
         self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
-                          self.data.user['name'],
-                          self.data.user_password,
-                          self.data.tenant['name'])
+                          user['name'],
+                          password,
+                          tenant['name'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('921f1ad6-7907-40b8-853f-637e7ee52178')
     def test_authentication_with_invalid_tenant(self):
         # User's token for an invalid tenant should not be authenticated
-        self.data.setup_test_user()
+        password = data_utils.rand_password()
+        user = self.setup_test_user(password)
         self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
-                          self.data.user['name'],
-                          self.data.user_password,
+                          user['name'],
+                          password,
                           'junktenant1234')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('bde9aecd-3b1c-4079-858f-beb5deaa5b5e')
     def test_authentication_with_invalid_username(self):
         # Non-existent user's token should not get authenticated
-        self.data.setup_test_user()
+        password = data_utils.rand_password()
+        user = self.setup_test_user(password)
+        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
         self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
-                          'junkuser123', self.data.user_password,
-                          self.data.tenant['name'])
+                          'junkuser123', password, tenant['name'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('d5308b33-3574-43c3-8d87-1c090c5e1eca')
     def test_authentication_with_invalid_password(self):
         # User's token with invalid password should not be authenticated
-        self.data.setup_test_user()
+        user = self.setup_test_user()
+        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
         self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
-                          self.data.user['name'], 'junkpass1234',
-                          self.data.tenant['name'])
+                          user['name'], 'junkpass1234', tenant['name'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('284192ce-fb7c-4909-a63b-9a502e0ddd11')
     def test_get_users_by_unauthorized_user(self):
         # Non-administrator user should not be authorized to get user list
-        self.data.setup_test_user()
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_users_client.list_users)
 
@@ -232,8 +245,11 @@
         # Request to get list of users without a valid token should fail
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
+
+        # Unset the token to allow further tests to generate a new token
+        self.addCleanup(self.client.auth_provider.clear_auth)
+
         self.assertRaises(lib_exc.Unauthorized, self.users_client.list_users)
-        self.client.auth_provider.clear_auth()
 
     @test.attr(type=['negative'])
     @test.idempotent_id('f5d39046-fc5f-425c-b29e-bac2632da28e')
diff --git a/tempest/api/identity/admin/v3/test_credentials.py b/tempest/api/identity/admin/v3/test_credentials.py
index 7c2e8e0..a0d8748 100644
--- a/tempest/api/identity/admin/v3/test_credentials.py
+++ b/tempest/api/identity/admin/v3/test_credentials.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_serialization import jsonutils as json
 
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
@@ -37,7 +38,7 @@
             cls.projects.append(cls.project['id'])
 
         cls.user_body = cls.users_client.create_user(
-            u_name, description=u_desc, password=u_password,
+            name=u_name, description=u_desc, password=u_password,
             email=u_email, project_id=cls.projects[0])['user']
 
     @classmethod
@@ -70,6 +71,7 @@
         update_body = self.creds_client.update_credential(
             cred['id'], blob=blob, project_id=self.projects[1],
             type='ec2')['credential']
+        update_body['blob'] = json.loads(update_body['blob'])
         self.assertEqual(cred['id'], update_body['id'])
         self.assertEqual(self.projects[1], update_body['project_id'])
         self.assertEqual(self.user_body['id'], update_body['user_id'])
@@ -77,6 +79,7 @@
         self.assertEqual(update_body['blob']['secret'], new_keys[1])
 
         get_body = self.creds_client.show_credential(cred['id'])['credential']
+        get_body['blob'] = json.loads(get_body['blob'])
         for value1 in self.creds_list[0]:
             self.assertEqual(update_body[value1],
                              get_body[value1])
diff --git a/tempest/api/identity/admin/v3/test_default_project_id.py b/tempest/api/identity/admin/v3/test_default_project_id.py
index 3dc3cb6..361ff31 100644
--- a/tempest/api/identity/admin/v3/test_default_project_id.py
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -9,14 +9,11 @@
 #    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 auth
-
 from tempest.api.identity import base
 from tempest import clients
 from tempest.common.utils import data_utils
 from tempest import config
-from tempest import manager
+from tempest.lib import auth
 from tempest import test
 
 CONF = config.CONF
@@ -32,14 +29,15 @@
     def _delete_domain(self, domain_id):
         # It is necessary to disable the domain before deleting,
         # or else it would result in unauthorized error
-        self.client.update_domain(domain_id, enabled=False)
-        self.client.delete_domain(domain_id)
+        self.domains_client.update_domain(domain_id, enabled=False)
+        self.domains_client.delete_domain(domain_id)
 
     @test.idempotent_id('d6110661-6a71-49a7-a453-b5e26640ff6d')
     def test_default_project_id(self):
         # create a domain
         dom_name = data_utils.rand_name('dom')
-        domain_body = self.client.create_domain(dom_name)['domain']
+        domain_body = self.domains_client.create_domain(
+            name=dom_name)['domain']
         dom_id = domain_body['id']
         self.addCleanup(self._delete_domain, dom_id)
 
@@ -57,7 +55,7 @@
         # default project
         user_name = data_utils.rand_name('user')
         user_body = self.users_client.create_user(
-            user_name,
+            name=user_name,
             password=user_name,
             domain_id=dom_id,
             default_project_id=proj_id)['user']
@@ -72,14 +70,14 @@
         admin_role_id = admin_role['id']
 
         # grant the admin role to the user on his project
-        self.client.assign_user_role_on_project(proj_id, user_id,
-                                                admin_role_id)
+        self.roles_client.create_user_role_on_project(proj_id, user_id,
+                                                      admin_role_id)
 
         # create a new client with user's credentials (NOTE: unscoped token!)
         creds = auth.KeystoneV3Credentials(username=user_name,
                                            password=user_name,
                                            user_domain_name=dom_name)
-        auth_provider = manager.get_auth_provider(creds)
+        auth_provider = clients.get_auth_provider(creds)
         creds = auth_provider.fill_credentials()
         admin_client = clients.Manager(credentials=creds)
 
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index 1729dc9..e71341f 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -16,6 +16,7 @@
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest import test
 
 CONF = config.CONF
@@ -23,44 +24,78 @@
 
 class DomainsTestJSON(base.BaseIdentityV3AdminTest):
 
-    def _delete_domain(self, domain_id):
+    @classmethod
+    def resource_setup(cls):
+        super(DomainsTestJSON, cls).resource_setup()
+        # Create some test domains to be used during tests
+        # One of those domains will be disabled
+        cls.setup_domains = list()
+        for i in range(3):
+            domain = cls.domains_client.create_domain(
+                name=data_utils.rand_name('domain'),
+                description=data_utils.rand_name('domain-desc'),
+                enabled=i < 2)['domain']
+            cls.setup_domains.append(domain)
+
+    @classmethod
+    def resource_cleanup(cls):
+        for domain in cls.setup_domains:
+            cls._delete_domain(domain['id'])
+        super(DomainsTestJSON, cls).resource_cleanup()
+
+    @classmethod
+    def _delete_domain(cls, domain_id):
         # It is necessary to disable the domain before deleting,
         # or else it would result in unauthorized error
-        self.client.update_domain(domain_id, enabled=False)
-        self.client.delete_domain(domain_id)
-        # Asserting that the domain is not found in the list
-        # after deletion
-        body = self.client.list_domains()['domains']
-        domains_list = [d['id'] for d in body]
-        self.assertNotIn(domain_id, domains_list)
+        cls.domains_client.update_domain(domain_id, enabled=False)
+        cls.domains_client.delete_domain(domain_id)
 
     @test.idempotent_id('8cf516ef-2114-48f1-907b-d32726c734d4')
     def test_list_domains(self):
         # Test to list domains
-        domain_ids = list()
         fetched_ids = list()
-        for _ in range(3):
-            domain = self.client.create_domain(
-                data_utils.rand_name('domain'),
-                description=data_utils.rand_name('domain-desc'))['domain']
-            # Delete the domain at the end of this method
-            self.addCleanup(self._delete_domain, domain['id'])
-            domain_ids.append(domain['id'])
         # List and Verify Domains
-        body = self.client.list_domains()['domains']
+        body = self.domains_client.list_domains()['domains']
         for d in body:
             fetched_ids.append(d['id'])
-        missing_doms = [d for d in domain_ids if d not in fetched_ids]
+        missing_doms = [d for d in self.setup_domains
+                        if d['id'] not in fetched_ids]
         self.assertEqual(0, len(missing_doms))
 
+    @test.idempotent_id('c6aee07b-4981-440c-bb0b-eb598f58ffe9')
+    def test_list_domains_filter_by_name(self):
+        # List domains filtering by name
+        params = {'name': self.setup_domains[0]['name']}
+        fetched_domains = self.domains_client.list_domains(
+            **params)['domains']
+        # Verify the filtered list is correct, domain names are unique
+        # so exactly one domain should be found with the provided name
+        self.assertEqual(1, len(fetched_domains))
+        self.assertEqual(self.setup_domains[0]['name'],
+                         fetched_domains[0]['name'])
+
+    @test.idempotent_id('3fd19840-65c1-43f8-b48c-51bdd066dff9')
+    def test_list_domains_filter_by_enabled(self):
+        # List domains filtering by enabled domains
+        params = {'enabled': True}
+        fetched_domains = self.domains_client.list_domains(
+            **params)['domains']
+        # Verify the filtered list is correct
+        self.assertIn(self.setup_domains[0], fetched_domains)
+        self.assertIn(self.setup_domains[1], fetched_domains)
+        for domain in fetched_domains:
+            self.assertEqual(True, domain['enabled'])
+
     @test.attr(type='smoke')
     @test.idempotent_id('f2f5b44a-82e8-4dad-8084-0661ea3b18cf')
     def test_create_update_delete_domain(self):
+        # Create domain
         d_name = data_utils.rand_name('domain')
         d_desc = data_utils.rand_name('domain-desc')
-        domain = self.client.create_domain(
-            d_name, description=d_desc)['domain']
-        self.addCleanup(self._delete_domain, domain['id'])
+        domain = self.domains_client.create_domain(
+            name=d_name, description=d_desc)['domain']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self._delete_domain, domain['id'])
         self.assertIn('id', domain)
         self.assertIn('description', domain)
         self.assertIn('name', domain)
@@ -70,11 +105,12 @@
         self.assertEqual(d_name, domain['name'])
         self.assertEqual(d_desc, domain['description'])
         self.assertEqual(True, domain['enabled'])
+        # Update domain
         new_desc = data_utils.rand_name('new-desc')
         new_name = data_utils.rand_name('new-name')
-
-        updated_domain = self.client.update_domain(
-            domain['id'], name=new_name, description=new_desc)['domain']
+        updated_domain = self.domains_client.update_domain(
+            domain['id'], name=new_name, description=new_desc,
+            enabled=False)['domain']
         self.assertIn('id', updated_domain)
         self.assertIn('description', updated_domain)
         self.assertIn('name', updated_domain)
@@ -83,21 +119,27 @@
         self.assertIsNotNone(updated_domain['id'])
         self.assertEqual(new_name, updated_domain['name'])
         self.assertEqual(new_desc, updated_domain['description'])
-        self.assertEqual(True, updated_domain['enabled'])
-
-        fetched_domain = self.client.show_domain(domain['id'])['domain']
+        self.assertEqual(False, updated_domain['enabled'])
+        # Show domain
+        fetched_domain = self.domains_client.show_domain(
+            domain['id'])['domain']
         self.assertEqual(new_name, fetched_domain['name'])
         self.assertEqual(new_desc, fetched_domain['description'])
-        self.assertEqual(True, fetched_domain['enabled'])
+        self.assertEqual(False, fetched_domain['enabled'])
+        # Delete domain
+        self.domains_client.delete_domain(domain['id'])
+        body = self.domains_client.list_domains()['domains']
+        domains_list = [d['id'] for d in body]
+        self.assertNotIn(domain['id'], domains_list)
 
     @test.idempotent_id('036df86e-bb5d-42c0-a7c2-66b9db3a6046')
     def test_create_domain_with_disabled_status(self):
         # Create domain with enabled status as false
         d_name = data_utils.rand_name('domain')
         d_desc = data_utils.rand_name('domain-desc')
-        domain = self.client.create_domain(
-            d_name, description=d_desc, enabled=False)['domain']
-        self.addCleanup(self.client.delete_domain, domain['id'])
+        domain = self.domains_client.create_domain(
+            name=d_name, description=d_desc, enabled=False)['domain']
+        self.addCleanup(self.domains_client.delete_domain, domain['id'])
         self.assertEqual(d_name, domain['name'])
         self.assertFalse(domain['enabled'])
         self.assertEqual(d_desc, domain['description'])
@@ -106,11 +148,15 @@
     def test_create_domain_without_description(self):
         # Create domain only with name
         d_name = data_utils.rand_name('domain')
-        domain = self.client.create_domain(d_name)['domain']
+        domain = self.domains_client.create_domain(name=d_name)['domain']
         self.addCleanup(self._delete_domain, domain['id'])
         self.assertIn('id', domain)
         expected_data = {'name': d_name, 'enabled': True}
-        self.assertIsNone(domain['description'])
+        # TODO(gmann): there is bug in keystone liberty version where
+        # description is not being returned if it is not being passed in
+        # request. Bug#1649245. Once bug is fixed then we can enable the below
+        # check.
+        # self.assertEqual('', domain['description'])
         self.assertDictContainsSubset(expected_data, domain)
 
 
@@ -124,6 +170,6 @@
     @test.attr(type='smoke')
     @test.idempotent_id('17a5de24-e6a0-4e4a-a9ee-d85b6e5612b5')
     def test_default_domain_exists(self):
-        domain = self.client.show_domain(self.domain_id)['domain']
+        domain = self.domains_client.show_domain(self.domain_id)['domain']
 
         self.assertTrue(domain['enabled'])
diff --git a/tempest/api/identity/admin/v3/test_domains_negative.py b/tempest/api/identity/admin/v3/test_domains_negative.py
index 9eb3149..100a121 100644
--- a/tempest/api/identity/admin/v3/test_domains_negative.py
+++ b/tempest/api/identity/admin/v3/test_domains_negative.py
@@ -14,11 +14,10 @@
 #    under the License.
 
 from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
-from tempest_lib.common.utils import data_utils
-from tempest_lib import exceptions as lib_exc
-
 
 class DomainsNegativeTestJSON(base.BaseIdentityV3AdminTest):
     _interface = 'json'
@@ -28,45 +27,48 @@
     def test_delete_active_domain(self):
         d_name = data_utils.rand_name('domain')
         d_desc = data_utils.rand_name('domain-desc')
-        domain = self.client.create_domain(d_name,
-                                           description=d_desc)['domain']
+        domain = self.domains_client.create_domain(
+            name=d_name,
+            description=d_desc)['domain']
         domain_id = domain['id']
 
         self.addCleanup(self.delete_domain, domain_id)
 
         # domain need to be disabled before deleting
-        self.assertRaises(lib_exc.Forbidden, self.client.delete_domain,
+        self.assertRaises(lib_exc.Forbidden, self.domains_client.delete_domain,
                           domain_id)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('9018461d-7d24-408d-b3fe-ae37e8cd5c9e')
     def test_create_domain_with_empty_name(self):
         # Domain name should not be empty
-        self.assertRaises(lib_exc.BadRequest, self.client.create_domain,
-                          name='')
+        self.assertRaises(lib_exc.BadRequest,
+                          self.domains_client.create_domain, name='')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('37b1bbf2-d664-4785-9a11-333438586eae')
     def test_create_domain_with_name_length_over_64(self):
         # Domain name length should not ne greater than 64 characters
         d_name = 'a' * 65
-        self.assertRaises(lib_exc.BadRequest, self.client.create_domain,
-                          d_name)
+        self.assertRaises(lib_exc.BadRequest,
+                          self.domains_client.create_domain,
+                          name=d_name)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('43781c07-764f-4cf2-a405-953c1916f605')
     def test_delete_non_existent_domain(self):
         # Attempt to delete a non existent domain should fail
-        self.assertRaises(lib_exc.NotFound, self.client.delete_domain,
+        self.assertRaises(lib_exc.NotFound, self.domains_client.delete_domain,
                           data_utils.rand_uuid_hex())
 
     @test.attr(type=['negative'])
     @test.idempotent_id('e6f9e4a2-4f36-4be8-bdbc-4e199ae29427')
     def test_domain_create_duplicate(self):
         domain_name = data_utils.rand_name('domain-dup')
-        domain = self.client.create_domain(domain_name)['domain']
+        domain = self.domains_client.create_domain(name=domain_name)['domain']
         domain_id = domain['id']
         self.addCleanup(self.delete_domain, domain_id)
         # Domain name should be unique
         self.assertRaises(
-            lib_exc.Conflict, self.client.create_domain, domain_name)
+            lib_exc.Conflict, self.domains_client.create_domain,
+            name=domain_name)
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 50cf258..655e4ef 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -23,7 +23,6 @@
     @classmethod
     def setup_clients(cls):
         super(EndPointsTestJSON, cls).setup_clients()
-        cls.identity_client = cls.client
         cls.client = cls.endpoints_client
 
     @classmethod
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index 372254f..f0f8707 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -14,10 +14,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -26,7 +25,6 @@
     @classmethod
     def setup_clients(cls):
         super(EndpointsNegativeTestJSON, cls).setup_clients()
-        cls.identity_client = cls.client
         cls.client = cls.endpoints_client
 
     @classmethod
diff --git a/tempest/api/identity/admin/v3/test_groups.py b/tempest/api/identity/admin/v3/test_groups.py
index 010e4a0..3cbcc1f 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -20,12 +20,25 @@
 
 class GroupsV3TestJSON(base.BaseIdentityV3AdminTest):
 
+    @classmethod
+    def resource_setup(cls):
+        super(GroupsV3TestJSON, cls).resource_setup()
+        cls.domain = cls.create_domain()
+
+    @classmethod
+    def resource_cleanup(cls):
+        # Cleanup the domains created in the setup
+        cls.domains_client.update_domain(cls.domain['id'], enabled=False)
+        cls.domains_client.delete_domain(cls.domain['id'])
+        super(GroupsV3TestJSON, cls).resource_cleanup()
+
     @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.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 +60,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.domain['id'],
+            description=old_description)['group']
         self.addCleanup(self.groups_client.delete_group, group['id'])
 
         new_name = data_utils.rand_name('UpdateGroup')
@@ -61,21 +75,24 @@
     @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.domain['id'])['group']
         self.addCleanup(self.groups_client.delete_group, group['id'])
         # add user into group
         users = []
         for i in range(3):
             name = data_utils.rand_name('User')
             password = data_utils.rand_password()
-            user = self.users_client.create_user(name, password)['user']
+            user = self.users_client.create_user(name=name,
+                                                 password=password)['user']
             users.append(user)
             self.addCleanup(self.users_client.delete_user, user['id'])
             self.groups_client.add_group_user(group['id'], user['id'])
 
         # list users in group
         group_users = self.groups_client.list_group_users(group['id'])['users']
-        self.assertEqual(sorted(users), sorted(group_users))
+        self.assertEqual(sorted(users, key=lambda k: k['name']),
+                         sorted(group_users, key=lambda k: k['name']))
         # check and delete user in group
         for user in users:
             self.groups_client.check_group_user_existence(
@@ -88,19 +105,22 @@
     def test_list_user_groups(self):
         # create a user
         user = self.users_client.create_user(
-            data_utils.rand_name('User'), data_utils.rand_password())['user']
+            name=data_utils.rand_name('User'),
+            password=data_utils.rand_password())['user']
         self.addCleanup(self.users_client.delete_user, user['id'])
         # create two groups, and add user into them
         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.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'])
         # list groups which user belongs to
         user_groups = self.users_client.list_user_groups(user['id'])['groups']
-        self.assertEqual(sorted(groups), sorted(user_groups))
+        self.assertEqual(sorted(groups, key=lambda k: k['name']),
+                         sorted(user_groups, key=lambda k: k['name']))
         self.assertEqual(2, len(user_groups))
 
     @test.idempotent_id('cc9a57a5-a9ed-4f2d-a29f-4f979a06ec71')
@@ -112,7 +132,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.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..33fce8d
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -0,0 +1,235 @@
+#    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 test
+
+
+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(
+            name=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(
+            name=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.inherited_roles_client.create_inherited_role_on_domains_user(
+            self.domain['id'], self.user['id'], src_role['id'])
+        # list role on domains user
+        roles = self.inherited_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.inherited_roles_client.
+         check_user_inherited_project_role_on_domain(
+             self.domain['id'], self.user['id'], src_role['id']))
+        # Revoke role from domains user.
+        self.inherited_roles_client.delete_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.inherited_roles_client.create_inherited_role_on_domains_group(
+            self.domain['id'], self.group['id'], src_role['id'])
+        # List role on domains group
+        roles = self.inherited_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.inherited_roles_client.
+         check_group_inherited_project_role_on_domain(
+             self.domain['id'], self.group['id'], src_role['id']))
+        # Revoke role from domains group
+        self.inherited_roles_client.delete_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.inherited_roles_client.create_inherited_role_on_projects_user(
+            self.project['id'], self.user['id'], src_role['id'])
+        # Check role on projects user
+        (self.inherited_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.inherited_roles_client.delete_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.inherited_roles_client.create_inherited_role_on_projects_group(
+            self.project['id'], self.group['id'], src_role['id'])
+        # Check role on projects group
+        (self.inherited_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.inherited_roles_client.
+         delete_inherited_role_from_group_on_project(
+             self.project['id'], self.group['id'], src_role['id']))
+
+    @test.idempotent_id('3acf666e-5354-42ac-8e17-8b68893bcd36')
+    def test_inherit_assign_list_revoke_user_roles_on_domain(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'])
+
+        # Create a project hierarchy
+        leaf_project_name = data_utils.rand_name('project')
+        leaf_project = self.projects_client.create_project(
+            leaf_project_name, domain_id=self.domain['id'],
+            parent_id=self.project['id'])['project']
+        self.addCleanup(
+            self.projects_client.delete_project, leaf_project['id'])
+
+        # Assign role on domain
+        self.inherited_roles_client.create_inherited_role_on_domains_user(
+            self.domain['id'], self.user['id'], src_role['id'])
+
+        # List "effective" role assignments from user on the parent project
+        params = {'scope.project.id': self.project['id'],
+                  'user.id': self.user['id']}
+        assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
+        self.assertNotEmpty(assignments)
+
+        # List "effective" role assignments from user on the leaf project
+        params['scope.project.id'] = leaf_project['id']
+        assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
+        self.assertNotEmpty(assignments)
+
+        # Revoke role from domain
+        self.inherited_roles_client.delete_inherited_role_from_user_on_domain(
+            self.domain['id'], self.user['id'], src_role['id'])
+
+        # List "effective" role assignments from user on the parent project
+        # should return an empty list
+        params['scope.project.id'] = self.project['id']
+        assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
+        self.assertEmpty(assignments)
+
+        # List "effective" role assignments from user on the leaf project
+        # should return an empty list
+        params['scope.project.id'] = leaf_project['id']
+        assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
+        self.assertEmpty(assignments)
+
+    @test.idempotent_id('9f02ccd9-9b57-46b4-8f77-dd5a736f3a06')
+    def test_inherit_assign_list_revoke_user_roles_on_project_tree(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'])
+
+        # Create a project hierarchy
+        leaf_project_name = data_utils.rand_name('project')
+        leaf_project = self.projects_client.create_project(
+            leaf_project_name, domain_id=self.domain['id'],
+            parent_id=self.project['id'])['project']
+        self.addCleanup(
+            self.projects_client.delete_project, leaf_project['id'])
+
+        # Assign role on parent project
+        self.inherited_roles_client.create_inherited_role_on_projects_user(
+            self.project['id'], self.user['id'], src_role['id'])
+
+        # List "effective" role assignments from user on the leaf project
+        params = {'scope.project.id': leaf_project['id'],
+                  'user.id': self.user['id']}
+        assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
+        self.assertNotEmpty(assignments)
+
+        # Revoke role from parent project
+        self.inherited_roles_client.delete_inherited_role_from_user_on_project(
+            self.project['id'], self.user['id'], src_role['id'])
+
+        # List "effective" role assignments from user on the leaf project
+        # should return an empty list
+        assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
+        self.assertEmpty(assignments)
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 928437c..7d9e41b 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -24,22 +24,40 @@
     def resource_setup(cls):
         super(ListProjectsTestJSON, cls).resource_setup()
         cls.project_ids = list()
-        cls.data.setup_test_domain()
+        # Create a domain
+        cls.domain = cls.create_domain()
         # Create project with domain
+        cls.projects = list()
         cls.p1_name = data_utils.rand_name('project')
         cls.p1 = cls.projects_client.create_project(
             cls.p1_name, enabled=False,
-            domain_id=cls.data.domain['id'])['project']
-        cls.data.projects.append(cls.p1)
+            domain_id=cls.domain['id'])['project']
+        cls.projects.append(cls.p1)
         cls.project_ids.append(cls.p1['id'])
         # Create default project
         p2_name = data_utils.rand_name('project')
         cls.p2 = cls.projects_client.create_project(p2_name)['project']
-        cls.data.projects.append(cls.p2)
+        cls.projects.append(cls.p2)
         cls.project_ids.append(cls.p2['id'])
+        # Create a new project (p3) using p2 as parent project
+        p3_name = data_utils.rand_name('project')
+        cls.p3 = cls.projects_client.create_project(
+            p3_name, parent_id=cls.p2['id'])['project']
+        cls.projects.append(cls.p3)
+        cls.project_ids.append(cls.p3['id'])
+
+    @classmethod
+    def resource_cleanup(cls):
+        # Cleanup the projects created during setup in inverse order
+        for project in reversed(cls.projects):
+            cls.projects_client.delete_project(project['id'])
+        # Cleanup the domain created during setup
+        cls.domains_client.update_domain(cls.domain['id'], enabled=False)
+        cls.domains_client.delete_domain(cls.domain['id'])
+        super(ListProjectsTestJSON, cls).resource_cleanup()
 
     @test.idempotent_id('1d830662-22ad-427c-8c3e-4ec854b0af44')
-    def test_projects_list(self):
+    def test_list_projects(self):
         # List projects
         list_projects = self.projects_client.list_projects()['projects']
 
@@ -51,7 +69,7 @@
     def test_list_projects_with_domains(self):
         # List projects with domain
         self._list_projects_with_params(
-            {'domain_id': self.data.domain['id']}, 'domain_id')
+            {'domain_id': self.domain['id']}, 'domain_id')
 
     @test.idempotent_id('0fe7a334-675a-4509-b00e-1c4b95d5dae8')
     def test_list_projects_with_enabled(self):
@@ -63,6 +81,16 @@
         # List projects with name
         self._list_projects_with_params({'name': self.p1_name}, 'name')
 
+    @test.idempotent_id('6edc66f5-2941-4a17-9526-4073311c1fac')
+    def test_list_projects_with_parent(self):
+        # List projects with parent
+        params = {'parent_id': self.p3['parent_id']}
+        fetched_projects = self.projects_client.list_projects(
+            params)['projects']
+        self.assertNotEmpty(fetched_projects)
+        for project in fetched_projects:
+            self.assertEqual(self.p3['parent_id'], project['parent_id'])
+
     def _list_projects_with_params(self, params, key):
         body = self.projects_client.list_projects(params)['projects']
         self.assertIn(self.p1[key], map(lambda x: x[key], body))
diff --git a/tempest/api/identity/admin/v3/test_list_users.py b/tempest/api/identity/admin/v3/test_list_users.py
index 5b27ab1..99df559 100644
--- a/tempest/api/identity/admin/v3/test_list_users.py
+++ b/tempest/api/identity/admin/v3/test_list_users.py
@@ -25,7 +25,7 @@
         # assert the response based on expected and not_expected
         # expected: user expected in the list response
         # not_expected: user, which should not be present in list response
-        body = self.users_client.list_users(params)['users']
+        body = self.users_client.list_users(**params)['users']
         self.assertIn(expected[key], map(lambda x: x[key], body))
         self.assertNotIn(not_expected[key],
                          map(lambda x: x[key], body))
@@ -36,24 +36,36 @@
         alt_user = data_utils.rand_name('test_user')
         alt_password = data_utils.rand_password()
         cls.alt_email = alt_user + '@testmail.tm'
-        cls.data.setup_test_domain()
+        # Create a domain
+        cls.domain = cls.create_domain()
         # Create user with Domain
+        cls.users = list()
         u1_name = data_utils.rand_name('test_user')
         cls.domain_enabled_user = cls.users_client.create_user(
-            u1_name, password=alt_password,
-            email=cls.alt_email, domain_id=cls.data.domain['id'])['user']
-        cls.data.users.append(cls.domain_enabled_user)
+            name=u1_name, password=alt_password,
+            email=cls.alt_email, domain_id=cls.domain['id'])['user']
+        cls.users.append(cls.domain_enabled_user)
         # Create default not enabled user
         u2_name = data_utils.rand_name('test_user')
         cls.non_domain_enabled_user = cls.users_client.create_user(
-            u2_name, password=alt_password,
+            name=u2_name, password=alt_password,
             email=cls.alt_email, enabled=False)['user']
-        cls.data.users.append(cls.non_domain_enabled_user)
+        cls.users.append(cls.non_domain_enabled_user)
+
+    @classmethod
+    def resource_cleanup(cls):
+        # Cleanup the users created during setup
+        for user in cls.users:
+            cls.users_client.delete_user(user['id'])
+        # Cleanup the domain created during setup
+        cls.domains_client.update_domain(cls.domain['id'], enabled=False)
+        cls.domains_client.delete_domain(cls.domain['id'])
+        super(UsersV3TestJSON, cls).resource_cleanup()
 
     @test.idempotent_id('08f9aabb-dcfe-41d0-8172-82b5fa0bd73d')
     def test_list_user_domains(self):
         # List users with domain
-        params = {'domain_id': self.data.domain['id']}
+        params = {'domain_id': self.domain['id']}
         self._list_users_with_params(params, 'domain_id',
                                      self.domain_enabled_user,
                                      self.non_domain_enabled_user)
@@ -79,7 +91,7 @@
         # List users
         body = self.users_client.list_users()['users']
         fetched_ids = [u['id'] for u in body]
-        missing_users = [u['id'] for u in self.data.users
+        missing_users = [u['id'] for u in self.users
                          if u['id'] not in fetched_ids]
         self.assertEqual(0, len(missing_users),
                          "Failed to find user %s in fetched list" %
@@ -88,8 +100,8 @@
     @test.idempotent_id('b4baa3ae-ac00-4b4e-9e27-80deaad7771f')
     def test_get_user(self):
         # Get a user detail
-        user = self.users_client.show_user(self.data.users[0]['id'])['user']
-        self.assertEqual(self.data.users[0]['id'], user['id'])
-        self.assertEqual(self.data.users[0]['name'], user['name'])
+        user = self.users_client.show_user(self.users[0]['id'])['user']
+        self.assertEqual(self.users[0]['id'], user['id'])
+        self.assertEqual(self.users[0]['name'], user['name'])
         self.assertEqual(self.alt_email, user['email'])
-        self.assertEqual(self.data.domain['id'], user['domain_id'])
+        self.assertEqual(self.domain['id'], user['domain_id'])
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index 607bebe..1137191 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -1,4 +1,4 @@
-# Copyright 2013 OpenStack, LLC
+# Copyright 2013 OpenStack Foundation
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -13,10 +13,15 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 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 ProjectsTestJSON(base.BaseIdentityV3AdminTest):
 
@@ -27,7 +32,7 @@
         project_desc = data_utils.rand_name('desc')
         project = self.projects_client.create_project(
             project_name, description=project_desc)['project']
-        self.data.projects.append(project)
+        self.addCleanup(self.projects_client.delete_project, project['id'])
         project_id = project['id']
         desc1 = project['description']
         self.assertEqual(desc1, project_desc, 'Description should have '
@@ -40,17 +45,48 @@
     @test.idempotent_id('5f50fe07-8166-430b-a882-3b2ee0abe26f')
     def test_project_create_with_domain(self):
         # Create project with a domain
-        self.data.setup_test_domain()
+        domain = self.setup_test_domain()
         project_name = data_utils.rand_name('project')
         project = self.projects_client.create_project(
-            project_name, domain_id=self.data.domain['id'])['project']
-        self.data.projects.append(project)
+            project_name, domain_id=domain['id'])['project']
+        self.addCleanup(self.projects_client.delete_project, project['id'])
         project_id = project['id']
         self.assertEqual(project_name, project['name'])
-        self.assertEqual(self.data.domain['id'], project['domain_id'])
+        self.assertEqual(domain['id'], project['domain_id'])
         body = self.projects_client.show_project(project_id)['project']
         self.assertEqual(project_name, body['name'])
-        self.assertEqual(self.data.domain['id'], body['domain_id'])
+        self.assertEqual(domain['id'], body['domain_id'])
+
+    @testtools.skipUnless(CONF.identity_feature_enabled.reseller,
+                          'Reseller not available.')
+    @test.idempotent_id('1854f9c0-70bc-4d11-a08a-1c789d339e3d')
+    def test_project_create_with_parent(self):
+        # Create root project without providing a parent_id
+        domain = self.setup_test_domain()
+        domain_id = domain['id']
+
+        root_project_name = data_utils.rand_name('root_project')
+        root_project = self.projects_client.create_project(
+            root_project_name, domain_id=domain_id)['project']
+        self.addCleanup(
+            self.projects_client.delete_project, root_project['id'])
+
+        root_project_id = root_project['id']
+        parent_id = root_project['parent_id']
+        self.assertEqual(root_project_name, root_project['name'])
+        # If not provided, the parent_id must point to the top level
+        # project in the hierarchy, i.e. its domain
+        self.assertEqual(domain_id, parent_id)
+
+        # Create a project using root_project_id as parent_id
+        project_name = data_utils.rand_name('project')
+        project = self.projects_client.create_project(
+            project_name, domain_id=domain_id,
+            parent_id=root_project_id)['project']
+        self.addCleanup(self.projects_client.delete_project, project['id'])
+        parent_id = project['parent_id']
+        self.assertEqual(project_name, project['name'])
+        self.assertEqual(root_project_id, parent_id)
 
     @test.idempotent_id('1f66dc76-50cc-4741-a200-af984509e480')
     def test_project_create_enabled(self):
@@ -58,7 +94,7 @@
         project_name = data_utils.rand_name('project')
         project = self.projects_client.create_project(
             project_name, enabled=True)['project']
-        self.data.projects.append(project)
+        self.addCleanup(self.projects_client.delete_project, project['id'])
         project_id = project['id']
         en1 = project['enabled']
         self.assertTrue(en1, 'Enable should be True in response')
@@ -72,7 +108,7 @@
         project_name = data_utils.rand_name('project')
         project = self.projects_client.create_project(
             project_name, enabled=False)['project']
-        self.data.projects.append(project)
+        self.addCleanup(self.projects_client.delete_project, project['id'])
         en1 = project['enabled']
         self.assertEqual('false', str(en1).lower(),
                          'Enable should be False in response')
@@ -86,7 +122,7 @@
         # Update name attribute of a project
         p_name1 = data_utils.rand_name('project')
         project = self.projects_client.create_project(p_name1)['project']
-        self.data.projects.append(project)
+        self.addCleanup(self.projects_client.delete_project, project['id'])
 
         resp1_name = project['name']
 
@@ -110,7 +146,7 @@
         p_desc = data_utils.rand_name('desc')
         project = self.projects_client.create_project(
             p_name, description=p_desc)['project']
-        self.data.projects.append(project)
+        self.addCleanup(self.projects_client.delete_project, project['id'])
         resp1_desc = project['description']
 
         p_desc2 = data_utils.rand_name('desc2')
@@ -133,7 +169,7 @@
         p_en = False
         project = self.projects_client.create_project(p_name,
                                                       enabled=p_en)['project']
-        self.data.projects.append(project)
+        self.addCleanup(self.projects_client.delete_project, project['id'])
 
         resp1_en = project['enabled']
 
@@ -156,7 +192,7 @@
         # Create a Project
         p_name = data_utils.rand_name('project')
         project = self.projects_client.create_project(p_name)['project']
-        self.data.projects.append(project)
+        self.addCleanup(self.projects_client.delete_project, project['id'])
 
         # Create a User
         u_name = data_utils.rand_name('user')
@@ -164,7 +200,7 @@
         u_email = u_name + '@testmail.tm'
         u_password = data_utils.rand_password()
         user = self.users_client.create_user(
-            u_name, description=u_desc, password=u_password,
+            name=u_name, description=u_desc, password=u_password,
             email=u_email, project_id=project['id'])['user']
         # Delete the User at the end of this method
         self.addCleanup(self.users_client.delete_user, user['id'])
diff --git a/tempest/api/identity/admin/v3/test_projects_negative.py b/tempest/api/identity/admin/v3/test_projects_negative.py
index 79cfc91..c76b9ee 100644
--- a/tempest/api/identity/admin/v3/test_projects_negative.py
+++ b/tempest/api/identity/admin/v3/test_projects_negative.py
@@ -1,4 +1,4 @@
-# Copyright 2013 OpenStack, LLC
+# Copyright 2013 OpenStack Foundation
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -13,10 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -35,7 +34,7 @@
         # Project names should be unique
         project_name = data_utils.rand_name('project-dup')
         project = self.projects_client.create_project(project_name)['project']
-        self.data.projects.append(project)
+        self.addCleanup(self.projects_client.delete_project, project['id'])
 
         self.assertRaises(lib_exc.Conflict,
                           self.projects_client.create_project, project_name)
@@ -70,7 +69,7 @@
         # Non-admin user should not be able to delete a project
         project_name = data_utils.rand_name('project')
         project = self.projects_client.create_project(project_name)['project']
-        self.data.projects.append(project)
+        self.addCleanup(self.projects_client.delete_project, project['id'])
         self.assertRaises(
             lib_exc.Forbidden, self.non_admin_projects_client.delete_project,
             project['id'])
diff --git a/tempest/api/identity/admin/v3/test_regions.py b/tempest/api/identity/admin/v3/test_regions.py
index 8bba3cb..95894a6 100644
--- a/tempest/api/identity/admin/v3/test_regions.py
+++ b/tempest/api/identity/admin/v3/test_regions.py
@@ -13,10 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest import test
 
 
@@ -43,18 +42,19 @@
             cls.client.delete_region(r['id'])
         super(RegionsTestJSON, cls).resource_cleanup()
 
-    def _delete_region(self, region_id):
-        self.client.delete_region(region_id)
-        self.assertRaises(lib_exc.NotFound,
-                          self.client.show_region, region_id)
-
     @test.idempotent_id('56186092-82e4-43f2-b954-91013218ba42')
     def test_create_update_get_delete_region(self):
+        # Create region
         r_description = data_utils.rand_name('description')
         region = self.client.create_region(
             description=r_description,
             parent_region_id=self.setup_regions[0]['id'])['region']
-        self.addCleanup(self._delete_region, region['id'])
+        # This test will delete the region as part of the validation
+        # procedure, so it needs a different cleanup method that
+        # would be useful in case the tests fails at any point before
+        # reaching the deletion part.
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_region, region['id'])
         self.assertEqual(r_description, region['description'])
         self.assertEqual(self.setup_regions[0]['id'],
                          region['parent_region_id'])
@@ -72,6 +72,11 @@
         self.assertEqual(r_alt_description, region['description'])
         self.assertEqual(self.setup_regions[1]['id'],
                          region['parent_region_id'])
+        # Delete the region
+        self.client.delete_region(region['id'])
+        body = self.client.list_regions()['regions']
+        regions_list = [r['id'] for r in body]
+        self.assertNotIn(region['id'], regions_list)
 
     @test.attr(type='smoke')
     @test.idempotent_id('2c12c5b5-efcf-4aa5-90c5-bff1ab0cdbe2')
@@ -81,7 +86,7 @@
         r_description = data_utils.rand_name('description')
         region = self.client.create_region(
             region_id=r_region_id, description=r_description)['region']
-        self.addCleanup(self._delete_region, region['id'])
+        self.addCleanup(self.client.delete_region, region['id'])
         # Asserting Create Region with specific id response body
         self.assertEqual(r_region_id, region['id'])
         self.assertEqual(r_description, region['description'])
@@ -96,3 +101,20 @@
         self.assertEqual(0, len(missing_regions),
                          "Failed to find region %s in fetched list" %
                          ', '.join(str(e) for e in missing_regions))
+
+    @test.idempotent_id('2d1057cb-bbde-413a-acdf-e2d265284542')
+    def test_list_regions_filter_by_parent_region_id(self):
+        # Add a sub-region to one of the existing test regions
+        r_description = data_utils.rand_name('description')
+        region = self.client.create_region(
+            description=r_description,
+            parent_region_id=self.setup_regions[0]['id'])['region']
+        self.addCleanup(self.client.delete_region, region['id'])
+        # Get the list of regions filtering with the parent_region_id
+        params = {'parent_region_id': self.setup_regions[0]['id']}
+        fetched_regions = self.client.list_regions(params=params)['regions']
+        # Asserting list regions response
+        self.assertIn(region, fetched_regions)
+        for r in fetched_regions:
+            self.assertEqual(self.setup_regions[0]['id'],
+                             r['parent_region_id'])
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index 468f169..670cb2f 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -23,17 +23,18 @@
     @classmethod
     def resource_setup(cls):
         super(RolesV3TestJSON, cls).resource_setup()
+        cls.roles = list()
         for _ in range(3):
             role_name = data_utils.rand_name(name='role')
-            role = cls.client.create_role(name=role_name)['role']
-            cls.data.roles.append(role)
+            role = cls.roles_client.create_role(name=role_name)['role']
+            cls.roles.append(role)
         cls.fetched_role_ids = list()
         u_name = data_utils.rand_name('user')
         u_desc = '%s description' % u_name
         u_email = '%s@testmail.tm' % u_name
         cls.u_password = data_utils.rand_password()
-        cls.domain = cls.client.create_domain(
-            data_utils.rand_name('domain'),
+        cls.domain = cls.domains_client.create_domain(
+            name=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'),
@@ -43,22 +44,24 @@
             name=data_utils.rand_name('Group'), project_id=cls.project['id'],
             domain_id=cls.domain['id'])['group']
         cls.user_body = cls.users_client.create_user(
-            u_name, description=u_desc, password=cls.u_password,
+            name=u_name, description=u_desc, password=cls.u_password,
             email=u_email, project_id=cls.project['id'],
             domain_id=cls.domain['id'])['user']
-        cls.role = cls.client.create_role(
+        cls.role = cls.roles_client.create_role(
             name=data_utils.rand_name('Role'))['role']
 
     @classmethod
     def resource_cleanup(cls):
-        cls.client.delete_role(cls.role['id'])
+        cls.roles_client.delete_role(cls.role['id'])
         cls.groups_client.delete_group(cls.group_body['id'])
         cls.users_client.delete_user(cls.user_body['id'])
         cls.projects_client.delete_project(cls.project['id'])
         # NOTE(harika-vakadi): It is necessary to disable the domain
         # before deleting,or else it would result in unauthorized error
-        cls.client.update_domain(cls.domain['id'], enabled=False)
-        cls.client.delete_domain(cls.domain['id'])
+        cls.domains_client.update_domain(cls.domain['id'], enabled=False)
+        cls.domains_client.delete_domain(cls.domain['id'])
+        for role in cls.roles:
+            cls.roles_client.delete_role(role['id'])
         super(RolesV3TestJSON, cls).resource_cleanup()
 
     def _list_assertions(self, body, fetched_role_ids, role_id):
@@ -67,34 +70,35 @@
 
     @test.attr(type='smoke')
     @test.idempotent_id('18afc6c0-46cf-4911-824e-9989cc056c3a')
-    def test_role_create_update_get_list(self):
+    def test_role_create_update_show_list(self):
         r_name = data_utils.rand_name('Role')
-        role = self.client.create_role(name=r_name)['role']
-        self.addCleanup(self.client.delete_role, role['id'])
+        role = self.roles_client.create_role(name=r_name)['role']
+        self.addCleanup(self.roles_client.delete_role, role['id'])
         self.assertIn('name', role)
         self.assertEqual(role['name'], r_name)
 
         new_name = data_utils.rand_name('NewRole')
-        updated_role = self.client.update_role(role['id'],
-                                               name=new_name)['role']
+        updated_role = self.roles_client.update_role(role['id'],
+                                                     name=new_name)['role']
         self.assertIn('name', updated_role)
         self.assertIn('id', updated_role)
         self.assertIn('links', updated_role)
         self.assertNotEqual(r_name, updated_role['name'])
 
-        new_role = self.client.show_role(role['id'])['role']
+        new_role = self.roles_client.show_role(role['id'])['role']
         self.assertEqual(new_name, new_role['name'])
         self.assertEqual(updated_role['id'], new_role['id'])
 
-        roles = self.client.list_roles()['roles']
+        roles = self.roles_client.list_roles()['roles']
         self.assertIn(role['id'], [r['id'] for r in roles])
 
     @test.idempotent_id('c6b80012-fe4a-498b-9ce8-eb391c05169f')
     def test_grant_list_revoke_role_to_user_on_project(self):
-        self.client.assign_user_role_on_project(
-            self.project['id'], self.user_body['id'], self.role['id'])
+        self.roles_client.create_user_role_on_project(self.project['id'],
+                                                      self.user_body['id'],
+                                                      self.role['id'])
 
-        roles = self.client.list_user_roles_on_project(
+        roles = self.roles_client.list_user_roles_on_project(
             self.project['id'], self.user_body['id'])['roles']
 
         for i in roles:
@@ -103,18 +107,18 @@
         self._list_assertions(roles, self.fetched_role_ids,
                               self.role['id'])
 
-        self.client.check_user_role_existence_on_project(
+        self.roles_client.check_user_role_existence_on_project(
             self.project['id'], self.user_body['id'], self.role['id'])
 
-        self.client.delete_role_from_user_on_project(
+        self.roles_client.delete_role_from_user_on_project(
             self.project['id'], self.user_body['id'], self.role['id'])
 
     @test.idempotent_id('6c9a2940-3625-43a3-ac02-5dcec62ef3bd')
     def test_grant_list_revoke_role_to_user_on_domain(self):
-        self.client.assign_user_role_on_domain(
+        self.roles_client.create_user_role_on_domain(
             self.domain['id'], self.user_body['id'], self.role['id'])
 
-        roles = self.client.list_user_roles_on_domain(
+        roles = self.roles_client.list_user_roles_on_domain(
             self.domain['id'], self.user_body['id'])['roles']
 
         for i in roles:
@@ -123,19 +127,19 @@
         self._list_assertions(roles, self.fetched_role_ids,
                               self.role['id'])
 
-        self.client.check_user_role_existence_on_domain(
+        self.roles_client.check_user_role_existence_on_domain(
             self.domain['id'], self.user_body['id'], self.role['id'])
 
-        self.client.delete_role_from_user_on_domain(
+        self.roles_client.delete_role_from_user_on_domain(
             self.domain['id'], self.user_body['id'], self.role['id'])
 
     @test.idempotent_id('cbf11737-1904-4690-9613-97bcbb3df1c4')
     def test_grant_list_revoke_role_to_group_on_project(self):
         # Grant role to group on project
-        self.client.assign_group_role_on_project(
+        self.roles_client.create_group_role_on_project(
             self.project['id'], self.group_body['id'], self.role['id'])
         # List group roles on project
-        roles = self.client.list_group_roles_on_project(
+        roles = self.roles_client.list_group_roles_on_project(
             self.project['id'], self.group_body['id'])['roles']
 
         for i in roles:
@@ -157,19 +161,19 @@
         self.assertEqual(len(roles), 1)
         self.assertEqual(roles[0]['id'], self.role['id'])
 
-        self.client.check_role_from_group_on_project_existence(
+        self.roles_client.check_role_from_group_on_project_existence(
             self.project['id'], self.group_body['id'], self.role['id'])
 
         # Revoke role to group on project
-        self.client.delete_role_from_group_on_project(
+        self.roles_client.delete_role_from_group_on_project(
             self.project['id'], self.group_body['id'], self.role['id'])
 
     @test.idempotent_id('4bf8a70b-e785-413a-ad53-9f91ce02faa7')
     def test_grant_list_revoke_role_to_group_on_domain(self):
-        self.client.assign_group_role_on_domain(
+        self.roles_client.create_group_role_on_domain(
             self.domain['id'], self.group_body['id'], self.role['id'])
 
-        roles = self.client.list_group_roles_on_domain(
+        roles = self.roles_client.list_group_roles_on_domain(
             self.domain['id'], self.group_body['id'])['roles']
 
         for i in roles:
@@ -178,15 +182,15 @@
         self._list_assertions(roles, self.fetched_role_ids,
                               self.role['id'])
 
-        self.client.check_role_from_group_on_domain_existence(
+        self.roles_client.check_role_from_group_on_domain_existence(
             self.domain['id'], self.group_body['id'], self.role['id'])
 
-        self.client.delete_role_from_group_on_domain(
+        self.roles_client.delete_role_from_group_on_domain(
             self.domain['id'], self.group_body['id'], self.role['id'])
 
     @test.idempotent_id('f5654bcc-08c4-4f71-88fe-05d64e06de94')
     def test_list_roles(self):
         # Return a list of all roles
-        body = self.client.list_roles()['roles']
-        found = [role for role in body if role in self.data.roles]
-        self.assertEqual(len(found), len(self.data.roles))
+        body = self.roles_client.list_roles()['roles']
+        found = [role for role in body if role in self.roles]
+        self.assertEqual(len(found), len(self.roles))
diff --git a/tempest/api/identity/admin/v3/test_services.py b/tempest/api/identity/admin/v3/test_services.py
index c6e3df4..2c3cae5 100644
--- a/tempest/api/identity/admin/v3/test_services.py
+++ b/tempest/api/identity/admin/v3/test_services.py
@@ -13,10 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index 531ff56..8706cf7 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -13,10 +13,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
+import six
 
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -31,7 +32,7 @@
         u_email = '%s@testmail.tm' % u_name
         u_password = data_utils.rand_password()
         user = self.users_client.create_user(
-            u_name, description=u_desc, password=u_password,
+            name=u_name, description=u_desc, password=u_password,
             email=u_email)['user']
         self.addCleanup(self.users_client.delete_user, user['id'])
         # Perform Authentication
@@ -61,7 +62,7 @@
         # Create a user.
         user_name = data_utils.rand_name(name='user')
         user_password = data_utils.rand_password()
-        user = self.users_client.create_user(user_name,
+        user = self.users_client.create_user(name=user_name,
                                              password=user_password)['user']
         self.addCleanup(self.users_client.delete_user, user['id'])
 
@@ -78,15 +79,17 @@
 
         # Create a role
         role_name = data_utils.rand_name(name='role')
-        role = self.client.create_role(name=role_name)['role']
-        self.addCleanup(self.client.delete_role, role['id'])
+        role = self.roles_client.create_role(name=role_name)['role']
+        self.addCleanup(self.roles_client.delete_role, role['id'])
 
         # Grant the user the role on both projects.
-        self.client.assign_user_role_on_project(project1['id'], user['id'],
-                                                role['id'])
+        self.roles_client.create_user_role_on_project(project1['id'],
+                                                      user['id'],
+                                                      role['id'])
 
-        self.client.assign_user_role_on_project(project2['id'], user['id'],
-                                                role['id'])
+        self.roles_client.create_user_role_on_project(project2['id'],
+                                                      user['id'],
+                                                      role['id'])
 
         # Get an unscoped token.
         token_auth = self.token.auth(user_id=user['id'],
@@ -96,8 +99,8 @@
         orig_expires_at = token_auth['token']['expires_at']
         orig_user = token_auth['token']['user']
 
-        self.assertIsInstance(token_auth['token']['expires_at'], unicode)
-        self.assertIsInstance(token_auth['token']['issued_at'], unicode)
+        self.assertIsInstance(token_auth['token']['expires_at'], six.text_type)
+        self.assertIsInstance(token_auth['token']['issued_at'], six.text_type)
         self.assertEqual(['password'], token_auth['token']['methods'])
         self.assertEqual(user['id'], token_auth['token']['user']['id'])
         self.assertEqual(user['name'], token_auth['token']['user']['name'])
@@ -117,7 +120,7 @@
 
         self.assertEqual(orig_expires_at, token_auth['token']['expires_at'],
                          'Expiration time should match original token')
-        self.assertIsInstance(token_auth['token']['issued_at'], unicode)
+        self.assertIsInstance(token_auth['token']['issued_at'], six.text_type)
         self.assertEqual(set(['password', 'token']),
                          set(token_auth['token']['methods']))
         self.assertEqual(orig_user, token_auth['token']['user'],
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 180e695..4e69de8 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -14,13 +14,13 @@
 import re
 
 from oslo_utils import timeutils
-from tempest_lib import exceptions as lib_exc
 
 from tempest.api.identity import base
 from tempest import clients
 from tempest.common import credentials_factory as common_creds
 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
@@ -57,7 +57,7 @@
         u_email = self.trustor_username + '@testmail.xx'
         self.trustor_password = data_utils.rand_password()
         user = self.users_client.create_user(
-            self.trustor_username,
+            name=self.trustor_username,
             description=u_desc,
             password=self.trustor_password,
             email=u_email,
@@ -69,19 +69,22 @@
         self.delegated_role = data_utils.rand_name('DelegatedRole')
         self.not_delegated_role = data_utils.rand_name('NotDelegatedRole')
 
-        role = self.client.create_role(name=self.delegated_role)['role']
+        role = self.roles_client.create_role(name=self.delegated_role)['role']
         self.delegated_role_id = role['id']
 
-        role = self.client.create_role(name=self.not_delegated_role)['role']
+        role = self.roles_client.create_role(
+            name=self.not_delegated_role)['role']
         self.not_delegated_role_id = role['id']
 
         # Assign roles to trustor
-        self.client.assign_user_role_on_project(self.trustor_project_id,
-                                                self.trustor_user_id,
-                                                self.delegated_role_id)
-        self.client.assign_user_role_on_project(self.trustor_project_id,
-                                                self.trustor_user_id,
-                                                self.not_delegated_role_id)
+        self.roles_client.create_user_role_on_project(
+            self.trustor_project_id,
+            self.trustor_user_id,
+            self.delegated_role_id)
+        self.roles_client.create_user_role_on_project(
+            self.trustor_project_id,
+            self.trustor_user_id,
+            self.not_delegated_role_id)
 
         # Get trustee user ID, use the demo user
         trustee_username = self.non_admin_client.user
@@ -95,9 +98,10 @@
             password=self.trustor_password,
             user_domain_id='default',
             tenant_name=self.trustor_project_name,
-            project_domain_id='default')
+            project_domain_id='default',
+            domain_id='default')
         os = clients.Manager(credentials=creds)
-        self.trustor_client = os.identity_v3_client
+        self.trustor_client = os.trusts_client
 
     def cleanup_user_and_roles(self):
         if self.trustor_user_id:
@@ -105,9 +109,9 @@
         if self.trustor_project_id:
             self.projects_client.delete_project(self.trustor_project_id)
         if self.delegated_role_id:
-            self.client.delete_role(self.delegated_role_id)
+            self.roles_client.delete_role(self.delegated_role_id)
         if self.not_delegated_role_id:
-            self.client.delete_role(self.not_delegated_role_id)
+            self.roles_client.delete_role(self.not_delegated_role_id)
 
     def create_trust(self, impersonate=True, expires=None):
 
@@ -263,8 +267,19 @@
     @test.attr(type='smoke')
     @test.idempotent_id('4773ebd5-ecbf-4255-b8d8-b63e6f72b65d')
     def test_get_trusts_all(self):
+
+        # Simple function that can be used for cleanup
+        def set_scope(auth_provider, scope):
+            auth_provider.scope = scope
+
         self.create_trust()
-        trusts_get = self.client.list_trusts()['trusts']
+        # Listing trusts can be done by trustor, by trustee, or without
+        # any filter if scoped to a project, so we must ensure token scope is
+        # project for this test.
+        original_scope = self.os_adm.auth_provider.scope
+        set_scope(self.os_adm.auth_provider, 'project')
+        self.addCleanup(set_scope, self.os_adm.auth_provider, original_scope)
+        trusts_get = self.trusts_client.list_trusts()['trusts']
         trusts = [t for t in trusts_get
                   if t['id'] == self.trust_id]
         self.assertEqual(1, len(trusts))
diff --git a/tempest/api/identity/admin/v3/test_users.py b/tempest/api/identity/admin/v3/test_users.py
index de659d8..3ec4ff1 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -15,11 +15,17 @@
 
 import time
 
+import testtools
+
 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 UsersV3TestJSON(base.BaseIdentityV3AdminTest):
 
     @test.idempotent_id('b537d090-afb9-4519-b95d-270b0708e87e')
@@ -31,7 +37,7 @@
         u_email = u_name + '@testmail.tm'
         u_password = data_utils.rand_password()
         user = self.users_client.create_user(
-            u_name, description=u_desc, password=u_password,
+            name=u_name, description=u_desc, password=u_password,
             email=u_email, enabled=False)['user']
         # Delete the User at the end of this method
         self.addCleanup(self.users_client.delete_user, user['id'])
@@ -71,7 +77,7 @@
         u_name = data_utils.rand_name('user')
         original_password = data_utils.rand_password()
         user = self.users_client.create_user(
-            u_name, password=original_password)['user']
+            name=u_name, password=original_password)['user']
         # Delete the User at the end all test methods
         self.addCleanup(self.users_client.delete_user, user['id'])
         # Update user with new password
@@ -79,20 +85,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)
 
@@ -112,18 +113,18 @@
         u_email = u_name + '@testmail.tm'
         u_password = data_utils.rand_password()
         user_body = self.users_client.create_user(
-            u_name, description=u_desc, password=u_password,
+            name=u_name, description=u_desc, password=u_password,
             email=u_email, enabled=False, project_id=u_project['id'])['user']
         # Delete the User at the end of this method
         self.addCleanup(self.users_client.delete_user, user_body['id'])
         # Creating Role
-        role_body = self.client.create_role(
+        role_body = self.roles_client.create_role(
             name=data_utils.rand_name('role'))['role']
         # Delete the Role at the end of this method
-        self.addCleanup(self.client.delete_role, role_body['id'])
+        self.addCleanup(self.roles_client.delete_role, role_body['id'])
 
         user = self.users_client.show_user(user_body['id'])['user']
-        role = self.client.show_role(role_body['id'])['role']
+        role = self.roles_client.show_role(role_body['id'])['role']
         for i in range(2):
             # Creating project so as to assign role
             project_body = self.projects_client.create_project(
@@ -135,9 +136,9 @@
             self.addCleanup(
                 self.projects_client.delete_project, project_body['id'])
             # Assigning roles to user on project
-            self.client.assign_user_role_on_project(project['id'],
-                                                    user['id'],
-                                                    role['id'])
+            self.roles_client.create_user_role_on_project(project['id'],
+                                                          user['id'],
+                                                          role['id'])
             assigned_project_ids.append(project['id'])
         body = self.users_client.list_user_projects(user['id'])['projects']
         for i in body:
@@ -154,6 +155,33 @@
     @test.idempotent_id('c10dcd90-461d-4b16-8e23-4eb836c00644')
     def test_get_user(self):
         # Get a user detail
-        self.data.setup_test_user()
-        user = self.users_client.show_user(self.data.user['id'])['user']
-        self.assertEqual(self.data.user['id'], user['id'])
+        user = self.setup_test_user()
+        fetched_user = self.users_client.show_user(user['id'])['user']
+        self.assertEqual(user['id'], fetched_user['id'])
+
+    @testtools.skipUnless(CONF.identity_feature_enabled.security_compliance,
+                          'Security compliance not available.')
+    @test.idempotent_id('568cd46c-ee6c-4ab4-a33a-d3791931979e')
+    def test_password_history_not_enforced_in_admin_reset(self):
+        old_password = self.os.credentials.password
+        user_id = self.os.credentials.user_id
+
+        new_password = data_utils.rand_password()
+        self.users_client.update_user(user_id, password=new_password)
+        # To be safe, we add this cleanup to restore the original password in
+        # case something goes wrong before it is restored later.
+        self.addCleanup(
+            self.users_client.update_user, user_id, password=old_password)
+
+        # Check authorization with new password
+        self.token.auth(user_id=user_id, password=new_password)
+
+        if CONF.identity.user_unique_last_password_count > 1:
+            # The password history is not enforced via the admin reset route.
+            # We can set the same password.
+            self.users_client.update_user(user_id, password=new_password)
+
+        # Restore original password
+        self.users_client.update_user(user_id, password=old_password)
+        # Check authorization with old password
+        self.token.auth(user_id=user_id, password=old_password)
diff --git a/tempest/api/identity/admin/v3/test_users_negative.py b/tempest/api/identity/admin/v3/test_users_negative.py
index 9dd477b..5b0fc97 100644
--- a/tempest/api/identity/admin/v3/test_users_negative.py
+++ b/tempest/api/identity/admin/v3/test_users_negative.py
@@ -13,10 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -30,7 +29,7 @@
         u_email = u_name + '@testmail.tm'
         u_password = data_utils.rand_password()
         self.assertRaises(lib_exc.NotFound, self.users_client.create_user,
-                          u_name, u_password,
+                          name=u_name, password=u_password,
                           email=u_email,
                           domain_id=data_utils.rand_uuid_hex())
 
@@ -38,9 +37,10 @@
     @test.idempotent_id('b3c9fccc-4134-46f5-b600-1da6fb0a3b1f')
     def test_authentication_for_disabled_user(self):
         # Attempt to authenticate for disabled user should fail
-        self.data.setup_test_user()
-        self.disable_user(self.data.user['name'])
+        password = data_utils.rand_password()
+        user = self.setup_test_user(password)
+        self.disable_user(user['name'], user['domain_id'])
         self.assertRaises(lib_exc.Unauthorized, self.token.auth,
-                          username=self.data.user['name'],
-                          password=self.data.user_password,
+                          username=user['name'],
+                          password=password,
                           user_domain_id='default')
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 1025de7..9515788 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -13,23 +13,25 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_log import log as logging
-from tempest_lib import exceptions as lib_exc
-
 from tempest.common.utils import data_utils
 from tempest import config
 import tempest.test
 
 CONF = config.CONF
-LOG = logging.getLogger(__name__)
 
 
 class BaseIdentityTest(tempest.test.BaseTestCase):
 
     @classmethod
+    def setup_credentials(cls):
+        # Create no network resources for these test.
+        cls.set_network_resources()
+        super(BaseIdentityTest, cls).setup_credentials()
+
+    @classmethod
     def disable_user(cls, user_name):
         user = cls.get_user_by_name(user_name)
-        cls.users_client.enable_disable_user(user['id'], enabled=False)
+        cls.users_client.update_user_enabled(user['id'], enabled=False)
 
     @classmethod
     def disable_tenant(cls, tenant_name):
@@ -37,8 +39,12 @@
         cls.tenants_client.update_tenant(tenant['id'], enabled=False)
 
     @classmethod
-    def get_user_by_name(cls, name):
-        users = cls.users_client.list_users()['users']
+    def get_user_by_name(cls, name, domain_id=None):
+        if domain_id:
+            params = {'domain_id': domain_id}
+            users = cls.users_client.list_users(**params)['users']
+        else:
+            users = cls.users_client.list_users()['users']
         user = [u for u in users if u['name'] == name]
         if len(user) > 0:
             return user[0]
@@ -60,6 +66,23 @@
         if len(role) > 0:
             return role[0]
 
+    def _create_test_user(self, **kwargs):
+        if kwargs['password'] is None:
+            user_password = data_utils.rand_password()
+            kwargs['password'] = user_password
+        user = self.users_client.create_user(**kwargs)['user']
+        # Delete the user at the end of the test
+        self.addCleanup(self.users_client.delete_user, user['id'])
+        return user
+
+    def setup_test_role(self):
+        """Set up a test role."""
+        role = self.roles_client.create_role(
+            name=data_utils.rand_name('test_role'))['role']
+        # Delete the role at the end of the test
+        self.addCleanup(self.roles_client.delete_role, role['id'])
+        return role
+
 
 class BaseIdentityV2Test(BaseIdentityTest):
 
@@ -77,19 +100,20 @@
         cls.non_admin_tenants_client = cls.os.tenants_public_client
         cls.non_admin_users_client = cls.os.users_public_client
 
-    @classmethod
-    def resource_setup(cls):
-        super(BaseIdentityV2Test, cls).resource_setup()
-
-    @classmethod
-    def resource_cleanup(cls):
-        super(BaseIdentityV2Test, cls).resource_cleanup()
-
 
 class BaseIdentityV2AdminTest(BaseIdentityV2Test):
 
     credentials = ['primary', 'admin']
 
+    # NOTE(andreaf) Identity tests work with credentials, so it is safer
+    # for them to always use disposable credentials. Forcing dynamic creds
+    # on regular identity tests would be however to restrictive, since it
+    # would prevent any identity test from being executed against clouds where
+    # admin credentials are not available.
+    # Since All admin tests require admin credentials to be
+    # executed, so this will not impact the ability to execute tests.
+    force_tenant_isolation = True
+
     @classmethod
     def setup_clients(cls):
         super(BaseIdentityV2AdminTest, cls).setup_clients()
@@ -102,19 +126,31 @@
         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):
         super(BaseIdentityV2AdminTest, cls).resource_setup()
-        cls.data = DataGeneratorV2(cls.client, cls.tenants_client,
-                                   cls.users_client, cls.roles_client)
+        cls.projects_client = cls.tenants_client
 
-    @classmethod
-    def resource_cleanup(cls):
-        cls.data.teardown_all()
-        super(BaseIdentityV2AdminTest, cls).resource_cleanup()
+    def setup_test_user(self, password=None):
+        """Set up a test user."""
+        tenant = self.setup_test_tenant()
+        username = data_utils.rand_name('test_user')
+        email = username + '@testmail.tm'
+        user = self._create_test_user(name=username, email=email,
+                                      tenantId=tenant['id'], password=password)
+        return user
+
+    def setup_test_tenant(self):
+        """Set up a test tenant."""
+        tenant = self.projects_client.create_tenant(
+            name=data_utils.rand_name('test_tenant'),
+            description=data_utils.rand_name('desc'))['tenant']
+        # Delete the tenant at the end of the test
+        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+        return tenant
 
 
 class BaseIdentityV3Test(BaseIdentityTest):
@@ -133,151 +169,86 @@
         cls.non_admin_token = cls.os.token_v3_client
         cls.non_admin_projects_client = cls.os.projects_client
 
-    @classmethod
-    def resource_cleanup(cls):
-        super(BaseIdentityV3Test, cls).resource_cleanup()
-
 
 class BaseIdentityV3AdminTest(BaseIdentityV3Test):
 
     credentials = ['primary', 'admin']
 
+    # NOTE(andreaf) Identity tests work with credentials, so it is safer
+    # for them to always use disposable credentials. Forcing dynamic creds
+    # on regular identity tests would be however to restrictive, since it
+    # would prevent any identity test from being executed against clouds where
+    # admin credentials are not available.
+    # Since All admin tests require admin credentials to be
+    # executed, so this will not impact the ability to execute tests.
+    force_tenant_isolation = True
+
     @classmethod
     def setup_clients(cls):
         super(BaseIdentityV3AdminTest, cls).setup_clients()
         cls.client = cls.os_adm.identity_v3_client
+        cls.domains_client = cls.os_adm.domains_client
         cls.users_client = cls.os_adm.users_v3_client
+        cls.trusts_client = cls.os_adm.trusts_client
+        cls.roles_client = cls.os_adm.roles_v3_client
+        cls.inherited_roles_client = cls.os_adm.inherited_roles_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
         cls.projects_client = cls.os_adm.projects_client
+        cls.role_assignments = cls.os_admin.role_assignments_client
+        if CONF.identity.admin_domain_scope:
+            # NOTE(andreaf) When keystone policy requires it, the identity
+            # admin clients for these tests shall use 'domain' scoped tokens.
+            # As the client manager is already created by the base class,
+            # we set the scope for the inner auth provider.
+            cls.os_adm.auth_provider.scope = 'domain'
 
     @classmethod
-    def resource_setup(cls):
-        super(BaseIdentityV3AdminTest, cls).resource_setup()
-        cls.data = DataGeneratorV3(cls.client, cls.projects_client,
-                                   cls.users_client)
+    def disable_user(cls, user_name, domain_id=None):
+        user = cls.get_user_by_name(user_name, domain_id)
+        cls.users_client.update_user(user['id'], name=user_name, enabled=False)
 
     @classmethod
-    def resource_cleanup(cls):
-        cls.data.teardown_all()
-        super(BaseIdentityV3AdminTest, cls).resource_cleanup()
-
-    @classmethod
-    def get_role_by_name(cls, name):
-        roles = cls.client.list_roles()['roles']
-        role = [r for r in roles if r['name'] == name]
-        if len(role) > 0:
-            return role[0]
-
-    @classmethod
-    def disable_user(cls, user_name):
-        user = cls.get_user_by_name(user_name)
-        cls.users_client.update_user(user['id'], user_name, enabled=False)
+    def create_domain(cls):
+        """Create a domain."""
+        domain = cls.domains_client.create_domain(
+            name=data_utils.rand_name('test_domain'),
+            description=data_utils.rand_name('desc'))['domain']
+        return domain
 
     def delete_domain(self, domain_id):
         # NOTE(mpavlase) It is necessary to disable the domain before deleting
         # otherwise it raises Forbidden exception
-        self.client.update_domain(domain_id, enabled=False)
-        self.client.delete_domain(domain_id)
+        self.domains_client.update_domain(domain_id, enabled=False)
+        self.domains_client.delete_domain(domain_id)
 
-
-class BaseDataGenerator(object):
-
-    def __init__(self, client, projects_client,
-                 users_client, roles_client=None):
-        self.client = client
-        self.projects_client = projects_client
-        self.users_client = users_client
-        self.roles_client = roles_client or client
-
-        self.user_password = None
-        self.user = None
-        self.tenant = None
-        self.project = None
-        self.role = None
-        self.domain = None
-
-        self.users = []
-        self.tenants = []
-        self.projects = []
-        self.roles = []
-        self.domains = []
-
-    def _create_test_user(self, **kwargs):
+    def setup_test_user(self, password=None):
+        """Set up a test user."""
+        project = self.setup_test_project()
         username = data_utils.rand_name('test_user')
-        self.user_password = data_utils.rand_password()
-        self.user = self.users_client.create_user(
-            username, password=self.user_password,
-            email=username + '@testmail.tm', **kwargs)['user']
-        self.users.append(self.user)
-
-    def setup_test_role(self):
-        """Set up a test role."""
-        self.role = self.roles_client.create_role(
-            name=data_utils.rand_name('test_role'))['role']
-        self.roles.append(self.role)
-
-    @staticmethod
-    def _try_wrapper(func, item, **kwargs):
-        try:
-            func(item['id'], **kwargs)
-        except lib_exc.NotFound:
-            pass
-        except Exception:
-            LOG.exception("Unexpected exception occurred in %s deletion. "
-                          "But ignored here." % item['id'])
-
-    def teardown_all(self):
-        for user in self.users:
-            self._try_wrapper(self.users_client.delete_user, user)
-        for tenant in self.tenants:
-            self._try_wrapper(self.projects_client.delete_tenant, tenant)
-        for project in self.projects:
-            self._try_wrapper(self.projects_client.delete_project, project)
-        for role in self.roles:
-            self._try_wrapper(self.roles_client.delete_role, role)
-        for domain in self.domains:
-            self._try_wrapper(self.client.update_domain, domain, enabled=False)
-            self._try_wrapper(self.client.delete_domain, domain)
-
-
-class DataGeneratorV2(BaseDataGenerator):
-
-    def setup_test_user(self):
-        """Set up a test user."""
-        self.setup_test_tenant()
-        self._create_test_user(tenant_id=self.tenant['id'])
-
-    def setup_test_tenant(self):
-        """Set up a test tenant."""
-        self.tenant = self.projects_client.create_tenant(
-            name=data_utils.rand_name('test_tenant'),
-            description=data_utils.rand_name('desc'))['tenant']
-        self.tenants.append(self.tenant)
-
-
-class DataGeneratorV3(BaseDataGenerator):
-
-    def setup_test_user(self):
-        """Set up a test user."""
-        self.setup_test_project()
-        self._create_test_user(project_id=self.project['id'])
+        email = username + '@testmail.tm'
+        user = self._create_test_user(name=username, email=email,
+                                      project_id=project['id'],
+                                      password=password)
+        return user
 
     def setup_test_project(self):
         """Set up a test project."""
-        self.project = self.projects_client.create_project(
+        project = self.projects_client.create_project(
             name=data_utils.rand_name('test_project'),
             description=data_utils.rand_name('desc'))['project']
-        self.projects.append(self.project)
+        # Delete the project at the end of the test
+        self.addCleanup(self.projects_client.delete_project, project['id'])
+        return project
 
     def setup_test_domain(self):
         """Set up a test domain."""
-        self.domain = self.client.create_domain(
-            name=data_utils.rand_name('test_domain'),
-            description=data_utils.rand_name('desc'))['domain']
-        self.domains.append(self.domain)
+        domain = self.create_domain()
+        # Delete the domain at the end of the test
+        self.addCleanup(self.delete_domain, domain['id'])
+        return domain
diff --git a/tempest/api/identity/v2/test_ec2_credentials.py b/tempest/api/identity/v2/test_ec2_credentials.py
index bd49326..8f493aa 100644
--- a/tempest/api/identity/v2/test_ec2_credentials.py
+++ b/tempest/api/identity/v2/test_ec2_credentials.py
@@ -13,9 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.identity import base
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -34,45 +33,44 @@
         cls.creds = cls.os.credentials
 
     @test.idempotent_id('b580fab9-7ae9-46e8-8138-417260cb6f9f')
-    def test_create_ec2_credentials(self):
-        """Create user ec2 credentials."""
-        resp = self.non_admin_users_client.create_user_ec2_credentials(
-            self.creds.credentials.user_id,
-            tenant_id=self.creds.credentials.tenant_id)["credential"]
+    def test_create_ec2_credential(self):
+        """Create user ec2 credential."""
+        resp = self.non_admin_users_client.create_user_ec2_credential(
+            self.creds.user_id,
+            tenant_id=self.creds.tenant_id)["credential"]
         access = resp['access']
         self.addCleanup(
-            self.non_admin_users_client.delete_user_ec2_credentials,
-            self.creds.credentials.user_id, access)
+            self.non_admin_users_client.delete_user_ec2_credential,
+            self.creds.user_id, access)
         self.assertNotEmpty(resp['access'])
         self.assertNotEmpty(resp['secret'])
-        self.assertEqual(self.creds.credentials.user_id, resp['user_id'])
-        self.assertEqual(self.creds.credentials.tenant_id, resp['tenant_id'])
+        self.assertEqual(self.creds.user_id, resp['user_id'])
+        self.assertEqual(self.creds.tenant_id, resp['tenant_id'])
 
     @test.idempotent_id('9e2ea42f-0a4f-468c-a768-51859ce492e0')
     def test_list_ec2_credentials(self):
         """Get the list of user ec2 credentials."""
         created_creds = []
-        fetched_creds = []
         # create first ec2 credentials
-        creds1 = self.non_admin_users_client.create_user_ec2_credentials(
-            self.creds.credentials.user_id,
-            tenant_id=self.creds.credentials.tenant_id)["credential"]
+        creds1 = self.non_admin_users_client.create_user_ec2_credential(
+            self.creds.user_id,
+            tenant_id=self.creds.tenant_id)["credential"]
         created_creds.append(creds1['access'])
         # create second ec2 credentials
-        creds2 = self.non_admin_users_client.create_user_ec2_credentials(
-            self.creds.credentials.user_id,
-            tenant_id=self.creds.credentials.tenant_id)["credential"]
+        creds2 = self.non_admin_users_client.create_user_ec2_credential(
+            self.creds.user_id,
+            tenant_id=self.creds.tenant_id)["credential"]
         created_creds.append(creds2['access'])
         # add credentials to be cleaned up
         self.addCleanup(
-            self.non_admin_users_client.delete_user_ec2_credentials,
-            self.creds.credentials.user_id, creds1['access'])
+            self.non_admin_users_client.delete_user_ec2_credential,
+            self.creds.user_id, creds1['access'])
         self.addCleanup(
-            self.non_admin_users_client.delete_user_ec2_credentials,
-            self.creds.credentials.user_id, creds2['access'])
+            self.non_admin_users_client.delete_user_ec2_credential,
+            self.creds.user_id, creds2['access'])
         # get the list of user ec2 credentials
         resp = self.non_admin_users_client.list_user_ec2_credentials(
-            self.creds.credentials.user_id)["credentials"]
+            self.creds.user_id)["credentials"]
         fetched_creds = [cred['access'] for cred in resp]
         # created credentials should be in a fetched list
         missing = [cred for cred in created_creds
@@ -82,32 +80,32 @@
                          ', '.join(cred for cred in missing))
 
     @test.idempotent_id('cb284075-b613-440d-83ca-fe0b33b3c2b8')
-    def test_show_ec2_credentials(self):
-        """Get the definite user ec2 credentials."""
-        resp = self.non_admin_users_client.create_user_ec2_credentials(
-            self.creds.credentials.user_id,
-            tenant_id=self.creds.credentials.tenant_id)["credential"]
+    def test_show_ec2_credential(self):
+        """Get the definite user ec2 credential."""
+        resp = self.non_admin_users_client.create_user_ec2_credential(
+            self.creds.user_id,
+            tenant_id=self.creds.tenant_id)["credential"]
         self.addCleanup(
-            self.non_admin_users_client.delete_user_ec2_credentials,
-            self.creds.credentials.user_id, resp['access'])
+            self.non_admin_users_client.delete_user_ec2_credential,
+            self.creds.user_id, resp['access'])
 
-        ec2_creds = self.non_admin_users_client.show_user_ec2_credentials(
-            self.creds.credentials.user_id, resp['access']
+        ec2_creds = self.non_admin_users_client.show_user_ec2_credential(
+            self.creds.user_id, resp['access']
         )["credential"]
         for key in ['access', 'secret', 'user_id', 'tenant_id']:
             self.assertEqual(ec2_creds[key], resp[key])
 
     @test.idempotent_id('6aba0d4c-b76b-4e46-aa42-add79bc1551d')
-    def test_delete_ec2_credentials(self):
-        """Delete user ec2 credentials."""
-        resp = self.non_admin_users_client.create_user_ec2_credentials(
-            self.creds.credentials.user_id,
-            tenant_id=self.creds.credentials.tenant_id)["credential"]
+    def test_delete_ec2_credential(self):
+        """Delete user ec2 credential."""
+        resp = self.non_admin_users_client.create_user_ec2_credential(
+            self.creds.user_id,
+            tenant_id=self.creds.tenant_id)["credential"]
         access = resp['access']
-        self.non_admin_users_client.delete_user_ec2_credentials(
-            self.creds.credentials.user_id, access)
+        self.non_admin_users_client.delete_user_ec2_credential(
+            self.creds.user_id, access)
         self.assertRaises(
             lib_exc.NotFound,
-            self.non_admin_users_client.show_user_ec2_credentials,
-            self.creds.credentials.user_id,
+            self.non_admin_users_client.show_user_ec2_credential,
+            self.creds.user_id,
             access)
diff --git a/tempest/api/identity/v2/test_tenants.py b/tempest/api/identity/v2/test_tenants.py
index 4e31557..cc6de47 100644
--- a/tempest/api/identity/v2/test_tenants.py
+++ b/tempest/api/identity/v2/test_tenants.py
@@ -13,9 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.identity import base
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -25,7 +24,7 @@
 
     @test.idempotent_id('ecae2459-243d-4ba1-ad02-65f15dc82b78')
     def test_list_tenants_returns_only_authorized_tenants(self):
-        alt_tenant_name = self.alt_manager.credentials.credentials.tenant_name
+        alt_tenant_name = self.alt_manager.credentials.tenant_name
         resp = self.non_admin_tenants_client.list_tenants()
 
         # check that user can see only that tenants that he presents in so user
diff --git a/tempest/api/identity/v2/test_tokens.py b/tempest/api/identity/v2/test_tokens.py
index 3b508f4..bdca1e0 100644
--- a/tempest/api/identity/v2/test_tokens.py
+++ b/tempest/api/identity/v2/test_tokens.py
@@ -43,8 +43,8 @@
         self.assertGreater(expires_at, now)
 
         self.assertEqual(body['token']['tenant']['id'],
-                         creds.credentials.tenant_id)
+                         creds.tenant_id)
         self.assertEqual(body['token']['tenant']['name'],
                          tenant_name)
 
-        self.assertEqual(body['user']['id'], creds.credentials.user_id)
+        self.assertEqual(body['user']['id'], creds.user_id)
diff --git a/tempest/api/identity/v2/test_users.py b/tempest/api/identity/v2/test_users.py
index a59a1a0..bafb1f2 100644
--- a/tempest/api/identity/v2/test_users.py
+++ b/tempest/api/identity/v2/test_users.py
@@ -13,17 +13,18 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import copy
 import time
 
-from tempest_lib.common.utils import data_utils
-from tempest_lib import exceptions
-
 from tempest.api.identity import base
-from tempest import manager
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
 from tempest import test
 
 
+CONF = config.CONF
+
+
 class IdentityUsersTest(base.BaseIdentityV2Test):
 
     @classmethod
@@ -34,46 +35,66 @@
         cls.password = cls.creds.password
         cls.tenant_name = cls.creds.tenant_name
 
-    @test.idempotent_id('165859c9-277f-4124-9479-a7d1627b0ca7')
-    def test_user_update_own_password(self):
-        self.new_creds = copy.copy(self.creds.credentials)
-        self.new_creds.password = data_utils.rand_password()
-        # we need new non-admin Identity Client with new credentials, since
-        # current non_admin_client token will be revoked after updating
-        # password
-        self.non_admin_users_client_for_cleanup = copy.copy(
-            self.non_admin_users_client)
-        self.non_admin_users_client_for_cleanup.auth_provider = (
-            manager.get_auth_provider(self.new_creds))
-        user_id = self.creds.credentials.user_id
-        old_pass = self.creds.credentials.password
-        new_pass = self.new_creds.password
-
-        # to change password back. important for allow_tenant_isolation = false
-        self.addCleanup(
-            self.non_admin_users_client_for_cleanup.update_user_own_password,
-            user_id, original_password=new_pass, password=old_pass)
-        # user updates own password
+    def _update_password(self, user_id, original_password, 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.
+            user_id, password=password, original_password=original_password)
+
+        # 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
         self.non_admin_token_client.auth(self.username,
-                                         new_pass,
+                                         password,
                                          self.tenant_name)
 
+        # Reset auth to get a new token with the new password
+        self.non_admin_users_client.auth_provider.clear_auth()
+        self.non_admin_users_client.auth_provider.credentials.password = (
+            password)
+
+    def _restore_password(self, user_id, old_pass, new_pass):
+        if CONF.identity_feature_enabled.security_compliance:
+            # First we need to clear the password history
+            unique_count = CONF.identity.user_unique_last_password_count
+            for i in range(unique_count):
+                random_pass = data_utils.rand_password()
+                self._update_password(
+                    user_id, original_password=new_pass, password=random_pass)
+                new_pass = random_pass
+
+        self._update_password(
+            user_id, original_password=new_pass, password=old_pass)
+        # Reset auth again to verify the password restore does work.
+        # Clear auth restores the original credentials and deletes
+        # cached auth data
+        self.non_admin_users_client.auth_provider.clear_auth()
+        # NOTE(lbragstad): Fernet tokens are not subsecond aware and
+        # Keystone should only be precise to the second. Sleep to ensure we
+        # are passing the second boundary before attempting to
+        # authenticate.
+        time.sleep(1)
+        self.non_admin_users_client.auth_provider.set_auth()
+
+    @test.idempotent_id('165859c9-277f-4124-9479-a7d1627b0ca7')
+    def test_user_update_own_password(self):
+        old_pass = self.creds.password
+        old_token = self.non_admin_users_client.token
+        new_pass = data_utils.rand_password()
+        user_id = self.creds.user_id
+
+        # to change password back. important for allow_tenant_isolation = false
+        self.addCleanup(self._restore_password, user_id, old_pass, new_pass)
+
+        # user updates own password
+        self._update_password(
+            user_id, original_password=old_pass, password=new_pass)
+
         # authorize with old token should lead to Unauthorized
         self.assertRaises(exceptions.Unauthorized,
                           self.non_admin_token_client.auth_token,
-                          self.non_admin_users_client.token)
+                          old_token)
 
         # authorize with old password should lead to Unauthorized
         self.assertRaises(exceptions.Unauthorized,
diff --git a/tempest/api/identity/v3/test_projects.py b/tempest/api/identity/v3/test_projects.py
index b42cf43..26cb90b 100644
--- a/tempest/api/identity/v3/test_projects.py
+++ b/tempest/api/identity/v3/test_projects.py
@@ -13,9 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.identity import base
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -26,7 +25,7 @@
     @test.idempotent_id('86128d46-e170-4644-866a-cc487f699e1d')
     def test_list_projects_returns_only_authorized_projects(self):
         alt_project_name =\
-            self.alt_manager.credentials.credentials.project_name
+            self.alt_manager.credentials.project_name
         resp = self.non_admin_users_client.list_user_projects(
             self.os.credentials.user_id)
 
@@ -34,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'])
@@ -49,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..b410da6 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -28,10 +28,16 @@
         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,
+            username=username,
+            user_domain_id=user_domain_id,
+            password=password,
+            auth_data=True)
 
         self.assertNotEmpty(token_id)
         self.assertIsInstance(token_id, six.string_types)
@@ -44,9 +50,19 @@
         self.assertGreater(expires_at, now)
 
         subject_id = resp['user']['id']
-        self.assertEqual(subject_id, user_id)
+        if user_id:
+            self.assertEqual(subject_id, user_id)
+        else:
+            # Expect a user ID, but don't know what it will be.
+            self.assertGreaterEqual(len(subject_id), 0,
+                                    'Expected user ID in token.')
 
         subject_name = resp['user']['name']
-        self.assertEqual(subject_name, username)
+        if username:
+            self.assertEqual(subject_name, username)
+        else:
+            # Expect a user name, but don't know what it will be.
+            self.assertGreaterEqual(len(subject_name), 0,
+                                    'Expected user name in token.')
 
         self.assertEqual(resp['methods'][0], 'password')
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index 29396a8..f389a8f 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -13,17 +13,20 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import copy
 import time
 
-from tempest_lib.common.utils import data_utils
-from tempest_lib import exceptions
+import testtools
 
 from tempest.api.identity import base
-from tempest import manager
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
 from tempest import test
 
 
+CONF = config.CONF
+
+
 class IdentityV3UsersTest(base.BaseIdentityV3Test):
 
     @classmethod
@@ -34,50 +37,124 @@
         cls.username = cls.creds.username
         cls.password = cls.creds.password
 
-    @test.idempotent_id('ad71bd23-12ad-426b-bb8b-195d2b635f27')
-    def test_user_update_own_password(self):
-        self.new_creds = copy.copy(self.creds.credentials)
-        self.new_creds.password = data_utils.rand_password()
-        # we need new non-admin Identity V3 Client with new credentials, since
-        # current non_admin_users_client token will be revoked after updating
-        # password
-        self.non_admin_users_client_for_cleanup = (
-            copy.copy(self.non_admin_users_client))
-        self.non_admin_users_client_for_cleanup.auth_provider = (
-            manager.get_auth_provider(self.new_creds))
-        user_id = self.creds.credentials.user_id
-        old_pass = self.creds.credentials.password
-        new_pass = self.new_creds.password
-        # to change password back. important for allow_tenant_isolation = false
-        self.addCleanup(
-            self.non_admin_users_client_for_cleanup.update_user_password,
-            user_id,
-            password=old_pass,
-            original_password=new_pass)
-
-        # user updates own password
+    def _update_password(self, original_password, password):
         self.non_admin_users_client.update_user_password(
-            user_id, password=new_pass, original_password=old_pass)
+            self.user_id,
+            password=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.
+        # 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
-        self.non_admin_token.auth(user_id=self.user_id, password=new_pass)
+        self.non_admin_token.auth(user_id=self.user_id, password=password)
+
+        # Reset auth to get a new token with the new password
+        self.non_admin_users_client.auth_provider.clear_auth()
+        self.non_admin_users_client.auth_provider.credentials.password = (
+            password)
+
+    def _restore_password(self, old_pass, new_pass):
+        if CONF.identity_feature_enabled.security_compliance:
+            # First we need to clear the password history
+            unique_count = CONF.identity.user_unique_last_password_count
+            for i in range(unique_count):
+                random_pass = data_utils.rand_password()
+                self._update_password(
+                    original_password=new_pass, password=random_pass)
+                new_pass = random_pass
+
+        self._update_password(original_password=new_pass, password=old_pass)
+        # Reset auth again to verify the password restore does work.
+        # Clear auth restores the original credentials and deletes
+        # cached auth data
+        self.non_admin_users_client.auth_provider.clear_auth()
+        # NOTE(lbragstad): Fernet tokens are not subsecond aware and
+        # Keystone should only be precise to the second. Sleep to ensure we
+        # are passing the second boundary before attempting to
+        # authenticate.
+        time.sleep(1)
+        self.non_admin_users_client.auth_provider.set_auth()
+
+    @test.idempotent_id('ad71bd23-12ad-426b-bb8b-195d2b635f27')
+    def test_user_update_own_password(self):
+        old_pass = self.creds.password
+        old_token = self.non_admin_client.token
+        new_pass = data_utils.rand_password()
+
+        # to change password back. important for allow_tenant_isolation = false
+        self.addCleanup(self._restore_password, old_pass, new_pass)
+
+        # user updates own password
+        self._update_password(original_password=old_pass, password=new_pass)
 
         # authorize with old token should lead to IdentityError (404 code)
         self.assertRaises(exceptions.IdentityError,
                           self.non_admin_token.auth,
-                          token=self.non_admin_client.token)
+                          token=old_token)
 
         # authorize with old password should lead to Unauthorized
         self.assertRaises(exceptions.Unauthorized,
                           self.non_admin_token.auth,
                           user_id=self.user_id,
                           password=old_pass)
+
+    @testtools.skipUnless(CONF.identity_feature_enabled.security_compliance,
+                          'Security compliance not available.')
+    @test.idempotent_id('941784ee-5342-4571-959b-b80dd2cea516')
+    def test_password_history_check_self_service_api(self):
+        old_pass = self.creds.password
+        new_pass1 = data_utils.rand_password()
+        new_pass2 = data_utils.rand_password()
+
+        self.addCleanup(self._restore_password, old_pass, new_pass2)
+
+        # Update password
+        self._update_password(original_password=old_pass, password=new_pass1)
+
+        if CONF.identity.user_unique_last_password_count > 1:
+            # Can not reuse a previously set password
+            self.assertRaises(exceptions.BadRequest,
+                              self.non_admin_users_client.update_user_password,
+                              self.user_id,
+                              password=new_pass1,
+                              original_password=new_pass1)
+
+            self.assertRaises(exceptions.BadRequest,
+                              self.non_admin_users_client.update_user_password,
+                              self.user_id,
+                              password=old_pass,
+                              original_password=new_pass1)
+
+        # A different password can be set
+        self._update_password(original_password=new_pass1, password=new_pass2)
+
+    @testtools.skipUnless(CONF.identity_feature_enabled.security_compliance,
+                          'Security compliance not available.')
+    @test.idempotent_id('a7ad8bbf-2cff-4520-8c1d-96332e151658')
+    def test_user_account_lockout(self):
+        password = self.creds.password
+
+        # First, we login using the correct credentials
+        self.non_admin_token.auth(user_id=self.user_id, password=password)
+
+        # Lock user account by using the wrong password to login
+        bad_password = data_utils.rand_password()
+        for i in range(CONF.identity.user_lockout_failure_attempts):
+            self.assertRaises(exceptions.Unauthorized,
+                              self.non_admin_token.auth,
+                              user_id=self.user_id,
+                              password=bad_password)
+
+        # The user account must be locked, so now it is not possible to login
+        # even using the correct password
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_token.auth,
+                          user_id=self.user_id,
+                          password=password)
+
+        # If we wait the required time, the user account will be unlocked
+        time.sleep(CONF.identity.user_lockout_duration + 1)
+        self.non_admin_token.auth(user_id=self.user_id, password=password)
diff --git a/tempest/api/image/admin/v2/test_images.py b/tempest/api/image/admin/v2/test_images.py
index b171da3..f22f321 100644
--- a/tempest/api/image/admin/v2/test_images.py
+++ b/tempest/api/image/admin/v2/test_images.py
@@ -13,14 +13,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from six import moves
-from tempest_lib.common.utils import data_utils
+import six
 import testtools
 
 from tempest.api.image import base
 from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
-from tempest_lib import exceptions as lib_exc
 
 CONF = config.CONF
 
@@ -34,27 +34,25 @@
     def test_admin_deactivate_reactivate_image(self):
         # Create image by non-admin tenant
         image_name = data_utils.rand_name('image')
-        body = self.client.create_image(name=image_name,
-                                        container_format='bare',
-                                        disk_format='raw',
-                                        visibility='private')
-        image_id = body['id']
-        self.addCleanup(self.client.delete_image, image_id)
+        image = self.create_image(name=image_name,
+                                  container_format='bare',
+                                  disk_format='raw',
+                                  visibility='private')
         # upload an image file
         content = data_utils.random_bytes()
-        image_file = moves.cStringIO(content)
-        self.client.store_image_file(image_id, image_file)
+        image_file = six.BytesIO(content)
+        self.client.store_image_file(image['id'], image_file)
         # deactivate image
-        self.admin_client.deactivate_image(image_id)
-        body = self.client.show_image(image_id)
+        self.admin_client.deactivate_image(image['id'])
+        body = self.client.show_image(image['id'])
         self.assertEqual("deactivated", body['status'])
         # non-admin user unable to download deactivated image
         self.assertRaises(lib_exc.Forbidden, self.client.show_image_file,
-                          image_id)
+                          image['id'])
         # reactivate image
-        self.admin_client.reactivate_image(image_id)
-        body = self.client.show_image(image_id)
+        self.admin_client.reactivate_image(image['id'])
+        body = self.client.show_image(image['id'])
         self.assertEqual("active", body['status'])
         # non-admin user able to download image after reactivation by admin
-        body = self.client.show_image_file(image_id)
+        body = self.client.show_image_file(image['id'])
         self.assertEqual(content, body.data)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index ade7b67..cd4f820 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -12,11 +12,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from six import moves
-from tempest_lib import exceptions as lib_exc
+import six
 
+from tempest.common import image as common_image
 from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import test_utils
 import tempest.test
 
 CONF = config.CONF
@@ -47,24 +48,28 @@
     @classmethod
     def resource_cleanup(cls):
         for image_id in cls.created_images:
-            try:
-                cls.client.delete_image(image_id)
-            except lib_exc.NotFound:
-                pass
+            test_utils.call_and_ignore_notfound_exc(
+                cls.client.delete_image, image_id)
 
         for image_id in cls.created_images:
                 cls.client.wait_for_resource_deletion(image_id)
         super(BaseImageTest, cls).resource_cleanup()
 
     @classmethod
-    def create_image(cls, **kwargs):
+    def create_image(cls, data=None, **kwargs):
         """Wrapper that returns a test image."""
 
         if 'name' not in kwargs:
-            name = data_utils.rand_name(cls.__name__ + "-instance")
+            name = data_utils.rand_name(cls.__name__ + "-image")
             kwargs['name'] = name
 
-        image = cls.client.create_image(**kwargs)
+        params = cls._get_create_params(**kwargs)
+        if data:
+            # NOTE: On glance v1 API, the data should be passed on
+            # a header. Then here handles the data separately.
+            params['data'] = data
+
+        image = cls.client.create_image(**params)
         # Image objects returned by the v1 client have the image
         # data inside a dict that is keyed against 'image'.
         if 'image' in image:
@@ -72,6 +77,10 @@
         cls.created_images.append(image['id'])
         return image
 
+    @classmethod
+    def _get_create_params(cls, **kwargs):
+        return kwargs
+
 
 class BaseV1ImageTest(BaseImageTest):
 
@@ -87,6 +96,10 @@
         super(BaseV1ImageTest, cls).setup_clients()
         cls.client = cls.os.image_client
 
+    @classmethod
+    def _get_create_params(cls, **kwargs):
+        return {'headers': common_image.image_meta_to_headers(**kwargs)}
+
 
 class BaseV1ImageMembersTest(BaseV1ImageTest):
 
@@ -95,21 +108,22 @@
     @classmethod
     def setup_clients(cls):
         super(BaseV1ImageMembersTest, cls).setup_clients()
+        cls.image_member_client = cls.os.image_member_client
+        cls.alt_image_member_client = cls.os_alt.image_member_client
         cls.alt_img_cli = cls.os_alt.image_client
 
     @classmethod
     def resource_setup(cls):
         super(BaseV1ImageMembersTest, cls).resource_setup()
-        cls.alt_tenant_id = cls.alt_img_cli.tenant_id
+        cls.alt_tenant_id = cls.alt_image_member_client.tenant_id
 
     def _create_image(self):
-        image_file = moves.cStringIO(data_utils.random_bytes())
+        image_file = six.BytesIO(data_utils.random_bytes())
         image = self.create_image(container_format='bare',
                                   disk_format='raw',
                                   is_public=False,
                                   data=image_file)
-        image_id = image['id']
-        return image_id
+        return image['id']
 
 
 class BaseV2ImageTest(BaseImageTest):
@@ -125,6 +139,24 @@
     def setup_clients(cls):
         super(BaseV2ImageTest, cls).setup_clients()
         cls.client = cls.os.image_client_v2
+        cls.namespaces_client = cls.os.namespaces_client
+        cls.resource_types_client = cls.os.resource_types_client
+        cls.namespace_properties_client = cls.os.namespace_properties_client
+        cls.namespace_objects_client = cls.os.namespace_objects_client
+        cls.namespace_tags_client = cls.os.namespace_tags_client
+        cls.schemas_client = cls.os.schemas_client
+
+    def create_namespace(cls, namespace_name=None, visibility='public',
+                         description='Tempest', protected=False,
+                         **kwargs):
+        if not namespace_name:
+            namespace_name = data_utils.rand_name('test-ns')
+        kwargs.setdefault('display_name', namespace_name)
+        namespace = cls.namespaces_client.create_namespace(
+            namespace=namespace_name, visibility=visibility,
+            description=description, protected=protected, **kwargs)
+        cls.addCleanup(cls.namespaces_client.delete_namespace, namespace_name)
+        return namespace
 
 
 class BaseV2MemberImageTest(BaseV2ImageTest):
@@ -134,13 +166,14 @@
     @classmethod
     def setup_clients(cls):
         super(BaseV2MemberImageTest, cls).setup_clients()
-        cls.os_img_client = cls.os.image_client_v2
+        cls.image_member_client = cls.os.image_member_client_v2
+        cls.alt_image_member_client = cls.os_alt.image_member_client_v2
         cls.alt_img_client = cls.os_alt.image_client_v2
 
     @classmethod
     def resource_setup(cls):
         super(BaseV2MemberImageTest, cls).resource_setup()
-        cls.alt_tenant_id = cls.alt_img_client.tenant_id
+        cls.alt_tenant_id = cls.alt_image_member_client.tenant_id
 
     def _list_image_ids_as_alt(self):
         image_list = self.alt_img_client.list_images()['images']
@@ -148,13 +181,12 @@
         return image_ids
 
     def _create_image(self):
-        name = data_utils.rand_name('image')
-        image = self.os_img_client.create_image(name=name,
-                                                container_format='bare',
-                                                disk_format='raw')
-        image_id = image['id']
-        self.addCleanup(self.os_img_client.delete_image, image_id)
-        return image_id
+        name = data_utils.rand_name(self.__class__.__name__ + '-image')
+        image = self.client.create_image(name=name,
+                                         container_format='bare',
+                                         disk_format='raw')
+        self.addCleanup(self.client.delete_image, image['id'])
+        return image['id']
 
 
 class BaseV1ImageAdminTest(BaseImageTest):
diff --git a/tempest/api/image/v1/test_image_members.py b/tempest/api/image/v1/test_image_members.py
index eb6969b..9c211ef 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -14,6 +14,7 @@
 
 
 from tempest.api.image import base
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -22,10 +23,10 @@
     @test.idempotent_id('1d6ef640-3a20-4c84-8710-d95828fdb6ad')
     def test_add_image_member(self):
         image = self._create_image()
-        self.client.add_member(self.alt_tenant_id, image)
-        body = self.client.list_image_members(image)
+        self.image_member_client.create_image_member(image, self.alt_tenant_id)
+        body = self.image_member_client.list_image_members(image)
         members = body['members']
-        members = map(lambda x: x['member_id'], members)
+        members = [member['member_id'] for member in members]
         self.assertIn(self.alt_tenant_id, members)
         # get image as alt user
         self.alt_img_cli.show_image(image)
@@ -33,20 +34,26 @@
     @test.idempotent_id('6a5328a5-80e8-4b82-bd32-6c061f128da9')
     def test_get_shared_images(self):
         image = self._create_image()
-        self.client.add_member(self.alt_tenant_id, image)
+        self.image_member_client.create_image_member(image, self.alt_tenant_id)
         share_image = self._create_image()
-        self.client.add_member(self.alt_tenant_id, share_image)
-        body = self.client.list_shared_images(self.alt_tenant_id)
+        self.image_member_client.create_image_member(share_image,
+                                                     self.alt_tenant_id)
+        body = self.image_member_client.list_shared_images(
+            self.alt_tenant_id)
         images = body['shared_images']
-        images = map(lambda x: x['image_id'], images)
+        images = [img['image_id'] for img in images]
         self.assertIn(share_image, images)
         self.assertIn(image, images)
 
     @test.idempotent_id('a76a3191-8948-4b44-a9d6-4053e5f2b138')
     def test_remove_member(self):
         image_id = self._create_image()
-        self.client.add_member(self.alt_tenant_id, image_id)
-        self.client.delete_member(self.alt_tenant_id, image_id)
-        body = self.client.list_image_members(image_id)
+        self.image_member_client.create_image_member(image_id,
+                                                     self.alt_tenant_id)
+        self.image_member_client.delete_image_member(image_id,
+                                                     self.alt_tenant_id)
+        body = self.image_member_client.list_image_members(image_id)
         members = body['members']
         self.assertEqual(0, len(members), str(members))
+        self.assertRaises(
+            lib_exc.NotFound, self.alt_img_cli.show_image, image_id)
diff --git a/tempest/api/image/v1/test_image_members_negative.py b/tempest/api/image/v1/test_image_members_negative.py
index 50f5048..2538781 100644
--- a/tempest/api/image/v1/test_image_members_negative.py
+++ b/tempest/api/image/v1/test_image_members_negative.py
@@ -12,10 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.image import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -26,16 +25,18 @@
     def test_add_member_with_non_existing_image(self):
         # Add member with non existing image.
         non_exist_image = data_utils.rand_uuid()
-        self.assertRaises(lib_exc.NotFound, self.client.add_member,
-                          self.alt_tenant_id, non_exist_image)
+        self.assertRaises(lib_exc.NotFound,
+                          self.image_member_client.create_image_member,
+                          non_exist_image, self.alt_tenant_id)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('e1559f05-b667-4f1b-a7af-518b52dc0c0f')
     def test_delete_member_with_non_existing_image(self):
         # Delete member with non existing image.
         non_exist_image = data_utils.rand_uuid()
-        self.assertRaises(lib_exc.NotFound, self.client.delete_member,
-                          self.alt_tenant_id, non_exist_image)
+        self.assertRaises(lib_exc.NotFound,
+                          self.image_member_client.delete_image_member,
+                          non_exist_image, self.alt_tenant_id)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('f5720333-dd69-4194-bb76-d2f048addd56')
@@ -43,8 +44,9 @@
         # Delete member with non existing tenant.
         image_id = self._create_image()
         non_exist_tenant = data_utils.rand_uuid_hex()
-        self.assertRaises(lib_exc.NotFound, self.client.delete_member,
-                          non_exist_tenant, image_id)
+        self.assertRaises(lib_exc.NotFound,
+                          self.image_member_client.delete_image_member,
+                          image_id, non_exist_tenant)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('f25f89e4-0b6c-453b-a853-1f80b9d7ef26')
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 1a84d06..b22ceed 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -13,12 +13,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from six import moves
+import six
 
 from tempest.api.image import base
+from tempest.common import image as common_image
 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
 from tempest import test
 
 CONF = config.CONF
@@ -28,13 +30,23 @@
     a_formats = ['ami', 'ari', 'aki']
 
     container_format = CONF.image.container_formats[0]
-    disk_format = CONF.image.disk_formats[0]
 
-    if container_format in a_formats and container_format != disk_format:
-        msg = ("The container format and the disk format don't match. "
-               "Contaiter format: %(container)s, Disk format: %(disk)s." %
-               {'container': container_format, 'disk': disk_format})
-        raise exceptions.InvalidConfiguration(message=msg)
+    # In v1, If container_format is one of ['ami', 'ari', 'aki'], then
+    # disk_format must be same with container_format.
+    # If they are of different item sequence in tempest.conf, such as:
+    #     container_formats = ami,ari,aki,bare
+    #     disk_formats = ari,ami,aki,vhd
+    # we can select one in disk_format list that is same with container_format.
+    if container_format in a_formats:
+        if container_format in CONF.image.disk_formats:
+            disk_format = container_format
+        else:
+            msg = ("The container format and the disk format don't match. "
+                   "Container format: %(container)s, Disk format: %(disk)s." %
+                   {'container': container_format, 'disk': disk_format})
+            raise exceptions.InvalidConfiguration(msg)
+    else:
+        disk_format = CONF.image.disk_formats[0]
 
     return container_format, disk_format
 
@@ -47,22 +59,20 @@
         # Register, then upload an image
         properties = {'prop1': 'val1'}
         container_format, disk_format = get_container_and_disk_format()
-        body = self.create_image(name='New Name',
-                                 container_format=container_format,
-                                 disk_format=disk_format,
-                                 is_public=False,
-                                 properties=properties)
-        self.assertIn('id', body)
-        image_id = body.get('id')
-        self.assertEqual('New Name', body.get('name'))
-        self.assertFalse(body.get('is_public'))
-        self.assertEqual('queued', body.get('status'))
+        image = self.create_image(name='New Name',
+                                  container_format=container_format,
+                                  disk_format=disk_format,
+                                  is_public=False,
+                                  properties=properties)
+        self.assertEqual('New Name', image.get('name'))
+        self.assertFalse(image.get('is_public'))
+        self.assertEqual('queued', image.get('status'))
         for key, val in properties.items():
-            self.assertEqual(val, body.get('properties')[key])
+            self.assertEqual(val, image.get('properties')[key])
 
         # Now try uploading an image file
-        image_file = moves.cStringIO(data_utils.random_bytes())
-        body = self.client.update_image(image_id, data=image_file)['image']
+        image_file = six.BytesIO(data_utils.random_bytes())
+        body = self.client.update_image(image['id'], data=image_file)['image']
         self.assertIn('size', body)
         self.assertEqual(1024, body.get('size'))
 
@@ -76,7 +86,6 @@
                                  location=CONF.image.http_image,
                                  properties={'key1': 'value1',
                                              'key2': 'value2'})
-        self.assertIn('id', body)
         self.assertEqual('New Remote Image', body.get('name'))
         self.assertFalse(body.get('is_public'))
         self.assertEqual('active', body.get('status'))
@@ -87,16 +96,14 @@
     @test.idempotent_id('6d0e13a7-515b-460c-b91f-9f4793f09816')
     def test_register_http_image(self):
         container_format, disk_format = get_container_and_disk_format()
-        body = self.create_image(name='New Http Image',
-                                 container_format=container_format,
-                                 disk_format=disk_format, is_public=False,
-                                 copy_from=CONF.image.http_image)
-        self.assertIn('id', body)
-        image_id = body.get('id')
-        self.assertEqual('New Http Image', body.get('name'))
-        self.assertFalse(body.get('is_public'))
-        self.client.wait_for_image_status(image_id, 'active')
-        self.client.show_image(image_id)
+        image = self.create_image(name='New Http Image',
+                                  container_format=container_format,
+                                  disk_format=disk_format, is_public=False,
+                                  copy_from=CONF.image.http_image)
+        self.assertEqual('New Http Image', image.get('name'))
+        self.assertFalse(image.get('is_public'))
+        waiters.wait_for_image_status(self.client, image['id'], 'active')
+        self.client.show_image(image['id'])
 
     @test.idempotent_id('05b19d55-140c-40d0-b36b-fafd774d421b')
     def test_register_image_with_min_ram(self):
@@ -109,7 +116,6 @@
                                  is_public=False,
                                  min_ram=40,
                                  properties=properties)
-        self.assertIn('id', body)
         self.assertEqual('New_image_with_min_ram', body.get('name'))
         self.assertFalse(body.get('is_public'))
         self.assertEqual('queued', body.get('status'))
@@ -186,8 +192,7 @@
                                  disk_format=disk_format,
                                  is_public=False,
                                  location=location)
-        image_id = image['id']
-        return image_id
+        return image['id']
 
     @classmethod
     def _create_standard_image(cls, name, container_format,
@@ -197,20 +202,19 @@
         Note that the size of the new image is a random number between
         1024 and 4096
         """
-        image_file = moves.cStringIO(data_utils.random_bytes(size))
+        image_file = six.BytesIO(data_utils.random_bytes(size))
         name = 'New Standard Image %s' % name
         image = cls.create_image(name=name,
                                  container_format=container_format,
                                  disk_format=disk_format,
                                  is_public=False, data=image_file)
-        image_id = image['id']
-        return image_id
+        return image['id']
 
     @test.idempotent_id('246178ab-3b33-4212-9a4b-a7fe8261794d')
     def test_index_no_params(self):
         # Simple test to see all fixture images returned
         images_list = self.client.list_images()['images']
-        image_list = map(lambda x: x['id'], images_list)
+        image_list = [image['id'] for image in images_list]
         for image_id in self.created_images:
             self.assertIn(image_id, image_list)
 
@@ -240,7 +244,7 @@
     def test_index_max_size(self):
         images_list = self.client.list_images(size_max=42)['images']
         for image in images_list:
-            self.assertTrue(image['size'] <= 42)
+            self.assertLessEqual(image['size'], 42)
         result_set = set(map(lambda x: x['id'], images_list))
         self.assertTrue(self.size42_set <= result_set)
         self.assertFalse(self.created_set - self.size42_set <= result_set)
@@ -249,7 +253,7 @@
     def test_index_min_size(self):
         images_list = self.client.list_images(size_min=142)['images']
         for image in images_list:
-            self.assertTrue(image['size'] >= 142)
+            self.assertGreaterEqual(image['size'], 142)
         result_set = set(map(lambda x: x['id'], images_list))
         self.assertTrue(self.size142_set <= result_set)
         self.assertFalse(self.size42_set <= result_set)
@@ -263,7 +267,7 @@
         top_size = images_list[0]['size']  # We have non-zero sized images
         for image in images_list:
             size = image['size']
-            self.assertTrue(size <= top_size)
+            self.assertLessEqual(size, top_size)
             top_size = size
             self.assertEqual(image['status'], 'active')
 
@@ -292,20 +296,20 @@
                                disk_format, size):
         """Create a new standard image and return newly-registered image-id"""
 
-        image_file = moves.cStringIO(data_utils.random_bytes(size))
+        image_file = six.BytesIO(data_utils.random_bytes(size))
         name = 'New Standard Image %s' % name
         image = cls.create_image(name=name,
                                  container_format=container_format,
                                  disk_format=disk_format,
                                  is_public=False, data=image_file,
                                  properties={'key1': 'value1'})
-        image_id = image['id']
-        return image_id
+        return image['id']
 
     @test.idempotent_id('01752c1c-0275-4de3-9e5b-876e44541928')
     def test_list_image_metadata(self):
         # All metadata key/value pairs for an image should be returned
-        resp_metadata = self.client.get_image_meta(self.image_id)
+        resp = self.client.check_image(self.image_id)
+        resp_metadata = common_image.get_image_meta_from_headers(resp)
         expected = {'key1': 'value1'}
         self.assertEqual(expected, resp_metadata['properties'])
 
@@ -313,12 +317,13 @@
     def test_update_image_metadata(self):
         # The metadata for the image should match the updated values
         req_metadata = {'key1': 'alt1', 'key2': 'value2'}
-        metadata = self.client.get_image_meta(self.image_id)
+        resp = self.client.check_image(self.image_id)
+        metadata = common_image.get_image_meta_from_headers(resp)
         self.assertEqual(metadata['properties'], {'key1': 'value1'})
         metadata['properties'].update(req_metadata)
-        metadata = self.client.update_image(
-            self.image_id, properties=metadata['properties'])['image']
-
-        resp_metadata = self.client.get_image_meta(self.image_id)
-        expected = {'key1': 'alt1', 'key2': 'value2'}
-        self.assertEqual(expected, resp_metadata['properties'])
+        headers = common_image.image_meta_to_headers(
+            properties=metadata['properties'])
+        self.client.update_image(self.image_id, headers=headers)
+        resp = self.client.check_image(self.image_id)
+        resp_metadata = common_image.get_image_meta_from_headers(resp)
+        self.assertEqual(req_metadata, resp_metadata['properties'])
diff --git a/tempest/api/image/v1/test_images_negative.py b/tempest/api/image/v1/test_images_negative.py
index f16b80e..3493cc2 100644
--- a/tempest/api/image/v1/test_images_negative.py
+++ b/tempest/api/image/v1/test_images_negative.py
@@ -13,9 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
 
 from tempest.api.image import base
+from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -27,31 +28,24 @@
     def test_register_with_invalid_container_format(self):
         # Negative tests for invalid data supplied to POST /images
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
-                          name='test',
-                          container_format='wrong',
-                          disk_format='vhd',)
+                          headers={'x-image-meta-name': 'test',
+                                   'x-image-meta-container_format': 'wrong',
+                                   'x-image-meta-disk_format': 'vhd'})
 
     @test.attr(type=['negative'])
     @test.idempotent_id('993face5-921d-4e84-aabf-c1bba4234a67')
     def test_register_with_invalid_disk_format(self):
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
-                          name='test',
-                          container_format='bare',
-                          disk_format='wrong',)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('bb016f15-0820-4f27-a92d-09b2f67d2488')
-    def test_delete_image_with_invalid_image_id(self):
-        # An image should not be deleted with invalid image id
-        self.assertRaises(lib_exc.NotFound, self.client.delete_image,
-                          '!@$%^&*()')
+                          headers={'x-image-meta-name': 'test',
+                                   'x-image-meta-container_format': 'bare',
+                                   'x-image-meta-disk_format': 'wrong'})
 
     @test.attr(type=['negative'])
     @test.idempotent_id('ec652588-7e3c-4b67-a2f2-0fa96f57c8fc')
     def test_delete_non_existent_image(self):
         # Return an error while trying to delete a non-existent image
 
-        non_existent_image_id = '11a22b9-12a9-5555-cc11-00ab112223fa'
+        non_existent_image_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
                           non_existent_image_id)
 
@@ -65,9 +59,9 @@
     @test.idempotent_id('950e5054-a3c7-4dee-ada5-e576f1087abd')
     def test_delete_image_non_hex_string_id(self):
         # Return an error while trying to delete an image with non hex id
-        image_id = '11a22b9-120q-5555-cc11-00ab112223gj'
+        invalid_image_id = data_utils.rand_uuid()[:-1] + "j"
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
-                          image_id)
+                          invalid_image_id)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('4ed757cd-450c-44b1-9fd1-c819748c650d')
@@ -77,7 +71,8 @@
 
     @test.attr(type=['negative'])
     @test.idempotent_id('a4a448ab-3db2-4d2d-b9b2-6a1271241dfe')
-    def test_delete_image_id_is_over_35_character_limit(self):
+    def test_delete_image_id_over_character_limit(self):
         # Return an error while trying to delete image with id over limit
+        overlimit_image_id = data_utils.rand_uuid() + "1"
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
-                          '11a22b9-12a9-5555-cc11-00ab112223fa-3fac')
+                          overlimit_image_id)
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 04582c6..cdf0b23 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -16,7 +16,7 @@
 
 import random
 
-from six import moves
+import six
 
 from oslo_log import log as logging
 from tempest.api.image import base
@@ -44,35 +44,33 @@
         image_name = data_utils.rand_name('image')
         container_format = CONF.image.container_formats[0]
         disk_format = CONF.image.disk_formats[0]
-        body = self.create_image(name=image_name,
-                                 container_format=container_format,
-                                 disk_format=disk_format,
-                                 visibility='private',
-                                 ramdisk_id=uuid)
-        self.assertIn('id', body)
-        image_id = body.get('id')
-        self.assertIn('name', body)
-        self.assertEqual(image_name, body['name'])
-        self.assertIn('visibility', body)
-        self.assertEqual('private', body['visibility'])
-        self.assertIn('status', body)
-        self.assertEqual('queued', body['status'])
+        image = self.create_image(name=image_name,
+                                  container_format=container_format,
+                                  disk_format=disk_format,
+                                  visibility='private',
+                                  ramdisk_id=uuid)
+        self.assertIn('name', image)
+        self.assertEqual(image_name, image['name'])
+        self.assertIn('visibility', image)
+        self.assertEqual('private', image['visibility'])
+        self.assertIn('status', image)
+        self.assertEqual('queued', image['status'])
 
         # Now try uploading an image file
         file_content = data_utils.random_bytes()
-        image_file = moves.cStringIO(file_content)
-        self.client.store_image_file(image_id, image_file)
+        image_file = six.BytesIO(file_content)
+        self.client.store_image_file(image['id'], image_file)
 
         # Now try to get image details
-        body = self.client.show_image(image_id)
-        self.assertEqual(image_id, body['id'])
+        body = self.client.show_image(image['id'])
+        self.assertEqual(image['id'], body['id'])
         self.assertEqual(image_name, body['name'])
         self.assertEqual(uuid, body['ramdisk_id'])
         self.assertIn('size', body)
         self.assertEqual(1024, body.get('size'))
 
         # Now try get image file
-        body = self.client.show_image_file(image_id)
+        body = self.client.show_image_file(image['id'])
         self.assertEqual(file_content, body.data)
 
     @test.attr(type='smoke')
@@ -84,20 +82,18 @@
         image_name = data_utils.rand_name('image')
         container_format = CONF.image.container_formats[0]
         disk_format = CONF.image.disk_formats[0]
-        body = self.client.create_image(name=image_name,
-                                        container_format=container_format,
-                                        disk_format=disk_format,
-                                        visibility='private')
-        image_id = body['id']
-
+        image = self.create_image(name=image_name,
+                                  container_format=container_format,
+                                  disk_format=disk_format,
+                                  visibility='private')
         # Delete Image
-        self.client.delete_image(image_id)
-        self.client.wait_for_resource_deletion(image_id)
+        self.client.delete_image(image['id'])
+        self.client.wait_for_resource_deletion(image['id'])
 
         # Verifying deletion
         images = self.client.list_images()['images']
         images_id = [item['id'] for item in images]
-        self.assertNotIn(image_id, images_id)
+        self.assertNotIn(image['id'], images_id)
 
     @test.attr(type='smoke')
     @test.idempotent_id('f66891a7-a35c-41a8-b590-a065c2a1caa6')
@@ -108,32 +104,29 @@
         image_name = data_utils.rand_name('image')
         container_format = CONF.image.container_formats[0]
         disk_format = CONF.image.disk_formats[0]
-        body = self.client.create_image(name=image_name,
-                                        container_format=container_format,
-                                        disk_format=disk_format,
-                                        visibility='private')
-        self.addCleanup(self.client.delete_image, body['id'])
-        self.assertEqual('queued', body['status'])
-        image_id = body['id']
+        image = self.create_image(name=image_name,
+                                  container_format=container_format,
+                                  disk_format=disk_format,
+                                  visibility='private')
+        self.assertEqual('queued', image['status'])
 
         # Now try uploading an image file
-        image_file = moves.cStringIO(data_utils.random_bytes())
-        self.client.store_image_file(image_id, image_file)
+        image_file = six.BytesIO(data_utils.random_bytes())
+        self.client.store_image_file(image['id'], image_file)
 
         # Update Image
         new_image_name = data_utils.rand_name('new-image')
-        body = self.client.update_image(image_id, [
+        body = self.client.update_image(image['id'], [
             dict(replace='/name', value=new_image_name)])
 
         # Verifying updating
 
-        body = self.client.show_image(image_id)
-        self.assertEqual(image_id, body['id'])
+        body = self.client.show_image(image['id'])
+        self.assertEqual(image['id'], body['id'])
         self.assertEqual(new_image_name, body['name'])
 
 
 class ListImagesTest(base.BaseV2ImageTest):
-    """Here we test the listing of image information"""
 
     @classmethod
     def resource_setup(cls):
@@ -160,40 +153,63 @@
         1024 and 4096
         """
         size = random.randint(1024, 4096)
-        image_file = moves.cStringIO(data_utils.random_bytes(size))
-        name = data_utils.rand_name('image')
-        body = cls.create_image(name=name,
-                                container_format=container_format,
-                                disk_format=disk_format,
-                                visibility='private')
-        image_id = body['id']
-        cls.client.store_image_file(image_id, data=image_file)
+        image_file = six.BytesIO(data_utils.random_bytes(size))
+        tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
+        image = cls.create_image(container_format=container_format,
+                                 disk_format=disk_format,
+                                 visibility='private',
+                                 tags=tags)
+        cls.client.store_image_file(image['id'], data=image_file)
+        # Keep the data of one test image so it can be used to filter lists
+        cls.test_data = image
+        cls.test_data['size'] = size
 
-        return image_id
+        return image['id']
+
+
+class ListUserImagesTest(ListImagesTest):
+    """Here we test the listing of image information"""
 
     def _list_by_param_value_and_assert(self, params):
         """Perform list action with given params and validates result."""
-
+        # Retrieve the list of images that meet the filter
         images_list = self.client.list_images(params=params)['images']
         # Validating params of fetched images
+        msg = 'No images were found that met the filter criteria.'
+        self.assertNotEmpty(images_list, msg)
         for image in images_list:
             for key in params:
                 msg = "Failed to list images by %s" % key
                 self.assertEqual(params[key], image[key], msg)
 
+    def _list_sorted_by_image_size_and_assert(self, params, desc=False):
+        """Validate an image list that has been sorted by size
+
+        Perform list action with given params and validates the results are
+        sorted by image size in either ascending or descending order.
+        """
+        # Retrieve the list of images that meet the filter
+        images_list = self.client.list_images(params=params)['images']
+        # Validate that the list was fetched sorted accordingly
+        msg = 'No images were found that met the filter criteria.'
+        self.assertNotEmpty(images_list, msg)
+        sorted_list = [image['size'] for image in images_list]
+        msg = 'The list of images was not sorted correctly.'
+        self.assertEqual(sorted(sorted_list, reverse=desc), sorted_list, msg)
+
     @test.idempotent_id('1e341d7a-90a9-494c-b143-2cdf2aeb6aee')
     def test_list_no_params(self):
         # Simple test to see all fixture images returned
         images_list = self.client.list_images()['images']
-        image_list = map(lambda x: x['id'], images_list)
+        image_list = [image['id'] for image in images_list]
 
         for image in self.created_images:
             self.assertIn(image, image_list)
 
     @test.idempotent_id('9959ca1d-1aa7-4b7a-a1ea-0fff0499b37e')
     def test_list_images_param_container_format(self):
-        # Test to get all images with container_format='bare'
-        params = {"container_format": "bare"}
+        # Test to get all images with a specific container_format
+        params = {"container_format": self.test_data['container_format']}
         self._list_by_param_value_and_assert(params)
 
     @test.idempotent_id('4a4735a7-f22f-49b6-b0d9-66e1ef7453eb')
@@ -231,9 +247,10 @@
         image_size_list = map(lambda x: x['size'], images_list)
 
         for image_size in image_size_list:
-            self.assertTrue(image_size >= params['size_min'] and
-                            image_size <= params['size_max'],
-                            "Failed to get images by size_min and size_max")
+            self.assertGreaterEqual(image_size, params['size_min'],
+                                    "Failed to get images by size_min")
+            self.assertLessEqual(image_size, params['size_max'],
+                                 "Failed to get images by size_max")
 
     @test.idempotent_id('7fc9e369-0f58-4d05-9aa5-0969e2d59d15')
     def test_list_images_param_status(self):
@@ -250,16 +267,91 @@
         self.assertEqual(len(images_list), params['limit'],
                          "Failed to get images by limit")
 
+    @test.idempotent_id('e9a44b91-31c8-4b40-a332-e0a39ffb4dbb')
+    def test_list_image_param_owner(self):
+        # Test to get images by owner
+        image_id = self.created_images[0]
+        # Get image metadata
+        image = self.client.show_image(image_id)
+
+        params = {"owner": image['owner']}
+        self._list_by_param_value_and_assert(params)
+
+    @test.idempotent_id('55c8f5f5-bfed-409d-a6d5-4caeda985d7b')
+    def test_list_images_param_name(self):
+        # Test to get images by name
+        params = {'name': self.test_data['name']}
+        self._list_by_param_value_and_assert(params)
+
+    @test.idempotent_id('aa8ac4df-cff9-418b-8d0f-dd9c67b072c9')
+    def test_list_images_param_tag(self):
+        # Test to get images matching a tag
+        params = {'tag': self.test_data['tags'][0]}
+        images_list = self.client.list_images(params=params)['images']
+        # Validating properties of fetched images
+        self.assertNotEmpty(images_list)
+        for image in images_list:
+            msg = ("The image {image_name} does not have the expected tag "
+                   "{expected_tag} among its tags: {observerd_tags}."
+                   .format(image_name=image['name'],
+                           expected_tag=self.test_data['tags'][0],
+                           observerd_tags=image['tags']))
+            self.assertIn(self.test_data['tags'][0], image['tags'], msg)
+
+    @test.idempotent_id('eeadce49-04e0-43b7-aec7-52535d903e7a')
+    def test_list_images_param_sort(self):
+        params = {'sort': 'size:desc'}
+        self._list_sorted_by_image_size_and_assert(params, desc=True)
+
+    @test.idempotent_id('9faaa0c2-c3a5-43e1-8f61-61c54b409a49')
+    def test_list_images_param_sort_key_dir(self):
+        params = {'sort_key': 'size', 'sort_dir': 'desc'}
+        self._list_sorted_by_image_size_and_assert(params, desc=True)
+
     @test.idempotent_id('622b925c-479f-4736-860d-adeaf13bc371')
     def test_get_image_schema(self):
         # Test to get image schema
         schema = "image"
-        body = self.client.show_schema(schema)
+        body = self.schemas_client.show_schema(schema)
         self.assertEqual("image", body['name'])
 
     @test.idempotent_id('25c8d7b2-df21-460f-87ac-93130bcdc684')
     def test_get_images_schema(self):
         # Test to get images schema
         schema = "images"
-        body = self.client.show_schema(schema)
+        body = self.schemas_client.show_schema(schema)
         self.assertEqual("images", body['name'])
+
+
+class ListSharedImagesTest(ListImagesTest):
+    """Here we test the listing of a shared image information"""
+
+    credentials = ['primary', 'alt']
+
+    @classmethod
+    def setup_clients(cls):
+        super(ListSharedImagesTest, cls).setup_clients()
+        cls.image_member_client = cls.os.image_member_client_v2
+        cls.alt_img_client = cls.os_alt.image_client_v2
+
+    @test.idempotent_id('3fa50be4-8e38-4c02-a8db-7811bb780122')
+    def test_list_images_param_member_status(self):
+        # Create an image to be shared using default visibility
+        image_file = six.BytesIO(data_utils.random_bytes(2048))
+        container_format = CONF.image.container_formats[0]
+        disk_format = CONF.image.disk_formats[0]
+        image = self.create_image(container_format=container_format,
+                                  disk_format=disk_format)
+        self.client.store_image_file(image['id'], data=image_file)
+
+        # Share the image created with the alt user
+        self.image_member_client.create_image_member(
+            image_id=image['id'], member=self.alt_img_client.tenant_id)
+
+        # As an image consumer you need to provide the member_status parameter
+        # along with the visibility=shared parameter in order for it to show
+        # results
+        params = {'member_status': 'pending', 'visibility': 'shared'}
+        fetched_images = self.alt_img_client.list_images(params)['images']
+        self.assertEqual(1, len(fetched_images))
+        self.assertEqual(image['id'], fetched_images[0]['id'])
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index bb73318..8a4b334 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -19,17 +19,19 @@
     @test.idempotent_id('5934c6ea-27dc-4d6e-9421-eeb5e045494a')
     def test_image_share_accept(self):
         image_id = self._create_image()
-        member = self.os_img_client.create_image_member(
+        member = self.image_member_client.create_image_member(
             image_id, member=self.alt_tenant_id)
+        self.addCleanup(self.image_member_client.delete_image_member,
+                        image_id, self.alt_tenant_id)
         self.assertEqual(member['member_id'], self.alt_tenant_id)
         self.assertEqual(member['image_id'], image_id)
         self.assertEqual(member['status'], 'pending')
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
-        self.alt_img_client.update_image_member(image_id,
-                                                self.alt_tenant_id,
-                                                status='accepted')
+        self.alt_image_member_client.update_image_member(image_id,
+                                                         self.alt_tenant_id,
+                                                         status='accepted')
         self.assertIn(image_id, self._list_image_ids_as_alt())
-        body = self.os_img_client.list_image_members(image_id)
+        body = self.image_member_client.list_image_members(image_id)
         members = body['members']
         member = members[0]
         self.assertEqual(len(members), 1, str(members))
@@ -40,29 +42,33 @@
     @test.idempotent_id('d9e83e5f-3524-4b38-a900-22abcb26e90e')
     def test_image_share_reject(self):
         image_id = self._create_image()
-        member = self.os_img_client.create_image_member(
+        member = self.image_member_client.create_image_member(
             image_id, member=self.alt_tenant_id)
+        self.addCleanup(self.image_member_client.delete_image_member,
+                        image_id, self.alt_tenant_id)
         self.assertEqual(member['member_id'], self.alt_tenant_id)
         self.assertEqual(member['image_id'], image_id)
         self.assertEqual(member['status'], 'pending')
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
-        self.alt_img_client.update_image_member(image_id,
-                                                self.alt_tenant_id,
-                                                status='rejected')
+        self.alt_image_member_client.update_image_member(image_id,
+                                                         self.alt_tenant_id,
+                                                         status='rejected')
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
 
     @test.idempotent_id('a6ee18b9-4378-465e-9ad9-9a6de58a3287')
     def test_get_image_member(self):
         image_id = self._create_image()
-        self.os_img_client.create_image_member(
+        self.image_member_client.create_image_member(
             image_id, member=self.alt_tenant_id)
-        self.alt_img_client.update_image_member(image_id,
-                                                self.alt_tenant_id,
-                                                status='accepted')
+        self.addCleanup(self.image_member_client.delete_image_member,
+                        image_id, self.alt_tenant_id)
+        self.alt_image_member_client.update_image_member(image_id,
+                                                         self.alt_tenant_id,
+                                                         status='accepted')
 
         self.assertIn(image_id, self._list_image_ids_as_alt())
-        member = self.os_img_client.show_image_member(image_id,
-                                                      self.alt_tenant_id)
+        member = self.image_member_client.show_image_member(
+            image_id, self.alt_tenant_id)
         self.assertEqual(self.alt_tenant_id, member['member_id'])
         self.assertEqual(image_id, member['image_id'])
         self.assertEqual('accepted', member['status'])
@@ -70,38 +76,40 @@
     @test.idempotent_id('72989bc7-2268-48ed-af22-8821e835c914')
     def test_remove_image_member(self):
         image_id = self._create_image()
-        self.os_img_client.create_image_member(
+        self.image_member_client.create_image_member(
             image_id, member=self.alt_tenant_id)
-        self.alt_img_client.update_image_member(image_id,
-                                                self.alt_tenant_id,
-                                                status='accepted')
+        self.alt_image_member_client.update_image_member(image_id,
+                                                         self.alt_tenant_id,
+                                                         status='accepted')
 
         self.assertIn(image_id, self._list_image_ids_as_alt())
-        self.os_img_client.delete_image_member(image_id, self.alt_tenant_id)
+        self.image_member_client.delete_image_member(image_id,
+                                                     self.alt_tenant_id)
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
 
     @test.idempotent_id('634dcc3f-f6e2-4409-b8fd-354a0bb25d83')
     def test_get_image_member_schema(self):
-        body = self.os_img_client.show_schema("member")
+        body = self.schemas_client.show_schema("member")
         self.assertEqual("member", body['name'])
 
     @test.idempotent_id('6ae916ef-1052-4e11-8d36-b3ae14853cbb')
     def test_get_image_members_schema(self):
-        body = self.os_img_client.show_schema("members")
+        body = self.schemas_client.show_schema("members")
         self.assertEqual("members", body['name'])
 
     @test.idempotent_id('cb961424-3f68-4d21-8e36-30ad66fb6bfb')
     def test_get_private_image(self):
         image_id = self._create_image()
-        member = self.os_img_client.create_image_member(
+        member = self.image_member_client.create_image_member(
             image_id, member=self.alt_tenant_id)
         self.assertEqual(member['member_id'], self.alt_tenant_id)
         self.assertEqual(member['image_id'], image_id)
         self.assertEqual(member['status'], 'pending')
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
-        self.alt_img_client.update_image_member(image_id,
-                                                self.alt_tenant_id,
-                                                status='accepted')
+        self.alt_image_member_client.update_image_member(image_id,
+                                                         self.alt_tenant_id,
+                                                         status='accepted')
         self.assertIn(image_id, self._list_image_ids_as_alt())
-        self.os_img_client.delete_image_member(image_id, self.alt_tenant_id)
+        self.image_member_client.delete_image_member(image_id,
+                                                     self.alt_tenant_id)
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
diff --git a/tempest/api/image/v2/test_images_member_negative.py b/tempest/api/image/v2/test_images_member_negative.py
index eb90719..fa29a92 100644
--- a/tempest/api/image/v2/test_images_member_negative.py
+++ b/tempest/api/image/v2/test_images_member_negative.py
@@ -10,9 +10,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.image import base
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -22,11 +21,11 @@
     @test.idempotent_id('b79efb37-820d-4cf0-b54c-308b00cf842c')
     def test_image_share_invalid_status(self):
         image_id = self._create_image()
-        member = self.os_img_client.create_image_member(
+        member = self.image_member_client.create_image_member(
             image_id, member=self.alt_tenant_id)
         self.assertEqual(member['status'], 'pending')
         self.assertRaises(lib_exc.BadRequest,
-                          self.alt_img_client.update_image_member,
+                          self.alt_image_member_client.update_image_member,
                           image_id, self.alt_tenant_id,
                           status='notavalidstatus')
 
@@ -34,11 +33,11 @@
     @test.idempotent_id('27002f74-109e-4a37-acd0-f91cd4597967')
     def test_image_share_owner_cannot_accept(self):
         image_id = self._create_image()
-        member = self.os_img_client.create_image_member(
+        member = self.image_member_client.create_image_member(
             image_id, member=self.alt_tenant_id)
         self.assertEqual(member['status'], 'pending')
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
         self.assertRaises(lib_exc.Forbidden,
-                          self.os_img_client.update_image_member,
+                          self.image_member_client.update_image_member,
                           image_id, self.alt_tenant_id, status='accepted')
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_objects.py b/tempest/api/image/v2/test_images_metadefs_namespace_objects.py
new file mode 100644
index 0000000..95d1521
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_objects.py
@@ -0,0 +1,73 @@
+#    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.image import base
+from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest import test
+
+
+class MetadataNamespaceObjectsTest(base.BaseV2ImageTest):
+    """Test the Metadata definition namespace objects basic functionality"""
+
+    def _create_namespace_object(self, namespace):
+        object_name = data_utils.rand_name(self.__class__.__name__ + '-object')
+        namespace_object = self.namespace_objects_client.\
+            create_namespace_object(namespace['namespace'], name=object_name)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.namespace_objects_client.delete_namespace_object,
+                        namespace['namespace'], object_name)
+        return namespace_object
+
+    @test.idempotent_id('b1a3775e-3b5c-4f6a-a3b4-1ba3574ae718')
+    def test_create_update_delete_meta_namespace_objects(self):
+        # Create a namespace
+        namespace = self.create_namespace()
+        # Create a namespace object
+        body = self._create_namespace_object(namespace)
+        # Update a namespace object
+        up_object_name = data_utils.rand_name('update-object')
+        body = self.namespace_objects_client.update_namespace_object(
+            namespace['namespace'], body['name'],
+            name=up_object_name)
+        self.assertEqual(up_object_name, body['name'])
+        # Delete a namespace object
+        self.namespace_objects_client.delete_namespace_object(
+            namespace['namespace'], up_object_name)
+        # List namespace objects and validate deletion
+        namespace_objects = [
+            namespace_object['name'] for namespace_object in
+            self.namespace_objects_client.list_namespace_objects(
+                namespace['namespace'])['objects']]
+        self.assertNotIn(up_object_name, namespace_objects)
+
+    @test.idempotent_id('a2a3615e-3b5c-3f6a-a2b1-1ba3574ae738')
+    def test_list_meta_namespace_objects(self):
+        # Create a namespace object
+        namespace = self.create_namespace()
+        meta_namespace_object = self._create_namespace_object(namespace)
+        # List namespace objects
+        namespace_objects = [
+            namespace_object['name'] for namespace_object in
+            self.namespace_objects_client.list_namespace_objects(
+                namespace['namespace'])['objects']]
+        self.assertIn(meta_namespace_object['name'], namespace_objects)
+
+    @test.idempotent_id('b1a3674e-3b4c-3f6a-a3b4-1ba3573ca768')
+    def test_show_meta_namespace_objects(self):
+        # Create a namespace object
+        namespace = self.create_namespace()
+        namespace_object = self._create_namespace_object(namespace)
+        # Show a namespace object
+        body = self.namespace_objects_client.show_namespace_object(
+            namespace['namespace'], namespace_object['name'])
+        self.assertEqual(namespace_object['name'], body['name'])
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_properties.py b/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
new file mode 100644
index 0000000..7113db4
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
@@ -0,0 +1,57 @@
+#    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.image import base
+from tempest.common.utils import data_utils
+from tempest import test
+
+
+class MetadataNamespacePropertiesTest(base.BaseV2ImageTest):
+    """Test the Metadata definition namespace property basic functionality"""
+
+    @test.idempotent_id('b1a3765e-3a5d-4f6d-a3a7-3ca3476ae768')
+    def test_basic_meta_def_namespace_property(self):
+        # Get the available resource types and use one resource_type
+        body = self.resource_types_client.list_resource_types()
+        resource_name = body['resource_types'][0]['name']
+        enum = ["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"]
+        # Create a namespace
+        namespace = self.create_namespace()
+        # Create resource type association
+        body = self.resource_types_client.create_resource_type_association(
+            namespace['namespace'], name=resource_name)
+        # Create a property
+        property_title = data_utils.rand_name('property')
+        body = self.namespace_properties_client.create_namespace_property(
+            namespace=namespace['namespace'], title=property_title,
+            name=resource_name, type="string", enum=enum)
+        self.assertEqual(property_title, body['title'])
+        # Show namespace property
+        body = self.namespace_properties_client.show_namespace_properties(
+            namespace['namespace'], resource_name)
+        self.assertEqual(resource_name, body['name'])
+        # Update namespace property
+        update_property_title = data_utils.rand_name('update-property')
+        body = self.namespace_properties_client.update_namespace_properties(
+            namespace['namespace'], resource_name,
+            title=update_property_title, type="string",
+            enum=enum, name=resource_name)
+        self.assertEqual(update_property_title, body['title'])
+        # Delete namespace property
+        self.namespace_properties_client.delete_namespace_property(
+            namespace['namespace'], resource_name)
+        # List namespace properties and validate deletion
+        namespace_property = [
+            namespace_property['title'] for namespace_property in
+            self.namespace_properties_client.list_namespace_properties(
+                namespace['namespace'])['properties']]
+        self.assertNotIn(update_property_title, namespace_property)
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_tags.py b/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
new file mode 100644
index 0000000..186d9c8
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
@@ -0,0 +1,90 @@
+#    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.image import base
+from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest import test
+
+
+class MetadataNamespaceTagsTest(base.BaseV2ImageTest):
+    """Test the Metadata definition namespace tags basic functionality"""
+
+    tags = [
+        {
+            "name": "sample-tag1"
+        },
+        {
+            "name": "sample-tag2"
+        },
+        {
+            "name": "sample-tag3"
+        }
+    ]
+    tag_list = ["sample-tag1", "sample-tag2", "sample-tag3"]
+
+    def _create_namespace_tags(self, namespace):
+        # Create a namespace
+        namespace_tags = self.namespace_tags_client.create_namespace_tags(
+            namespace['namespace'], tags=self.tags)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.namespace_tags_client.delete_namespace_tags,
+                        namespace['namespace'])
+        return namespace_tags
+
+    @test.idempotent_id('a2a3765e-3a6d-4f6d-a3a7-3cc3476aa876')
+    def test_create_list_delete_namespace_tags(self):
+        # Create a namespace
+        namespace = self.create_namespace()
+        self._create_namespace_tags(namespace)
+        # List namespace tags
+        body = self.namespace_tags_client.list_namespace_tags(
+            namespace['namespace'])
+        self.assertTrue(3, len(body['tags']))
+        self.assertIn(body['tags'][0]['name'], self.tag_list)
+        self.assertIn(body['tags'][1]['name'], self.tag_list)
+        self.assertIn(body['tags'][2]['name'], self.tag_list)
+        # Delete all tag definitions
+        self.namespace_tags_client.delete_namespace_tags(
+            namespace['namespace'])
+        body = self.namespace_tags_client.list_namespace_tags(
+            namespace['namespace'])
+        self.assertEqual([], body['tags'])
+
+    @test.idempotent_id('a2a3765e-1a2c-3f6d-a3a7-3cc3466ab875')
+    def test_create_update_delete_tag(self):
+        # Create a namespace
+        namespace = self.create_namespace()
+        self._create_namespace_tags(namespace)
+        # Create a tag
+        tag_name = data_utils.rand_name('tag_name')
+        self.namespace_tags_client.create_namespace_tag(
+            namespace=namespace['namespace'], tag_name=tag_name)
+
+        body = self.namespace_tags_client.show_namespace_tag(
+            namespace['namespace'], tag_name)
+        self.assertEqual(tag_name, body['name'])
+        # Update tag definition
+        update_tag_definition = data_utils.rand_name('update-tag')
+        body = self.namespace_tags_client.update_namespace_tag(
+            namespace['namespace'], tag_name=tag_name,
+            name=update_tag_definition)
+        self.assertEqual(update_tag_definition, body['name'])
+        # Delete tag definition
+        self.namespace_tags_client.delete_namespace_tag(
+            namespace['namespace'], update_tag_definition)
+        # List namespace tags and validate deletion
+        namespace_tags = [
+            namespace_tag['name'] for namespace_tag in
+            self.namespace_tags_client.list_namespace_tags(
+                namespace['namespace'])['tags']]
+        self.assertNotIn(update_tag_definition, namespace_tags)
diff --git a/tempest/api/image/v2/test_images_metadefs_namespaces.py b/tempest/api/image/v2/test_images_metadefs_namespaces.py
index efb7b8b..a80a0cf 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespaces.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespaces.py
@@ -15,8 +15,9 @@
 
 from tempest.api.image import base
 from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
-from tempest_lib import exceptions as lib_exc
 
 
 class MetadataNamespacesTest(base.BaseV2ImageTest):
@@ -25,46 +26,51 @@
     @test.idempotent_id('319b765e-7f3d-4b3d-8b37-3ca3876ee768')
     def test_basic_metadata_definition_namespaces(self):
         # get the available resource types and use one resource_type
-        body = self.client.list_resource_types()
+        body = self.resource_types_client.list_resource_types()
         resource_name = body['resource_types'][0]['name']
         name = [{'name': resource_name}]
         namespace_name = data_utils.rand_name('namespace')
         # create the metadef namespace
-        body = self.client.create_namespace(namespace=namespace_name,
-                                            visibility='public',
-                                            description='Tempest',
-                                            display_name=namespace_name,
-                                            resource_type_associations=name,
-                                            protected=True)
-        self.addCleanup(self._cleanup_namespace, namespace_name)
+        body = self.namespaces_client.create_namespace(
+            namespace=namespace_name,
+            visibility='public',
+            description='Tempest',
+            display_name=namespace_name,
+            resource_type_associations=name,
+            protected=True)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self._cleanup_namespace, namespace_name)
+        # list namespaces
+        bodys = self.namespaces_client.list_namespaces()['namespaces']
+        body = [namespace['namespace'] for namespace in bodys]
+        self.assertIn(namespace_name, body)
         # get namespace details
-        body = self.client.show_namespace(namespace_name)
+        body = self.namespaces_client.show_namespace(namespace_name)
         self.assertEqual(namespace_name, body['namespace'])
         self.assertEqual('public', body['visibility'])
         # unable to delete protected namespace
-        self.assertRaises(lib_exc.Forbidden, self.client.delete_namespace,
+        self.assertRaises(lib_exc.Forbidden,
+                          self.namespaces_client.delete_namespace,
                           namespace_name)
         # update the visibility to private and protected to False
-        body = self.client.update_namespace(namespace=namespace_name,
-                                            description='Tempest',
-                                            visibility='private',
-                                            display_name=namespace_name,
-                                            protected=False)
+        body = self.namespaces_client.update_namespace(
+            namespace=namespace_name,
+            description='Tempest',
+            visibility='private',
+            display_name=namespace_name,
+            protected=False)
         self.assertEqual('private', body['visibility'])
         self.assertEqual(False, body['protected'])
         # now able to delete the non-protected namespace
-        self.client.delete_namespace(namespace_name)
+        self.namespaces_client.delete_namespace(namespace_name)
 
     def _cleanup_namespace(self, namespace_name):
-        # this is used to cleanup the resources
-        try:
-            body = self.client.show_namespace(namespace_name)
-            self.assertEqual(namespace_name, body['namespace'])
-            body = self.client.update_namespace(namespace=namespace_name,
-                                                description='Tempest',
-                                                visibility='private',
-                                                display_name=namespace_name,
-                                                protected=False)
-            self.client.delete_namespace(namespace_name)
-        except lib_exc.NotFound:
-            pass
+        body = self.namespaces_client.show_namespace(namespace_name)
+        self.assertEqual(namespace_name, body['namespace'])
+        body = self.namespaces_client.update_namespace(
+            namespace=namespace_name,
+            description='Tempest',
+            visibility='private',
+            display_name=namespace_name,
+            protected=False)
+        self.namespaces_client.delete_namespace(namespace_name)
diff --git a/tempest/api/image/v2/test_images_metadefs_resource_types.py b/tempest/api/image/v2/test_images_metadefs_resource_types.py
new file mode 100644
index 0000000..3dd432b
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_resource_types.py
@@ -0,0 +1,54 @@
+# Copyright 2016 Ericsson India Global Services Private Limited
+# 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.image import base
+from tempest import test
+
+
+class MetadataResourceTypesTest(base.BaseV2ImageTest):
+    """Test the Metadata definition resource types basic functionality"""
+
+    @test.idempotent_id('6f358a4e-5ef0-11e6-a795-080027d0d606')
+    def test_basic_meta_def_resource_type_association(self):
+        # Get the available resource types and use one resource_type
+        body = self.resource_types_client.list_resource_types()
+        resource_name = body['resource_types'][0]['name']
+        # Create a namespace
+        namespace = self.create_namespace()
+        # Create resource type association
+        body = self.resource_types_client.create_resource_type_association(
+            namespace['namespace'], name=resource_name)
+        self.assertEqual(body['name'], resource_name)
+        # NOTE(raiesmh08): Here intentionally I have not added addcleanup
+        # method for resource type dissociation because its a metadata add and
+        # being cleaned as soon as namespace is cleaned at test case level.
+        # When namespace cleans, resource type association will automatically
+        # clean without any error or dependency.
+
+        # List resource type associations and validate creation
+        rs_type_associations = [
+            rs_type_association['name'] for rs_type_association in
+            self.resource_types_client.list_resource_type_association(
+                namespace['namespace'])['resource_type_associations']]
+        self.assertIn(resource_name, rs_type_associations)
+        # Delete resource type association
+        self.resource_types_client.delete_resource_type_association(
+            namespace['namespace'], resource_name)
+        # List resource type associations and validate deletion
+        rs_type_associations = [
+            rs_type_association['name'] for rs_type_association in
+            self.resource_types_client.list_resource_type_association(
+                namespace['namespace'])['resource_type_associations']]
+        self.assertNotIn(resource_name, rs_type_associations)
diff --git a/tempest/api/image/v2/test_images_metadefs_schema.py b/tempest/api/image/v2/test_images_metadefs_schema.py
new file mode 100644
index 0000000..7edf1af
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_schema.py
@@ -0,0 +1,81 @@
+# Copyright 2016 EasyStack.
+# 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.image import base
+from tempest import test
+
+
+class MetadataSchemaTest(base.BaseV2ImageTest):
+    """Test to get metadata schema"""
+
+    @test.idempotent_id('e9e44891-3cb8-3b40-a532-e0a39fea3dab')
+    def test_get_metadata_namespace_schema(self):
+        # Test to get namespace schema
+        body = self.schemas_client.show_schema("metadefs/namespace")
+        self.assertEqual("namespace", body['name'])
+
+    @test.idempotent_id('ffe44891-678b-3ba0-a3e2-e0a3967b3aeb')
+    def test_get_metadata_namespaces_schema(self):
+        # Test to get namespaces schema
+        body = self.schemas_client.show_schema("metadefs/namespaces")
+        self.assertEqual("namespaces", body['name'])
+
+    @test.idempotent_id('fde34891-678b-3b40-ae32-e0a3e67b6beb')
+    def test_get_metadata_resource_type_schema(self):
+        # Test to get resource_type schema
+        body = self.schemas_client.show_schema("metadefs/resource_type")
+        self.assertEqual("resource_type_association", body['name'])
+
+    @test.idempotent_id('dfe4a891-b38b-3bf0-a3b2-e03ee67b3a3a')
+    def test_get_metadata_resources_types_schema(self):
+        # Test to get resource_types schema
+        body = self.schemas_client.show_schema("metadefs/resource_types")
+        self.assertEqual("resource_type_associations", body['name'])
+
+    @test.idempotent_id('dff4a891-b38b-3bf0-a3b2-e03ee67b3a3b')
+    def test_get_metadata_object_schema(self):
+        # Test to get object schema
+        body = self.schemas_client.show_schema("metadefs/object")
+        self.assertEqual("object", body['name'])
+
+    @test.idempotent_id('dee4a891-b38b-3bf0-a3b2-e03ee67b3a3c')
+    def test_get_metadata_objects_schema(self):
+        # Test to get objects schema
+        body = self.schemas_client.show_schema("metadefs/objects")
+        self.assertEqual("objects", body['name'])
+
+    @test.idempotent_id('dae4a891-b38b-3bf0-a3b2-e03ee67b3a3d')
+    def test_get_metadata_property_schema(self):
+        # Test to get property schema
+        body = self.schemas_client.show_schema("metadefs/property")
+        self.assertEqual("property", body['name'])
+
+    @test.idempotent_id('dce4a891-b38b-3bf0-a3b2-e03ee67b3a3e')
+    def test_get_metadata_properties_schema(self):
+        # Test to get properties schema
+        body = self.schemas_client.show_schema("metadefs/properties")
+        self.assertEqual("properties", body['name'])
+
+    @test.idempotent_id('dde4a891-b38b-3bf0-a3b2-e03ee67b3a3e')
+    def test_get_metadata_tag_schema(self):
+        # Test to get tag schema
+        body = self.schemas_client.show_schema("metadefs/tag")
+        self.assertEqual("tag", body['name'])
+
+    @test.idempotent_id('cde4a891-b38b-3bf0-a3b2-e03ee67b3a3a')
+    def test_get_metadata_tags_schema(self):
+        # Test to get tags schema
+        body = self.schemas_client.show_schema("metadefs/tags")
+        self.assertEqual("tags", body['name'])
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index 485942e..cd1bca0 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -14,11 +14,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.image import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -31,7 +29,7 @@
         ** get image with image_id=NULL
         ** get the deleted image
         ** delete non-existent image
-        ** delete rimage with  image_id=NULL
+        ** delete image with image_id=NULL
         ** delete the deleted image
      """
 
@@ -39,7 +37,7 @@
     @test.idempotent_id('668743d5-08ad-4480-b2b8-15da34f81d9f')
     def test_get_non_existent_image(self):
         # get the non-existent image
-        non_existent_id = str(uuid.uuid4())
+        non_existent_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.client.show_image,
                           non_existent_id)
 
@@ -55,25 +53,25 @@
     def test_get_delete_deleted_image(self):
         # get and delete the deleted image
         # create and delete image
-        body = self.client.create_image(name='test',
-                                        container_format='bare',
-                                        disk_format='raw')
-        image_id = body['id']
-        self.client.delete_image(image_id)
-        self.client.wait_for_resource_deletion(image_id)
+        image = self.client.create_image(name='test',
+                                         container_format='bare',
+                                         disk_format='raw')
+        self.client.delete_image(image['id'])
+        self.client.wait_for_resource_deletion(image['id'])
 
         # get the deleted image
-        self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
+        self.assertRaises(lib_exc.NotFound,
+                          self.client.show_image, image['id'])
 
         # delete the deleted image
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
-                          image_id)
+                          image['id'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('6fe40f1c-57bd-4918-89cc-8500f850f3de')
     def test_delete_non_existing_image(self):
         # delete non-existent image
-        non_existent_image_id = str(uuid.uuid4())
+        non_existent_image_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
                           non_existent_image_id)
 
diff --git a/tempest/api/image/v2/test_images_tags.py b/tempest/api/image/v2/test_images_tags.py
index 42a4b87..03f29bd 100644
--- a/tempest/api/image/v2/test_images_tags.py
+++ b/tempest/api/image/v2/test_images_tags.py
@@ -21,19 +21,18 @@
 
     @test.idempotent_id('10407036-6059-4f95-a2cd-cbbbee7ed329')
     def test_update_delete_tags_for_image(self):
-        body = self.create_image(container_format='bare',
-                                 disk_format='raw',
-                                 visibility='private')
-        image_id = body['id']
+        image = self.create_image(container_format='bare',
+                                  disk_format='raw',
+                                  visibility='private')
         tag = data_utils.rand_name('tag')
-        self.addCleanup(self.client.delete_image, image_id)
+        self.addCleanup(self.client.delete_image, image['id'])
 
         # Creating image tag and verify it.
-        self.client.add_image_tag(image_id, tag)
-        body = self.client.show_image(image_id)
+        self.client.add_image_tag(image['id'], tag)
+        body = self.client.show_image(image['id'])
         self.assertIn(tag, body['tags'])
 
         # Deleting image tag and verify it.
-        self.client.delete_image_tag(image_id, tag)
-        body = self.client.show_image(image_id)
+        self.client.delete_image_tag(image['id'], tag)
+        body = self.client.show_image(image['id'])
         self.assertNotIn(tag, body['tags'])
diff --git a/tempest/api/image/v2/test_images_tags_negative.py b/tempest/api/image/v2/test_images_tags_negative.py
index a3f4ca8..af4ffcf 100644
--- a/tempest/api/image/v2/test_images_tags_negative.py
+++ b/tempest/api/image/v2/test_images_tags_negative.py
@@ -12,12 +12,9 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.image import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -28,7 +25,7 @@
     def test_update_tags_for_non_existing_image(self):
         # Update tag with non existing image.
         tag = data_utils.rand_name('tag')
-        non_exist_image = str(uuid.uuid4())
+        non_exist_image = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.client.add_image_tag,
                           non_exist_image, tag)
 
@@ -36,12 +33,11 @@
     @test.idempotent_id('39c023a2-325a-433a-9eea-649bf1414b19')
     def test_delete_non_existing_tag(self):
         # Delete non existing tag.
-        body = self.create_image(container_format='bare',
-                                 disk_format='raw',
-                                 visibility='private'
-                                 )
-        image_id = body['id']
+        image = self.create_image(container_format='bare',
+                                  disk_format='raw',
+                                  visibility='private'
+                                  )
         tag = data_utils.rand_name('non-exist-tag')
-        self.addCleanup(self.client.delete_image, image_id)
+        self.addCleanup(self.client.delete_image, image['id'])
         self.assertRaises(lib_exc.NotFound, self.client.delete_image_tag,
-                          image_id, tag)
+                          image['id'], tag)
diff --git a/tempest/api/network/admin/test_dhcp_agent_scheduler.py b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
index fcb6fce..b3555b6 100644
--- a/tempest/api/network/admin/test_dhcp_agent_scheduler.py
+++ b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
@@ -36,15 +36,15 @@
 
     @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)
+        self.assertNotEmpty(agents, "no dhcp agent")
         agent = agents[0]
         self.assertTrue(self._check_network_in_dhcp_agent(
             self.network['id'], agent))
diff --git a/tempest/api/network/admin/test_external_network_extension.py b/tempest/api/network/admin/test_external_network_extension.py
index a32bfbc..2d53265 100644
--- a/tempest/api/network/admin/test_external_network_extension.py
+++ b/tempest/api/network/admin/test_external_network_extension.py
@@ -12,6 +12,7 @@
 
 from tempest.api.network import base
 from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest import test
 
 
@@ -97,7 +98,7 @@
         body = self.admin_networks_client.create_network(
             **{'router:external': True})
         external_network = body['network']
-        self.addCleanup(self._try_delete_resource,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.admin_networks_client.delete_network,
                         external_network['id'])
         subnet = self.create_subnet(
@@ -106,7 +107,7 @@
         body = self.admin_floating_ips_client.create_floatingip(
             floating_network_id=external_network['id'])
         created_floating_ip = body['floatingip']
-        self.addCleanup(self._try_delete_resource,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.admin_floating_ips_client.delete_floatingip,
                         created_floating_ip['id'])
         floatingip_list = self.admin_floating_ips_client.list_floatingips(
diff --git a/tempest/api/network/admin/test_external_networks_negative.py b/tempest/api/network/admin/test_external_networks_negative.py
index d031108..94d65c3 100644
--- a/tempest/api/network/admin/test_external_networks_negative.py
+++ b/tempest/api/network/admin/test_external_networks_negative.py
@@ -13,10 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.network import base
 from tempest import config
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
@@ -35,7 +35,7 @@
         body = self.admin_floating_ips_client.create_floatingip(
             floating_network_id=CONF.network.public_network_id)
         created_floating_ip = body['floatingip']
-        self.addCleanup(self._try_delete_resource,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.admin_floating_ips_client.delete_floatingip,
                         created_floating_ip['id'])
         floating_ip_address = created_floating_ip['floating_ip_address']
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..a32e7da 100644
--- a/tempest/api/network/admin/test_floating_ips_admin_actions.py
+++ b/tempest/api/network/admin/test_floating_ips_admin_actions.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from tempest.api.network import base
-from tempest.common.utils import data_utils
 from tempest import config
 from tempest import test
 
@@ -26,9 +25,15 @@
     credentials = ['primary', 'alt', 'admin']
 
     @classmethod
+    def skip_checks(cls):
+        super(FloatingIPAdminTestJSON, cls).skip_checks()
+        if not test.is_extension_enabled('router', 'network'):
+            msg = "router extension not enabled."
+            raise cls.skipException(msg)
+
+    @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
@@ -38,8 +43,7 @@
         cls.floating_ip = cls.create_floatingip(cls.ext_net_id)
         cls.network = cls.create_network()
         cls.subnet = cls.create_subnet(cls.network)
-        cls.router = cls.create_router(data_utils.rand_name('router-'),
-                                       external_network_id=cls.ext_net_id)
+        cls.router = cls.create_router(external_network_id=cls.ext_net_id)
         cls.create_router_interface(cls.router['id'], cls.subnet['id'])
         cls.port = cls.create_port(cls.network)
 
@@ -68,7 +72,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..c2ff038 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -13,9 +13,8 @@
 #    under the License.
 
 from tempest.api.network import base
-from tempest.common.utils import data_utils
 from tempest import config
-from tempest import exceptions
+from tempest.lib import exceptions
 from tempest import test
 
 CONF = config.CONF
@@ -66,41 +65,42 @@
         else:
             msg = "L3 Agent Scheduler enabled in conf, but L3 Agent not found"
             raise exceptions.InvalidConfiguration(msg)
-        cls.router = cls.create_router(data_utils.rand_name('router'))
-        # NOTE(armax): If DVR is an available extension, and the created router
-        # is indeed a distributed one, more resources need to be provisioned
-        # in order to bind the router to the L3 agent.
-        # That said, let's preserve the existing test logic, where the extra
-        # 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.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'])
-                # 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
-                # on the given agent. By adding the external gateway info to
-                # the router, the router should be properly scheduled in the
-                # dvr_snat node.
-                # This is a temporary work around to prevent a race condition.
-                external_gateway_info = {
-                    'network_id': CONF.network.public_network_id,
-                    'enable_snat': True}
-                cls.admin_client.update_router_with_snat_gw_info(
-                    cls.router['id'],
-                    external_gateway_info=external_gateway_info)
+        cls.router = cls.create_router()
+
+        if CONF.network.dvr_extra_resources:
+            # NOTE(armax): If DVR is an available extension, and the created
+            # router is indeed a distributed one, more resources need to be
+            # provisioned in order to bind the router to the L3 agent in the
+            # Liberty release or older, and are not required since the Mitaka
+            # release.
+            if test.is_extension_enabled('dvr', 'network'):
+                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.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 on the given agent. By adding the external
+                    # gateway info to the router, the router should be properly
+                    # scheduled in the dvr_snat node. This is a temporary work
+                    # around to prevent a race condition.
+                    external_gateway_info = {
+                        'network_id': CONF.network.public_network_id,
+                        'enable_snat': True}
+                    cls.admin_routers_client.update_router(
+                        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 47da08c..beb6ce6 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -14,8 +14,8 @@
 #    under the License.
 
 from tempest.api.network import base
+from tempest.lib import exceptions as lib_exc
 from tempest import test
-from tempest_lib import exceptions as lib_exc
 
 
 class QuotasNegativeTest(base.BaseAdminNetworkTest):
@@ -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
@@ -38,11 +38,6 @@
             msg = "quotas extension not enabled."
             raise cls.skipException(msg)
 
-    @classmethod
-    def setup_clients(cls):
-        super(QuotasNegativeTest, cls).setup_clients()
-        cls.identity_admin_client = cls.os_adm.identity_client
-
     @test.idempotent_id('644f4e1b-1bf9-4af0-9fd8-eb56ac0f51cf')
     def test_network_quota_exceeding(self):
         # Set the network quota to two
@@ -60,10 +55,9 @@
                         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'\].*"):
+                "Quota exceeded for resources: \['network'\].*"):
             n3 = self.networks_client.create_network()
             self.addCleanup(self.networks_client.delete_network,
                             n3['network']['id'])
diff --git a/tempest/api/network/admin/test_quotas.py b/tempest/api/network/admin/test_quotas.py
index 8b32a94..8695ebd 100644
--- a/tempest/api/network/admin/test_quotas.py
+++ b/tempest/api/network/admin/test_quotas.py
@@ -13,24 +13,22 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
-
 from tempest.api.network import base
 from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest import test
-from tempest_lib import exceptions as lib_exc
 
 
 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
@@ -43,13 +41,8 @@
             msg = "quotas extension not enabled."
             raise cls.skipException(msg)
 
-    @classmethod
-    def setup_clients(cls):
-        super(QuotasTest, cls).setup_clients()
-        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 +50,15 @@
         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.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.admin_quotas_client.reset_quotas, project_id)
+        for key, value in new_quotas.items():
             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,10 +66,10 @@
                 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):
+        for key, value in new_quotas.items():
             self.assertEqual(value, quota_set[key])
 
         # Reset quotas to default and confirm
@@ -86,14 +80,5 @@
 
     @test.idempotent_id('2390f766-836d-40ef-9aeb-e810d78207fb')
     def test_quotas(self):
-        new_quotas = {'network': 0, 'security_group': 0}
+        new_quotas = {'network': 0, 'port': 0}
         self._check_quotas(new_quotas)
-
-    def _cleanup_quotas(self, project_id):
-        # try to clean up the resources.If it fails, then
-        # assume that everything was already deleted, so
-        # it is OK to continue.
-        try:
-            self.admin_quotas_client.reset_quotas(project_id)
-        except lib_exc.NotFound:
-            pass
diff --git a/tempest/api/network/admin/test_routers_dvr.py b/tempest/api/network/admin/test_routers_dvr.py
index 3e787af..aaac921 100644
--- a/tempest/api/network/admin/test_routers_dvr.py
+++ b/tempest/api/network/admin/test_routers_dvr.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.network import base_routers as base
 from tempest.common.utils import data_utils
 from tempest import test
@@ -34,8 +36,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 +55,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,12 +75,15 @@
         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'])
 
     @test.idempotent_id('acd43596-c1fb-439d-ada8-31ad48ae3c2e')
+    @testtools.skipUnless(test.is_extension_enabled('l3-ha', 'network'),
+                          'HA routers are not available.')
     def test_centralized_router_update_to_dvr(self):
         """Test centralized router update
 
@@ -92,12 +98,22 @@
         attribute will be set to True
         """
         name = data_utils.rand_name('router')
+        tenant_id = self.routers_client.tenant_id
         # 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['router']['id'])
+        # l3ha routers are not upgradable to dvr, make it explicitly non ha
+        router = self.admin_routers_client.create_router(name=name,
+                                                         distributed=False,
+                                                         admin_state_up=False,
+                                                         ha=False,
+                                                         tenant_id=tenant_id)
+        router_id = router['router']['id']
+        self.addCleanup(self.admin_routers_client.delete_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_id, distributed=True)
         self.assertTrue(router['router']['distributed'])
+        show_body = self.admin_routers_client.show_router(router_id)
+        self.assertTrue(show_body['router']['distributed'])
+        show_body = self.routers_client.show_router(router_id)
+        self.assertNotIn('distributed', show_body['router'])
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index f209f89..132e23e 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -14,29 +14,30 @@
 #    under the License.
 
 import netaddr
-from tempest_lib import exceptions as lib_exc
 
 from tempest.common.utils import data_utils
 from tempest import config
 from tempest import exceptions
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions as lib_exc
 import tempest.test
 
 CONF = config.CONF
 
 
 class BaseNetworkTest(tempest.test.BaseTestCase):
-    """Base class for the Neutron tests
+    """Base class for the Neutron tests.
 
     Per the Neutron API Guide, API v1.x was removed from the source code tree
     (docs.openstack.org/api/openstack-network/2.0/content/Overview-d1e71.html)
     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 +68,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
@@ -79,6 +80,8 @@
         cls.security_groups_client = cls.os.security_groups_client
         cls.security_group_rules_client = (
             cls.os.security_group_rules_client)
+        cls.network_versions_client = cls.os.network_versions_client
+        cls.service_providers_client = cls.os.service_providers_client
 
     @classmethod
     def resource_setup(cls):
@@ -97,7 +100,7 @@
         if CONF.service_available.neutron:
             # Clean up floating IPs
             for floating_ip in cls.floating_ips:
-                cls._try_delete_resource(
+                test_utils.call_and_ignore_notfound_exc(
                     cls.floating_ips_client.delete_floatingip,
                     floating_ip['id'])
 
@@ -105,59 +108,40 @@
             # Not all classes in the hierarchy have the client class variable
             if len(cls.metering_label_rules) > 0:
                 label_rules_client = cls.admin_metering_label_rules_client
-            for metering_label_rule in cls.metering_label_rules:
-                cls._try_delete_resource(
-                    label_rules_client.delete_metering_label_rule,
-                    metering_label_rule['id'])
+                for metering_label_rule in cls.metering_label_rules:
+                    test_utils.call_and_ignore_notfound_exc(
+                        label_rules_client.delete_metering_label_rule,
+                        metering_label_rule['id'])
             # Clean up metering labels
             for metering_label in cls.metering_labels:
-                cls._try_delete_resource(
+                test_utils.call_and_ignore_notfound_exc(
                     cls.admin_metering_labels_client.delete_metering_label,
                     metering_label['id'])
             # Clean up ports
             for port in cls.ports:
-                cls._try_delete_resource(cls.ports_client.delete_port,
-                                         port['id'])
+                test_utils.call_and_ignore_notfound_exc(
+                    cls.ports_client.delete_port, port['id'])
             # Clean up routers
             for router in cls.routers:
-                cls._try_delete_resource(cls.delete_router,
-                                         router)
+                test_utils.call_and_ignore_notfound_exc(
+                    cls.delete_router, router)
             # Clean up subnets
             for subnet in cls.subnets:
-                cls._try_delete_resource(cls.subnets_client.delete_subnet,
-                                         subnet['id'])
+                test_utils.call_and_ignore_notfound_exc(
+                    cls.subnets_client.delete_subnet, subnet['id'])
             # Clean up networks
             for network in cls.networks:
-                cls._try_delete_resource(cls.networks_client.delete_network,
-                                         network['id'])
+                test_utils.call_and_ignore_notfound_exc(
+                    cls.networks_client.delete_network, network['id'])
         super(BaseNetworkTest, cls).resource_cleanup()
 
     @classmethod
-    def _try_delete_resource(self, delete_callable, *args, **kwargs):
-        """Cleanup resources in case of test-failure
-
-        Some resources are explicitly deleted by the test.
-        If the test failed to delete a resource, this method will execute
-        the appropriate delete methods. Otherwise, the method ignores NotFound
-        exceptions thrown for resources that were correctly deleted by the
-        test.
-
-        :param delete_callable: delete method
-        :param args: arguments for delete method
-        :param kwargs: keyword arguments for delete method
-        """
-        try:
-            delete_callable(*args, **kwargs)
-        # if resource is not found, this means it was deleted in the test
-        except lib_exc.NotFound:
-            pass
-
-    @classmethod
-    def create_network(cls, network_name=None):
+    def create_network(cls, network_name=None, **kwargs):
         """Wrapper utility that returns a test network."""
-        network_name = network_name or data_utils.rand_name('test-network-')
+        network_name = network_name or data_utils.rand_name(
+            cls.__name__ + '-test-network')
 
-        body = cls.networks_client.create_network(name=network_name)
+        body = cls.networks_client.create_network(name=network_name, **kwargs)
         network = body['network']
         cls.networks.append(network)
         return network
@@ -166,7 +150,6 @@
     def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
                       ip_version=None, client=None, **kwargs):
         """Wrapper utility that returns a test subnet."""
-
         # allow tests to use admin client
         if not client:
             client = cls.subnets_client
@@ -175,12 +158,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:
@@ -226,13 +209,16 @@
     def create_router(cls, router_name=None, admin_state_up=False,
                       external_network_id=None, enable_snat=None,
                       **kwargs):
+        router_name = router_name or data_utils.rand_name(
+            cls.__name__ + "-router")
+
         ext_gw_info = {}
         if external_network_id:
             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 +236,19 @@
     @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(
-                    router['id'],
-                    subnet_id=i['fixed_ips'][0]['subnet_id'])
-            except lib_exc.NotFound:
-                pass
-        cls.client.delete_router(router['id'])
+            test_utils.call_and_ignore_notfound_exc(
+                cls.routers_client.remove_router_interface, router['id'],
+                subnet_id=i['fixed_ips'][0]['subnet_id'])
+        cls.routers_client.delete_router(router['id'])
 
 
 class BaseAdminNetworkTest(BaseNetworkTest):
@@ -275,9 +258,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
@@ -291,7 +274,7 @@
         """Wrapper utility that returns a test metering label."""
         body = cls.admin_metering_labels_client.create_metering_label(
             description=description,
-            name=data_utils.rand_name("metering-label"))
+            name=name)
         metering_label = body['metering_label']
         cls.metering_labels.append(metering_label)
         return metering_label
diff --git a/tempest/api/network/base_routers.py b/tempest/api/network/base_routers.py
index 3495b76f..5fb5232 100644
--- a/tempest/api/network/base_routers.py
+++ b/tempest/api/network/base_routers.py
@@ -25,7 +25,7 @@
         self.delete_router(router)
         self.routers.remove(router)
 
-    def _create_router(self, name, admin_state_up=False,
+    def _create_router(self, name=None, admin_state_up=False,
                        external_network_id=None, enable_snat=None):
         # associate a cleanup with created routers to avoid quota limits
         router = self.create_router(name, admin_state_up,
@@ -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..92dfc56 100644
--- a/tempest/api/network/test_allowed_address_pair.py
+++ b/tempest/api/network/test_allowed_address_pair.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 import netaddr
+import six
 
 from tempest.api.network import base
 from tempest import config
@@ -90,7 +91,8 @@
         body = self.ports_client.update_port(
             port_id, allowed_address_pairs=allowed_address_pairs)
         allowed_address_pair = body['port']['allowed_address_pairs']
-        self.assertEqual(allowed_address_pair, allowed_address_pairs)
+        six.assertCountEqual(self, allowed_address_pair,
+                             allowed_address_pairs)
 
     @test.idempotent_id('9599b337-272c-47fd-b3cf-509414414ac4')
     def test_update_port_with_address_pair(self):
@@ -100,7 +102,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 dbb0d14..3c96a93 100644
--- a/tempest/api/network/test_dhcp_ipv6.py
+++ b/tempest/api/network/test_dhcp_ipv6.py
@@ -16,12 +16,11 @@
 import netaddr
 import random
 
-import six
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.network import base
 from tempest.common.utils import data_utils
+from tempest.common.utils import net_info
 from tempest import config
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
@@ -30,7 +29,7 @@
 class NetworksTestDHCPv6(base.BaseNetworkTest):
     _ip_version = 6
 
-    """ Test DHCPv6 specific features using SLAAC, stateless and
+    """Test DHCPv6 specific features using SLAAC, stateless and
     stateful settings for subnets. Also it shall check dual-stack
     functionality (IPv4 + IPv6 together).
     The tests include:
@@ -66,10 +65,10 @@
         body = self.ports_client.list_ports()
         ports = body['ports']
         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'])
+            if (net_info.is_router_interface_port(port) and
+                port['device_id'] in [r['id'] for r in self.routers]):
+                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 +79,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):
@@ -125,7 +124,7 @@
         ):
             kwargs = {'ipv6_ra_mode': ra_mode,
                       'ipv6_address_mode': add_mode}
-            kwargs = dict((k, v) for k, v in six.iteritems(kwargs) if v)
+            kwargs = dict((k, v) for k, v in kwargs.items() if v)
             real_ip, eui_ip = self._get_ips_from_subnet(**kwargs)
             self._clean_network()
             self.assertEqual(eui_ip, real_ip,
@@ -268,7 +267,7 @@
         ):
             kwargs = {'ipv6_ra_mode': ra_mode,
                       'ipv6_address_mode': add_mode}
-            kwargs = dict((k, v) for k, v in six.iteritems(kwargs) if v)
+            kwargs = dict((k, v) for k, v in kwargs.items() if v)
             subnet = self.create_subnet(self.network, **kwargs)
             port = self.create_port(self.network)
             port_ip = next(iter(port['fixed_ips']), None)['ip_address']
@@ -290,7 +289,7 @@
         ):
             kwargs = {'ipv6_ra_mode': ra_mode,
                       'ipv6_address_mode': add_mode}
-            kwargs = dict((k, v) for k, v in six.iteritems(kwargs) if v)
+            kwargs = dict((k, v) for k, v in kwargs.items() if v)
             subnet = self.create_subnet(self.network, **kwargs)
             ip_range = netaddr.IPRange(subnet["allocation_pools"][0]["start"],
                                        subnet["allocation_pools"][0]["end"])
@@ -338,18 +337,16 @@
                          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,
+                               "IpAddressAlreadyAllocated|IpAddressInUse",
+                               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)
-        router = self.create_router(
-            router_name=data_utils.rand_name("routerv6-"),
-            admin_state_up=True)
+        router = self.create_router(admin_state_up=True)
         port = self.create_router_interface(router['id'],
                                             subnet['id'])
         body = self.ports_client.show_port(port['port_id'])
@@ -365,7 +362,7 @@
         ):
             kwargs = {'ipv6_ra_mode': ra_mode,
                       'ipv6_address_mode': add_mode}
-            kwargs = dict((k, v) for k, v in six.iteritems(kwargs) if v)
+            kwargs = dict((k, v) for k, v in kwargs.items() if v)
             subnet, port = self._create_subnet_router(kwargs)
             port_ip = next(iter(port['fixed_ips']), None)['ip_address']
             self._clean_network()
diff --git a/tempest/api/network/test_extensions.py b/tempest/api/network/test_extensions.py
index d71d600..84150b4 100644
--- a/tempest/api/network/test_extensions.py
+++ b/tempest/api/network/test_extensions.py
@@ -23,15 +23,15 @@
 
         List all available extensions
 
-    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:
-
+    v2.0 of the Neutron API is assumed. It is also assumed that api-extensions
+    option is defined in the [network-feature-enabled] section of
+    etc/tempest.conf.
     """
 
     @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..efe8982 100644
--- a/tempest/api/network/test_floating_ips.py
+++ b/tempest/api/network/test_floating_ips.py
@@ -13,10 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import netaddr
-
 from tempest.api.network import base
-from tempest.common.utils import data_utils
+from tempest.common.utils import net_utils
 from tempest import config
 from tempest import test
 
@@ -55,11 +53,9 @@
 
         # Create network, subnet, router and add interface
         cls.network = cls.create_network()
-        cls.subnet = cls.create_subnet(cls.network)
-        cls.router = cls.create_router(data_utils.rand_name('router-'),
-                                       external_network_id=cls.ext_net_id)
+        cls.subnet = cls.create_subnet(cls.network, enable_dhcp=False)
+        cls.router = cls.create_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)
@@ -158,8 +154,7 @@
         self.assertEqual(created_floating_ip['router_id'], self.router['id'])
         network2 = self.create_network()
         subnet2 = self.create_subnet(network2)
-        router2 = self.create_router(data_utils.rand_name('router-'),
-                                     external_network_id=self.ext_net_id)
+        router2 = self.create_router(external_network_id=self.ext_net_id)
         self.create_router_interface(router2['id'], subnet2['id'])
         port_other_router = self.create_port(network2)
         # Associate floating IP to the other port on another router
@@ -193,8 +188,12 @@
     @test.idempotent_id('45c4c683-ea97-41ef-9c51-5e9802f2f3d7')
     def test_create_update_floatingip_with_port_multiple_ip_address(self):
         # Find out ips that can be used for tests
-        ips = list(netaddr.IPNetwork(self.subnet['cidr']))
-        list_ips = [str(ip) for ip in ips[-3:-1]]
+        list_ips = net_utils.get_unused_ip_addresses(
+            self.ports_client,
+            self.subnets_client,
+            self.subnet['network_id'],
+            self.subnet['id'],
+            2)
         fixed_ips = [{'ip_address': list_ips[0]}, {'ip_address': list_ips[1]}]
         # Create port
         body = self.ports_client.create_port(network_id=self.network['id'],
diff --git a/tempest/api/network/test_floating_ips_negative.py b/tempest/api/network/test_floating_ips_negative.py
index f915615..7ffc30f 100644
--- a/tempest/api/network/test_floating_ips_negative.py
+++ b/tempest/api/network/test_floating_ips_negative.py
@@ -14,11 +14,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.network 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
@@ -46,7 +44,7 @@
         # Create a network with a subnet connected to a router.
         cls.network = cls.create_network()
         cls.subnet = cls.create_subnet(cls.network)
-        cls.router = cls.create_router(data_utils.rand_name('router'))
+        cls.router = cls.create_router()
         cls.create_router_interface(cls.router['id'], cls.subnet['id'])
         cls.port = cls.create_port(cls.network)
 
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 1c446ef..8e2f3f6 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -12,54 +12,26 @@
 #    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
-from tempest_lib import exceptions as lib_exc
+import testtools
 
 from tempest.api.network import base
 from tempest.common import custom_matchers
 from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
 
 
-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
-        network update
-        subnet update
-        delete a network also deletes its subnets
-        list external networks
-
-        All subnet tests are run once with ipv4 and once with ipv6.
-
-    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
-
-        tenant_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
-
-        tenant_network_v6_mask_bits is the equivalent for ipv6 subnets
-    """
+class BaseNetworkTestResources(base.BaseNetworkTest):
 
     @classmethod
     def resource_setup(cls):
-        super(NetworksTest, cls).resource_setup()
+        super(BaseNetworkTestResources, cls).resource_setup()
         cls.network = cls.create_network()
         cls.name = cls.network['name']
         cls.subnet = cls._create_subnet_with_last_subnet_block(cls.network,
@@ -93,14 +65,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 +83,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
@@ -127,7 +99,7 @@
     def _get_allocation_pools_from_gateway(cls, ip_version):
         """Return allocation range for subnet of given gateway"""
         gateway = cls._get_gateway_from_tempest_conf(ip_version)
-        return [{'start': str(gateway + 2), 'end': str(gateway + 3)}]
+        return [{'start': str(gateway + 2), 'end': str(gateway + 6)}]
 
     def subnet_dict(self, include_keys):
         # Return a subnet dict which has include_keys and their corresponding
@@ -158,7 +130,7 @@
                                     **kwargs)
         compare_args_full = dict(gateway_ip=gateway, cidr=cidr,
                                  mask_bits=mask_bits, **kwargs)
-        compare_args = dict((k, v) for k, v in six.iteritems(compare_args_full)
+        compare_args = dict((k, v) for k, v in compare_args_full.items()
                             if v is not None)
 
         if 'dns_nameservers' in set(subnet).intersection(compare_args):
@@ -171,12 +143,42 @@
         self.networks.pop()
         self.subnets.pop()
 
+
+class NetworksTest(BaseNetworkTestResources):
+    """Tests the following operations in the Neutron API:
+
+        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
+        list external networks
+
+        All subnet tests are run once with ipv4 and once with ipv6.
+
+    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:
+
+        project_network_cidr with a block of cidr's from which smaller blocks
+        can be allocated for project ipv4 subnets
+
+        project_network_v6_cidr is the equivalent for ipv6 subnets
+
+        project_network_mask_bits with the mask bits to be used to partition
+        the block defined by project_network_cidr
+
+        project_network_v6_mask_bits is the equivalent for ipv6 subnets
+    """
+
     @test.attr(type='smoke')
     @test.idempotent_id('0e269138-0da6-4efc-a46d-578161e7b221')
     def test_create_update_delete_network_subnet(self):
         # Create a network
-        name = data_utils.rand_name('network-')
-        network = self.create_network(network_name=name)
+        network = self.create_network()
         self.addCleanup(self._delete_network, network)
         net_id = network['id']
         self.assertEqual('ACTIVE', network['status'])
@@ -207,12 +209,16 @@
     def test_show_network_fields(self):
         # Verify specific fields of a network
         fields = ['id', 'name']
+        if test.is_extension_enabled('net-mtu', 'network'):
+            fields.append('mtu')
         body = self.networks_client.show_network(self.network['id'],
                                                  fields=fields)
         network = body['network']
         self.assertEqual(sorted(network.keys()), sorted(fields))
         for field_name in fields:
             self.assertEqual(network[field_name], self.network[field_name])
+        self.assertNotIn('tenant_id', network)
+        self.assertNotIn('project_id', network)
 
     @test.attr(type='smoke')
     @test.idempotent_id('f7ffdeda-e200-4a7a-bcbe-05716e86bf43')
@@ -227,6 +233,8 @@
     def test_list_networks_fields(self):
         # Verify specific fields of the networks
         fields = ['id', 'name']
+        if test.is_extension_enabled('net-mtu', 'network'):
+            fields.append('mtu')
         body = self.networks_client.list_networks(fields=fields)
         networks = body['networks']
         self.assertNotEmpty(networks, "Network list returned is empty")
@@ -274,39 +282,25 @@
         for subnet in subnets:
             self.assertEqual(sorted(subnet.keys()), sorted(fields))
 
-    def _try_delete_network(self, net_id):
-        # delete network, if it exists
-        try:
-            self.networks_client.delete_network(net_id)
-        # if network is not found, this means it was deleted in the test
-        except lib_exc.NotFound:
-            pass
-
     @test.idempotent_id('f04f61a9-b7f3-4194-90b2-9bcf660d1bfe')
     def test_delete_network_with_subnet(self):
         # Creates a network
-        name = data_utils.rand_name('network-')
-        body = self.networks_client.create_network(name=name)
-        network = body['network']
+        network = self.create_network()
         net_id = network['id']
-        self.addCleanup(self._try_delete_network, net_id)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self._delete_network, network)
 
         # Find a cidr that is not in use yet and create a subnet with it
         subnet = self.create_subnet(network)
         subnet_id = subnet['id']
 
         # Delete network while the subnet still exists
-        body = self.networks_client.delete_network(net_id)
+        self.networks_client.delete_network(net_id)
 
         # Verify that the subnet got automatically deleted.
         self.assertRaises(lib_exc.NotFound, self.subnets_client.show_subnet,
                           subnet_id)
 
-        # Since create_subnet adds the subnet to the delete list, and it is
-        # is actually deleted here - this will create and issue, hence remove
-        # it from the list.
-        self.subnets.pop()
-
     @test.idempotent_id('d2d596e2-8e76-47a9-ac51-d4648009f4d3')
     def test_create_delete_subnet_without_gateway(self):
         self._create_verify_delete_subnet()
@@ -376,6 +370,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 +384,45 @@
         self.assertEmpty(nonexternal, "Found non-external networks"
                                       " in filtered list (%s)." % nonexternal)
         self.assertIn(CONF.network.public_network_id, networks)
+        # only check the public network ID because the other networks may
+        # belong to other tests and their state may have changed during this
+        # test
+        body = self.subnets_client.list_subnets(
+            network_id=CONF.network.public_network_id)
+        self.assertEmpty(body['subnets'], "Public subnets visible")
 
-        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")
+    @test.idempotent_id('c72c1c0c-2193-4aca-ccc4-b1442640bbbb')
+    @test.requires_ext(extension="standard-attr-description",
+                       service="network")
+    def test_create_update_network_description(self):
+        body = self.create_network(description='d1')
+        self.assertEqual('d1', body['description'])
+        net_id = body['id']
+        body = self.networks_client.list_networks(id=net_id)['networks'][0]
+        self.assertEqual('d1', body['description'])
+        body = self.networks_client.update_network(body['id'],
+                                                   description='d2')
+        self.assertEqual('d2', body['network']['description'])
+        body = self.networks_client.list_networks(id=net_id)['networks'][0]
+        self.assertEqual('d2', body['description'])
 
 
-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 +458,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 +474,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 +493,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 +519,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,37 +530,34 @@
             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)
+        network = self.create_network()
         subnet = self.create_subnet(network, gateway)
         # Verifies Subnet GW in IPv6
         self.assertEqual(subnet['gateway_ip'], gateway)
 
     @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)
+        network = self.create_network()
         subnet = self.create_subnet(network)
         # Verifies Subnet GW in IPv6
         self.assertEqual(subnet['gateway_ip'], gateway_ip)
 
     @test.idempotent_id('a9653883-b2a4-469b-8c3c-4518430a7e55')
     def test_create_list_subnet_with_no_gw64_one_network(self):
-        name = data_utils.rand_name('network-')
-        network = self.create_network(name)
+        network = self.create_network()
         ipv6_gateway = self.subnet_dict(['gateway'])['gateway']
         subnet1 = self.create_subnet(network,
                                      ip_version=6,
@@ -568,18 +572,20 @@
         # Verifies Subnet GW is set in IPv6
         self.assertEqual(subnet1['gateway_ip'], ipv6_gateway)
         # Verifies Subnet GW is None in IPv4
-        self.assertEqual(subnet2['gateway_ip'], None)
+        self.assertIsNone(subnet2['gateway_ip'])
         # Verifies all 2 subnets in the same network
         body = self.subnets_client.list_subnets()
         subnets = [sub['id'] for sub in body['subnets']
                    if sub['network_id'] == network['id']]
         test_subnet_ids = [sub['id'] for sub in (subnet1, subnet2)]
-        self.assertItemsEqual(subnets,
-                              test_subnet_ids,
-                              'Subnet are not in the same network')
+        six.assertCountEqual(self, subnets,
+                             test_subnet_ids,
+                             'Subnet are not in the same network')
 
 
-class NetworksIpV6TestAttrs(NetworksIpV6TestJSON):
+class NetworksIpV6TestAttrs(BaseNetworkTestResources):
+
+    _ip_version = 6
 
     @classmethod
     def skip_checks(cls):
@@ -621,7 +627,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_networks_negative.py b/tempest/api/network/test_networks_negative.py
index 0ef96a6..d87c2b6 100644
--- a/tempest/api/network/test_networks_negative.py
+++ b/tempest/api/network/test_networks_negative.py
@@ -14,10 +14,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.network import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index d7b220b..e7153f0 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -16,13 +16,14 @@
 import socket
 
 import netaddr
+import testtools
 
 from tempest.api.network import base
 from tempest.api.network import base_security_groups as sec_base
 from tempest.common import custom_matchers
 from tempest.common.utils import data_utils
 from tempest import config
-from tempest import exceptions
+from tempest.lib import exceptions
 from tempest import test
 
 CONF = config.CONF
@@ -71,11 +72,10 @@
     @test.idempotent_id('67f1b811-f8db-43e2-86bd-72c074d4a42c')
     def test_create_bulk_port(self):
         network1 = self.network
-        name = data_utils.rand_name('network-')
-        network2 = self.create_network(network_name=name)
+        network2 = self.create_network()
         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
 
@@ -108,7 +108,7 @@
         if ((address.version == 4 and address.prefixlen >= 30) or
            (address.version == 6 and address.prefixlen >= 126)):
             msg = ("Subnet %s isn't large enough for the test" % address.cidr)
-            raise exceptions.InvalidConfiguration(message=msg)
+            raise exceptions.InvalidConfiguration(msg)
         allocation_pools = {'allocation_pools': [{'start': str(address[2]),
                                                   'end': str(address[-2])}]}
         subnet = self.create_subnet(network, cidr=address,
@@ -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):
@@ -196,14 +199,14 @@
         self.addCleanup(self.networks_client.delete_network, network['id'])
         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'])
+        router = self.create_router()
+        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']
@@ -305,11 +308,17 @@
             self.assertIn(security_group, port_show['security_groups'])
 
     @test.idempotent_id('58091b66-4ff4-4cc1-a549-05d60c7acd1a')
+    @testtools.skipUnless(
+        test.is_extension_enabled('security-group', 'network'),
+        'security-group extension not enabled.')
     def test_update_port_with_security_group_and_extra_attributes(self):
         self._update_port_with_security_groups(
             [data_utils.rand_name('secgroup')])
 
     @test.idempotent_id('edf6766d-3d40-4621-bc6e-2521a44c257d')
+    @testtools.skipUnless(
+        test.is_extension_enabled('security-group', 'network'),
+        'security-group extension not enabled.')
     def test_update_port_with_two_security_groups_and_extra_attributes(self):
         self._update_port_with_security_groups(
             [data_utils.rand_name('secgroup'),
@@ -334,6 +343,9 @@
 
     @test.attr(type='smoke')
     @test.idempotent_id('4179dcb9-1382-4ced-84fe-1b91c54f5735')
+    @testtools.skipUnless(
+        test.is_extension_enabled('security-group', 'network'),
+        'security-group extension not enabled.')
     def test_create_port_with_no_securitygroups(self):
         network = self.create_network()
         self.addCleanup(self.networks_client.delete_network, network['id'])
@@ -348,11 +360,6 @@
 class PortsAdminExtendedAttrsTestJSON(base.BaseAdminNetworkTest):
 
     @classmethod
-    def setup_clients(cls):
-        super(PortsAdminExtendedAttrsTestJSON, cls).setup_clients()
-        cls.identity_client = cls.os_adm.identity_client
-
-    @classmethod
     def resource_setup(cls):
         super(PortsAdminExtendedAttrsTestJSON, cls).resource_setup()
         cls.network = cls.create_network()
@@ -425,11 +432,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..101e4dd 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 import netaddr
-import six
 
 from tempest.api.network import base_routers as base
 from tempest.common.utils import data_utils
@@ -34,55 +33,41 @@
             raise cls.skipException(msg)
 
     @classmethod
-    def setup_clients(cls):
-        super(RoutersTest, cls).setup_clients()
-        cls.identity_admin_client = cls.os_adm.identity_client
-
-    @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')
     def test_create_show_list_update_delete_router(self):
         # Create a router
-        # 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={
-                "network_id": CONF.network.public_network_id},
-            admin_state_up=False)
-        self.addCleanup(self._delete_router, create_body['router']['id'])
-        self.assertEqual(create_body['router']['name'], name)
+        router = self._create_router(
+            admin_state_up=False,
+            external_network_id=CONF.network.public_network_id)
+        self.assertEqual(router['admin_state_up'], False)
         self.assertEqual(
-            create_body['router']['external_gateway_info']['network_id'],
+            router['external_gateway_info']['network_id'],
             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'])
-        self.assertEqual(show_body['router']['name'], name)
+        router_show = self.routers_client.show_router(
+            router['id'])['router']
+        self.assertEqual(router_show['name'], router['name'])
         self.assertEqual(
-            show_body['router']['external_gateway_info']['network_id'],
+            router_show['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()
-        routers_list = list()
-        for router in list_body['routers']:
-            routers_list.append(router['id'])
-        self.assertIn(create_body['router']['id'], routers_list)
+        routers = self.routers_client.list_routers()['routers']
+        self.assertIn(router['id'], map(lambda x: x['id'], routers))
         # 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)
-        self.assertEqual(update_body['router']['name'], updated_name)
-        show_body = self.client.show_router(
-            create_body['router']['id'])
-        self.assertEqual(show_body['router']['name'], updated_name)
+        updated_name = 'updated' + router['name']
+        router_update = self.routers_client.update_router(
+            router['id'], name=updated_name)['router']
+        self.assertEqual(router_update['name'], updated_name)
+        router_show = self.routers_client.show_router(
+            router['id'])['router']
+        self.assertEqual(router_show['name'], updated_name)
 
     @test.idempotent_id('e54dd3a3-4352-4921-b09d-44369ae17397')
     def test_create_router_setting_project_id(self):
@@ -95,9 +80,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'])
 
@@ -105,9 +90,8 @@
     @test.requires_ext(extension='ext-gw-mode', service='network')
     def test_create_router_with_default_snat_value(self):
         # Create a router with default snat rule
-        name = data_utils.rand_name('router')
         router = self._create_router(
-            name, external_network_id=CONF.network.public_network_id)
+            external_network_id=CONF.network.public_network_id)
         self._verify_router_gateway(
             router['id'], {'network_id': CONF.network.public_network_id,
                            'enable_snat': True})
@@ -122,9 +106,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'],
@@ -135,10 +119,10 @@
     def test_add_remove_router_interface_with_subnet_id(self):
         network = self.create_network()
         subnet = self.create_subnet(network)
-        router = self._create_router(data_utils.rand_name('router-'))
+        router = self._create_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())
@@ -154,11 +138,11 @@
     def test_add_remove_router_interface_with_port_id(self):
         network = self.create_network()
         self.create_subnet(network)
-        router = self._create_router(data_utils.rand_name('router-'))
+        router = self._create_router()
         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,13 +156,13 @@
                          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)
             return
         # Verify only keys passed in exp_ext_gw_info
-        for k, v in six.iteritems(exp_ext_gw_info):
+        for k, v in exp_ext_gw_info.items():
             self.assertEqual(v, actual_ext_gw_info[k])
 
     def _verify_gateway_port(self, router_id):
@@ -189,16 +173,19 @@
         gw_port = list_body['ports'][0]
         fixed_ips = gw_port['fixed_ips']
         self.assertGreaterEqual(len(fixed_ips), 1)
+        # Assert that all of the IPs from the router gateway port
+        # are allocated from a valid public subnet.
         public_net_body = self.admin_networks_client.show_network(
             CONF.network.public_network_id)
-        public_subnet_id = public_net_body['network']['subnets'][0]
-        self.assertIn(public_subnet_id,
-                      map(lambda x: x['subnet_id'], fixed_ips))
+        public_subnet_ids = public_net_body['network']['subnets']
+        for fixed_ip in fixed_ips:
+            subnet_id = fixed_ip['subnet_id']
+            self.assertIn(subnet_id, public_subnet_ids)
 
     @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(
+        router = self._create_router()
+        self.routers_client.update_router(
             router['id'],
             external_gateway_info={
                 'network_id': CONF.network.public_network_id})
@@ -211,8 +198,8 @@
     @test.idempotent_id('b386c111-3b21-466d-880c-5e72b01e1a33')
     @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(
+        router = self._create_router()
+        self.admin_routers_client.update_router(
             router['id'],
             external_gateway_info={
                 'network_id': CONF.network.public_network_id,
@@ -226,8 +213,8 @@
     @test.idempotent_id('96536bc7-8262-4fb2-9967-5c46940fa279')
     @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(
+        router = self._create_router()
+        self.admin_routers_client.update_router(
             router['id'],
             external_gateway_info={
                 'network_id': CONF.network.public_network_id,
@@ -241,9 +228,9 @@
     @test.idempotent_id('ad81b7ee-4f81-407b-a19c-17e623f763e8')
     def test_update_router_unset_gateway(self):
         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(
@@ -255,9 +242,8 @@
     @test.requires_ext(extension='ext-gw-mode', service='network')
     def test_update_router_reset_gateway_without_snat(self):
         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(
             router['id'],
             external_gateway_info={
                 'network_id': CONF.network.public_network_id,
@@ -270,16 +256,15 @@
 
     @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 = []
         routes_num = 4
         # Create a router
-        router = self._create_router(
-            data_utils.rand_name('router-'), True)
+        router = self._create_router(admin_state_up=True)
         self.addCleanup(
             self._delete_extra_routes,
             router['id'])
@@ -301,9 +286,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_router(
+            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 +308,23 @@
                              routes[i]['destination'])
             self.assertEqual(test_routes[i]['nexthop'], routes[i]['nexthop'])
 
+        self._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.update_router(router_id, routes=None)
 
     @test.idempotent_id('a8902683-c788-4246-95c7-ad9c6d63a4d9')
     def test_update_router_admin_state(self):
-        router = self._create_router(data_utils.rand_name('router-'))
+        router = self._create_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')
@@ -347,7 +337,7 @@
         subnet01 = self.create_subnet(network01)
         sub02_cidr = netaddr.IPNetwork(self.tenant_cidr).next()
         subnet02 = self.create_subnet(network02, cidr=sub02_cidr)
-        router = self._create_router(data_utils.rand_name('router-'))
+        router = self._create_router()
         interface01 = self._add_router_interface_with_subnet_id(router['id'],
                                                                 subnet01['id'])
         self._verify_router_interface(router['id'], subnet01['id'],
@@ -357,6 +347,23 @@
         self._verify_router_interface(router['id'], subnet02['id'],
                                       interface02['port_id'])
 
+    @test.idempotent_id('96522edf-b4b5-45d9-8443-fa11c26e6eff')
+    def test_router_interface_port_update_with_fixed_ip(self):
+        network = self.create_network()
+        subnet = self.create_subnet(network)
+        router = self._create_router()
+        fixed_ip = [{'subnet_id': subnet['id']}]
+        interface = self._add_router_interface_with_subnet_id(router['id'],
+                                                              subnet['id'])
+        self.assertIn('port_id', interface)
+        self.assertIn('subnet_id', interface)
+        port = self.ports_client.show_port(interface['port_id'])
+        self.assertEqual(port['port']['id'], interface['port_id'])
+        router_port = self.ports_client.update_port(port['port']['id'],
+                                                    fixed_ips=fixed_ip)
+        self.assertEqual(subnet['id'],
+                         router_port['port']['fixed_ips'][0]['subnet_id'])
+
     def _verify_router_interface(self, router_id, subnet_id, port_id):
         show_port_body = self.ports_client.show_port(port_id)
         interface_port = show_port_body['port']
@@ -367,35 +374,3 @@
 
 class RoutersIpV6Test(RoutersTest):
     _ip_version = 6
-
-
-class DvrRoutersTest(base.BaseRouterTest):
-
-    @classmethod
-    def skip_checks(cls):
-        super(DvrRoutersTest, cls).skip_checks()
-        if not test.is_extension_enabled('dvr', 'network'):
-            msg = "DVR extension not enabled."
-            raise cls.skipException(msg)
-
-    @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)
-        self.addCleanup(self._delete_router,
-                        create_body['router']['id'],
-                        self.admin_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)
-        self.assertTrue(update_body['router']['distributed'])
-        show_body = self.admin_client.show_router(router['id'])
-        self.assertTrue(show_body['router']['distributed'])
-        show_body = self.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 7b07d42..b3983de 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -14,11 +14,11 @@
 #    under the License.
 
 import netaddr
-from tempest_lib import exceptions as lib_exc
 
 from tempest.api.network import base_routers as 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
@@ -36,18 +36,18 @@
     @classmethod
     def resource_setup(cls):
         super(RoutersNegativeTest, cls).resource_setup()
-        cls.router = cls.create_router(data_utils.rand_name('router-'))
+        cls.router = cls.create_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']})
@@ -55,12 +55,11 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('11836a18-0b15-4327-a50b-f0d9dc66bddd')
     def test_router_add_gateway_net_not_external_returns_400(self):
-        alt_network = self.create_network(
-            network_name=data_utils.rand_name('router-negative-'))
+        alt_network = self.create_network()
         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 +83,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)
 
 
@@ -128,14 +127,12 @@
     @classmethod
     def resource_setup(cls):
         super(DvrRoutersNegativeTest, cls).resource_setup()
-        cls.router = cls.create_router(data_utils.rand_name('router'))
+        cls.router = cls.create_router()
         cls.network = cls.create_network()
         cls.subnet = cls.create_subnet(cls.network)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('4990b055-8fc7-48ab-bba7-aa28beaad0b9')
     def test_router_create_tenant_distributed_returns_forbidden(self):
-        self.assertRaises(lib_exc.Forbidden,
-                          self.create_router,
-                          data_utils.rand_name('router'),
+        self.assertRaises(lib_exc.Forbidden, self.create_router,
                           distributed=True)
diff --git a/tempest/api/network/test_security_groups.py b/tempest/api/network/test_security_groups.py
index 7d0765e..be01852 100644
--- a/tempest/api/network/test_security_groups.py
+++ b/tempest/api/network/test_security_groups.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
-
 from tempest.api.network import base_security_groups as base
 from tempest.common.utils import data_utils
 from tempest import config
@@ -24,7 +22,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):
@@ -62,7 +60,7 @@
                     'port_range_max': port_range_max,
                     'remote_group_id': remote_group_id,
                     'remote_ip_prefix': remote_ip_prefix}
-        for key, value in six.iteritems(expected):
+        for key, value in expected.items():
             self.assertEqual(value, sec_group_rule[key],
                              "Field %s of the created security group "
                              "rule does not match with %s." %
@@ -71,7 +69,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 security group belonging to project exist in list
         body = self.security_groups_client.list_security_groups()
         security_groups = body['security_groups']
         found = None
@@ -131,7 +129,7 @@
                 rule_create_body['security_group_rule']['id']
             )
             create_dict = rule_create_body['security_group_rule']
-            for key, value in six.iteritems(create_dict):
+            for key, value in create_dict.items():
                 self.assertEqual(value,
                                  show_rule_body['security_group_rule'][key],
                                  "%s does not match." % key)
@@ -210,7 +208,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 +237,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 ff38e9e..a3b0a82 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -13,19 +13,17 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.network import base_security_groups as base
 from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
 
 
 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):
@@ -37,7 +35,7 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('424fd5c3-9ddc-486a-b45f-39bf0c820fc6')
     def test_show_non_existent_security_group(self):
-        non_exist_id = str(uuid.uuid4())
+        non_exist_id = data_utils.rand_uuid()
         self.assertRaises(
             lib_exc.NotFound, self.security_groups_client.show_security_group,
             non_exist_id)
@@ -45,7 +43,7 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('4c094c09-000b-4e41-8100-9617600c02a6')
     def test_show_non_existent_security_group_rule(self):
-        non_exist_id = str(uuid.uuid4())
+        non_exist_id = data_utils.rand_uuid()
         self.assertRaises(
             lib_exc.NotFound,
             self.security_group_rules_client.show_security_group_rule,
@@ -54,7 +52,7 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('1f1bb89d-5664-4956-9fcd-83ee0fa603df')
     def test_delete_non_existent_security_group(self):
-        non_exist_id = str(uuid.uuid4())
+        non_exist_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound,
                           self.security_groups_client.delete_security_group,
                           non_exist_id
@@ -92,7 +90,7 @@
     @test.idempotent_id('4bf786fd-2f02-443c-9716-5b98e159a49a')
     def test_create_security_group_rule_with_non_existent_remote_groupid(self):
         group_create_body, _ = self._create_security_group()
-        non_exist_id = str(uuid.uuid4())
+        non_exist_id = data_utils.rand_uuid()
 
         # Create rule with non existent remote_group_id
         group_ids = ['bad_group_id', non_exist_id]
@@ -111,7 +109,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,
@@ -155,6 +153,7 @@
 
         # Create rule for icmp protocol with invalid ports
         states = [(1, 256, 'Invalid value for ICMP code'),
+                  (-1, 25, 'Invalid value'),
                   (None, 6, 'ICMP type (port-range-min) is missing'),
                   (300, 1, 'Invalid value for ICMP type')]
         for pmin, pmax, msg in states:
@@ -205,7 +204,7 @@
     @test.idempotent_id('be308db6-a7cf-4d5c-9baf-71bafd73f35e')
     def test_create_security_group_rule_with_non_existent_security_group(self):
         # Create security group rules with not existing security group.
-        non_existent_sg = str(uuid.uuid4())
+        non_existent_sg = data_utils.rand_uuid()
         self.assertRaises(
             lib_exc.NotFound,
             self.security_group_rules_client.create_security_group_rule,
@@ -215,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')
@@ -224,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_service_providers.py b/tempest/api/network/test_service_providers.py
new file mode 100644
index 0000000..be17b3e
--- /dev/null
+++ b/tempest/api/network/test_service_providers.py
@@ -0,0 +1,28 @@
+#    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 testtools
+
+from tempest.api.network import base
+from tempest import test
+
+
+class ServiceProvidersTest(base.BaseNetworkTest):
+
+    @test.idempotent_id('2cbbeea9-f010-40f6-8df5-4eaa0c918ea6')
+    @testtools.skipUnless(
+        test.is_extension_enabled('service-type', 'network'),
+        'service-type extension not enabled.')
+    def test_service_providers_list(self):
+        body = self.service_providers_client.list_service_providers()
+        self.assertIn('service_providers', body)
+        self.assertIsInstance(body['service_providers'], list)
diff --git a/tempest/api/network/test_service_type_management.py b/tempest/api/network/test_service_type_management.py
deleted file mode 100644
index ad1ecc4..0000000
--- a/tempest/api/network/test_service_type_management.py
+++ /dev/null
@@ -1,32 +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_lib import decorators
-
-from tempest.api.network import base
-from tempest import test
-
-
-class ServiceTypeManagementTestJSON(base.BaseNetworkTest):
-
-    @classmethod
-    def skip_checks(cls):
-        super(ServiceTypeManagementTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('service-type', 'network'):
-            msg = "Neutron Service Type Management not enabled."
-            raise cls.skipException(msg)
-
-    @decorators.skip_because(bug="1400370")
-    @test.idempotent_id('2cbbeea9-f010-40f6-8df5-4eaa0c918ea6')
-    def test_service_provider_list(self):
-        body = self.client.list_service_providers()
-        self.assertIsInstance(body['service_providers'], list)
diff --git a/tempest/api/network/test_subnetpools_extensions.py b/tempest/api/network/test_subnetpools_extensions.py
index e5d0462..d574d72 100644
--- a/tempest/api/network/test_subnetpools_extensions.py
+++ b/tempest/api/network/test_subnetpools_extensions.py
@@ -15,8 +15,9 @@
 from tempest.api.network import base
 from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
-from tempest_lib import exceptions as lib_exc
 
 CONF = config.CONF
 
@@ -30,7 +31,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 +41,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')
@@ -53,7 +54,9 @@
         body = self.subnetpools_client.create_subnetpool(name=subnetpool_name,
                                                          prefixes=prefix)
         subnetpool_id = body["subnetpool"]["id"]
-        self.addCleanup(self._cleanup_subnetpools, subnetpool_id)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.subnetpools_client.delete_subnetpool,
+                        subnetpool_id)
         self.assertEqual(subnetpool_name, body["subnetpool"]["name"])
         # get detail about subnet pool
         body = self.subnetpools_client.show_subnetpool(subnetpool_id)
@@ -68,10 +71,3 @@
         self.assertRaises(lib_exc.NotFound,
                           self.subnetpools_client.show_subnetpool,
                           subnetpool_id)
-
-    def _cleanup_subnetpools(self, subnetpool_id):
-        # this is used to cleanup the resources
-        try:
-            self.subnetpools_client.delete_subnetpool(subnetpool_id)
-        except lib_exc.NotFound:
-            pass
diff --git a/tempest/api/network/test_versions.py b/tempest/api/network/test_versions.py
new file mode 100644
index 0000000..9cf93f6
--- /dev/null
+++ b/tempest/api/network/test_versions.py
@@ -0,0 +1,40 @@
+# Copyright 2016 VMware, Inc.
+#
+# 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.network import base
+from tempest import test
+
+
+class NetworksApiDiscovery(base.BaseNetworkTest):
+    @test.attr(type='smoke')
+    @test.idempotent_id('cac8a836-c2e0-4304-b556-cd299c7281d1')
+    def test_api_version_resources(self):
+        """Test that GET / returns expected resources.
+
+        The versions document returned by Neutron returns a few other
+        resources other than just available API versions: it also
+        states the status of each API version and provides links to
+        schema.
+        """
+
+        result = self.network_versions_client.list_versions()
+        expected_versions = ('v2.0')
+        expected_resources = ('id', 'links', 'status')
+        received_list = result.values()
+
+        for item in received_list:
+            for version in item:
+                for resource in expected_resources:
+                    self.assertIn(resource, version)
+                self.assertIn(version['id'], expected_versions)
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 2621581..e0216fd 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -13,15 +13,50 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
+import time
 
 from tempest.common import custom_matchers
 from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions as lib_exc
 import tempest.test
 
 CONF = config.CONF
 
 
+def delete_containers(containers, container_client, object_client):
+    """Remove containers and all objects in them.
+
+    The containers should be visible from the container_client given.
+    Will not throw any error if the containers don't exist.
+    Will not check that object and container deletions succeed.
+    After delete all the objects from a container, it will wait 2
+    seconds before delete the container itself, in order to deployments
+    using HA proxy sync the deletion properly, otherwise, the container
+    might fail to be deleted because it's not empty.
+
+    :param containers: List of containers to be deleted
+    :param container_client: Client to be used to delete containers
+    :param object_client: Client to be used to delete objects
+    """
+    for cont in containers:
+        try:
+            params = {'limit': 9999, 'format': 'json'}
+            resp, objlist = container_client.list_container_contents(
+                cont, params)
+            # delete every object in the container
+            for obj in objlist:
+                test_utils.call_and_ignore_notfound_exc(
+                    object_client.delete_object, cont, obj['name'])
+            # sleep 2 seconds to sync the deletion of the objects
+            # in HA deployment
+            time.sleep(2)
+            container_client.delete_container(cont)
+        except lib_exc.NotFound:
+            pass
+
+
 class BaseObjectTest(tempest.test.BaseTestCase):
 
     credentials = [['operator', CONF.object_storage.operator_role]]
@@ -47,6 +82,7 @@
         cls.object_client = cls.os.object_client
         cls.container_client = cls.os.container_client
         cls.account_client = cls.os.account_client
+        cls.capabilities_client = cls.os.capabilities_client
 
     @classmethod
     def resource_setup(cls):
@@ -57,41 +93,53 @@
         cls.container_client.auth_provider.clear_auth()
         cls.account_client.auth_provider.clear_auth()
 
+        # make sure that discoverability is enabled and that the sections
+        # have not been disallowed by Swift
+        cls.policies = None
+
+        if CONF.object_storage_feature_enabled.discoverability:
+            _, body = cls.capabilities_client.list_capabilities()
+
+            if 'swift' in body and 'policies' in body['swift']:
+                cls.policies = body['swift']['policies']
+
+        cls.containers = []
+
     @classmethod
-    def delete_containers(cls, containers, container_client=None,
-                          object_client=None):
-        """Remove given containers and all objects in them.
+    def create_container(cls):
+        # wrapper that returns a test container
+        container_name = data_utils.rand_name(name='TestContainer')
+        cls.container_client.create_container(container_name)
+        cls.containers.append(container_name)
 
-        The containers should be visible from the container_client given.
-        Will not throw any error if the containers don't exist.
-        Will not check that object and container deletions succeed.
+        return container_name
 
-        :param containers: list of container names to remove
-        :param container_client: if None, use cls.container_client, this means
-            that the default testing user will be used (see 'username' in
-            'etc/tempest.conf')
-        :param object_client: if None, use cls.object_client
-        """
+    @classmethod
+    def create_object(cls, container_name, object_name=None,
+                      data=None, metadata=None):
+        # wrapper that returns a test object
+        if object_name is None:
+            object_name = data_utils.rand_name(name='TestObject')
+        if data is None:
+            data = data_utils.random_bytes()
+        cls.object_client.create_object(container_name,
+                                        object_name,
+                                        data,
+                                        metadata=metadata)
+
+        return object_name, data
+
+    @classmethod
+    def delete_containers(cls, container_client=None, object_client=None):
         if container_client is None:
             container_client = cls.container_client
         if object_client is None:
             object_client = cls.object_client
-        for cont in containers:
-            try:
-                objlist = container_client.list_all_container_objects(cont)
-                # delete every object in the container
-                for obj in objlist:
-                    try:
-                        object_client.delete_object(cont, obj['name'])
-                    except lib_exc.NotFound:
-                        pass
-                container_client.delete_container(cont)
-            except lib_exc.NotFound:
-                pass
+        delete_containers(cls.containers, container_client, object_client)
 
     def assertHeaders(self, resp, target, method):
         """Check the existence and the format of response headers"""
 
         self.assertThat(resp, custom_matchers.ExistsAllResponseHeaders(
-                        target, method))
+                        target, method, self.policies))
         self.assertThat(resp, custom_matchers.AreAllWellFormatted())
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index da4c80c..1eda49a 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -27,7 +27,10 @@
         self.containers = []
 
     def tearDown(self):
-        self.delete_containers(self.containers)
+        # NOTE(andreaf) BulkTests needs to cleanup containers after each
+        # test is executed.
+        base.delete_containers(self.containers, self.container_client,
+                               self.object_client)
         super(BulkTest, self).tearDown()
 
     def _create_archive(self):
@@ -66,7 +69,7 @@
         self.assertNotIn(container_name, body)
 
     @test.idempotent_id('a407de51-1983-47cc-9f14-47c2b059413c')
-    @test.requires_ext(extension='bulk', service='object')
+    @test.requires_ext(extension='bulk_upload', service='object')
     def test_extract_archive(self):
         # Test bulk operation of file upload with an archived file
         filepath, container_name, object_name = self._create_archive()
@@ -102,7 +105,7 @@
         self.assertIn(object_name, [c['name'] for c in contents_list])
 
     @test.idempotent_id('c075e682-0d2a-43b2-808d-4116200d736d')
-    @test.requires_ext(extension='bulk', service='object')
+    @test.requires_ext(extension='bulk_delete', service='object')
     def test_bulk_delete(self):
         # Test bulk operation of deleting multiple files
         filepath, container_name, object_name = self._create_archive()
@@ -129,7 +132,7 @@
         self._check_contents_deleted(container_name)
 
     @test.idempotent_id('dbea2bcb-efbb-4674-ac8a-a5a0e33d1d79')
-    @test.requires_ext(extension='bulk', service='object')
+    @test.requires_ext(extension='bulk_delete', service='object')
     def test_bulk_delete_by_POST(self):
         # Test bulk operation of deleting multiple files
         filepath, container_name, object_name = self._create_archive()
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 0f6a330..fcbd6eb 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -34,8 +34,7 @@
     @classmethod
     def resource_setup(cls):
         super(AccountQuotasTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name="TestContainer")
-        cls.container_client.create_container(cls.container_name)
+        cls.container_name = cls.create_container()
 
         # Retrieve a ResellerAdmin auth data and use it to set a quota
         # on the client's account
@@ -73,8 +72,7 @@
 
     @classmethod
     def resource_cleanup(cls):
-        if hasattr(cls, "container_name"):
-            cls.delete_containers([cls.container_name])
+        cls.delete_containers()
         super(AccountQuotasTest, cls).resource_cleanup()
 
     @test.attr(type="smoke")
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index aee17d3..ae8dfcc 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -12,12 +12,9 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-from tempest_lib import decorators
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.object_storage 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
@@ -37,8 +34,7 @@
     @classmethod
     def resource_setup(cls):
         super(AccountQuotasNegativeTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name="TestContainer")
-        cls.container_client.create_container(cls.container_name)
+        cls.container_name = cls.create_container()
 
         # Retrieve a ResellerAdmin auth data and use it to set a quota
         # on the client's account
@@ -75,8 +71,7 @@
 
     @classmethod
     def resource_cleanup(cls):
-        if hasattr(cls, "container_name"):
-            cls.delete_containers([cls.container_name])
+        cls.delete_containers()
         super(AccountQuotasNegativeTest, cls).resource_cleanup()
 
     @test.attr(type=["negative"])
@@ -94,14 +89,3 @@
         self.assertRaises(lib_exc.Forbidden,
                           self.account_client.create_account_metadata,
                           {"Quota-Bytes": "100"})
-
-    @test.attr(type=["negative"])
-    @decorators.skip_because(bug="1310597")
-    @test.idempotent_id('cf9e21f5-3aa4-41b1-9462-28ac550d8d3f')
-    @test.requires_ext(extension='account_quotas', service='object')
-    def test_upload_large_object(self):
-        object_name = data_utils.rand_name(name="TestObject")
-        data = data_utils.arbitrary_string(30)
-        self.assertRaises(lib_exc.OverLimit,
-                          self.object_client.create_object,
-                          self.container_name, object_name, data)
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 6bab9b3..59129e5 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -15,7 +15,7 @@
 
 import random
 
-from six import moves
+import six
 import testtools
 
 from tempest.api.object_storage import base
@@ -42,7 +42,7 @@
     @classmethod
     def resource_setup(cls):
         super(AccountTest, cls).resource_setup()
-        for i in moves.xrange(ord('a'), ord('f') + 1):
+        for i in range(ord('a'), ord('f') + 1):
             name = data_utils.rand_name(name='%s-' % chr(i))
             cls.container_client.create_container(name)
             cls.containers.append(name)
@@ -50,7 +50,7 @@
 
     @classmethod
     def resource_cleanup(cls):
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
         super(AccountTest, cls).resource_cleanup()
 
     @test.attr(type='smoke')
@@ -61,8 +61,10 @@
         self.assertHeaders(resp, 'Account', 'GET')
 
         self.assertIsNotNone(container_list)
+
         for container_name in self.containers:
-            self.assertIn(container_name, container_list)
+            self.assertIn(six.text_type(container_name).encode('utf-8'),
+                          container_list)
 
     @test.idempotent_id('884ec421-fbad-4fcc-916b-0580f2699565')
     def test_list_no_containers(self):
@@ -78,7 +80,16 @@
         # container request, the response does not contain 'accept-ranges'
         # header. This is a special case, therefore the existence of response
         # headers is checked without custom matcher.
-        self.assertIn('content-length', resp)
+        #
+        # As the expected response is 204 No Content, Content-Length presence
+        # is not checked here intentionally. According to RFC 7230 a server
+        # MUST NOT send the header in such responses. Thus, clients should not
+        # depend on this header. However, the standard does not require them
+        # to validate the server's behavior. We leverage that to not refuse
+        # any implementation violating it like Swift [1] or some versions of
+        # Ceph RadosGW [2].
+        # [1] https://bugs.launchpad.net/swift/+bug/1537811
+        # [2] http://tracker.ceph.com/issues/13582
         self.assertIn('x-timestamp', resp)
         self.assertIn('x-account-bytes-used', resp)
         self.assertIn('x-account-container-count', resp)
@@ -113,7 +124,7 @@
         self.assertHeaders(resp, 'Account', 'GET')
         self.assertIsNotNone(container_list)
         self.assertEqual(container_list.tag, 'account')
-        self.assertTrue('name' in container_list.keys())
+        self.assertIn('name', container_list.keys())
         self.assertEqual(container_list.find(".//container").tag, 'container')
         self.assertEqual(container_list.find(".//name").tag, 'name')
         self.assertEqual(container_list.find(".//count").tag, 'count')
@@ -124,14 +135,15 @@
         not CONF.object_storage_feature_enabled.discoverability,
         'Discoverability function is disabled')
     def test_list_extensions(self):
-        resp, extensions = self.account_client.list_extensions()
+        resp, extensions = self.capabilities_client.list_capabilities()
 
         self.assertThat(resp, custom_matchers.AreAllWellFormatted())
 
     @test.idempotent_id('5cfa4ab2-4373-48dd-a41f-a532b12b08b2')
     def test_list_containers_with_limit(self):
         # list containers one of them, half of them then all of them
-        for limit in (1, self.containers_count / 2, self.containers_count):
+        for limit in (1, self.containers_count // 2,
+                      self.containers_count):
             params = {'limit': limit}
             resp, container_list = \
                 self.account_client.list_account_containers(params=params)
@@ -152,12 +164,13 @@
 
         self.assertEqual(len(container_list), 0)
 
-        params = {'marker': self.containers[self.containers_count / 2]}
+        params = {'marker': self.containers[self.containers_count // 2]}
         resp, container_list = \
             self.account_client.list_account_containers(params=params)
         self.assertHeaders(resp, 'Account', 'GET')
 
-        self.assertEqual(len(container_list), self.containers_count / 2 - 1)
+        self.assertEqual(len(container_list),
+                         self.containers_count // 2 - 1)
 
     @test.idempotent_id('5ca164e4-7bde-43fa-bafb-913b53b9e786')
     def test_list_containers_with_end_marker(self):
@@ -171,11 +184,11 @@
         self.assertHeaders(resp, 'Account', 'GET')
         self.assertEqual(len(container_list), 0)
 
-        params = {'end_marker': self.containers[self.containers_count / 2]}
+        params = {'end_marker': self.containers[self.containers_count // 2]}
         resp, container_list = \
             self.account_client.list_account_containers(params=params)
         self.assertHeaders(resp, 'Account', 'GET')
-        self.assertEqual(len(container_list), self.containers_count / 2)
+        self.assertEqual(len(container_list), self.containers_count // 2)
 
     @test.idempotent_id('ac8502c2-d4e4-4f68-85a6-40befea2ef5e')
     def test_list_containers_with_marker_and_end_marker(self):
@@ -199,19 +212,20 @@
                 self.account_client.list_account_containers(params=params)
             self.assertHeaders(resp, 'Account', 'GET')
 
-            self.assertTrue(len(container_list) <= limit, str(container_list))
+            self.assertLessEqual(len(container_list), limit,
+                                 str(container_list))
 
     @test.idempotent_id('888a3f0e-7214-4806-8e50-5e0c9a69bb5e')
     def test_list_containers_with_limit_and_end_marker(self):
         # list containers combining limit and end_marker param
         limit = random.randint(1, self.containers_count)
         params = {'limit': limit,
-                  'end_marker': self.containers[self.containers_count / 2]}
+                  'end_marker': self.containers[self.containers_count // 2]}
         resp, container_list = self.account_client.list_account_containers(
             params=params)
         self.assertHeaders(resp, 'Account', 'GET')
         self.assertEqual(len(container_list),
-                         min(limit, self.containers_count / 2))
+                         min(limit, self.containers_count // 2))
 
     @test.idempotent_id('8cf98d9c-e3a0-4e44-971b-c87656fdddbd')
     def test_list_containers_with_limit_and_marker_and_end_marker(self):
@@ -226,6 +240,18 @@
         self.assertEqual(len(container_list),
                          min(limit, self.containers_count - 2))
 
+    @test.idempotent_id('365e6fc7-1cfe-463b-a37c-8bd08d47b6aa')
+    def test_list_containers_with_prefix(self):
+        # list containers that have a name that starts with a prefix
+        prefix = '{0}-a'.format(CONF.resources_prefix)
+        params = {'prefix': prefix}
+        resp, container_list = self.account_client.list_account_containers(
+            params=params)
+        self.assertHeaders(resp, 'Account', 'GET')
+        for container in container_list:
+            self.assertEqual(True, container.decode(
+                'utf-8').startswith(prefix))
+
     @test.attr(type='smoke')
     @test.idempotent_id('4894c312-6056-4587-8d6f-86ffbf861f80')
     def test_list_account_metadata(self):
diff --git a/tempest/api/object_storage/test_account_services_negative.py b/tempest/api/object_storage/test_account_services_negative.py
index 998c2bd..254a9b3 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -12,10 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.object_storage import base
 from tempest import config
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index ec75f93..e555fd9 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -28,11 +28,10 @@
 
     def setUp(self):
         super(ObjectTestACLs, self).setUp()
-        self.container_name = data_utils.rand_name(name='TestContainer')
-        self.container_client.create_container(self.container_name)
+        self.container_name = self.create_container()
 
     def tearDown(self):
-        self.delete_containers([self.container_name])
+        self.delete_containers()
         super(ObjectTestACLs, self).tearDown()
 
     @test.idempotent_id('a3270f3f-7640-4944-8448-c7ea783ea5b6')
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index 3bb47f0..0055bf9 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -12,11 +12,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.object_storage 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
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index 896352b..8cbe441 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -13,14 +13,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.object_storage 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
 QUOTA_BYTES = 10
 QUOTA_COUNT = 3
 
@@ -39,8 +36,7 @@
                      Maximum object count of the container.
         """
         super(ContainerQuotasTest, self).setUp()
-        self.container_name = data_utils.rand_name(name="TestContainer")
-        self.container_client.create_container(self.container_name)
+        self.container_name = self.create_container()
         metadata = {"quota-bytes": str(QUOTA_BYTES),
                     "quota-count": str(QUOTA_COUNT), }
         self.container_client.update_container_metadata(
@@ -48,7 +44,7 @@
 
     def tearDown(self):
         """Cleans the container of any object after each test."""
-        self.delete_containers([self.container_name])
+        self.delete_containers()
         super(ContainerQuotasTest, self).tearDown()
 
     @test.idempotent_id('9a0fb034-86af-4df0-86fa-f8bd7db21ae0')
@@ -72,7 +68,7 @@
     @test.requires_ext(extension='container_quotas', service='object')
     @test.attr(type="smoke")
     def test_upload_large_object(self):
-        """Attempts to upload an object lagger than the bytes quota."""
+        """Attempts to upload an object larger than the bytes quota."""
         object_name = data_utils.rand_name(name="TestObject")
         data = data_utils.arbitrary_string(QUOTA_BYTES + 1)
 
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index 1cc9437..e4476a1 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -13,40 +13,16 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib.common.utils import data_utils
-
 from tempest.api.object_storage import base
+from tempest.lib.common.utils import data_utils
 from tempest import test
 
 
 class ContainerTest(base.BaseObjectTest):
-    def setUp(self):
-        super(ContainerTest, self).setUp()
-        self.containers = []
-
     def tearDown(self):
-        self.delete_containers(self.containers)
+        self.delete_containers()
         super(ContainerTest, self).tearDown()
 
-    def _create_container(self):
-        # setup container
-        container_name = data_utils.rand_name(name='TestContainer')
-        self.container_client.create_container(container_name)
-        self.containers.append(container_name)
-
-        return container_name
-
-    def _create_object(self, container_name, object_name=None):
-        # setup object
-        if object_name is None:
-            object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
-        self.object_client.create_object(container_name,
-                                         object_name,
-                                         data)
-
-        return object_name
-
     @test.attr(type='smoke')
     @test.idempotent_id('92139d73-7819-4db1-85f8-3f2f22a8d91f')
     def test_create_container(self):
@@ -87,7 +63,8 @@
         # create container with metadata value
         container_name = data_utils.rand_name(name='TestContainer')
 
-        metadata = {'test-container-meta': 'Meta1'}
+        # metadata name using underscores should be converted to hyphens
+        metadata = {'test_container_meta': 'Meta1'}
         resp, _ = self.container_client.create_container(
             container_name,
             metadata=metadata)
@@ -98,7 +75,7 @@
             container_name)
         self.assertIn('x-container-meta-test-container-meta', resp)
         self.assertEqual(resp['x-container-meta-test-container-meta'],
-                         metadata['test-container-meta'])
+                         metadata['test_container_meta'])
 
     @test.idempotent_id('24d16451-1c0c-4e4f-b59c-9840a3aba40e')
     def test_create_container_with_remove_metadata_key(self):
@@ -141,66 +118,65 @@
     @test.idempotent_id('95d3a249-b702-4082-a2c4-14bb860cf06a')
     def test_delete_container(self):
         # create a container
-        container_name = self._create_container()
+        container_name = self.create_container()
         # delete container, success asserted within
         resp, _ = self.container_client.delete_container(container_name)
         self.assertHeaders(resp, 'Container', 'DELETE')
-        self.containers.remove(container_name)
 
     @test.attr(type='smoke')
     @test.idempotent_id('312ff6bd-5290-497f-bda1-7c5fec6697ab')
     def test_list_container_contents(self):
         # get container contents list
-        container_name = self._create_container()
-        object_name = self._create_object(container_name)
+        container_name = self.create_container()
+        object_name, _ = self.create_object(container_name)
 
         resp, object_list = self.container_client.list_container_contents(
             container_name)
         self.assertHeaders(resp, 'Container', 'GET')
-        self.assertEqual(object_name, object_list.strip('\n'))
+        self.assertEqual([object_name], object_list)
 
     @test.idempotent_id('4646ac2d-9bfb-4c7d-a3c5-0f527402b3df')
     def test_list_container_contents_with_no_object(self):
         # get empty container contents list
-        container_name = self._create_container()
+        container_name = self.create_container()
 
         resp, object_list = self.container_client.list_container_contents(
             container_name)
         self.assertHeaders(resp, 'Container', 'GET')
-        self.assertEqual('', object_list.strip('\n'))
+        self.assertEmpty(object_list)
 
     @test.idempotent_id('fe323a32-57b9-4704-a996-2e68f83b09bc')
     def test_list_container_contents_with_delimiter(self):
         # get container contents list using delimiter param
-        container_name = self._create_container()
+        container_name = self.create_container()
         object_name = data_utils.rand_name(name='TestObject/')
-        self._create_object(container_name, object_name)
+        self.create_object(container_name, object_name)
 
         params = {'delimiter': '/'}
         resp, object_list = self.container_client.list_container_contents(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
-        self.assertEqual(object_name.split('/')[0], object_list.strip('/\n'))
+        self.assertEqual([object_name.split('/')[0] + '/'], object_list)
 
     @test.idempotent_id('55b4fa5c-e12e-4ca9-8fcf-a79afe118522')
     def test_list_container_contents_with_end_marker(self):
         # get container contents list using end_marker param
-        container_name = self._create_container()
-        object_name = self._create_object(container_name)
+        container_name = self.create_container()
+        object_name, _ = self.create_object(container_name)
 
         params = {'end_marker': 'ZzzzObject1234567890'}
         resp, object_list = self.container_client.list_container_contents(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
-        self.assertEqual(object_name, object_list.strip('\n'))
+        self.assertEqual([object_name], object_list)
 
     @test.idempotent_id('196f5034-6ab0-4032-9da9-a937bbb9fba9')
     def test_list_container_contents_with_format_json(self):
         # get container contents list using format_json param
-        container_name = self._create_container()
-        self._create_object(container_name)
+        container_name = self.create_container()
+        self.create_object(container_name)
 
         params = {'format': 'json'}
         resp, object_list = self.container_client.list_container_contents(
@@ -218,8 +194,8 @@
     @test.idempotent_id('655a53ca-4d15-408c-a377-f4c6dbd0a1fa')
     def test_list_container_contents_with_format_xml(self):
         # get container contents list using format_xml param
-        container_name = self._create_container()
-        self._create_object(container_name)
+        container_name = self.create_container()
+        self.create_object(container_name)
 
         params = {'format': 'xml'}
         resp, object_list = self.container_client.list_container_contents(
@@ -229,7 +205,7 @@
 
         self.assertIsNotNone(object_list)
         self.assertEqual(object_list.tag, 'container')
-        self.assertTrue('name' in object_list.keys())
+        self.assertIn('name', object_list.keys())
         self.assertEqual(object_list.find(".//object").tag, 'object')
         self.assertEqual(object_list.find(".//name").tag, 'name')
         self.assertEqual(object_list.find(".//hash").tag, 'hash')
@@ -242,48 +218,48 @@
     @test.idempotent_id('297ec38b-2b61-4ff4-bcd1-7fa055e97b61')
     def test_list_container_contents_with_limit(self):
         # get container contents list using limit param
-        container_name = self._create_container()
-        object_name = self._create_object(container_name)
+        container_name = self.create_container()
+        object_name, _ = self.create_object(container_name)
 
         params = {'limit': data_utils.rand_int_id(1, 10000)}
         resp, object_list = self.container_client.list_container_contents(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
-        self.assertEqual(object_name, object_list.strip('\n'))
+        self.assertEqual([object_name], object_list)
 
     @test.idempotent_id('c31ddc63-2a58-4f6b-b25c-94d2937e6867')
     def test_list_container_contents_with_marker(self):
         # get container contents list using marker param
-        container_name = self._create_container()
-        object_name = self._create_object(container_name)
+        container_name = self.create_container()
+        object_name, _ = self.create_object(container_name)
 
         params = {'marker': 'AaaaObject1234567890'}
         resp, object_list = self.container_client.list_container_contents(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
-        self.assertEqual(object_name, object_list.strip('\n'))
+        self.assertEqual([object_name], object_list)
 
     @test.idempotent_id('58ca6cc9-6af0-408d-aaec-2a6a7b2f0df9')
     def test_list_container_contents_with_path(self):
         # get container contents list using path param
-        container_name = self._create_container()
+        container_name = self.create_container()
         object_name = data_utils.rand_name(name='Swift/TestObject')
-        self._create_object(container_name, object_name)
+        self.create_object(container_name, object_name)
 
         params = {'path': 'Swift'}
         resp, object_list = self.container_client.list_container_contents(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
-        self.assertEqual(object_name, object_list.strip('\n'))
+        self.assertEqual([object_name], object_list)
 
     @test.idempotent_id('77e742c7-caf2-4ec9-8aa4-f7d509a3344c')
     def test_list_container_contents_with_prefix(self):
         # get container contents list using prefix param
-        container_name = self._create_container()
-        object_name = self._create_object(container_name)
+        container_name = self.create_container()
+        object_name, _ = self.create_object(container_name)
 
         prefix_key = object_name[0:8]
         params = {'prefix': prefix_key}
@@ -291,13 +267,13 @@
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
-        self.assertEqual(object_name, object_list.strip('\n'))
+        self.assertEqual([object_name], object_list)
 
     @test.attr(type='smoke')
     @test.idempotent_id('96e68f0e-19ec-4aa2-86f3-adc6a45e14dd')
     def test_list_container_metadata(self):
         # List container metadata
-        container_name = self._create_container()
+        container_name = self.create_container()
 
         metadata = {'name': 'Pictures'}
         self.container_client.update_container_metadata(
@@ -313,7 +289,7 @@
     @test.idempotent_id('a2faf936-6b13-4f8d-92a2-c2278355821e')
     def test_list_no_container_metadata(self):
         # HEAD container without metadata
-        container_name = self._create_container()
+        container_name = self.create_container()
 
         resp, _ = self.container_client.list_container_metadata(
             container_name)
@@ -346,7 +322,7 @@
     @test.idempotent_id('2ae5f295-4bf1-4e04-bfad-21e54b62cec5')
     def test_update_container_metadata_with_create_metadata(self):
         # update container metadata using add metadata
-        container_name = self._create_container()
+        container_name = self.create_container()
 
         metadata = {'test-container-meta1': 'Meta1'}
         resp, _ = self.container_client.update_container_metadata(
@@ -381,7 +357,7 @@
     @test.idempotent_id('31f40a5f-6a52-4314-8794-cd89baed3040')
     def test_update_container_metadata_with_create_metadata_key(self):
         # update container metadata with a blank value of metadata
-        container_name = self._create_container()
+        container_name = self.create_container()
 
         metadata = {'test-container-meta1': ''}
         resp, _ = self.container_client.update_container_metadata(
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
new file mode 100644
index 0000000..2856fab
--- /dev/null
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -0,0 +1,177 @@
+# Copyright 2016 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 testtools
+
+from tempest.api.object_storage import base
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+from tempest import test
+
+CONF = config.CONF
+
+
+class ContainerNegativeTest(base.BaseObjectTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(ContainerNegativeTest, cls).resource_setup()
+
+        if CONF.object_storage_feature_enabled.discoverability:
+            # use /info to get default constraints
+            _, body = cls.capabilities_client.list_capabilities()
+            cls.constraints = body['swift']
+
+    @test.attr(type=["negative"])
+    @test.idempotent_id('30686921-4bed-4764-a038-40d741ed4e78')
+    @testtools.skipUnless(
+        CONF.object_storage_feature_enabled.discoverability,
+        'Discoverability function is disabled')
+    def test_create_container_name_exceeds_max_length(self):
+        # Attempts to create a container name that is longer than max
+        max_length = self.constraints['max_container_name_length']
+        # create a container with long name
+        container_name = data_utils.arbitrary_string(size=max_length + 1)
+        ex = self.assertRaises(exceptions.BadRequest,
+                               self.container_client.create_container,
+                               container_name)
+        self.assertIn('Container name length of ' + str(max_length + 1) +
+                      ' longer than ' + str(max_length), str(ex))
+
+    @test.attr(type=["negative"])
+    @test.idempotent_id('41e645bf-2e68-4f84-bf7b-c71aa5cd76ce')
+    @testtools.skipUnless(
+        CONF.object_storage_feature_enabled.discoverability,
+        'Discoverability function is disabled')
+    def test_create_container_metadata_name_exceeds_max_length(self):
+        # Attempts to create container with metadata name
+        # that is longer than max.
+        max_length = self.constraints['max_meta_name_length']
+        container_name = data_utils.rand_name(name='TestContainer')
+        metadata_name = data_utils.arbitrary_string(size=max_length + 1)
+        metadata = {metadata_name: 'penguin'}
+        ex = self.assertRaises(exceptions.BadRequest,
+                               self.container_client.create_container,
+                               container_name, metadata=metadata)
+        self.assertIn('Metadata name too long', str(ex))
+
+    @test.attr(type=["negative"])
+    @test.idempotent_id('81e36922-326b-4b7c-8155-3bbceecd7a82')
+    @testtools.skipUnless(
+        CONF.object_storage_feature_enabled.discoverability,
+        'Discoverability function is disabled')
+    def test_create_container_metadata_value_exceeds_max_length(self):
+        # Attempts to create container with metadata value
+        # that is longer than max.
+        max_length = self.constraints['max_meta_value_length']
+        container_name = data_utils.rand_name(name='TestContainer')
+        metadata_value = data_utils.arbitrary_string(size=max_length + 1)
+        metadata = {'animal': metadata_value}
+        ex = self.assertRaises(exceptions.BadRequest,
+                               self.container_client.create_container,
+                               container_name, metadata=metadata)
+        self.assertIn('Metadata value longer than ' + str(max_length), str(ex))
+
+    @test.attr(type=["negative"])
+    @test.idempotent_id('ac666539-d566-4f02-8ceb-58e968dfb732')
+    @testtools.skipUnless(
+        CONF.object_storage_feature_enabled.discoverability,
+        'Discoverability function is disabled')
+    def test_create_container_metadata_exceeds_overall_metadata_count(self):
+        # Attempts to create container with metadata that exceeds the
+        # default count
+        max_count = self.constraints['max_meta_count']
+        container_name = data_utils.rand_name(name='TestContainer')
+        metadata = {}
+        for i in range(max_count + 1):
+            metadata['animal-' + str(i)] = 'penguin'
+
+        ex = self.assertRaises(exceptions.BadRequest,
+                               self.container_client.create_container,
+                               container_name, metadata=metadata)
+        self.assertIn('Too many metadata items; max ' + str(max_count),
+                      str(ex))
+
+    @test.attr(type=["negative"])
+    @test.idempotent_id('1a95ab2e-b712-4a98-8a4d-8ce21b7557d6')
+    def test_get_metadata_headers_with_invalid_container_name(self):
+        # Attempts to retrieve metadata headers with an invalid
+        # container name.
+        self.assertRaises(exceptions.NotFound,
+                          self.container_client.list_container_metadata,
+                          'invalid_container_name')
+
+    @test.attr(type=["negative"])
+    @test.idempotent_id('125a24fa-90a7-4cfc-b604-44e49d788390')
+    def test_update_metadata_with_nonexistent_container_name(self):
+        # Attempts to update metadata using a nonexistent container name.
+        metadata = {'animal': 'penguin'}
+
+        self.assertRaises(exceptions.NotFound,
+                          self.container_client.update_container_metadata,
+                          'nonexistent_container_name', metadata)
+
+    @test.attr(type=["negative"])
+    @test.idempotent_id('65387dbf-a0e2-4aac-9ddc-16eb3f1f69ba')
+    def test_delete_with_nonexistent_container_name(self):
+        # Attempts to delete metadata using a nonexistent container name.
+        metadata = {'animal': 'penguin'}
+
+        self.assertRaises(exceptions.NotFound,
+                          self.container_client.delete_container_metadata,
+                          'nonexistent_container_name', metadata)
+
+    @test.attr(type=["negative"])
+    @test.idempotent_id('14331d21-1e81-420a-beea-19cb5e5207f5')
+    def test_list_all_container_objects_with_nonexistent_container(self):
+        # Attempts to get a listing of all objects on a container
+        # that doesn't exist.
+        params = {'limit': 9999, 'format': 'json'}
+        self.assertRaises(exceptions.NotFound,
+                          self.container_client.list_container_contents,
+                          'nonexistent_container_name', params)
+
+    @test.attr(type=["negative"])
+    @test.idempotent_id('86b2ab08-92d5-493d-acd2-85f0c848819e')
+    def test_list_all_container_objects_on_deleted_container(self):
+        # Attempts to get a listing of all objects on a container
+        # that was deleted.
+        container_name = self.create_container()
+        # delete container
+        resp, _ = self.container_client.delete_container(container_name)
+        self.assertHeaders(resp, 'Container', 'DELETE')
+        params = {'limit': 9999, 'format': 'json'}
+        self.assertRaises(exceptions.NotFound,
+                          self.container_client.list_container_contents,
+                          container_name, params)
+
+    @test.attr(type=["negative"])
+    @test.idempotent_id('42da116e-1e8c-4c96-9e06-2f13884ed2b1')
+    def test_delete_non_empty_container(self):
+        # create a container and an object within it
+        # attempt to delete a container that isn't empty.
+        container_name = self.create_container()
+        self.addCleanup(self.container_client.delete_container,
+                        container_name)
+        object_name, _ = self.create_object(container_name)
+        self.addCleanup(self.object_client.delete_object,
+                        container_name, object_name)
+
+        ex = self.assertRaises(exceptions.Conflict,
+                               self.container_client.delete_container,
+                               container_name)
+        self.assertIn('There was a conflict when trying to complete your '
+                      'request.', str(ex))
diff --git a/tempest/api/object_storage/test_container_staticweb.py b/tempest/api/object_storage/test_container_staticweb.py
index 18593f3..edc9271 100644
--- a/tempest/api/object_storage/test_container_staticweb.py
+++ b/tempest/api/object_storage/test_container_staticweb.py
@@ -12,11 +12,10 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.object_storage import base
 from tempest.common import custom_matchers
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -25,18 +24,14 @@
     @classmethod
     def resource_setup(cls):
         super(StaticWebTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name="TestContainer")
 
         # This header should be posted on the container before every test
         cls.headers_public_read_acl = {'Read': '.r:*,.rlistings'}
 
         # Create test container and create one object in it
-        cls.container_client.create_container(cls.container_name)
-        cls.object_name = data_utils.rand_name(name="TestObject")
-        cls.object_data = data_utils.arbitrary_string()
-        cls.object_client.create_object(cls.container_name,
-                                        cls.object_name,
-                                        cls.object_data)
+        cls.container_name = cls.create_container()
+        cls.object_name, cls.object_data = cls.create_object(
+            cls.container_name)
 
         cls.container_client.update_container_metadata(
             cls.container_name,
@@ -45,8 +40,7 @@
 
     @classmethod
     def resource_cleanup(cls):
-        if hasattr(cls, "container_name"):
-            cls.delete_containers([cls.container_name])
+        cls.delete_containers()
         super(StaticWebTest, cls).resource_cleanup()
 
     @test.idempotent_id('c1f055ab-621d-4a6a-831f-846fcb578b8b')
@@ -102,7 +96,7 @@
         # Check only the format of common headers with custom matcher
         self.assertThat(resp, custom_matchers.AreAllWellFormatted())
 
-        self.assertIn(self.object_name, body)
+        self.assertIn(self.object_name, body.decode())
 
         # clean up before exiting
         self.container_client.update_container_metadata(self.container_name,
@@ -132,9 +126,9 @@
         resp, body = self.account_client.request("GET",
                                                  self.container_name,
                                                  headers={})
-        self.assertIn(self.object_name, body)
+        self.assertIn(self.object_name, body.decode())
         css = '<link rel="stylesheet" type="text/css" href="listings.css" />'
-        self.assertIn(css, body)
+        self.assertIn(css, body.decode())
 
     @test.idempotent_id('f18b4bef-212e-45e7-b3ca-59af3a465f82')
     @test.requires_ext(extension='staticweb', service='object')
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 0e39b7e..f134335 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -16,13 +16,13 @@
 import time
 
 from six.moves.urllib import parse as urlparse
-from tempest_lib import decorators
 import testtools
 
 
 from tempest.api.object_storage import base
 from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib import decorators
 from tempest import test
 
 CONF = config.CONF
@@ -80,7 +80,7 @@
     @classmethod
     def resource_cleanup(cls):
         for client in cls.clients.values():
-            cls.delete_containers(cls.containers, client[0], client[1])
+            cls.delete_containers(client[0], client[1])
         super(ContainerSyncTest, cls).resource_cleanup()
 
     def _test_container_synchronization(self, make_headers):
@@ -96,7 +96,7 @@
                 cont_client[0].put(str(cont[0]), body=None, headers=headers)
             # create object in container
             object_name = data_utils.rand_name(name='TestSyncObject')
-            data = object_name[::-1]  # data_utils.arbitrary_string()
+            data = object_name[::-1].encode()  # Raw data, we need bytes
             resp, _ = obj_client[0].create_object(cont[0], object_name, data)
             self.objects.append(object_name)
 
@@ -127,7 +127,7 @@
         for obj_client, cont in obj_clients:
             for obj_name in object_lists[0]:
                 resp, object_content = obj_client.get_object(cont, obj_name)
-                self.assertEqual(object_content, obj_name[::-1])
+                self.assertEqual(object_content, obj_name[::-1].encode())
 
     @test.attr(type='slow')
     @decorators.skip_because(bug='1317133')
diff --git a/tempest/api/object_storage/test_crossdomain.py b/tempest/api/object_storage/test_crossdomain.py
index 8dbfd06..18dc254 100644
--- a/tempest/api/object_storage/test_crossdomain.py
+++ b/tempest/api/object_storage/test_crossdomain.py
@@ -40,6 +40,7 @@
     @test.requires_ext(extension='crossdomain', service='object')
     def test_get_crossdomain_policy(self):
         resp, body = self.account_client.get("crossdomain.xml", {})
+        body = body.decode()
 
         self.assertTrue(body.startswith(self.xml_start) and
                         body.endswith(self.xml_end))
diff --git a/tempest/api/object_storage/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index 1c9d582..11acb31 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -13,11 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
 import time
 
 from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -25,19 +24,17 @@
     @classmethod
     def resource_setup(cls):
         super(ObjectExpiryTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name='TestContainer')
-        cls.container_client.create_container(cls.container_name)
+        cls.container_name = cls.create_container()
 
     def setUp(self):
         super(ObjectExpiryTest, self).setUp()
         # create object
-        self.object_name = data_utils.rand_name(name='TestObject')
-        resp, _ = self.object_client.create_object(self.container_name,
-                                                   self.object_name, '')
+        self.object_name, _ = self.create_object(
+            self.container_name)
 
     @classmethod
     def resource_cleanup(cls):
-        cls.delete_containers([cls.container_name])
+        cls.delete_containers()
         super(ObjectExpiryTest, cls).resource_cleanup()
 
     def _test_object_expiry(self, metadata):
diff --git a/tempest/api/object_storage/test_object_formpost.py b/tempest/api/object_storage/test_object_formpost.py
index 356f560..0a87a64 100644
--- a/tempest/api/object_storage/test_object_formpost.py
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -31,12 +31,9 @@
     @classmethod
     def resource_setup(cls):
         super(ObjectFormPostTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name='TestContainer')
+        cls.container_name = cls.create_container()
         cls.object_name = data_utils.rand_name(name='ObjectTemp')
 
-        cls.container_client.create_container(cls.container_name)
-        cls.containers = [cls.container_name]
-
         cls.key = 'Meta'
         cls.metadata = {'Temp-URL-Key': cls.key}
         cls.account_client.create_account_metadata(metadata=cls.metadata)
@@ -56,7 +53,7 @@
     @classmethod
     def resource_cleanup(cls):
         cls.account_client.delete_account_metadata(metadata=cls.metadata)
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
         super(ObjectFormPostTest, cls).resource_cleanup()
 
     def get_multipart_form(self, expires=600):
@@ -75,7 +72,9 @@
                                             max_file_count,
                                             expires)
 
-        signature = hmac.new(self.key, hmac_body, hashlib.sha1).hexdigest()
+        signature = hmac.new(
+            self.key.encode(), hmac_body.encode(), hashlib.sha1
+        ).hexdigest()
 
         fields = {'redirect': redirect,
                   'max_file_size': str(max_file_size),
@@ -122,4 +121,4 @@
         resp, body = self.object_client.get("%s/%s%s" % (
             self.container_name, self.object_name, "testfile"))
         self.assertHeaders(resp, "Object", "GET")
-        self.assertEqual(body, "hello world")
+        self.assertEqual(body.decode(), "hello world")
diff --git a/tempest/api/object_storage/test_object_formpost_negative.py b/tempest/api/object_storage/test_object_formpost_negative.py
index 7d9e115..f193111 100644
--- a/tempest/api/object_storage/test_object_formpost_negative.py
+++ b/tempest/api/object_storage/test_object_formpost_negative.py
@@ -17,10 +17,10 @@
 import time
 
 from six.moves.urllib import parse as urlparse
-from tempest_lib import exceptions as lib_exc
 
 from tempest.api.object_storage import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -32,12 +32,9 @@
     @classmethod
     def resource_setup(cls):
         super(ObjectFormPostNegativeTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name='TestContainer')
+        cls.container_name = cls.create_container()
         cls.object_name = data_utils.rand_name(name='ObjectTemp')
 
-        cls.container_client.create_container(cls.container_name)
-        cls.containers = [cls.container_name]
-
         cls.key = 'Meta'
         cls.metadata = {'Temp-URL-Key': cls.key}
         cls.account_client.create_account_metadata(metadata=cls.metadata)
@@ -57,7 +54,7 @@
     @classmethod
     def resource_cleanup(cls):
         cls.account_client.delete_account_metadata(metadata=cls.metadata)
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
         super(ObjectFormPostNegativeTest, cls).resource_cleanup()
 
     def get_multipart_form(self, expires=600):
@@ -76,7 +73,9 @@
                                             max_file_count,
                                             expires)
 
-        signature = hmac.new(self.key, hmac_body, hashlib.sha1).hexdigest()
+        signature = hmac.new(
+            self.key.encode(), hmac_body.encode(), hashlib.sha1
+        ).hexdigest()
 
         fields = {'redirect': redirect,
                   'max_file_size': str(max_file_size),
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index e8b035b..7716bdb 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -19,9 +19,6 @@
 import time
 import zlib
 
-import six
-from six import moves
-
 from tempest.api.object_storage import base
 from tempest.common import custom_matchers
 from tempest.common.utils import data_utils
@@ -36,32 +33,21 @@
     @classmethod
     def resource_setup(cls):
         super(ObjectTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name='TestContainer')
-        cls.container_client.create_container(cls.container_name)
-        cls.containers = [cls.container_name]
+        cls.container_name = cls.create_container()
 
     @classmethod
     def resource_cleanup(cls):
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
         super(ObjectTest, cls).resource_cleanup()
 
-    def _create_object(self, metadata=None):
-        # setup object
-        object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
-        self.object_client.create_object(self.container_name,
-                                         object_name, data, metadata=metadata)
-
-        return object_name, data
-
     def _upload_segments(self):
         # create object
         object_name = data_utils.rand_name(name='LObject')
         data = data_utils.arbitrary_string()
         segments = 10
-        data_segments = [data + str(i) for i in six.moves.xrange(segments)]
+        data_segments = [data + str(i) for i in range(segments)]
         # uploading segments
-        for i in six.moves.xrange(segments):
+        for i in range(segments):
             resp, _ = self.object_client.create_object_segments(
                 self.container_name, object_name, i, data_segments[i])
 
@@ -93,12 +79,12 @@
     def test_create_object(self):
         # create object
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         resp, _ = self.object_client.create_object(self.container_name,
                                                    object_name, data)
         # create another object
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         resp, _ = self.object_client.create_object(self.container_name,
                                                    object_name, data)
         self.assertHeaders(resp, 'Object', 'PUT')
@@ -112,7 +98,7 @@
     def test_create_object_with_content_disposition(self):
         # create object with content_disposition
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         metadata = {}
         metadata['content-disposition'] = 'inline'
         resp, _ = self.object_client.create_object(
@@ -136,7 +122,7 @@
         object_name = data_utils.rand_name(name='TestObject')
 
         # put compressed string
-        data_before = 'x' * 2000
+        data_before = b'x' * 2000
         data = zlib.compress(data_before)
         metadata = {}
         metadata['content-encoding'] = 'deflate'
@@ -161,7 +147,7 @@
     def test_create_object_with_etag(self):
         # create object with etag
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         md5 = hashlib.md5(data).hexdigest()
         metadata = {'Etag': md5}
         resp, _ = self.object_client.create_object(
@@ -179,23 +165,14 @@
     @test.idempotent_id('84dafe57-9666-4f6d-84c8-0814d37923b8')
     def test_create_object_with_expect_continue(self):
         # create object with expect_continue
+
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
-        metadata = {'Expect': '100-continue'}
-        resp = self.object_client.create_object_continue(
-            self.container_name,
-            object_name,
-            data,
-            metadata=metadata)
+        data = data_utils.random_bytes()
 
-        self.assertIn('status', resp)
-        self.assertEqual(resp['status'], '100')
+        status, _ = self.object_client.create_object_continue(
+            self.container_name, object_name, data)
 
-        self.object_client.create_object_continue(
-            self.container_name,
-            object_name,
-            data,
-            metadata=None)
+        self.assertEqual(status, 201)
 
         # check uploaded content
         _, body = self.object_client.get_object(self.container_name,
@@ -206,12 +183,12 @@
     def test_create_object_with_transfer_encoding(self):
         # create object with transfer_encoding
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string(1024)
+        data = data_utils.random_bytes(1024)
         status, _, resp_headers = self.object_client.put_object_with_chunk(
             container=self.container_name,
             name=object_name,
-            contents=moves.cStringIO(data),
-            chunk_size=512)
+            contents=data_utils.chunkify(data, 512)
+        )
         self.assertHeaders(resp_headers, 'Object', 'PUT')
 
         # check uploaded content
@@ -223,7 +200,7 @@
     def test_create_object_with_x_fresh_metadata(self):
         # create object with x_fresh_metadata
         object_name_base = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         metadata_1 = {'X-Object-Meta-test-meta': 'Meta'}
         self.object_client.create_object(self.container_name,
                                          object_name_base,
@@ -249,7 +226,7 @@
     def test_create_object_with_x_object_meta(self):
         # create object with object_meta
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         metadata = {'X-Object-Meta-test-meta': 'Meta'}
         resp, _ = self.object_client.create_object(
             self.container_name,
@@ -268,7 +245,7 @@
     def test_create_object_with_x_object_metakey(self):
         # create object with the blank value of metadata
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         metadata = {'X-Object-Meta-test-meta': ''}
         resp, _ = self.object_client.create_object(
             self.container_name,
@@ -287,7 +264,7 @@
     def test_create_object_with_x_remove_object_meta(self):
         # create object with x_remove_object_meta
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         metadata_add = {'X-Object-Meta-test-meta': 'Meta'}
         self.object_client.create_object(self.container_name,
                                          object_name,
@@ -310,7 +287,7 @@
     def test_create_object_with_x_remove_object_metakey(self):
         # create object with the blank value of remove metadata
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         metadata_add = {'X-Object-Meta-test-meta': 'Meta'}
         self.object_client.create_object(self.container_name,
                                          object_name,
@@ -333,7 +310,7 @@
     def test_delete_object(self):
         # create object
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         resp, _ = self.object_client.create_object(self.container_name,
                                                    object_name, data)
         # delete object
@@ -345,7 +322,7 @@
     @test.idempotent_id('7a94c25d-66e6-434c-9c38-97d4e2c29945')
     def test_update_object_metadata(self):
         # update object metadata
-        object_name, data = self._create_object()
+        object_name, _ = self.create_object(self.container_name)
 
         metadata = {'X-Object-Meta-test-meta': 'Meta'}
         resp, _ = self.object_client.update_object_metadata(
@@ -365,7 +342,7 @@
     def test_update_object_metadata_with_remove_metadata(self):
         # update object metadata with remove metadata
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         create_metadata = {'X-Object-Meta-test-meta1': 'Meta1'}
         self.object_client.create_object(self.container_name,
                                          object_name,
@@ -389,7 +366,7 @@
     def test_update_object_metadata_with_create_and_remove_metadata(self):
         # creation and deletion of metadata with one request
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         create_metadata = {'X-Object-Meta-test-meta1': 'Meta1'}
         self.object_client.create_object(self.container_name,
                                          object_name,
@@ -441,8 +418,8 @@
 
     @test.idempotent_id('0dbbe89c-6811-4d84-a2df-eca2bdd40c0e')
     def test_update_object_metadata_with_x_object_metakey(self):
-        # update object metadata with a blenk value of metadata
-        object_name, data = self._create_object()
+        # update object metadata with a blank value of metadata
+        object_name, _ = self.create_object(self.container_name)
 
         update_metadata = {'X-Object-Meta-test-meta': ''}
         resp, _ = self.object_client.update_object_metadata(
@@ -487,7 +464,7 @@
     def test_list_object_metadata(self):
         # get object metadata
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         metadata = {'X-Object-Meta-test-meta': 'Meta'}
         self.object_client.create_object(self.container_name,
                                          object_name,
@@ -504,7 +481,7 @@
     @test.idempotent_id('170fb90e-f5c3-4b1f-ae1b-a18810821172')
     def test_list_no_object_metadata(self):
         # get empty list of object metadata
-        object_name, data = self._create_object()
+        object_name, _ = self.create_object(self.container_name)
 
         resp, _ = self.object_client.list_object_metadata(
             self.container_name,
@@ -558,7 +535,7 @@
         # retrieve object's data (in response body)
 
         # create object
-        object_name, data = self._create_object()
+        object_name, data = self.create_object(self.container_name)
         # get object
         resp, body = self.object_client.get_object(self.container_name,
                                                    object_name)
@@ -570,7 +547,7 @@
     def test_get_object_with_metadata(self):
         # get object with metadata
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         metadata = {'X-Object-Meta-test-meta': 'Meta'}
         self.object_client.create_object(self.container_name,
                                          object_name,
@@ -589,7 +566,7 @@
     def test_get_object_with_range(self):
         # get object with range
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string(100)
+        data = data_utils.random_bytes(100)
         self.object_client.create_object(self.container_name,
                                          object_name,
                                          data,
@@ -644,13 +621,13 @@
         self.assertEqual(resp['x-object-manifest'],
                          '%s/%s' % (self.container_name, object_name))
 
-        self.assertEqual(''.join(data_segments), body)
+        self.assertEqual(''.join(data_segments), body.decode())
 
     @test.idempotent_id('c05b4013-e4de-47af-be84-e598062b16fc')
     def test_get_object_with_if_match(self):
         # get object with if_match
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string(10)
+        data = data_utils.random_bytes(10)
         create_md5 = hashlib.md5(data).hexdigest()
         create_metadata = {'Etag': create_md5}
         self.object_client.create_object(self.container_name,
@@ -670,7 +647,7 @@
     def test_get_object_with_if_modified_since(self):
         # get object with if_modified_since
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
+        data = data_utils.random_bytes()
         time_now = time.time()
         self.object_client.create_object(self.container_name,
                                          object_name,
@@ -690,7 +667,7 @@
     def test_get_object_with_if_none_match(self):
         # get object with if_none_match
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string(10)
+        data = data_utils.random_bytes()
         create_md5 = hashlib.md5(data).hexdigest()
         create_metadata = {'Etag': create_md5}
         self.object_client.create_object(self.container_name,
@@ -698,7 +675,7 @@
                                          data,
                                          metadata=create_metadata)
 
-        list_data = data_utils.arbitrary_string(15)
+        list_data = data_utils.random_bytes()
         list_md5 = hashlib.md5(list_data).hexdigest()
         list_metadata = {'If-None-Match': list_md5}
         resp, body = self.object_client.get_object(
@@ -711,7 +688,7 @@
     @test.idempotent_id('0aa1201c-10aa-467a-bee7-63cbdd463152')
     def test_get_object_with_if_unmodified_since(self):
         # get object with if_unmodified_since
-        object_name, data = self._create_object()
+        object_name, data = self.create_object(self.container_name)
 
         time_now = time.time()
         http_date = time.ctime(time_now + 86400)
@@ -726,7 +703,7 @@
     @test.idempotent_id('94587078-475f-48f9-a40f-389c246e31cd')
     def test_get_object_with_x_newest(self):
         # get object with x_newest
-        object_name, data = self._create_object()
+        object_name, data = self.create_object(self.container_name)
 
         list_metadata = {'X-Newest': 'true'}
         resp, body = self.object_client.get_object(
@@ -740,15 +717,13 @@
     def test_copy_object_in_same_container(self):
         # create source object
         src_object_name = data_utils.rand_name(name='SrcObject')
-        src_data = data_utils.arbitrary_string(size=len(src_object_name) * 2,
-                                               base_text=src_object_name)
+        src_data = data_utils.random_bytes(size=len(src_object_name) * 2)
         resp, _ = self.object_client.create_object(self.container_name,
                                                    src_object_name,
                                                    src_data)
         # create destination object
         dst_object_name = data_utils.rand_name(name='DstObject')
-        dst_data = data_utils.arbitrary_string(size=len(dst_object_name) * 3,
-                                               base_text=dst_object_name)
+        dst_data = data_utils.random_bytes(size=len(dst_object_name) * 3)
         resp, _ = self.object_client.create_object(self.container_name,
                                                    dst_object_name,
                                                    dst_data)
@@ -767,7 +742,7 @@
         # change the content type of an existing object
 
         # create object
-        object_name, data = self._create_object()
+        object_name, _ = self.create_object(self.container_name)
         # get the old content type
         resp_tmp, _ = self.object_client.list_object_metadata(
             self.container_name, object_name)
@@ -787,14 +762,12 @@
     def test_copy_object_2d_way(self):
         # create source object
         src_object_name = data_utils.rand_name(name='SrcObject')
-        src_data = data_utils.arbitrary_string(size=len(src_object_name) * 2,
-                                               base_text=src_object_name)
+        src_data = data_utils.random_bytes(size=len(src_object_name) * 2)
         resp, _ = self.object_client.create_object(self.container_name,
                                                    src_object_name, src_data)
         # create destination object
         dst_object_name = data_utils.rand_name(name='DstObject')
-        dst_data = data_utils.arbitrary_string(size=len(dst_object_name) * 3,
-                                               base_text=dst_object_name)
+        dst_data = data_utils.random_bytes(size=len(dst_object_name) * 3)
         resp, _ = self.object_client.create_object(self.container_name,
                                                    dst_object_name, dst_data)
         # copy source object to destination
@@ -822,8 +795,7 @@
         self.containers.append(dst_container_name)
         # create object in source container
         object_name = data_utils.rand_name(name='Object')
-        data = data_utils.arbitrary_string(size=len(object_name) * 2,
-                                           base_text=object_name)
+        data = data_utils.random_bytes(size=len(object_name) * 2)
         resp, _ = self.object_client.create_object(src_container_name,
                                                    object_name, data)
         # set object metadata
@@ -853,7 +825,8 @@
     def test_copy_object_with_x_fresh_metadata(self):
         # create source object
         metadata = {'x-object-meta-src': 'src_value'}
-        src_object_name, data = self._create_object(metadata)
+        src_object_name, data = self.create_object(self.container_name,
+                                                   metadata=metadata)
 
         # copy source object with x_fresh_metadata header
         metadata = {'X-Fresh-Metadata': 'true'}
@@ -873,7 +846,8 @@
     def test_copy_object_with_x_object_metakey(self):
         # create source object
         metadata = {'x-object-meta-src': 'src_value'}
-        src_obj_name, data = self._create_object(metadata)
+        src_obj_name, data = self.create_object(self.container_name,
+                                                metadata=metadata)
 
         # copy source object to destination with x-object-meta-key
         metadata = {'x-object-meta-test': ''}
@@ -884,7 +858,7 @@
         expected = {'x-object-meta-test': '',
                     'x-object-meta-src': 'src_value',
                     'x-copied-from': self.container_name + "/" + src_obj_name}
-        for key, value in six.iteritems(expected):
+        for key, value in expected.items():
             self.assertIn(key, resp)
             self.assertEqual(value, resp[key])
 
@@ -895,7 +869,8 @@
     def test_copy_object_with_x_object_meta(self):
         # create source object
         metadata = {'x-object-meta-src': 'src_value'}
-        src_obj_name, data = self._create_object(metadata)
+        src_obj_name, data = self.create_object(self.container_name,
+                                                metadata=metadata)
 
         # copy source object to destination with object metadata
         metadata = {'x-object-meta-test': 'value'}
@@ -906,7 +881,7 @@
         expected = {'x-object-meta-test': 'value',
                     'x-object-meta-src': 'src_value',
                     'x-copied-from': self.container_name + "/" + src_obj_name}
-        for key, value in six.iteritems(expected):
+        for key, value in expected.items():
             self.assertIn(key, resp)
             self.assertEqual(value, resp[key])
 
@@ -919,9 +894,9 @@
         object_name = data_utils.rand_name(name='LObject')
         data = data_utils.arbitrary_string()
         segments = 10
-        data_segments = [data + str(i) for i in six.moves.xrange(segments)]
+        data_segments = [data + str(i) for i in range(segments)]
         # uploading segments
-        for i in six.moves.xrange(segments):
+        for i in range(segments):
             resp, _ = self.object_client.create_object_segments(
                 self.container_name, object_name, i, data_segments[i])
         # creating a manifest file
@@ -953,7 +928,7 @@
         # downloading the object
         resp, body = self.object_client.get_object(
             self.container_name, object_name)
-        self.assertEqual(''.join(data_segments), body)
+        self.assertEqual(''.join(data_segments), body.decode())
 
     @test.idempotent_id('50d01f12-526f-4360-9ac2-75dd508d7b68')
     def test_get_object_if_different(self):
@@ -961,7 +936,7 @@
         # Make a conditional request for an object using the If-None-Match
         # header, it should get downloaded only if the local file is different,
         # otherwise the response code should be 304 Not Modified
-        object_name, data = self._create_object()
+        object_name, data = self.create_object(self.container_name)
         # local copy is identical, no download
         md5 = hashlib.md5(data).hexdigest()
         headers = {'If-None-Match': md5}
@@ -972,16 +947,13 @@
         # When the file is not downloaded from Swift server, response does
         # not contain 'X-Timestamp' header. This is the special case, therefore
         # the existence of response headers is checked without custom matcher.
-        self.assertIn('content-type', resp)
-        self.assertIn('x-trans-id', resp)
         self.assertIn('date', resp)
-        self.assertIn('accept-ranges', resp)
         # Check only the format of common headers with custom matcher
         self.assertThat(resp, custom_matchers.AreAllWellFormatted())
 
         # local copy is different, download
         local_data = "something different"
-        md5 = hashlib.md5(local_data).hexdigest()
+        md5 = hashlib.md5(local_data.encode()).hexdigest()
         headers = {'If-None-Match': md5}
         resp, body = self.object_client.get(url, headers=headers)
         self.assertHeaders(resp, 'Object', 'GET')
@@ -1025,8 +997,7 @@
 
         # create object
         object_name = data_utils.rand_name(name='Object')
-        data = data_utils.arbitrary_string(size=len(object_name),
-                                           base_text=object_name)
+        data = data_utils.random_bytes(size=len(object_name))
         resp, _ = self.object_client.create_object(self.container_name,
                                                    object_name, data)
         self.assertHeaders(resp, 'Object', 'PUT')
@@ -1062,8 +1033,7 @@
 
         # create object
         object_name = data_utils.rand_name(name='Object')
-        data = data_utils.arbitrary_string(size=len(object_name) * 1,
-                                           base_text=object_name)
+        data = data_utils.random_bytes(size=len(object_name))
         resp, _ = self.object_client.create_object(self.container_name,
                                                    object_name, data)
         self.assertHeaders(resp, 'Object', 'PUT')
diff --git a/tempest/api/object_storage/test_object_slo.py b/tempest/api/object_storage/test_object_slo.py
index 5811cb8..f9c1148 100644
--- a/tempest/api/object_storage/test_object_slo.py
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -15,11 +15,11 @@
 import hashlib
 
 from oslo_serialization import jsonutils as json
-from tempest_lib import exceptions as lib_exc
 
 from tempest.api.object_storage import base
 from tempest.common import custom_matchers
 from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest import test
 
 # Each segment, except for the final one, must be at least 1 megabyte
@@ -30,18 +30,14 @@
 
     def setUp(self):
         super(ObjectSloTest, self).setUp()
-        self.container_name = data_utils.rand_name(name='TestContainer')
-        self.container_client.create_container(self.container_name)
+        self.container_name = self.create_container()
         self.objects = []
 
     def tearDown(self):
         for obj in self.objects:
-            try:
-                self.object_client.delete_object(
-                    self.container_name,
-                    obj)
-            except lib_exc.NotFound:
-                pass
+            test_utils.call_and_ignore_notfound_exc(
+                self.object_client.delete_object,
+                self.container_name, obj)
         self.container_client.delete_container(self.container_name)
         super(ObjectSloTest, self).tearDown()
 
@@ -60,7 +56,7 @@
         object_name_base_1 = object_name + '_01'
         object_name_base_2 = object_name + '_02'
         data_size = MIN_SEGMENT_SIZE
-        self.content = data_utils.arbitrary_string(data_size)
+        self.content = data_utils.random_bytes(data_size)
         self._create_object(self.container_name,
                             object_name_base_1,
                             self.content)
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 3d28f6e..bd0d213 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -20,11 +20,8 @@
 
 from tempest.api.object_storage import base
 from tempest.common.utils import data_utils
-from tempest import config
 from tempest import test
 
-CONF = config.CONF
-
 
 class ObjectTempUrlTest(base.BaseObjectTest):
 
@@ -32,9 +29,7 @@
     def resource_setup(cls):
         super(ObjectTempUrlTest, cls).resource_setup()
         # create a container
-        cls.container_name = data_utils.rand_name(name='TestContainer')
-        cls.container_client.create_container(cls.container_name)
-        cls.containers = [cls.container_name]
+        cls.container_name = cls.create_container()
 
         # update account metadata
         cls.key = 'Meta'
@@ -44,11 +39,7 @@
         cls.account_client.create_account_metadata(metadata=metadata)
 
         # create an object
-        cls.object_name = data_utils.rand_name(name='ObjectTemp')
-        cls.content = data_utils.arbitrary_string(size=len(cls.object_name),
-                                                  base_text=cls.object_name)
-        cls.object_client.create_object(cls.container_name,
-                                        cls.object_name, cls.content)
+        cls.object_name, cls.content = cls.create_object(cls.container_name)
 
     @classmethod
     def resource_cleanup(cls):
@@ -56,7 +47,7 @@
             cls.account_client.delete_account_metadata(
                 metadata=metadata)
 
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
 
         super(ObjectTempUrlTest, cls).resource_cleanup()
 
@@ -84,7 +75,9 @@
             container, object_name)
 
         hmac_body = '%s\n%s\n%s' % (method, expires, path)
-        sig = hmac.new(key, hmac_body, hashlib.sha1).hexdigest()
+        sig = hmac.new(
+            key.encode(), hmac_body.encode(), hashlib.sha1
+        ).hexdigest()
 
         url = "%s/%s?temp_url_sig=%s&temp_url_expires=%s" % (container,
                                                              object_name,
@@ -138,9 +131,7 @@
     @test.idempotent_id('9b08dade-3571-4152-8a4f-a4f2a873a735')
     @test.requires_ext(extension='tempurl', service='object')
     def test_put_object_using_temp_url(self):
-        new_data = data_utils.arbitrary_string(
-            size=len(self.object_name),
-            base_text=data_utils.rand_name(name="random"))
+        new_data = data_utils.random_bytes(size=len(self.object_name))
 
         expires = self._get_expiry_date()
         url = self._get_temp_url(self.container_name,
diff --git a/tempest/api/object_storage/test_object_temp_url_negative.py b/tempest/api/object_storage/test_object_temp_url_negative.py
index 6d06143..df7a7f6 100644
--- a/tempest/api/object_storage/test_object_temp_url_negative.py
+++ b/tempest/api/object_storage/test_object_temp_url_negative.py
@@ -17,10 +17,10 @@
 import time
 
 from six.moves.urllib import parse as urlparse
-from tempest_lib import exceptions as lib_exc
 
 from tempest.api.object_storage import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -33,9 +33,7 @@
     def resource_setup(cls):
         super(ObjectTempUrlNegativeTest, cls).resource_setup()
 
-        cls.container_name = data_utils.rand_name(name='TestContainer')
-        cls.container_client.create_container(cls.container_name)
-        cls.containers = [cls.container_name]
+        cls.container_name = cls.create_container()
 
         # update account metadata
         cls.key = 'Meta'
@@ -49,7 +47,7 @@
         resp, _ = cls.account_client.delete_account_metadata(
             metadata=cls.metadata)
 
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
 
         super(ObjectTempUrlNegativeTest, cls).resource_cleanup()
 
@@ -82,7 +80,9 @@
             container, object_name)
 
         hmac_body = '%s\n%s\n%s' % (method, expires, path)
-        sig = hmac.new(key, hmac_body, hashlib.sha1).hexdigest()
+        sig = hmac.new(
+            key.encode(), hmac_body.encode(), hashlib.sha1
+        ).hexdigest()
 
         url = "%s/%s?temp_url_sig=%s&temp_url_expires=%s" % (container,
                                                              object_name,
diff --git a/tempest/api/object_storage/test_object_version.py b/tempest/api/object_storage/test_object_version.py
index 24ec3f5..6d064a2 100644
--- a/tempest/api/object_storage/test_object_version.py
+++ b/tempest/api/object_storage/test_object_version.py
@@ -31,7 +31,7 @@
 
     @classmethod
     def resource_cleanup(cls):
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
         super(ContainerTest, cls).resource_cleanup()
 
     def assertContainer(self, container, count, byte, versioned):
@@ -69,22 +69,24 @@
                              vers_container_name)
         object_name = data_utils.rand_name(name='TestObject')
         # create object
+        data_1 = data_utils.random_bytes()
         resp, _ = self.object_client.create_object(base_container_name,
-                                                   object_name, '1')
+                                                   object_name, data_1)
         # create 2nd version of object
+        data_2 = data_utils.random_bytes()
         resp, _ = self.object_client.create_object(base_container_name,
-                                                   object_name, '2')
+                                                   object_name, data_2)
         resp, body = self.object_client.get_object(base_container_name,
                                                    object_name)
-        self.assertEqual(body, '2')
+        self.assertEqual(body, data_2)
         # delete object version 2
         resp, _ = self.object_client.delete_object(base_container_name,
                                                    object_name)
-        self.assertContainer(base_container_name, '1', '1',
+        self.assertContainer(base_container_name, '1', '1024',
                              vers_container_name)
         resp, body = self.object_client.get_object(base_container_name,
                                                    object_name)
-        self.assertEqual(body, '1')
+        self.assertEqual(body, data_1)
         # delete object version 1
         resp, _ = self.object_client.delete_object(base_container_name,
                                                    object_name)
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index c93b5ed..3701b55 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -12,11 +12,11 @@
 
 import os.path
 
-from tempest_lib import exceptions as lib_exc
 import yaml
 
 from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import test_utils
 import tempest.test
 
 CONF = config.CONF
@@ -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
@@ -84,17 +83,13 @@
     @classmethod
     def _clear_stacks(cls):
         for stack_identifier in cls.stacks:
-            try:
-                cls.client.delete_stack(stack_identifier)
-            except lib_exc.NotFound:
-                pass
+            test_utils.call_and_ignore_notfound_exc(
+                cls.client.delete_stack, stack_identifier)
 
         for stack_identifier in cls.stacks:
-            try:
-                cls.client.wait_for_stack_status(
-                    stack_identifier, 'DELETE_COMPLETE')
-            except lib_exc.NotFound:
-                pass
+            test_utils.call_and_ignore_notfound_exc(
+                cls.client.wait_for_stack_status, stack_identifier,
+                'DELETE_COMPLETE')
 
     @classmethod
     def _create_keypair(cls, name_start='keypair-heat-'):
@@ -125,10 +120,8 @@
     @classmethod
     def _clear_images(cls):
         for image_id in cls.images:
-            try:
-                cls.images_v2_client.delete_image(image_id)
-            except lib_exc.NotFound:
-                pass
+            test_utils.call_and_ignore_notfound_exc(
+                cls.images_v2_client.delete_image, image_id)
 
     @classmethod
     def read_template(cls, name, ext='yaml'):
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/templates/neutron_basic.yaml b/tempest/api/orchestration/stacks/templates/neutron_basic.yaml
index be33c94..ccb1b54 100644
--- a/tempest/api/orchestration/stacks/templates/neutron_basic.yaml
+++ b/tempest/api/orchestration/stacks/templates/neutron_basic.yaml
@@ -58,7 +58,7 @@
             #!/bin/sh -v
 
             SIGNAL_DATA='{"Status": "SUCCESS", "Reason": "SmokeServerNeutron created", "Data": "Application has completed configuration.", "UniqueId": "00000"}'
-            while ! curl --fail -X PUT -H 'Content-Type:' --data-binary "$SIGNAL_DATA" \
+            while ! curl --insecure --fail -X PUT -H 'Content-Type:' --data-binary "$SIGNAL_DATA" \
             'wait_handle' ; do sleep 3; done
           params:
             wait_handle: {get_resource: WaitHandleNeutron}
diff --git a/tempest/api/orchestration/stacks/test_environment.py b/tempest/api/orchestration/stacks/test_environment.py
index 9d2b425..f2ffbd7 100644
--- a/tempest/api/orchestration/stacks/test_environment.py
+++ b/tempest/api/orchestration/stacks/test_environment.py
@@ -12,13 +12,9 @@
 
 from tempest.api.orchestration import base
 from tempest.common.utils import data_utils
-from tempest import config
 from tempest import test
 
 
-CONF = config.CONF
-
-
 class StackEnvironmentTest(base.BaseOrchestrationTest):
 
     @test.idempotent_id('37d4346b-1abd-4442-b7b1-2a4e5749a1e3')
diff --git a/tempest/api/orchestration/stacks/test_limits.py b/tempest/api/orchestration/stacks/test_limits.py
index 315b3e0..d85aa96 100644
--- a/tempest/api/orchestration/stacks/test_limits.py
+++ b/tempest/api/orchestration/stacks/test_limits.py
@@ -10,11 +10,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.orchestration 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
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index 09e863e..5d680d2 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -10,15 +10,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-
-import logging
-
 import netaddr
+from oslo_log import log as logging
 
 from tempest.api.orchestration import base
 from tempest.common.utils import data_utils
 from tempest import config
-from tempest import exceptions
+from tempest.lib import exceptions
 from tempest import test
 
 CONF = config.CONF
@@ -42,9 +40,9 @@
     @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
+        cls.routers_client = cls.os.routers_client
 
     @classmethod
     def resource_setup(cls):
@@ -56,8 +54,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
@@ -78,7 +76,7 @@
             cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
             resources = (cls.client.list_resources(cls.stack_identifier)
                          ['resources'])
-        except exceptions.TimeoutException as e:
+        except exceptions.TimeoutException:
             if CONF.compute_feature_enabled.console_output:
                 # attempt to log the server console to help with debugging
                 # the cause of the server not signalling the waitcondition
@@ -90,7 +88,7 @@
                 output = cls.servers_client.get_console_output(
                     server_id)['output']
                 LOG.debug(output)
-            raise e
+            raise
 
         cls.test_resources = {}
         for resource in resources:
@@ -149,7 +147,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_non_empty_stack.py b/tempest/api/orchestration/stacks/test_non_empty_stack.py
index 3be5bb6..4ead084 100644
--- a/tempest/api/orchestration/stacks/test_non_empty_stack.py
+++ b/tempest/api/orchestration/stacks/test_non_empty_stack.py
@@ -125,7 +125,7 @@
                                        'resource_status_reason',
                                        'resource_status', 'event_time')
 
-        resource_statuses = map(lambda event: event['resource_status'], events)
+        resource_statuses = [event['resource_status'] for event in events]
         self.assertIn('CREATE_IN_PROGRESS', resource_statuses)
         self.assertIn('CREATE_COMPLETE', resource_statuses)
 
diff --git a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
index 0400e76..16d8180 100644
--- a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
+++ b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
@@ -72,18 +72,18 @@
         for outputs in stack['outputs']:
             output_map[outputs['output_key']] = outputs['output_value']
         # Test that first key generated public and private keys
-        self.assertTrue('KeyPair_PublicKey' in output_map)
-        self.assertTrue("Generated" in output_map['KeyPair_PublicKey'])
-        self.assertTrue('KeyPair_PrivateKey' in output_map)
-        self.assertTrue('-----BEGIN' in output_map['KeyPair_PrivateKey'])
+        self.assertIn('KeyPair_PublicKey', output_map)
+        self.assertIn("Generated", output_map['KeyPair_PublicKey'])
+        self.assertIn('KeyPair_PrivateKey', output_map)
+        self.assertIn('-----BEGIN', output_map['KeyPair_PrivateKey'])
         # Test that second key generated public key, and private key is not
         # in the output due to save_private_key = false
-        self.assertTrue('KeyPairDontSavePrivate_PublicKey' in output_map)
-        self.assertTrue('Generated' in
-                        output_map['KeyPairDontSavePrivate_PublicKey'])
-        self.assertTrue(u'KeyPairDontSavePrivate_PrivateKey' in output_map)
+        self.assertIn('KeyPairDontSavePrivate_PublicKey', output_map)
+        self.assertIn('Generated',
+                      output_map['KeyPairDontSavePrivate_PublicKey'])
+        self.assertIn(u'KeyPairDontSavePrivate_PrivateKey', output_map)
         private_key = output_map['KeyPairDontSavePrivate_PrivateKey']
-        self.assertTrue(len(private_key) == 0)
+        self.assertEqual(0, len(private_key))
 
 
 class NovaKeyPairResourcesAWSTest(NovaKeyPairResourcesYAMLTest):
diff --git a/tempest/api/orchestration/stacks/test_soft_conf.py b/tempest/api/orchestration/stacks/test_soft_conf.py
index ab45929..b660f6e 100644
--- a/tempest/api/orchestration/stacks/test_soft_conf.py
+++ b/tempest/api/orchestration/stacks/test_soft_conf.py
@@ -10,15 +10,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.orchestration 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
-
 
 class TestSoftwareConfig(base.BaseOrchestrationTest):
 
@@ -46,7 +42,7 @@
 
     def _validate_config(self, configuration, api_config):
         # Assert all expected keys are present with matching data
-        for k in configuration.keys():
+        for k in configuration:
             self.assertEqual(configuration[k],
                              api_config['software_config'][k])
 
diff --git a/tempest/api/orchestration/stacks/test_stacks.py b/tempest/api/orchestration/stacks/test_stacks.py
index 28463ab..f13a2d9 100644
--- a/tempest/api/orchestration/stacks/test_stacks.py
+++ b/tempest/api/orchestration/stacks/test_stacks.py
@@ -18,10 +18,6 @@
 class StacksTestJSON(base.BaseOrchestrationTest):
     empty_template = "HeatTemplateFormatVersion: '2012-12-12'\n"
 
-    @classmethod
-    def resource_setup(cls):
-        super(StacksTestJSON, cls).resource_setup()
-
     @test.attr(type='smoke')
     @test.idempotent_id('d35d628c-07f6-4674-85a1-74db9919e986')
     def test_stack_list_responds(self):
diff --git a/tempest/api/orchestration/stacks/test_swift_resources.py b/tempest/api/orchestration/stacks/test_swift_resources.py
index c0f1c4b..3672526 100644
--- a/tempest/api/orchestration/stacks/test_swift_resources.py
+++ b/tempest/api/orchestration/stacks/test_swift_resources.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 # Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
 #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may
diff --git a/tempest/api/orchestration/stacks/test_templates_negative.py b/tempest/api/orchestration/stacks/test_templates_negative.py
index 4bd0f33..f8245c1 100644
--- a/tempest/api/orchestration/stacks/test_templates_negative.py
+++ b/tempest/api/orchestration/stacks/test_templates_negative.py
@@ -12,9 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.orchestration import base
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -28,7 +27,7 @@
     Type: AWS::IAM::User
 """
 
-    invalid_template_url = 'http://www.example.com/template.yaml'
+    invalid_template_url = 'http:///template.yaml'
 
     @classmethod
     def resource_setup(cls):
@@ -58,4 +57,4 @@
 }
 """
 
-    invalid_template_url = 'http://www.example.com/template.template'
+    invalid_template_url = 'http:///template.template'
diff --git a/tempest/api/orchestration/stacks/test_volumes.py b/tempest/api/orchestration/stacks/test_volumes.py
index e51551b..a5aaf6e 100644
--- a/tempest/api/orchestration/stacks/test_volumes.py
+++ b/tempest/api/orchestration/stacks/test_volumes.py
@@ -10,11 +10,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.orchestration 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
 
 
@@ -33,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:
@@ -52,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'))
@@ -66,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
@@ -95,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/telemetry/__init__.py b/tempest/api/telemetry/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/telemetry/__init__.py
+++ /dev/null
diff --git a/tempest/api/telemetry/base.py b/tempest/api/telemetry/base.py
deleted file mode 100644
index ff06810..0000000
--- a/tempest/api/telemetry/base.py
+++ /dev/null
@@ -1,196 +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 oslo_utils import timeutils
-from tempest_lib import exceptions as lib_exc
-
-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
-import tempest.test
-
-CONF = config.CONF
-
-
-class BaseTelemetryTest(tempest.test.BaseTestCase):
-
-    """Base test case class for all Telemetry API tests."""
-
-    credentials = ['primary']
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaseTelemetryTest, cls).skip_checks()
-        if not CONF.service_available.ceilometer:
-            raise cls.skipException("Ceilometer support is required")
-
-    @classmethod
-    def setup_credentials(cls):
-        cls.set_network_resources()
-        super(BaseTelemetryTest, cls).setup_credentials()
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseTelemetryTest, cls).setup_clients()
-        cls.telemetry_client = cls.os.telemetry_client
-        cls.servers_client = cls.os.servers_client
-        cls.flavors_client = cls.os.flavors_client
-        cls.image_client = cls.os.image_client
-        cls.image_client_v2 = cls.os.image_client_v2
-
-    @classmethod
-    def resource_setup(cls):
-        super(BaseTelemetryTest, cls).resource_setup()
-        cls.nova_notifications = ['memory', 'vcpus', 'disk.root.size',
-                                  'disk.ephemeral.size']
-
-        cls.glance_notifications = ['image.size']
-
-        cls.glance_v2_notifications = ['image.download', 'image.serve']
-
-        cls.server_ids = []
-        cls.image_ids = []
-
-    @classmethod
-    def create_server(cls):
-        tenant_network = cls.get_tenant_network()
-        body, server = compute.create_test_server(
-            cls.os,
-            tenant_network=tenant_network,
-            name=data_utils.rand_name('ceilometer-instance'),
-            wait_until='ACTIVE')
-        cls.server_ids.append(body['id'])
-        return body
-
-    @classmethod
-    def create_image(cls, client, **kwargs):
-        body = client.create_image(name=data_utils.rand_name('image'),
-                                   container_format='bare',
-                                   disk_format='raw',
-                                   **kwargs)
-        # TODO(jswarren) Move ['image'] up to initial body value assignment
-        # once both v1 and v2 glance clients include the full response
-        # object.
-        if 'image' in body:
-            body = body['image']
-        cls.image_ids.append(body['id'])
-        return body
-
-    @staticmethod
-    def cleanup_resources(method, list_of_ids):
-        for resource_id in list_of_ids:
-            try:
-                method(resource_id)
-            except lib_exc.NotFound:
-                pass
-
-    @classmethod
-    def wait_for_server_termination(cls, server_id):
-        waiters.wait_for_server_termination(cls.servers_client,
-                                            server_id)
-
-    @classmethod
-    def resource_cleanup(cls):
-        cls.cleanup_resources(cls.servers_client.delete_server, cls.server_ids)
-        cls.cleanup_resources(cls.wait_for_server_termination, cls.server_ids)
-        cls.cleanup_resources(cls.image_client.delete_image, cls.image_ids)
-        super(BaseTelemetryTest, cls).resource_cleanup()
-
-    def await_samples(self, metric, query):
-        """This method is to wait for sample to add it to database.
-
-        There are long time delays when using Postgresql (or Mysql)
-        database as ceilometer backend
-        """
-        timeout = CONF.compute.build_timeout
-        start = timeutils.utcnow()
-        while timeutils.delta_seconds(start, timeutils.utcnow()) < timeout:
-            body = self.telemetry_client.list_samples(metric, query)
-            if body:
-                return body
-            time.sleep(CONF.compute.build_interval)
-
-        raise exceptions.TimeoutException(
-            'Sample for metric:%s with query:%s has not been added to the '
-            'database within %d seconds' % (metric, query,
-                                            CONF.compute.build_timeout))
-
-
-class BaseTelemetryAdminTest(BaseTelemetryTest):
-    """Base test case class for admin Telemetry API tests."""
-
-    credentials = ['primary', 'admin']
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseTelemetryAdminTest, cls).setup_clients()
-        cls.telemetry_admin_client = cls.os_adm.telemetry_client
-
-    def await_events(self, query):
-        timeout = CONF.compute.build_timeout
-        start = timeutils.utcnow()
-        while timeutils.delta_seconds(start, timeutils.utcnow()) < timeout:
-            body = self.telemetry_admin_client.list_events(query)
-            if body:
-                return body
-            time.sleep(CONF.compute.build_interval)
-
-        raise exceptions.TimeoutException(
-            'Event with query:%s has not been added to the '
-            'database within %d seconds' % (query, CONF.compute.build_timeout))
-
-
-class BaseAlarmingTest(tempest.test.BaseTestCase):
-    """Base test case class for all Alarming API tests."""
-
-    credentials = ['primary']
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaseAlarmingTest, cls).skip_checks()
-        if not CONF.service_available.aodh:
-            raise cls.skipException("Aodh support is required")
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseAlarmingTest, cls).setup_clients()
-        cls.alarming_client = cls.os.alarming_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(BaseAlarmingTest, cls).resource_setup()
-        cls.alarm_ids = []
-
-    @classmethod
-    def create_alarm(cls, **kwargs):
-        body = cls.alarming_client.create_alarm(
-            name=data_utils.rand_name('telemetry_alarm'),
-            type='threshold', **kwargs)
-        cls.alarm_ids.append(body['alarm_id'])
-        return body
-
-    @staticmethod
-    def cleanup_resources(method, list_of_ids):
-        for resource_id in list_of_ids:
-            try:
-                method(resource_id)
-            except lib_exc.NotFound:
-                pass
-
-    @classmethod
-    def resource_cleanup(cls):
-        cls.cleanup_resources(cls.alarming_client.delete_alarm, cls.alarm_ids)
-        super(BaseAlarmingTest, cls).resource_cleanup()
diff --git a/tempest/api/telemetry/test_alarming_api.py b/tempest/api/telemetry/test_alarming_api.py
deleted file mode 100644
index daa0939..0000000
--- a/tempest/api/telemetry/test_alarming_api.py
+++ /dev/null
@@ -1,111 +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_lib import exceptions as lib_exc
-
-from tempest.api.telemetry import base
-from tempest.common.utils import data_utils
-from tempest import test
-
-
-class TelemetryAlarmingAPITestJSON(base.BaseAlarmingTest):
-
-    @classmethod
-    def resource_setup(cls):
-        super(TelemetryAlarmingAPITestJSON, cls).resource_setup()
-        cls.rule = {'meter_name': 'cpu_util',
-                    'comparison_operator': 'gt',
-                    'threshold': 80.0,
-                    'period': 70}
-        for i in range(2):
-            cls.create_alarm(threshold_rule=cls.rule)
-
-    @test.idempotent_id('1c918e06-210b-41eb-bd45-14676dd77cd6')
-    def test_alarm_list(self):
-        # List alarms
-        alarm_list = self.alarming_client.list_alarms()
-
-        # Verify created alarm in the list
-        fetched_ids = [a['alarm_id'] for a in alarm_list]
-        missing_alarms = [a for a in self.alarm_ids if a not in fetched_ids]
-        self.assertEqual(0, len(missing_alarms),
-                         "Failed to find the following created alarm(s)"
-                         " in a fetched list: %s" %
-                         ', '.join(str(a) for a in missing_alarms))
-
-    @test.idempotent_id('1297b095-39c1-4e74-8a1f-4ae998cedd67')
-    def test_create_update_get_delete_alarm(self):
-        # Create an alarm
-        alarm_name = data_utils.rand_name('telemetry_alarm')
-        body = self.alarming_client.create_alarm(
-            name=alarm_name, type='threshold', threshold_rule=self.rule)
-        self.assertEqual(alarm_name, body['name'])
-        alarm_id = body['alarm_id']
-        self.assertDictContainsSubset(self.rule, body['threshold_rule'])
-        # Update alarm with new rule and new name
-        new_rule = {'meter_name': 'cpu',
-                    'comparison_operator': 'eq',
-                    'threshold': 70.0,
-                    'period': 60}
-        alarm_name_updated = data_utils.rand_name('telemetry-alarm-update')
-        body = self.alarming_client.update_alarm(
-            alarm_id,
-            threshold_rule=new_rule,
-            name=alarm_name_updated,
-            type='threshold')
-        self.assertEqual(alarm_name_updated, body['name'])
-        self.assertDictContainsSubset(new_rule, body['threshold_rule'])
-        # Get and verify details of an alarm after update
-        body = self.alarming_client.show_alarm(alarm_id)
-        self.assertEqual(alarm_name_updated, body['name'])
-        self.assertDictContainsSubset(new_rule, body['threshold_rule'])
-        # Get history for the alarm and verify the same
-        body = self.alarming_client.show_alarm_history(alarm_id)
-        self.assertEqual("rule change", body[0]['type'])
-        self.assertIn(alarm_name_updated, body[0]['detail'])
-        self.assertEqual("creation", body[1]['type'])
-        self.assertIn(alarm_name, body[1]['detail'])
-        # Delete alarm and verify if deleted
-        self.alarming_client.delete_alarm(alarm_id)
-        self.assertRaises(lib_exc.NotFound,
-                          self.alarming_client.show_alarm, alarm_id)
-
-    @test.idempotent_id('aca49486-70bb-4016-87e0-f6131374f741')
-    def test_set_get_alarm_state(self):
-        alarm_states = ['ok', 'alarm', 'insufficient data']
-        alarm = self.create_alarm(threshold_rule=self.rule)
-        # Set alarm state and verify
-        new_state =\
-            [elem for elem in alarm_states if elem != alarm['state']][0]
-        state = self.alarming_client.alarm_set_state(alarm['alarm_id'],
-                                                     new_state)
-        self.assertEqual(new_state, state.data)
-        # Get alarm state and verify
-        state = self.alarming_client.show_alarm_state(alarm['alarm_id'])
-        self.assertEqual(new_state, state.data)
-
-    @test.idempotent_id('08d7e45a-1344-4e5c-ba6f-f6cbb77f55b9')
-    def test_create_delete_alarm_with_combination_rule(self):
-        rule = {"alarm_ids": self.alarm_ids,
-                "operator": "or"}
-        # Verifies alarm create
-        alarm_name = data_utils.rand_name('combination_alarm')
-        body = self.alarming_client.create_alarm(name=alarm_name,
-                                                 combination_rule=rule,
-                                                 type='combination')
-        self.assertEqual(alarm_name, body['name'])
-        alarm_id = body['alarm_id']
-        self.assertDictContainsSubset(rule, body['combination_rule'])
-        # Verify alarm delete
-        self.alarming_client.delete_alarm(alarm_id)
-        self.assertRaises(lib_exc.NotFound,
-                          self.alarming_client.show_alarm, alarm_id)
diff --git a/tempest/api/telemetry/test_alarming_api_negative.py b/tempest/api/telemetry/test_alarming_api_negative.py
deleted file mode 100644
index e945556..0000000
--- a/tempest/api/telemetry/test_alarming_api_negative.py
+++ /dev/null
@@ -1,71 +0,0 @@
-#    Copyright 2015 GlobalLogic.  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.telemetry import base
-from tempest.common.utils import data_utils
-from tempest import test
-from tempest_lib import exceptions as lib_exc
-
-import uuid
-
-
-class TelemetryAlarmingNegativeTest(base.BaseAlarmingTest):
-    """Negative tests for show_alarm, update_alarm, show_alarm_history tests
-
-        ** show non-existent alarm
-        ** show the deleted alarm
-        ** delete deleted alarm
-        ** update deleted alarm
-    """
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('668743d5-08ad-4480-b2b8-15da34f81e7d')
-    def test_get_non_existent_alarm(self):
-        # get the non-existent alarm
-        non_existent_id = str(uuid.uuid4())
-        self.assertRaises(lib_exc.NotFound, self.alarming_client.show_alarm,
-                          non_existent_id)
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('ef45000d-0a72-4781-866d-4cb7bf2582ad')
-    def test_get_update_show_history_delete_deleted_alarm(self):
-        # get, update and delete the deleted alarm
-        alarm_name = data_utils.rand_name('telemetry_alarm')
-        rule = {'meter_name': 'cpu',
-                'comparison_operator': 'eq',
-                'threshold': 100.0,
-                'period': 90}
-        body = self.alarming_client.create_alarm(
-            name=alarm_name,
-            type='threshold',
-            threshold_rule=rule)
-        alarm_id = body['alarm_id']
-        self.alarming_client.delete_alarm(alarm_id)
-        # get the deleted alarm
-        self.assertRaises(lib_exc.NotFound, self.alarming_client.show_alarm,
-                          alarm_id)
-
-        # update the deleted alarm
-        updated_alarm_name = data_utils.rand_name('telemetry_alarm_updated')
-        updated_rule = {'meter_name': 'cpu_new',
-                        'comparison_operator': 'eq',
-                        'threshold': 70,
-                        'period': 50}
-        self.assertRaises(lib_exc.NotFound, self.alarming_client.update_alarm,
-                          alarm_id, threshold_rule=updated_rule,
-                          name=updated_alarm_name,
-                          type='threshold')
-        # delete the deleted alarm
-        self.assertRaises(lib_exc.NotFound, self.alarming_client.delete_alarm,
-                          alarm_id)
diff --git a/tempest/api/telemetry/test_telemetry_notification_api.py b/tempest/api/telemetry/test_telemetry_notification_api.py
deleted file mode 100644
index a575125..0000000
--- a/tempest/api/telemetry/test_telemetry_notification_api.py
+++ /dev/null
@@ -1,84 +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_lib import decorators
-import testtools
-
-from tempest.api.telemetry import base
-from tempest import config
-from tempest import test
-
-CONF = config.CONF
-
-
-class TelemetryNotificationAPITestJSON(base.BaseTelemetryTest):
-
-    @test.idempotent_id('d7f8c1c8-d470-4731-8604-315d3956caad')
-    @test.services('compute')
-    def test_check_nova_notification(self):
-
-        body = self.create_server()
-
-        query = ('resource', 'eq', body['id'])
-
-        for metric in self.nova_notifications:
-            self.await_samples(metric, query)
-
-    @test.attr(type="smoke")
-    @test.idempotent_id('04b10bfe-a5dc-47af-b22f-0460426bf498')
-    @test.services("image")
-    @testtools.skipIf(not CONF.image_feature_enabled.api_v1,
-                      "Glance api v1 is disabled")
-    def test_check_glance_v1_notifications(self):
-        body = self.create_image(self.image_client, is_public=False)
-        self.image_client.update_image(body['id'], data='data')
-
-        query = 'resource', 'eq', body['id']
-
-        self.image_client.delete_image(body['id'])
-
-        for metric in self.glance_notifications:
-            self.await_samples(metric, query)
-
-    @test.attr(type="smoke")
-    @test.idempotent_id('c240457d-d943-439b-8aea-85e26d64fe8e')
-    @test.services("image")
-    @testtools.skipIf(not CONF.image_feature_enabled.api_v2,
-                      "Glance api v2 is disabled")
-    def test_check_glance_v2_notifications(self):
-        body = self.create_image(self.image_client_v2, visibility='private')
-
-        self.image_client_v2.store_image_file(body['id'], "file")
-        self.image_client_v2.show_image_file(body['id'])
-
-        query = 'resource', 'eq', body['id']
-
-        for metric in self.glance_v2_notifications:
-            self.await_samples(metric, query)
-
-
-class TelemetryNotificationAdminAPITestJSON(base.BaseTelemetryAdminTest):
-
-    @test.idempotent_id('29604198-8b45-4fc0-8af8-1cae4f94ebe9')
-    @test.services('compute')
-    @decorators.skip_because(bug='1480490')
-    def test_check_nova_notification_event_and_meter(self):
-
-        body = self.create_server()
-
-        if CONF.telemetry_feature_enabled.events:
-            query = ('instance_id', 'eq', body['id'])
-            self.await_events(query)
-
-        query = ('resource', 'eq', body['id'])
-        for metric in self.nova_notifications:
-            self.await_samples(metric, query)
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..5703313 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
 
@@ -31,14 +32,9 @@
     @classmethod
     def resource_setup(cls):
         super(VolumeMultiBackendV2Test, cls).resource_setup()
-        # support 2 backends names, deprecated_for_removal.
-        # keep support 2 backend names, in case they are not empty
-        if CONF.volume.backend1_name and CONF.volume.backend2_name:
-            cls.backend_names = {CONF.volume.backend1_name,
-                                 CONF.volume.backend2_name}
-        else:
-            # read backend name from a list .
-            cls.backend_names = set(CONF.volume.backend_names)
+
+        # read backend name from a list .
+        cls.backend_names = set(CONF.volume.backend_names)
 
         cls.name_field = cls.special_fields['name_field']
         cls.volume_type_id_list = []
@@ -57,31 +53,30 @@
             cls._create_type_and_volume(backend_name, True)
 
     @classmethod
-    def _create_type_and_volume(self, backend_name_key, with_prefix):
+    def _create_type_and_volume(cls, backend_name_key, with_prefix):
         # Volume/Type creation
-        type_name = data_utils.rand_name('Type')
-        vol_name = data_utils.rand_name('Volume')
+        type_name = data_utils.rand_name(cls.__name__ + '-Type')
+        vol_name = data_utils.rand_name(cls.__name__ + '-Volume')
         spec_key_with_prefix = "capabilities:volume_backend_name"
         spec_key_without_prefix = "volume_backend_name"
         if with_prefix:
             extra_specs = {spec_key_with_prefix: backend_name_key}
         else:
             extra_specs = {spec_key_without_prefix: backend_name_key}
-        self.type = self.volume_types_client.create_volume_type(
-            name=type_name, extra_specs=extra_specs)['volume_type']
-        self.volume_type_id_list.append(self.type['id'])
+        cls.type = cls.create_volume_type(name=type_name,
+                                          extra_specs=extra_specs)
 
-        params = {self.name_field: vol_name, 'volume_type': type_name}
-
-        self.volume = self.admin_volume_client.create_volume(
+        params = {cls.name_field: vol_name, 'volume_type': type_name,
+                  'size': CONF.volume.volume_size}
+        cls.volume = cls.admin_volume_client.create_volume(
             **params)['volume']
         if with_prefix:
-            self.volume_id_list_with_prefix.append(self.volume['id'])
+            cls.volume_id_list_with_prefix.append(cls.volume['id'])
         else:
-            self.volume_id_list_without_prefix.append(
-                self.volume['id'])
-        self.admin_volume_client.wait_for_volume_status(
-            self.volume['id'], 'available')
+            cls.volume_id_list_without_prefix.append(
+                cls.volume['id'])
+        waiters.wait_for_volume_status(cls.admin_volume_client,
+                                       cls.volume['id'], 'available')
 
     @classmethod
     def resource_cleanup(cls):
@@ -96,11 +91,6 @@
             cls.admin_volume_client.delete_volume(volume_id)
             cls.admin_volume_client.wait_for_resource_deletion(volume_id)
 
-        # volume types deletion
-        volume_type_id_list = getattr(cls, 'volume_type_id_list', [])
-        for volume_type_id in volume_type_id_list:
-            cls.volume_types_client.delete_volume_type(volume_type_id)
-
         super(VolumeMultiBackendV2Test, cls).resource_cleanup()
 
     @test.idempotent_id('c1a41f3f-9dad-493e-9f09-3ff197d477cc')
@@ -139,7 +129,7 @@
         volume1_host = volume['os-vol-host-attr:host']
         msg = ("multi-backend reporting incorrect values for volume %s" %
                volume_id)
-        self.assertTrue(len(volume1_host.split("@")) > 1, msg)
+        self.assertGreater(len(volume1_host.split("@")), 1, msg)
 
     def _test_backend_name_distinction(self, volume_id_list):
         # this test checks that the volumes created at setUp don't
diff --git a/tempest/api/volume/admin/test_qos.py b/tempest/api/volume/admin/test_qos.py
new file mode 100644
index 0000000..9275d2b
--- /dev/null
+++ b/tempest/api/volume/admin/test_qos.py
@@ -0,0 +1,163 @@
+# 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.volume import base
+from tempest.common.utils import data_utils as utils
+from tempest.common import waiters
+from tempest import test
+
+
+class QosSpecsV2TestJSON(base.BaseVolumeAdminTest):
+    """Test the Cinder QoS-specs.
+
+    Tests for  create, list, delete, show, associate,
+    disassociate, set/unset key V2 APIs.
+    """
+
+    @classmethod
+    def resource_setup(cls):
+        super(QosSpecsV2TestJSON, cls).resource_setup()
+        # Create admin qos client
+        # Create a test shared qos-specs for tests
+        cls.qos_name = utils.rand_name(cls.__name__ + '-QoS')
+        cls.qos_consumer = 'front-end'
+
+        cls.created_qos = cls.create_test_qos_specs(cls.qos_name,
+                                                    cls.qos_consumer,
+                                                    read_iops_sec='2000')
+
+    def _create_delete_test_qos_with_given_consumer(self, consumer):
+        name = utils.rand_name(self.__class__.__name__ + '-qos')
+        qos = {'name': name, 'consumer': consumer}
+        body = self.create_test_qos_specs(name, consumer)
+        for key in ['name', 'consumer']:
+            self.assertEqual(qos[key], body[key])
+
+        self.admin_volume_qos_client.delete_qos(body['id'])
+        self.admin_volume_qos_client.wait_for_resource_deletion(body['id'])
+
+        # validate the deletion
+        list_qos = self.admin_volume_qos_client.list_qos()['qos_specs']
+        self.assertNotIn(body, list_qos)
+
+    def _test_associate_qos(self, vol_type_id):
+        self.admin_volume_qos_client.associate_qos(
+            self.created_qos['id'], vol_type_id)
+
+    @test.idempotent_id('7e15f883-4bef-49a9-95eb-f94209a1ced1')
+    def test_create_delete_qos_with_front_end_consumer(self):
+        """Tests the creation and deletion of QoS specs
+
+        With consumer as front end
+        """
+        self._create_delete_test_qos_with_given_consumer('front-end')
+
+    @test.idempotent_id('b115cded-8f58-4ee4-aab5-9192cfada08f')
+    def test_create_delete_qos_with_back_end_consumer(self):
+        """Tests the creation and deletion of QoS specs
+
+        With consumer as back-end
+        """
+        self._create_delete_test_qos_with_given_consumer('back-end')
+
+    @test.idempotent_id('f88d65eb-ea0d-487d-af8d-71f4011575a4')
+    def test_create_delete_qos_with_both_consumer(self):
+        """Tests the creation and deletion of QoS specs
+
+        With consumer as both front end and back end
+        """
+        self._create_delete_test_qos_with_given_consumer('both')
+
+    @test.idempotent_id('7aa214cc-ac1a-4397-931f-3bb2e83bb0fd')
+    def test_get_qos(self):
+        """Tests the detail of a given qos-specs"""
+        body = self.admin_volume_qos_client.show_qos(
+            self.created_qos['id'])['qos_specs']
+        self.assertEqual(self.qos_name, body['name'])
+        self.assertEqual(self.qos_consumer, body['consumer'])
+
+    @test.idempotent_id('75e04226-bcf7-4595-a34b-fdf0736f38fc')
+    def test_list_qos(self):
+        """Tests the list of all qos-specs"""
+        body = self.admin_volume_qos_client.list_qos()['qos_specs']
+        self.assertIn(self.created_qos, body)
+
+    @test.idempotent_id('ed00fd85-4494-45f2-8ceb-9e2048919aed')
+    def test_set_unset_qos_key(self):
+        """Test the addition of a specs key to qos-specs"""
+        args = {'iops_bytes': '500'}
+        body = self.admin_volume_qos_client.set_qos_key(
+            self.created_qos['id'],
+            iops_bytes='500')['qos_specs']
+        self.assertEqual(args, body)
+        body = self.admin_volume_qos_client.show_qos(
+            self.created_qos['id'])['qos_specs']
+        self.assertEqual(args['iops_bytes'], body['specs']['iops_bytes'])
+
+        # test the deletion of a specs key from qos-specs
+        keys = ['iops_bytes']
+        self.admin_volume_qos_client.unset_qos_key(self.created_qos['id'],
+                                                   keys)
+        operation = 'qos-key-unset'
+        waiters.wait_for_qos_operations(self.admin_volume_qos_client,
+                                        self.created_qos['id'],
+                                        operation, keys)
+        body = self.admin_volume_qos_client.show_qos(
+            self.created_qos['id'])['qos_specs']
+        self.assertNotIn(keys[0], body['specs'])
+
+    @test.idempotent_id('1dd93c76-6420-485d-a771-874044c416ac')
+    def test_associate_disassociate_qos(self):
+        """Test the following operations :
+
+        1. associate_qos
+        2. get_association_qos
+        3. disassociate_qos
+        4. disassociate_all_qos
+        """
+
+        # create a test volume-type
+        vol_type = []
+        for _ in range(0, 3):
+            vol_type.append(self.create_volume_type())
+
+        # associate the qos-specs with volume-types
+        for i in range(0, 3):
+            self._test_associate_qos(vol_type[i]['id'])
+
+        # get the association of the qos-specs
+        body = self.admin_volume_qos_client.show_association_qos(
+            self.created_qos['id'])['qos_associations']
+        associations = [association['id'] for association in body]
+        for i in range(0, 3):
+            self.assertIn(vol_type[i]['id'], associations)
+
+        # disassociate a volume-type with qos-specs
+        self.admin_volume_qos_client.disassociate_qos(
+            self.created_qos['id'], vol_type[0]['id'])
+        operation = 'disassociate'
+        waiters.wait_for_qos_operations(self.admin_volume_qos_client,
+                                        self.created_qos['id'], operation,
+                                        vol_type[0]['id'])
+
+        # disassociate all volume-types from qos-specs
+        self.admin_volume_qos_client.disassociate_all_qos(
+            self.created_qos['id'])
+        operation = 'disassociate-all'
+        waiters.wait_for_qos_operations(self.admin_volume_qos_client,
+                                        self.created_qos['id'], operation)
+
+
+class QosSpecsV1TestJSON(QosSpecsV2TestJSON):
+    _api_version = 1
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index f2bf613..29a161b 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
 from tempest import config
 from tempest import test
 
@@ -29,42 +28,14 @@
             raise cls.skipException("Cinder snapshot feature disabled")
 
     @classmethod
-    def setup_clients(cls):
-        super(SnapshotsActionsV2Test, cls).setup_clients()
-        cls.client = cls.snapshots_client
-
-    @classmethod
     def resource_setup(cls):
         super(SnapshotsActionsV2Test, cls).resource_setup()
 
         # Create a test shared volume for tests
-        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.create_volume()
 
         # 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')
-
-    @classmethod
-    def resource_cleanup(cls):
-        # Delete the test snapshot
-        cls.client.delete_snapshot(cls.snapshot['id'])
-        cls.client.wait_for_resource_deletion(cls.snapshot['id'])
-
-        # Delete the test volume
-        cls.volumes_client.delete_volume(cls.volume['id'])
-        cls.volumes_client.wait_for_resource_deletion(cls.volume['id'])
-
-        super(SnapshotsActionsV2Test, cls).resource_cleanup()
+        cls.snapshot = cls.create_snapshot(volume_id=cls.volume['id'])
 
     def tearDown(self):
         # Set snapshot's status to available after test
@@ -83,7 +54,7 @@
                 reset_snapshot_status(temp_snapshot['id'], status)
         self.admin_snapshots_client.\
             force_delete_snapshot(temp_snapshot['id'])
-        self.client.wait_for_resource_deletion(temp_snapshot['id'])
+        self.snapshots_client.wait_for_resource_deletion(temp_snapshot['id'])
 
     def _get_progress_alias(self):
         return 'os-extended-snapshot-attributes:progress'
@@ -109,8 +80,9 @@
         progress = '80%'
         status = 'error'
         progress_alias = self._get_progress_alias()
-        self.client.update_snapshot_status(self.snapshot['id'],
-                                           status=status, progress=progress)
+        self.snapshots_client.update_snapshot_status(self.snapshot['id'],
+                                                     status=status,
+                                                     progress=progress)
         snapshot_get = self.admin_snapshots_client.show_snapshot(
             self.snapshot['id'])['snapshot']
         self.assertEqual(status, snapshot_get['status'])
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index b28488a..f6de9a6 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -21,9 +21,9 @@
 
     @test.idempotent_id('d5f3efa2-6684-4190-9ced-1c2f526352ad')
     def test_list_hosts(self):
-        hosts = self.hosts_client.list_hosts()['hosts']
-        self.assertTrue(len(hosts) >= 2, "No. of hosts are < 2,"
-                        "response of list hosts is: % s" % hosts)
+        hosts = self.admin_hosts_client.list_hosts()['hosts']
+        self.assertGreaterEqual(len(hosts), 2, "No. of hosts are < 2,"
+                                "response of list hosts is: % s" % hosts)
 
 
 class VolumeHostsAdminV1TestsJSON(VolumeHostsAdminV2TestsJSON):
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index b2e52bb..7d8c94d 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -12,34 +12,36 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
-
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import test
 
-QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes']
+QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes', 'backups']
 QUOTA_USAGE_KEYS = ['reserved', 'limit', 'in_use']
 
 
 class BaseVolumeQuotasAdminV2TestJSON(base.BaseVolumeAdminTest):
     force_tenant_isolation = True
 
+    credentials = ['primary', 'alt', 'admin']
+
     @classmethod
     def setup_credentials(cls):
         super(BaseVolumeQuotasAdminV2TestJSON, cls).setup_credentials()
         cls.demo_tenant_id = cls.os.credentials.tenant_id
+        cls.alt_client = cls.os_alt.volumes_client
 
     @test.idempotent_id('59eada70-403c-4cef-a2a3-a8ce2f1b07a0')
     def test_list_quotas(self):
-        quotas = (self.quotas_client.show_quota_set(self.demo_tenant_id)
+        quotas = (self.admin_quotas_client.show_quota_set(self.demo_tenant_id)
                   ['quota_set'])
         for key in QUOTA_KEYS:
             self.assertIn(key, quotas)
 
     @test.idempotent_id('2be020a2-5fdd-423d-8d35-a7ffbc36e9f7')
     def test_list_default_quotas(self):
-        quotas = self.quotas_client.show_default_quota_set(
+        quotas = self.admin_quotas_client.show_default_quota_set(
             self.demo_tenant_id)['quota_set']
         for key in QUOTA_KEYS:
             self.assertIn(key, quotas)
@@ -47,21 +49,22 @@
     @test.idempotent_id('3d45c99e-cc42-4424-a56e-5cbd212b63a6')
     def test_update_all_quota_resources_for_tenant(self):
         # Admin can update all the resource quota limits for a tenant
-        default_quota_set = self.quotas_client.show_default_quota_set(
+        default_quota_set = self.admin_quotas_client.show_default_quota_set(
             self.demo_tenant_id)['quota_set']
         new_quota_set = {'gigabytes': 1009,
                          'volumes': 11,
-                         'snapshots': 11}
+                         'snapshots': 11,
+                         'backups': 11}
 
         # Update limits for all quota resources
-        quota_set = self.quotas_client.update_quota_set(
+        quota_set = self.admin_quotas_client.update_quota_set(
             self.demo_tenant_id,
             **new_quota_set)['quota_set']
 
         cleanup_quota_set = dict(
-            (k, v) for k, v in six.iteritems(default_quota_set)
+            (k, v) for k, v in default_quota_set.items()
             if k in QUOTA_KEYS)
-        self.addCleanup(self.quotas_client.update_quota_set,
+        self.addCleanup(self.admin_quotas_client.update_quota_set,
                         self.demo_tenant_id, **cleanup_quota_set)
         # test that the specific values we set are actually in
         # the final result. There is nothing here that ensures there
@@ -70,8 +73,9 @@
 
     @test.idempotent_id('18c51ae9-cb03-48fc-b234-14a19374dbed')
     def test_show_quota_usage(self):
-        quota_usage = self.quotas_client.show_quota_usage(
-            self.os_adm.credentials.tenant_id)['quota_set']
+        quota_usage = self.admin_quotas_client.show_quota_set(
+            self.os_adm.credentials.tenant_id,
+            params={'usage': True})['quota_set']
         for key in QUOTA_KEYS:
             self.assertIn(key, quota_usage)
             for usage_key in QUOTA_USAGE_KEYS:
@@ -79,15 +83,15 @@
 
     @test.idempotent_id('ae8b6091-48ad-4bfa-a188-bbf5cc02115f')
     def test_quota_usage(self):
-        quota_usage = self.quotas_client.show_quota_usage(
-            self.demo_tenant_id)['quota_set']
+        quota_usage = self.admin_quotas_client.show_quota_set(
+            self.demo_tenant_id, params={'usage': True})['quota_set']
 
         volume = self.create_volume()
-        self.addCleanup(self.admin_volume_client.delete_volume,
-                        volume['id'])
+        self.addCleanup(self.delete_volume,
+                        self.admin_volume_client, volume['id'])
 
-        new_quota_usage = self.quotas_client.show_quota_usage(
-            self.demo_tenant_id)['quota_set']
+        new_quota_usage = self.admin_quotas_client.show_quota_set(
+            self.demo_tenant_id, params={'usage': True})['quota_set']
 
         self.assertEqual(quota_usage['volumes']['in_use'] + 1,
                          new_quota_usage['volumes']['in_use'])
@@ -105,18 +109,67 @@
                                                      description=description)
         project_id = project['id']
         self.addCleanup(self.identity_utils.delete_project, project_id)
-        quota_set_default = self.quotas_client.show_default_quota_set(
+        quota_set_default = self.admin_quotas_client.show_default_quota_set(
             project_id)['quota_set']
         volume_default = quota_set_default['volumes']
 
-        self.quotas_client.update_quota_set(project_id,
-                                            volumes=(int(volume_default) + 5))
+        self.admin_quotas_client.update_quota_set(
+            project_id, volumes=(int(volume_default) + 5))
 
-        self.quotas_client.delete_quota_set(project_id)
-        quota_set_new = (self.quotas_client.show_quota_set(project_id)
+        self.admin_quotas_client.delete_quota_set(project_id)
+        quota_set_new = (self.admin_quotas_client.show_quota_set(project_id)
                          ['quota_set'])
         self.assertEqual(volume_default, quota_set_new['volumes'])
 
+    @test.idempotent_id('8911036f-9d54-4720-80cc-a1c9796a8805')
+    def test_quota_usage_after_volume_transfer(self):
+        # Create a volume for transfer
+        volume = self.create_volume()
+        self.addCleanup(self.delete_volume,
+                        self.admin_volume_client, volume['id'])
+
+        # List of tenants quota usage pre-transfer
+        primary_quota = self.admin_quotas_client.show_quota_set(
+            self.demo_tenant_id, params={'usage': True})['quota_set']
+
+        alt_quota = self.admin_quotas_client.show_quota_set(
+            self.alt_client.tenant_id, params={'usage': True})['quota_set']
+
+        # Creates a volume transfer
+        transfer = self.volumes_client.create_volume_transfer(
+            volume_id=volume['id'])['transfer']
+        transfer_id = transfer['id']
+        auth_key = transfer['auth_key']
+
+        # Accepts a volume transfer
+        self.alt_client.accept_volume_transfer(
+            transfer_id, auth_key=auth_key)['transfer']
+
+        # Verify volume transferred is available
+        waiters.wait_for_volume_status(
+            self.alt_client, volume['id'], 'available')
+
+        # List of tenants quota usage post transfer
+        new_primary_quota = self.admin_quotas_client.show_quota_set(
+            self.demo_tenant_id, params={'usage': True})['quota_set']
+
+        new_alt_quota = self.admin_quotas_client.show_quota_set(
+            self.alt_client.tenant_id, params={'usage': True})['quota_set']
+
+        # Verify tenants quota usage was updated
+        self.assertEqual(primary_quota['volumes']['in_use'] -
+                         new_primary_quota['volumes']['in_use'],
+                         new_alt_quota['volumes']['in_use'] -
+                         alt_quota['volumes']['in_use'])
+
+        self.assertEqual(alt_quota['gigabytes']['in_use'] +
+                         volume['size'],
+                         new_alt_quota['gigabytes']['in_use'])
+
+        self.assertEqual(primary_quota['gigabytes']['in_use'] -
+                         volume['size'],
+                         new_primary_quota['gigabytes']['in_use'])
+
 
 class VolumeQuotasAdminV1TestJSON(BaseVolumeQuotasAdminV2TestJSON):
     _api_version = 1
diff --git a/tempest/api/volume/admin/test_volume_quotas_negative.py b/tempest/api/volume/admin/test_volume_quotas_negative.py
index 9185553..c19b1c4 100644
--- a/tempest/api/volume/admin/test_volume_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_quotas_negative.py
@@ -13,10 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.volume import base
 from tempest import config
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
@@ -33,13 +32,12 @@
     @classmethod
     def resource_setup(cls):
         super(BaseVolumeQuotasNegativeV2TestJSON, cls).resource_setup()
-        cls.default_volume_size = cls.volumes_client.default_volume_size
-        cls.shared_quota_set = {'gigabytes': 2 * cls.default_volume_size,
+        cls.shared_quota_set = {'gigabytes': 2 * CONF.volume.volume_size,
                                 'volumes': 1}
 
         # NOTE(gfidente): no need to restore original quota set
         # after the tests as they only work with dynamic credentials.
-        cls.quotas_client.update_quota_set(
+        cls.admin_quotas_client.update_quota_set(
             cls.demo_tenant_id,
             **cls.shared_quota_set)
 
@@ -51,7 +49,8 @@
     @test.idempotent_id('bf544854-d62a-47f2-a681-90f7a47d86b6')
     def test_quota_volumes(self):
         self.assertRaises(lib_exc.OverLimit,
-                          self.volumes_client.create_volume)
+                          self.volumes_client.create_volume,
+                          size=CONF.volume.volume_size)
 
     @test.attr(type='negative')
     @test.idempotent_id('2dc27eee-8659-4298-b900-169d71a91374')
@@ -59,16 +58,17 @@
         # NOTE(gfidente): quota set needs to be changed for this test
         # or we may be limited by the volumes or snaps quota number, not by
         # actual gigs usage; next line ensures shared set is restored.
-        self.addCleanup(self.quotas_client.update_quota_set,
+        self.addCleanup(self.admin_quotas_client.update_quota_set,
                         self.demo_tenant_id,
                         **self.shared_quota_set)
-        new_quota_set = {'gigabytes': self.default_volume_size,
+        new_quota_set = {'gigabytes': CONF.volume.volume_size,
                          'volumes': 2, 'snapshots': 1}
-        self.quotas_client.update_quota_set(
+        self.admin_quotas_client.update_quota_set(
             self.demo_tenant_id,
             **new_quota_set)
         self.assertRaises(lib_exc.OverLimit,
-                          self.volumes_client.create_volume)
+                          self.volumes_client.create_volume,
+                          size=CONF.volume.volume_size)
 
 
 class VolumeQuotasNegativeV1TestJSON(BaseVolumeQuotasNegativeV2TestJSON):
diff --git a/tempest/api/volume/admin/test_volume_services.py b/tempest/api/volume/admin/test_volume_services.py
index 2b7ee45..165874b 100644
--- a/tempest/api/volume/admin/test_volume_services.py
+++ b/tempest/api/volume/admin/test_volume_services.py
@@ -12,11 +12,20 @@
 #    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.volume import base
+from tempest import config
 from tempest import test
 
 
+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.
 
@@ -28,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')
@@ -48,7 +60,7 @@
     @test.idempotent_id('178710e4-7596-4e08-9333-745cb8bc4f8d')
     def test_get_service_by_host_name(self):
         services_on_host = [service for service in self.services if
-                            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'])
@@ -67,8 +79,8 @@
         services = (self.admin_volume_services_client.list_services(
             host=self.host_name, binary=self.binary_name))['services']
 
-        self.assertEqual(1, len(services))
-        self.assertEqual(self.host_name, services[0]['host'])
+        self.assertNotEqual(0, len(services))
+        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_snapshot_quotas_negative.py b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
index c66207f..09af7fe 100644
--- a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
@@ -13,10 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.volume import base
 from tempest import config
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
@@ -39,13 +38,13 @@
     @classmethod
     def resource_setup(cls):
         super(VolumeSnapshotQuotasNegativeV2TestJSON, cls).resource_setup()
-        cls.default_volume_size = cls.volumes_client.default_volume_size
+        cls.default_volume_size = CONF.volume.volume_size
         cls.shared_quota_set = {'gigabytes': 3 * cls.default_volume_size,
                                 'volumes': 1, 'snapshots': 1}
 
         # NOTE(gfidente): no need to restore original quota set
         # after the tests as they only work with tenant isolation.
-        cls.quotas_client.update_quota_set(
+        cls.admin_quotas_client.update_quota_set(
             cls.demo_tenant_id,
             **cls.shared_quota_set)
 
@@ -64,12 +63,12 @@
     @test.attr(type='negative')
     @test.idempotent_id('c99a1ca9-6cdf-498d-9fdf-25832babef27')
     def test_quota_volume_gigabytes_snapshots(self):
-        self.addCleanup(self.quotas_client.update_quota_set,
+        self.addCleanup(self.admin_quotas_client.update_quota_set,
                         self.demo_tenant_id,
                         **self.shared_quota_set)
         new_quota_set = {'gigabytes': 2 * self.default_volume_size,
                          'volumes': 1, 'snapshots': 2}
-        self.quotas_client.update_quota_set(
+        self.admin_quotas_client.update_quota_set(
             self.demo_tenant_id,
             **new_quota_set)
         self.assertRaises(lib_exc.OverLimit,
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index c032d9c..3d44bbe 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
 
@@ -23,55 +24,48 @@
 
 class VolumeTypesV2Test(base.BaseVolumeAdminTest):
 
-    def _delete_volume(self, volume_id):
-        self.volumes_client.delete_volume(volume_id)
-        self.volumes_client.wait_for_resource_deletion(volume_id)
-
-    def _delete_volume_type(self, volume_type_id):
-        self.volume_types_client.delete_volume_type(volume_type_id)
-
     @test.idempotent_id('9d9b28e3-1b2e-4483-a2cc-24aa0ea1de54')
     def test_volume_type_list(self):
         # List volume types.
-        body = self.volume_types_client.list_volume_types()['volume_types']
+        body = \
+            self.admin_volume_types_client.list_volume_types()['volume_types']
         self.assertIsInstance(body, list)
 
     @test.idempotent_id('c03cc62c-f4e9-4623-91ec-64ce2f9c1260')
     def test_volume_crud_with_volume_type_and_extra_specs(self):
         # Create/update/get/delete volume with volume_type and extra spec.
         volume_types = list()
-        vol_name = data_utils.rand_name("volume")
+        vol_name = data_utils.rand_name(self.__class__.__name__ + '-volume')
         self.name_field = self.special_fields['name_field']
         proto = CONF.volume.storage_protocol
         vendor = CONF.volume.vendor_name
         extra_specs = {"storage_protocol": proto,
                        "vendor_name": vendor}
         # Create two volume_types
-        for i in range(2):
-            vol_type_name = data_utils.rand_name("volume-type")
-            vol_type = self.volume_types_client.create_volume_type(
-                name=vol_type_name,
-                extra_specs=extra_specs)['volume_type']
+        for _ in range(2):
+            vol_type = self.create_volume_type(
+                extra_specs=extra_specs)
             volume_types.append(vol_type)
-            self.addCleanup(self._delete_volume_type, vol_type['id'])
         params = {self.name_field: vol_name,
-                  'volume_type': volume_types[0]['id']}
+                  'volume_type': volume_types[0]['id'],
+                  'size': CONF.volume.volume_size}
 
         # Create volume
-        volume = self.volumes_client.create_volume(**params)['volume']
-        self.addCleanup(self._delete_volume, volume['id'])
+        volume = self.create_volume(**params)
         self.assertEqual(volume_types[0]['name'], volume["volume_type"])
         self.assertEqual(volume[self.name_field], vol_name,
                          "The created volume name is not equal "
                          "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(
@@ -91,23 +85,24 @@
     def test_volume_type_create_get_delete(self):
         # Create/get volume type.
         body = {}
-        name = data_utils.rand_name("volume-type")
+        name = data_utils.rand_name(self.__class__.__name__ + '-volume-type')
+        description = data_utils.rand_name("volume-type-description")
         proto = CONF.volume.storage_protocol
         vendor = CONF.volume.vendor_name
         extra_specs = {"storage_protocol": proto,
                        "vendor_name": vendor}
-        body = self.volume_types_client.create_volume_type(
-            name=name,
-            extra_specs=extra_specs)['volume_type']
-        self.assertIn('id', body)
-        self.addCleanup(self._delete_volume_type, body['id'])
+        body = self.create_volume_type(description=description, name=name,
+                                       extra_specs=extra_specs)
         self.assertIn('name', body)
-        self.assertEqual(body['name'], name,
+        self.assertEqual(name, body['name'],
                          "The created volume_type name is not equal "
                          "to the requested name")
-        self.assertTrue(body['id'] is not None,
-                        "Field volume_type id is empty or not found.")
-        fetched_volume_type = self.volume_types_client.show_volume_type(
+        self.assertEqual(description, body['description'],
+                         "The created volume_type_description name is "
+                         "not equal to the requested name")
+        self.assertIsNotNone(body['id'],
+                             "Field volume_type id is empty or not found.")
+        fetched_volume_type = self.admin_volume_types_client.show_volume_type(
             body['id'])['volume_type']
         self.assertEqual(name, fetched_volume_type['name'],
                          'The fetched Volume_type is different '
@@ -124,15 +119,12 @@
         # Create/get/delete encryption type.
         provider = "LuksEncryptor"
         control_location = "front-end"
-        name = data_utils.rand_name("volume-type")
-        body = self.volume_types_client.create_volume_type(
-            name=name)['volume_type']
-        self.addCleanup(self._delete_volume_type, body['id'])
-
+        body = self.create_volume_type()
         # Create encryption type
-        encryption_type = self.volume_types_client.create_encryption_type(
-            body['id'], provider=provider,
-            control_location=control_location)['encryption']
+        encryption_type = \
+            self.admin_encryption_types_client.create_encryption_type(
+                body['id'], provider=provider,
+                control_location=control_location)['encryption']
         self.assertIn('volume_type_id', encryption_type)
         self.assertEqual(provider, encryption_type['provider'],
                          "The created encryption_type provider is not equal "
@@ -143,7 +135,7 @@
 
         # Get encryption type
         fetched_encryption_type = (
-            self.volume_types_client.show_encryption_type(
+            self.admin_encryption_types_client.show_encryption_type(
                 encryption_type['volume_type_id']))
         self.assertEqual(provider,
                          fetched_encryption_type['provider'],
@@ -155,16 +147,35 @@
                          'different from the created encryption_type')
 
         # Delete encryption type
-        self.volume_types_client.delete_encryption_type(
-            encryption_type['volume_type_id'])
-        resource = {"id": encryption_type['volume_type_id'],
-                    "type": "encryption-type"}
-        self.volume_types_client.wait_for_resource_deletion(resource)
+        type_id = encryption_type['volume_type_id']
+        self.admin_encryption_types_client.delete_encryption_type(type_id)
+        self.admin_encryption_types_client.wait_for_resource_deletion(type_id)
         deleted_encryption_type = (
-            self.volume_types_client.show_encryption_type(
-                encryption_type['volume_type_id']))
+            self.admin_encryption_types_client.show_encryption_type(type_id))
         self.assertEmpty(deleted_encryption_type)
 
+    @test.idempotent_id('cf9f07c6-db9e-4462-a243-5933ad65e9c8')
+    def test_volume_type_update(self):
+        # Create volume type
+        volume_type = self.create_volume_type()
+
+        # New volume type details
+        name = data_utils.rand_name("volume-type")
+        description = data_utils.rand_name("volume-type-description")
+        is_public = not volume_type['is_public']
+
+        # Update volume type details
+        kwargs = {'name': name,
+                  'description': description,
+                  'is_public': is_public}
+        updated_vol_type = self.admin_volume_types_client.update_volume_type(
+            volume_type['id'], **kwargs)['volume_type']
+
+        # Verify volume type details were updated
+        self.assertEqual(name, updated_vol_type['name'])
+        self.assertEqual(description, updated_vol_type['description'])
+        self.assertEqual(is_public, updated_vol_type['is_public'])
+
 
 class VolumeTypesV1Test(VolumeTypesV2Test):
     _api_version = 1
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs.py b/tempest/api/volume/admin/test_volume_types_extra_specs.py
index 502cd86..fdff2df 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -23,24 +23,17 @@
     @classmethod
     def resource_setup(cls):
         super(VolumeTypesExtraSpecsV2Test, cls).resource_setup()
-        vol_type_name = data_utils.rand_name('Volume-type')
-        cls.volume_type = cls.volume_types_client.create_volume_type(
-            name=vol_type_name)['volume_type']
-
-    @classmethod
-    def resource_cleanup(cls):
-        cls.volume_types_client.delete_volume_type(cls.volume_type['id'])
-        super(VolumeTypesExtraSpecsV2Test, cls).resource_cleanup()
+        cls.volume_type = cls.create_volume_type()
 
     @test.idempotent_id('b42923e9-0452-4945-be5b-d362ae533e60')
     def test_volume_type_extra_specs_list(self):
         # List Volume types extra specs.
         extra_specs = {"spec1": "val1"}
-        body = self.volume_types_client.create_volume_type_extra_specs(
+        body = self.admin_volume_types_client.create_volume_type_extra_specs(
             self.volume_type['id'], extra_specs)['extra_specs']
         self.assertEqual(extra_specs, body,
                          "Volume type extra spec incorrectly created")
-        body = self.volume_types_client.list_volume_types_extra_specs(
+        body = self.admin_volume_types_client.list_volume_types_extra_specs(
             self.volume_type['id'])['extra_specs']
         self.assertIsInstance(body, dict)
         self.assertIn('spec1', body)
@@ -49,39 +42,41 @@
     def test_volume_type_extra_specs_update(self):
         # Update volume type extra specs
         extra_specs = {"spec2": "val1"}
-        body = self.volume_types_client.create_volume_type_extra_specs(
+        body = self.admin_volume_types_client.create_volume_type_extra_specs(
             self.volume_type['id'], extra_specs)['extra_specs']
         self.assertEqual(extra_specs, body,
                          "Volume type extra spec incorrectly created")
-
-        extra_spec = {"spec2": "val2"}
-        body = self.volume_types_client.update_volume_type_extra_specs(
-            self.volume_type['id'],
-            extra_spec.keys()[0],
-            extra_spec)
-        self.assertIn('spec2', body)
-        self.assertEqual(extra_spec['spec2'], body['spec2'],
+        spec_key = "spec2"
+        extra_spec = {spec_key: "val2"}
+        body = self.admin_volume_types_client.update_volume_type_extra_specs(
+            self.volume_type['id'], spec_key, extra_spec)
+        self.assertIn(spec_key, body)
+        self.assertEqual(extra_spec[spec_key], body[spec_key],
                          "Volume type extra spec incorrectly updated")
 
     @test.idempotent_id('d4772798-601f-408a-b2a5-29e8a59d1220')
     def test_volume_type_extra_spec_create_get_delete(self):
         # Create/Get/Delete volume type extra spec.
-        extra_specs = {"spec3": "val1"}
-        body = self.volume_types_client.create_volume_type_extra_specs(
+        spec_key = "spec3"
+        extra_specs = {spec_key: "val1"}
+        body = self.admin_volume_types_client.create_volume_type_extra_specs(
             self.volume_type['id'],
             extra_specs)['extra_specs']
         self.assertEqual(extra_specs, body,
                          "Volume type extra spec incorrectly created")
 
-        self.volume_types_client.show_volume_type_extra_specs(
+        body = self.admin_volume_types_client.show_volume_type_extra_specs(
             self.volume_type['id'],
-            extra_specs.keys()[0])
+            spec_key)
         self.assertEqual(extra_specs, body,
                          "Volume type extra spec incorrectly fetched")
 
-        self.volume_types_client.delete_volume_type_extra_specs(
-            self.volume_type['id'],
-            extra_specs.keys()[0])
+        self.admin_volume_types_client.delete_volume_type_extra_specs(
+            self.volume_type['id'], spec_key)
+        self.assertRaises(
+            lib_exc.NotFound,
+            self.admin_volume_types_client.show_volume_type_extra_specs,
+            self.volume_type['id'], spec_key)
 
 
 class VolumeTypesExtraSpecsV1Test(VolumeTypesExtraSpecsV2Test):
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
index 6483af3..8040322 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
@@ -13,12 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -27,25 +24,16 @@
     @classmethod
     def resource_setup(cls):
         super(ExtraSpecsNegativeV2Test, cls).resource_setup()
-        vol_type_name = data_utils.rand_name('Volume-type')
         cls.extra_specs = {"spec1": "val1"}
-        cls.volume_type = cls.volume_types_client.create_volume_type(
-            name=vol_type_name,
-            extra_specs=cls.extra_specs)['volume_type']
-
-    @classmethod
-    def resource_cleanup(cls):
-        cls.volume_types_client.delete_volume_type(cls.volume_type['id'])
-        super(ExtraSpecsNegativeV2Test, cls).resource_cleanup()
+        cls.volume_type = cls.create_volume_type(extra_specs=cls.extra_specs)
 
     @test.idempotent_id('08961d20-5cbb-4910-ac0f-89ad6dbb2da1')
     def test_update_no_body(self):
         # Should not update volume type extra specs with no body
-        extra_spec = {"spec1": "val2"}
         self.assertRaises(
             lib_exc.BadRequest,
-            self.volume_types_client.update_volume_type_extra_specs,
-            self.volume_type['id'], extra_spec.keys()[0], None)
+            self.admin_volume_types_client.update_volume_type_extra_specs,
+            self.volume_type['id'], "spec1", None)
 
     @test.idempotent_id('25e5a0ee-89b3-4c53-8310-236f76c75365')
     def test_update_nonexistent_extra_spec_id(self):
@@ -53,8 +41,8 @@
         extra_spec = {"spec1": "val2"}
         self.assertRaises(
             lib_exc.BadRequest,
-            self.volume_types_client.update_volume_type_extra_specs,
-            self.volume_type['id'], str(uuid.uuid4()),
+            self.admin_volume_types_client.update_volume_type_extra_specs,
+            self.volume_type['id'], data_utils.rand_uuid(),
             extra_spec)
 
     @test.idempotent_id('9bf7a657-b011-4aec-866d-81c496fbe5c8')
@@ -63,7 +51,7 @@
         extra_spec = {"spec1": "val2"}
         self.assertRaises(
             lib_exc.BadRequest,
-            self.volume_types_client.update_volume_type_extra_specs,
+            self.admin_volume_types_client.update_volume_type_extra_specs,
             self.volume_type['id'], None, extra_spec)
 
     @test.idempotent_id('a77dfda2-9100-448e-9076-ed1711f4bdfc')
@@ -73,8 +61,8 @@
         extra_spec = {"spec1": "val2", "spec2": "val1"}
         self.assertRaises(
             lib_exc.BadRequest,
-            self.volume_types_client.update_volume_type_extra_specs,
-            self.volume_type['id'], extra_spec.keys()[0],
+            self.admin_volume_types_client.update_volume_type_extra_specs,
+            self.volume_type['id'], list(extra_spec)[0],
             extra_spec)
 
     @test.idempotent_id('49d5472c-a53d-4eab-a4d3-450c4db1c545')
@@ -84,15 +72,15 @@
         extra_specs = {"spec2": "val1"}
         self.assertRaises(
             lib_exc.NotFound,
-            self.volume_types_client.create_volume_type_extra_specs,
-            str(uuid.uuid4()), extra_specs)
+            self.admin_volume_types_client.create_volume_type_extra_specs,
+            data_utils.rand_uuid(), extra_specs)
 
     @test.idempotent_id('c821bdc8-43a4-4bf4-86c8-82f3858d5f7d')
     def test_create_none_body(self):
         # Should not create volume type extra spec for none POST body.
         self.assertRaises(
             lib_exc.BadRequest,
-            self.volume_types_client.create_volume_type_extra_specs,
+            self.admin_volume_types_client.create_volume_type_extra_specs,
             self.volume_type['id'], None)
 
     @test.idempotent_id('bc772c71-1ed4-4716-b945-8b5ed0f15e87')
@@ -100,35 +88,33 @@
         # Should not create volume type extra spec for invalid POST body.
         self.assertRaises(
             lib_exc.BadRequest,
-            self.volume_types_client.create_volume_type_extra_specs,
+            self.admin_volume_types_client.create_volume_type_extra_specs,
             self.volume_type['id'], extra_specs=['invalid'])
 
     @test.idempotent_id('031cda8b-7d23-4246-8bf6-bbe73fd67074')
     def test_delete_nonexistent_volume_type_id(self):
         # Should not delete volume type extra spec for nonexistent
-            # type id.
-        extra_specs = {"spec1": "val1"}
+        # type id.
         self.assertRaises(
             lib_exc.NotFound,
-            self.volume_types_client.delete_volume_type_extra_specs,
-            str(uuid.uuid4()), extra_specs.keys()[0])
+            self.admin_volume_types_client.delete_volume_type_extra_specs,
+            data_utils.rand_uuid(), "spec1")
 
     @test.idempotent_id('dee5cf0c-cdd6-4353-b70c-e847050d71fb')
     def test_list_nonexistent_volume_type_id(self):
         # Should not list volume type extra spec for nonexistent type id.
         self.assertRaises(
             lib_exc.NotFound,
-            self.volume_types_client.list_volume_types_extra_specs,
-            str(uuid.uuid4()))
+            self.admin_volume_types_client.list_volume_types_extra_specs,
+            data_utils.rand_uuid())
 
     @test.idempotent_id('9f402cbd-1838-4eb4-9554-126a6b1908c9')
     def test_get_nonexistent_volume_type_id(self):
         # Should not get volume type extra spec for nonexistent type id.
-        extra_specs = {"spec1": "val1"}
         self.assertRaises(
             lib_exc.NotFound,
-            self.volume_types_client.show_volume_type_extra_specs,
-            str(uuid.uuid4()), extra_specs.keys()[0])
+            self.admin_volume_types_client.show_volume_type_extra_specs,
+            data_utils.rand_uuid(), "spec1")
 
     @test.idempotent_id('c881797d-12ff-4f1a-b09d-9f6212159753')
     def test_get_nonexistent_extra_spec_id(self):
@@ -136,8 +122,8 @@
             # id.
         self.assertRaises(
             lib_exc.NotFound,
-            self.volume_types_client.show_volume_type_extra_specs,
-            self.volume_type['id'], str(uuid.uuid4()))
+            self.admin_volume_types_client.show_volume_type_extra_specs,
+            self.volume_type['id'], data_utils.rand_uuid())
 
 
 class ExtraSpecsNegativeV1Test(ExtraSpecsNegativeV2Test):
diff --git a/tempest/api/volume/admin/test_volume_types_negative.py b/tempest/api/volume/admin/test_volume_types_negative.py
index bc32fc9..857e7d2 100644
--- a/tempest/api/volume/admin/test_volume_types_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_negative.py
@@ -13,11 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.volume import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
@@ -27,30 +25,31 @@
     def test_create_with_nonexistent_volume_type(self):
         # Should not be able to create volume with nonexistent volume_type.
         self.name_field = self.special_fields['name_field']
-        params = {self.name_field: str(uuid.uuid4()),
-                  'volume_type': str(uuid.uuid4())}
+        params = {self.name_field: data_utils.rand_uuid(),
+                  'volume_type': data_utils.rand_uuid()}
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.create_volume, **params)
 
     @test.idempotent_id('878b4e57-faa2-4659-b0d1-ce740a06ae81')
     def test_create_with_empty_name(self):
         # Should not be able to create volume type with an empty name.
-        self.assertRaises(lib_exc.BadRequest,
-                          self.volume_types_client.create_volume_type, name='')
+        self.assertRaises(
+            lib_exc.BadRequest,
+            self.admin_volume_types_client.create_volume_type, name='')
 
     @test.idempotent_id('994610d6-0476-4018-a644-a2602ef5d4aa')
     def test_get_nonexistent_type_id(self):
         # Should not be able to get volume type with nonexistent type id.
         self.assertRaises(lib_exc.NotFound,
-                          self.volume_types_client.show_volume_type,
-                          str(uuid.uuid4()))
+                          self.admin_volume_types_client.show_volume_type,
+                          data_utils.rand_uuid())
 
     @test.idempotent_id('6b3926d2-7d73-4896-bc3d-e42dfd11a9f6')
     def test_delete_nonexistent_type_id(self):
         # Should not be able to delete volume type with nonexistent type id.
         self.assertRaises(lib_exc.NotFound,
-                          self.volume_types_client.delete_volume_type,
-                          str(uuid.uuid4()))
+                          self.admin_volume_types_client.delete_volume_type,
+                          data_utils.rand_uuid())
 
 
 class VolumeTypesNegativeV1Test(VolumeTypesNegativeV2Test):
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index 253a3e1..a63cbf0 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -14,72 +14,30 @@
 #    under the License.
 
 from tempest.api.volume import base
-from tempest.common.utils import data_utils as utils
 from tempest import test
 
 
 class VolumesActionsV2Test(base.BaseVolumeAdminTest):
 
-    @classmethod
-    def setup_clients(cls):
-        super(VolumesActionsV2Test, cls).setup_clients()
-        cls.client = cls.volumes_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(VolumesActionsV2Test, cls).resource_setup()
-
-        # Create a test shared volume for tests
-        vol_name = utils.rand_name(cls.__name__ + '-Volume')
-        cls.name_field = cls.special_fields['name_field']
-        params = {cls.name_field: vol_name}
-
-        cls.volume = cls.client.create_volume(**params)['volume']
-        cls.client.wait_for_volume_status(cls.volume['id'], 'available')
-
-    @classmethod
-    def resource_cleanup(cls):
-        # Delete the test volume
-        cls.client.delete_volume(cls.volume['id'])
-        cls.client.wait_for_resource_deletion(cls.volume['id'])
-
-        super(VolumesActionsV2Test, cls).resource_cleanup()
-
-    def _reset_volume_status(self, volume_id, status):
-        # Reset the volume status
-        body = self.admin_volume_client.reset_volume_status(volume_id,
-                                                            status=status)
-        return body
-
-    def tearDown(self):
-        # Set volume's status to available after test
-        self._reset_volume_status(self.volume['id'], status='available')
-        super(VolumesActionsV2Test, self).tearDown()
-
-    def _create_temp_volume(self):
-        # Create a temp volume for force delete tests
-        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')
-
-        return temp_volume
-
     def _create_reset_and_force_delete_temp_volume(self, status=None):
         # Create volume, reset volume status, and force delete temp volume
-        temp_volume = self._create_temp_volume()
+        temp_volume = self.create_volume()
         if status:
-            self._reset_volume_status(temp_volume['id'], status)
+            self.admin_volume_client.reset_volume_status(
+                temp_volume['id'], status=status)
         self.admin_volume_client.force_delete_volume(temp_volume['id'])
-        self.client.wait_for_resource_deletion(temp_volume['id'])
+        self.volumes_client.wait_for_resource_deletion(temp_volume['id'])
 
     @test.idempotent_id('d063f96e-a2e0-4f34-8b8a-395c42de1845')
     def test_volume_reset_status(self):
         # test volume reset status : available->error->available
-        self._reset_volume_status(self.volume['id'], 'error')
-        volume_get = self.admin_volume_client.show_volume(
-            self.volume['id'])['volume']
-        self.assertEqual('error', volume_get['status'])
+        volume = self.create_volume()
+        for status in ['error', 'available']:
+            self.admin_volume_client.reset_volume_status(
+                volume['id'], status=status)
+            volume_get = self.admin_volume_client.show_volume(
+                volume['id'])['volume']
+            self.assertEqual(status, volume_get['status'])
 
     @test.idempotent_id('21737d5a-92f2-46d7-b009-a0cc0ee7a570')
     def test_volume_force_delete_when_volume_is_creating(self):
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 4b2d3f3..7591612 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -13,85 +13,62 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import decorators
+from oslo_serialization import base64
+from oslo_serialization import jsonutils as json
 
 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
 
 CONF = config.CONF
 
 
-class VolumesBackupsV2Test(base.BaseVolumeAdminTest):
+class VolumesBackupsAdminV2Test(base.BaseVolumeAdminTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesBackupsV2Test, cls).skip_checks()
+        super(VolumesBackupsAdminV2Test, cls).skip_checks()
         if not CONF.volume_feature_enabled.backup:
             raise cls.skipException("Cinder backup feature disabled")
-
-    @classmethod
-    def resource_setup(cls):
-        super(VolumesBackupsV2Test, cls).resource_setup()
-
-        cls.volume = cls.create_volume()
+        if not CONF.service_available.swift:
+            skip_msg = ("%s skipped as swift is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
 
     def _delete_backup(self, backup_id):
-        self.backups_adm_client.delete_backup(backup_id)
-        self.backups_adm_client.wait_for_backup_deletion(backup_id)
+        self.admin_backups_client.delete_backup(backup_id)
+        self.admin_backups_client.wait_for_resource_deletion(backup_id)
 
-    @test.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
-    def test_volume_backup_create_get_detailed_list_restore_delete(self):
-        # Create backup
-        backup_name = data_utils.rand_name('Backup')
-        create_backup = self.backups_adm_client.create_backup
-        backup = create_backup(volume_id=self.volume['id'],
-                               name=backup_name)['backup']
-        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')
-        self.backups_adm_client.wait_for_backup_status(backup['id'],
-                                                       'available')
+    def _decode_url(self, backup_url):
+        return json.loads(base64.decode_as_text(backup_url))
 
-        # Get a given backup
-        backup = self.backups_adm_client.show_backup(backup['id'])['backup']
-        self.assertEqual(backup_name, backup['name'])
+    def _encode_backup(self, backup):
+        retval = json.dumps(backup)
+        return base64.encode_as_text(retval)
 
-        # Get all backups with detail
-        backups = self.backups_adm_client.list_backups(detail=True)['backups']
-        self.assertIn((backup['name'], backup['id']),
-                      [(m['name'], m['id']) for m in backups])
+    def _modify_backup_url(self, backup_url, changes):
+        backup = self._decode_url(backup_url)
+        backup.update(changes)
+        return self._encode_backup(backup)
 
-        # Restore backup
-        restore = self.backups_adm_client.restore_backup(
-            backup['id'])['restore']
-
-        # Delete backup
-        self.addCleanup(self.admin_volume_client.delete_volume,
-                        restore['volume_id'])
-        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')
-
-    @decorators.skip_because(bug='1455043')
     @test.idempotent_id('a99c54a1-dd80-4724-8a13-13bf58d4068d')
     def test_volume_backup_export_import(self):
+        """Test backup export import functionality.
+
+        Cinder allows exporting DB backup information through its API so it can
+        be imported back in case of a DB loss.
+        """
+        volume = self.create_volume()
         # Create backup
-        backup_name = data_utils.rand_name('Backup')
-        backup = (self.backups_adm_client.create_backup(
-            volume_id=self.volume['id'], name=backup_name)['backup'])
-        self.addCleanup(self._delete_backup, backup['id'])
+        backup_name = data_utils.rand_name(self.__class__.__name__ + '-Backup')
+        backup = (self.create_backup(backup_client=self.admin_backups_client,
+                                     volume_id=volume['id'],
+                                     name=backup_name))
         self.assertEqual(backup_name, backup['name'])
-        self.backups_adm_client.wait_for_backup_status(backup['id'],
-                                                       'available')
 
         # Export Backup
-        export_backup = (self.backups_adm_client.export_backup(backup['id'])
+        export_backup = (self.admin_backups_client.export_backup(backup['id'])
                          ['backup-record'])
         self.assertIn('backup_service', export_backup)
         self.assertIn('backup_url', export_backup)
@@ -99,34 +76,67 @@
                         'cinder.backup.drivers'))
         self.assertIsNotNone(export_backup['backup_url'])
 
+        # NOTE(geguileo): Backups are imported with the same backup id
+        # (important for incremental backups among other things), so we cannot
+        # import the exported backup information as it is, because that Backup
+        # ID already exists.  So we'll fake the data by changing the backup id
+        # in the exported backup DB info we have retrieved before importing it
+        # back.
+        new_id = data_utils.rand_uuid()
+        new_url = self._modify_backup_url(
+            export_backup['backup_url'], {'id': new_id})
+
         # Import Backup
-        import_backup = self.backups_adm_client.import_backup(
+        import_backup = self.admin_backups_client.import_backup(
             backup_service=export_backup['backup_service'],
-            backup_url=export_backup['backup_url'])['backup']
-        self.addCleanup(self._delete_backup, import_backup['id'])
+            backup_url=new_url)['backup']
+
+        # NOTE(geguileo): We delete both backups, but only one of those
+        # deletions will delete data from the backup back-end because they
+        # were both pointing to the same backend data.
+        self.addCleanup(self._delete_backup, new_id)
         self.assertIn("id", import_backup)
-        self.backups_adm_client.wait_for_backup_status(import_backup['id'],
-                                                       'available')
+        self.assertEqual(new_id, import_backup['id'])
+        waiters.wait_for_backup_status(self.admin_backups_client,
+                                       import_backup['id'], 'available')
 
         # Verify Import Backup
-        backups = self.backups_adm_client.list_backups(detail=True)['backups']
-        self.assertIn(import_backup['id'], [b['id'] for b in backups])
+        backups = self.admin_backups_client.list_backups(
+            detail=True)['backups']
+        self.assertIn(new_id, [b['id'] for b in backups])
 
         # Restore backup
-        restore = (self.backups_adm_client.restore_backup(import_backup['id'])
-                   ['restore'])
+        restore = self.admin_backups_client.restore_backup(
+            backup['id'])['restore']
         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')
+        self.assertEqual(backup['id'], restore['backup_id'])
+        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']
         self.assertIn(restore['volume_id'], [v['id'] for v in volumes])
-        self.backups_adm_client.wait_for_backup_status(import_backup['id'],
-                                                       'available')
+        waiters.wait_for_backup_status(self.admin_backups_client,
+                                       import_backup['id'], 'available')
+
+    @test.idempotent_id('47a35425-a891-4e13-961c-c45deea21e94')
+    def test_volume_backup_reset_status(self):
+        # Create a volume
+        volume = self.create_volume()
+        # Create a backup
+        backup_name = data_utils.rand_name(
+            self.__class__.__name__ + '-Backup')
+        backup = self.create_backup(backup_client=self.admin_backups_client,
+                                    volume_id=volume['id'],
+                                    name=backup_name)
+        self.assertEqual(backup_name, backup['name'])
+        # Reset backup status to error
+        self.admin_backups_client.reset_backup_status(backup_id=backup['id'],
+                                                      status="error")
+        waiters.wait_for_backup_status(self.admin_backups_client,
+                                       backup['id'], 'error')
 
 
-class VolumesBackupsV1Test(VolumesBackupsV2Test):
+class VolumesBackupsAdminV1Test(VolumesBackupsAdminV2Test):
     _api_version = 1
diff --git a/tempest/api/baremetal/__init__.py b/tempest/api/volume/admin/v2/__init__.py
similarity index 100%
copy from tempest/api/baremetal/__init__.py
copy to tempest/api/volume/admin/v2/__init__.py
diff --git a/tempest/api/volume/admin/v2/test_backends_capabilities.py b/tempest/api/volume/admin/v2/test_backends_capabilities.py
new file mode 100644
index 0000000..9751845
--- /dev/null
+++ b/tempest/api/volume/admin/v2/test_backends_capabilities.py
@@ -0,0 +1,79 @@
+# Copyright 2016 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 operator
+
+from tempest.api.volume import base
+from tempest import test
+
+
+class BackendsCapabilitiesAdminV2TestsJSON(base.BaseVolumeAdminTest):
+
+    CAPABILITIES = ('namespace',
+                    'vendor_name',
+                    'volume_backend_name',
+                    'pool_name',
+                    'driver_version',
+                    'storage_protocol',
+                    'display_name',
+                    'description',
+                    'visibility',
+                    'properties')
+
+    @classmethod
+    def resource_setup(cls):
+        super(BackendsCapabilitiesAdminV2TestsJSON, cls).resource_setup()
+        # Get host list, formation: host@backend-name
+        cls.hosts = [
+            pool['name'] for pool in
+            cls.admin_scheduler_stats_client.list_pools()['pools']
+        ]
+
+    @test.idempotent_id('3750af44-5ea2-4cd4-bc3e-56e7e6caf854')
+    def test_get_capabilities_backend(self):
+        # Test backend properties
+        backend = self.admin_capabilities_client.show_backend_capabilities(
+            self.hosts[0])
+
+        # Verify getting capabilities parameters from a backend
+        for key in self.CAPABILITIES:
+            self.assertIn(key, backend)
+
+    @test.idempotent_id('a9035743-d46a-47c5-9cb7-3c80ea16dea0')
+    def test_compare_volume_stats_values(self):
+        # Test values comparison between show_backend_capabilities
+        # to show_pools
+        VOLUME_STATS = ('vendor_name',
+                        'volume_backend_name',
+                        'storage_protocol')
+
+        # Get list backend capabilities using show_pools
+        cinder_pools = [
+            pool['capabilities'] for pool in
+            self.admin_scheduler_stats_client.list_pools(detail=True)['pools']
+        ]
+
+        # Get list backends capabilities using show_backend_capabilities
+        capabilities = [
+            self.admin_capabilities_client.show_backend_capabilities(
+                host=host) for host in self.hosts
+        ]
+
+        # Returns a tuple of VOLUME_STATS values
+        expected_list = list(map(operator.itemgetter(*VOLUME_STATS),
+                             cinder_pools))
+        observed_list = list(map(operator.itemgetter(*VOLUME_STATS),
+                             capabilities))
+        self.assertEqual(expected_list, observed_list)
diff --git a/tempest/api/volume/admin/v2/test_snapshot_manage.py b/tempest/api/volume/admin/v2/test_snapshot_manage.py
new file mode 100644
index 0000000..6a3f9ee
--- /dev/null
+++ b/tempest/api/volume/admin/v2/test_snapshot_manage.py
@@ -0,0 +1,73 @@
+# Copyright 2016 Red Hat, Inc.
+# 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 testtools
+
+from tempest.api.volume import base
+from tempest.common import waiters
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class SnapshotManageAdminV2Test(base.BaseVolumeAdminTest):
+    """Unmanage & manage snapshots
+
+     This feature provides the ability to import/export volume snapshot
+     from one Cinder to another and to import snapshots that have not been
+     managed by Cinder from a storage back end to Cinder
+    """
+
+    @test.idempotent_id('0132f42d-0147-4b45-8501-cc504bbf7810')
+    @testtools.skipUnless(CONF.volume_feature_enabled.manage_snapshot,
+                          "Manage snapshot tests are disabled")
+    def test_unmanage_manage_snapshot(self):
+        # Create a volume
+        volume = self.create_volume()
+
+        # Create a snapshot
+        snapshot = self.create_snapshot(volume_id=volume['id'])
+
+        # Unmanage the snapshot
+        # Unmanage snapshot function works almost the same as delete snapshot,
+        # but it does not delete the snapshot data
+        self.admin_snapshots_client.unmanage_snapshot(snapshot['id'])
+        self.admin_snapshots_client.wait_for_resource_deletion(snapshot['id'])
+
+        # Fetch snapshot ids
+        snapshot_list = [
+            snap['id'] for snap in
+            self.snapshots_client.list_snapshots()['snapshots']
+        ]
+
+        # Verify snapshot does not exist in snapshot list
+        self.assertNotIn(snapshot['id'], snapshot_list)
+
+        # Manage the snapshot
+        snapshot_ref = '_snapshot-%s' % snapshot['id']
+        new_snapshot = self.admin_snapshot_manage_client.manage_snapshot(
+            volume_id=volume['id'],
+            ref={'source-name': snapshot_ref})['snapshot']
+        self.addCleanup(self.delete_snapshot,
+                        self.admin_snapshots_client, new_snapshot['id'])
+
+        # Wait for the snapshot to be available after manage operation
+        waiters.wait_for_snapshot_status(self.admin_snapshots_client,
+                                         new_snapshot['id'],
+                                         'available')
+
+        # Verify the managed snapshot has the expected parent volume
+        self.assertEqual(new_snapshot['volume_id'], volume['id'])
diff --git a/tempest/api/volume/admin/v2/test_volume_pools.py b/tempest/api/volume/admin/v2/test_volume_pools.py
new file mode 100644
index 0000000..8544a6a
--- /dev/null
+++ b/tempest/api/volume/admin/v2/test_volume_pools.py
@@ -0,0 +1,42 @@
+# Copyright 2016 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 tempest.api.volume import base
+from tempest import test
+
+
+class VolumePoolsAdminV2TestsJSON(base.BaseVolumeAdminTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumePoolsAdminV2TestsJSON, cls).resource_setup()
+        # Create a test shared volume for tests
+        cls.volume = cls.create_volume()
+
+    def _assert_host_volume_in_pools(self, with_detail=False):
+        volume_info = self.admin_volume_client.show_volume(
+            self.volume['id'])['volume']
+        cinder_pools = self.admin_volume_client.show_pools(
+            detail=with_detail)['pools']
+        self.assertIn(volume_info['os-vol-host-attr:host'],
+                      [pool['name'] for pool in cinder_pools])
+
+    @test.idempotent_id('0248a46c-e226-4933-be10-ad6fca8227e7')
+    def test_get_pools_without_details(self):
+        self._assert_host_volume_in_pools()
+
+    @test.idempotent_id('d4bb61f7-762d-4437-b8a4-5785759a0ced')
+    def test_get_pools_with_details(self):
+        self._assert_host_volume_in_pools(with_detail=True)
diff --git a/tempest/api/volume/admin/v2/test_volume_type_access.py b/tempest/api/volume/admin/v2/test_volume_type_access.py
new file mode 100644
index 0000000..80dbf12
--- /dev/null
+++ b/tempest/api/volume/admin/v2/test_volume_type_access.py
@@ -0,0 +1,92 @@
+# Copyright 2016 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 operator
+
+from tempest.api.volume import base
+from tempest import config
+from tempest.lib import exceptions as lib_exc
+from tempest import test
+
+CONF = config.CONF
+
+
+class VolumeTypesAccessV2Test(base.BaseVolumeAdminTest):
+
+    credentials = ['primary', 'alt', 'admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(VolumeTypesAccessV2Test, cls).setup_clients()
+        cls.alt_client = cls.os_alt.volumes_client
+
+    @test.idempotent_id('d4dd0027-835f-4554-a6e5-50903fb79184')
+    def test_volume_type_access_add(self):
+        # Creating a NON public volume type
+        params = {'os-volume-type-access:is_public': False}
+        volume_type = self.create_volume_type(**params)
+
+        # Try creating a volume from volume type in primary tenant
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
+                          volume_type=volume_type['id'],
+                          size=CONF.volume.volume_size)
+
+        # Adding volume type access for primary tenant
+        self.admin_volume_types_client.add_type_access(
+            volume_type['id'], project=self.volumes_client.tenant_id)
+        self.addCleanup(self.admin_volume_types_client.remove_type_access,
+                        volume_type['id'],
+                        project=self.volumes_client.tenant_id)
+
+        # Creating a volume from primary tenant
+        volume = self.create_volume(volume_type=volume_type['id'])
+        # Validating the created volume is based on the volume type
+        self.assertEqual(volume_type['name'], volume['volume_type'])
+
+    @test.idempotent_id('5220eb28-a435-43ce-baaf-ed46f0e95159')
+    def test_volume_type_access_list(self):
+        # Creating a NON public volume type
+        params = {'os-volume-type-access:is_public': False}
+        volume_type = self.create_volume_type(**params)
+
+        # Adding volume type access for primary tenant
+        self.admin_volume_types_client.add_type_access(
+            volume_type['id'], project=self.volumes_client.tenant_id)
+        self.addCleanup(self.admin_volume_types_client.remove_type_access,
+                        volume_type['id'],
+                        project=self.volumes_client.tenant_id)
+
+        # Adding volume type access for alt tenant
+        self.admin_volume_types_client.add_type_access(
+            volume_type['id'], project=self.alt_client.tenant_id)
+        self.addCleanup(self.admin_volume_types_client.remove_type_access,
+                        volume_type['id'],
+                        project=self.alt_client.tenant_id)
+
+        # List tenant access for the given volume type
+        type_access_list = self.admin_volume_types_client.list_type_access(
+            volume_type['id'])['volume_type_access']
+        volume_type_ids = [
+            vol_type['volume_type_id'] for vol_type in type_access_list
+        ]
+
+        # Validating volume type available for only two tenants
+        self.assertEqual(2, volume_type_ids.count(volume_type['id']))
+
+        # Validating the permitted tenants are the expected tenants
+        self.assertIn(self.volumes_client.tenant_id,
+                      map(operator.itemgetter('project_id'), type_access_list))
+        self.assertIn(self.alt_client.tenant_id,
+                      map(operator.itemgetter('project_id'), type_access_list))
diff --git a/tempest/api/volume/admin/v2/test_volumes_list.py b/tempest/api/volume/admin/v2/test_volumes_list.py
new file mode 100644
index 0000000..fd36d0a
--- /dev/null
+++ b/tempest/api/volume/admin/v2/test_volumes_list.py
@@ -0,0 +1,66 @@
+# Copyright 2016 Red Hat, Inc.
+# 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 operator
+
+from tempest.api.volume import base
+from tempest.common import waiters
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class VolumesListAdminV2TestJSON(base.BaseVolumeAdminTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumesListAdminV2TestJSON, cls).resource_setup()
+        # Create 3 test volumes
+        # NOTE(zhufl): When using pre-provisioned credentials, the project
+        # may have volumes other than those created below.
+        cls.volume_list = cls.volumes_client.list_volumes()['volumes']
+        for _ in range(3):
+            volume = cls.create_volume()
+            # Fetch volume details
+            volume_details = cls.volumes_client.show_volume(
+                volume['id'])['volume']
+            cls.volume_list.append(volume_details)
+
+    @test.idempotent_id('5866286f-3290-4cfd-a414-088aa6cdc469')
+    def test_volume_list_param_tenant(self):
+        # Test to list volumes from single tenant
+        # Create a volume in admin tenant
+        adm_vol = self.admin_volume_client.create_volume(
+            size=CONF.volume.volume_size)['volume']
+        waiters.wait_for_volume_status(self.admin_volume_client,
+                                       adm_vol['id'], 'available')
+        self.addCleanup(self.admin_volume_client.delete_volume, adm_vol['id'])
+        params = {'all_tenants': 1,
+                  'project_id': self.volumes_client.tenant_id}
+        # Getting volume list from primary tenant using admin credentials
+        fetched_list = self.admin_volume_client.list_volumes(
+            detail=True, params=params)['volumes']
+        # Verifying fetched volume ids list is related to primary tenant
+        fetched_list_ids = map(operator.itemgetter('id'), fetched_list)
+        expected_list_ids = map(operator.itemgetter('id'), self.volume_list)
+        self.assertEqual(sorted(expected_list_ids), sorted(fetched_list_ids))
+        # Verifying tenant id of volumes fetched list is related to
+        # primary tenant
+        fetched_tenant_id = [operator.itemgetter(
+            'os-vol-tenant-attr:tenant_id')(item) for item in fetched_list]
+        expected_tenant_id = [self.volumes_client.tenant_id] * \
+            len(self.volume_list)
+        self.assertEqual(expected_tenant_id, fetched_tenant_id)
diff --git a/tempest/api/baremetal/__init__.py b/tempest/api/volume/admin/v3/__init__.py
similarity index 100%
copy from tempest/api/baremetal/__init__.py
copy to tempest/api/volume/admin/v3/__init__.py
diff --git a/tempest/api/volume/admin/v3/test_user_messages.py b/tempest/api/volume/admin/v3/test_user_messages.py
new file mode 100755
index 0000000..257a434
--- /dev/null
+++ b/tempest/api/volume/admin/v3/test_user_messages.py
@@ -0,0 +1,87 @@
+# Copyright 2016 Andrew Kerr
+# 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.volume.v3 import base
+from tempest.common.utils import data_utils
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+MESSAGE_KEYS = [
+    'created_at',
+    'event_id',
+    'guaranteed_until',
+    'id',
+    'message_level',
+    'request_id',
+    'resource_type',
+    'resource_uuid',
+    'user_message',
+    'links']
+
+
+class UserMessagesTest(base.VolumesV3AdminTest):
+    min_microversion = '3.3'
+    max_microversion = 'latest'
+
+    def _create_user_message(self):
+        """Trigger a 'no valid host' situation to generate a message."""
+        bad_protocol = data_utils.rand_name('storage_protocol')
+        bad_vendor = data_utils.rand_name('vendor_name')
+        extra_specs = {'storage_protocol': bad_protocol,
+                       'vendor_name': bad_vendor}
+        vol_type_name = data_utils.rand_name(
+            self.__class__.__name__ + '-volume-type')
+        bogus_type = self.create_volume_type(
+            name=vol_type_name, extra_specs=extra_specs)
+        params = {'volume_type': bogus_type['id'],
+                  'size': CONF.volume.volume_size}
+        volume = self.create_volume(wait_until="error", **params)
+        messages = self.messages_client.list_messages()['messages']
+        message_id = None
+        for message in messages:
+            if message['resource_uuid'] == volume['id']:
+                message_id = message['id']
+                break
+        self.assertIsNotNone(message_id, 'No user message generated for '
+                                         'volume %s' % volume['id'])
+        return message_id
+
+    @test.idempotent_id('50f29e6e-f363-42e1-8ad1-f67ae7fd4d5a')
+    def test_list_messages(self):
+        self._create_user_message()
+        messages = self.messages_client.list_messages()['messages']
+        self.assertIsInstance(messages, list)
+        for message in messages:
+            for key in MESSAGE_KEYS:
+                self.assertIn(key, message.keys(),
+                              'Missing expected key %s' % key)
+
+    @test.idempotent_id('55a4a61e-c7b2-4ba0-a05d-b914bdef3070')
+    def test_show_message(self):
+        message_id = self._create_user_message()
+        self.addCleanup(self.messages_client.delete_message, message_id)
+
+        message = self.messages_client.show_message(message_id)['message']
+
+        for key in MESSAGE_KEYS:
+            self.assertIn(key, message.keys(), 'Missing expected key %s' % key)
+
+    @test.idempotent_id('c6eb6901-cdcc-490f-b735-4fe251842aed')
+    def test_delete_message(self):
+        message_id = self._create_user_message()
+        self.messages_client.delete_message(message_id)
+        self.messages_client.wait_for_resource_deletion(message_id)
diff --git a/tempest/api/volume/api_microversion_fixture.py b/tempest/api/volume/api_microversion_fixture.py
new file mode 100644
index 0000000..64bc537
--- /dev/null
+++ b/tempest/api/volume/api_microversion_fixture.py
@@ -0,0 +1,30 @@
+#
+# 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 fixtures
+
+from tempest.lib.services.volume.v3 import base_client
+
+
+class APIMicroversionFixture(fixtures.Fixture):
+
+    def __init__(self, volume_microversion):
+        self.volume_microversion = volume_microversion
+
+    def _setUp(self):
+        super(APIMicroversionFixture, self)._setUp()
+        base_client.VOLUME_MICROVERSION = self.volume_microversion
+        self.addCleanup(self._reset_volume_microversion)
+
+    def _reset_volume_microversion(self):
+        base_client.VOLUME_MICROVERSION = None
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index cc906e5..98e050e 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -13,12 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
-
 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.utils import test_utils
+from tempest.lib import exceptions
 import tempest.test
 
 CONF = config.CONF
@@ -45,9 +45,13 @@
             if not CONF.volume_feature_enabled.api_v2:
                 msg = "Volume API v2 is disabled"
                 raise cls.skipException(msg)
+        elif cls._api_version == 3:
+            if not CONF.volume_feature_enabled.api_v3:
+                msg = "Volume API v3 is disabled"
+                raise cls.skipException(msg)
         else:
             msg = ("Invalid Cinder API version (%s)" % cls._api_version)
-            raise exceptions.InvalidConfiguration(message=msg)
+            raise exceptions.InvalidConfiguration(msg)
 
     @classmethod
     def setup_credentials(cls):
@@ -69,12 +73,15 @@
             cls.volumes_extension_client = cls.os.volumes_extension_client
             cls.availability_zone_client = (
                 cls.os.volume_availability_zone_client)
+            cls.volume_limits_client = cls.os.volume_limits_client
         else:
             cls.snapshots_client = cls.os.snapshots_v2_client
             cls.volumes_client = cls.os.volumes_v2_client
+            cls.backups_client = cls.os.backups_v2_client
             cls.volumes_extension_client = cls.os.volumes_v2_extension_client
             cls.availability_zone_client = (
                 cls.os.volume_v2_availability_zone_client)
+            cls.volume_limits_client = cls.os.volume_v2_limits_client
 
     @classmethod
     def resource_setup(cls):
@@ -103,32 +110,85 @@
         super(BaseVolumeTest, cls).resource_cleanup()
 
     @classmethod
-    def create_volume(cls, **kwargs):
-        """Wrapper utility that returns a test volume."""
-        name = data_utils.rand_name('Volume')
+    def create_volume(cls, wait_until='available', **kwargs):
+        """Wrapper utility that returns a test volume.
+
+           :param wait_until: wait till volume status.
+        """
+        if 'size' not in kwargs:
+            kwargs['size'] = CONF.volume.volume_size
+
+        if 'imageRef' in kwargs:
+            image = cls.compute_images_client.show_image(
+                kwargs['imageRef'])['image']
+            min_disk = image.get('minDisk')
+            kwargs['size'] = max(kwargs['size'], min_disk)
 
         name_field = cls.special_fields['name_field']
+        if name_field not in kwargs:
+            name = data_utils.rand_name(cls.__name__ + '-Volume')
+            kwargs[name_field] = name
 
-        kwargs[name_field] = name
         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'],
+                                       wait_until)
         return volume
 
     @classmethod
     def create_snapshot(cls, volume_id=1, **kwargs):
         """Wrapper utility that returns a test snapshot."""
+        name_field = cls.special_fields['name_field']
+        if name_field not in kwargs:
+            name = data_utils.rand_name(cls.__name__ + '-Snapshot')
+            kwargs[name_field] = name
+
         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
 
+    def create_backup(self, volume_id, backup_client=None, **kwargs):
+        """Wrapper utility that returns a test backup."""
+        if backup_client is None:
+            backup_client = self.backups_client
+
+        backup = backup_client.create_backup(
+            volume_id=volume_id, **kwargs)['backup']
+        self.addCleanup(backup_client.delete_backup, backup['id'])
+        waiters.wait_for_backup_status(backup_client, backup['id'],
+                                       'available')
+        return backup
+
     # NOTE(afazekas): these create_* and clean_* could be defined
     # only in a single location in the source, and could be more general.
 
+    @staticmethod
+    def delete_volume(client, volume_id):
+        """Delete volume by the given client"""
+        client.delete_volume(volume_id)
+        client.wait_for_resource_deletion(volume_id)
+
+    @staticmethod
+    def delete_snapshot(client, snapshot_id):
+        """Delete snapshot by the given client"""
+        client.delete_snapshot(snapshot_id)
+        client.wait_for_resource_deletion(snapshot_id)
+
+    def attach_volume(self, server_id, volume_id):
+        """Attach a volume to a server"""
+        self.servers_client.attach_volume(
+            server_id, volumeId=volume_id,
+            device='/dev/%s' % CONF.compute.volume_device_name)
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume_id, 'in-use')
+        self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
+                        volume_id, 'available')
+        self.addCleanup(self.servers_client.detach_volume, server_id,
+                        volume_id)
+
     @classmethod
     def clear_volumes(cls):
         for volume in cls.volumes:
@@ -146,25 +206,31 @@
     @classmethod
     def clear_snapshots(cls):
         for snapshot in cls.snapshots:
-            try:
-                cls.snapshots_client.delete_snapshot(snapshot['id'])
-            except Exception:
-                pass
+            test_utils.call_and_ignore_notfound_exc(
+                cls.snapshots_client.delete_snapshot, snapshot['id'])
 
         for snapshot in cls.snapshots:
-            try:
-                cls.snapshots_client.wait_for_resource_deletion(snapshot['id'])
-            except Exception:
-                pass
+            test_utils.call_and_ignore_notfound_exc(
+                cls.snapshots_client.wait_for_resource_deletion,
+                snapshot['id'])
 
-    @classmethod
-    def create_server(cls, name, **kwargs):
-        tenant_network = cls.get_tenant_network()
+    def create_server(self, **kwargs):
+        name = kwargs.pop(
+            'name',
+            data_utils.rand_name(self.__class__.__name__ + '-instance'))
+
+        tenant_network = self.get_tenant_network()
         body, _ = compute.create_test_server(
-            cls.os,
+            self.os,
             tenant_network=tenant_network,
             name=name,
             **kwargs)
+
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        waiters.wait_for_server_termination,
+                        self.servers_client, body['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.servers_client.delete_server, body['id'])
         return body
 
 
@@ -178,59 +244,87 @@
         super(BaseVolumeAdminTest, cls).setup_clients()
 
         if cls._api_version == 1:
-            cls.volume_qos_client = cls.os_adm.volume_qos_client
+            cls.admin_volume_qos_client = cls.os_adm.volume_qos_client
             cls.admin_volume_services_client = \
                 cls.os_adm.volume_services_client
-            cls.volume_types_client = cls.os_adm.volume_types_client
+            cls.admin_volume_types_client = cls.os_adm.volume_types_client
             cls.admin_volume_client = cls.os_adm.volumes_client
-            cls.hosts_client = cls.os_adm.volume_hosts_client
+            cls.admin_hosts_client = cls.os_adm.volume_hosts_client
             cls.admin_snapshots_client = cls.os_adm.snapshots_client
-            cls.backups_adm_client = cls.os_adm.backups_client
-            cls.quotas_client = cls.os_adm.volume_quotas_client
+            cls.admin_backups_client = cls.os_adm.backups_client
+            cls.admin_encryption_types_client = \
+                cls.os_adm.encryption_types_client
+            cls.admin_quotas_client = cls.os_adm.volume_quotas_client
+            cls.admin_volume_limits_client = cls.os_adm.volume_limits_client
         elif cls._api_version == 2:
-            cls.volume_qos_client = cls.os_adm.volume_qos_v2_client
+            cls.admin_volume_qos_client = cls.os_adm.volume_qos_v2_client
             cls.admin_volume_services_client = \
                 cls.os_adm.volume_services_v2_client
-            cls.volume_types_client = cls.os_adm.volume_types_v2_client
+            cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
             cls.admin_volume_client = cls.os_adm.volumes_v2_client
-            cls.hosts_client = cls.os_adm.volume_hosts_v2_client
+            cls.admin_hosts_client = cls.os_adm.volume_hosts_v2_client
+            cls.admin_snapshot_manage_client = \
+                cls.os_adm.snapshot_manage_v2_client
             cls.admin_snapshots_client = cls.os_adm.snapshots_v2_client
-            cls.backups_adm_client = cls.os_adm.backups_v2_client
-            cls.quotas_client = cls.os_adm.volume_quotas_v2_client
+            cls.admin_backups_client = cls.os_adm.backups_v2_client
+            cls.admin_encryption_types_client = \
+                cls.os_adm.encryption_types_v2_client
+            cls.admin_quotas_client = cls.os_adm.volume_quotas_v2_client
+            cls.admin_volume_limits_client = cls.os_adm.volume_v2_limits_client
+            cls.admin_capabilities_client = \
+                cls.os_adm.volume_capabilities_v2_client
+            cls.admin_scheduler_stats_client = \
+                cls.os_adm.volume_scheduler_stats_v2_client
 
     @classmethod
     def resource_setup(cls):
         super(BaseVolumeAdminTest, cls).resource_setup()
 
         cls.qos_specs = []
+        cls.volume_types = []
 
     @classmethod
     def resource_cleanup(cls):
         cls.clear_qos_specs()
         super(BaseVolumeAdminTest, cls).resource_cleanup()
+        cls.clear_volume_types()
 
     @classmethod
     def create_test_qos_specs(cls, name=None, consumer=None, **kwargs):
         """create a test Qos-Specs."""
         name = name or data_utils.rand_name(cls.__name__ + '-QoS')
         consumer = consumer or 'front-end'
-        qos_specs = cls.volume_qos_client.create_qos(
+        qos_specs = cls.admin_volume_qos_client.create_qos(
             name=name, consumer=consumer, **kwargs)['qos_specs']
         cls.qos_specs.append(qos_specs['id'])
         return qos_specs
 
     @classmethod
+    def create_volume_type(cls, name=None, **kwargs):
+        """Create a test volume-type"""
+        name = name or data_utils.rand_name(cls.__name__ + '-volume-type')
+        volume_type = cls.admin_volume_types_client.create_volume_type(
+            name=name, **kwargs)['volume_type']
+        cls.volume_types.append(volume_type['id'])
+        return volume_type
+
+    @classmethod
     def clear_qos_specs(cls):
         for qos_id in cls.qos_specs:
-            try:
-                cls.volume_qos_client.delete_qos(qos_id)
-            except lib_exc.NotFound:
-                # The qos_specs may have already been deleted which is OK.
-                pass
+            test_utils.call_and_ignore_notfound_exc(
+                cls.admin_volume_qos_client.delete_qos, qos_id)
 
         for qos_id in cls.qos_specs:
-            try:
-                cls.volume_qos_client.wait_for_resource_deletion(qos_id)
-            except lib_exc.NotFound:
-                # The qos_specs may have already been deleted which is OK.
-                pass
+            test_utils.call_and_ignore_notfound_exc(
+                cls.admin_volume_qos_client.wait_for_resource_deletion, qos_id)
+
+    @classmethod
+    def clear_volume_types(cls):
+        for vol_type in cls.volume_types:
+            test_utils.call_and_ignore_notfound_exc(
+                cls.admin_volume_types_client.delete_volume_type, vol_type)
+
+        for vol_type in cls.volume_types:
+            test_utils.call_and_ignore_notfound_exc(
+                cls.admin_volume_types_client.wait_for_resource_deletion,
+                vol_type)
diff --git a/tempest/api/volume/test_availability_zone.py b/tempest/api/volume/test_availability_zone.py
index fe51375..ae4b8f9 100644
--- a/tempest/api/volume/test_availability_zone.py
+++ b/tempest/api/volume/test_availability_zone.py
@@ -30,7 +30,7 @@
         # List of availability zone
         availability_zone = (self.client.list_availability_zones()
                              ['availabilityZoneInfo'])
-        self.assertTrue(len(availability_zone) > 0)
+        self.assertGreater(len(availability_zone), 0)
 
 
 class AvailabilityZoneV1TestJSON(AvailabilityZoneV2TestJSON):
diff --git a/tempest/api/volume/test_extensions.py b/tempest/api/volume/test_extensions.py
index cce9ace..f044124 100644
--- a/tempest/api/volume/test_extensions.py
+++ b/tempest/api/volume/test_extensions.py
@@ -35,7 +35,7 @@
         if len(CONF.volume_feature_enabled.api_extensions) == 0:
             raise self.skipException('There are not any extensions configured')
         extension_list = [extension.get('alias') for extension in extensions]
-        LOG.debug("Cinder extensions: %s" % ','.join(extension_list))
+        LOG.debug("Cinder extensions: %s", ','.join(extension_list))
         ext = CONF.volume_feature_enabled.api_extensions[0]
         if ext == 'all':
             self.assertIn('Hosts', map(lambda x: x['name'], extensions))
diff --git a/tempest/api/volume/test_qos.py b/tempest/api/volume/test_qos.py
deleted file mode 100644
index 722a39a..0000000
--- a/tempest/api/volume/test_qos.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# 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.volume import base
-from tempest.common.utils import data_utils as utils
-from tempest import test
-
-
-class QosSpecsV2TestJSON(base.BaseVolumeAdminTest):
-    """Test the Cinder QoS-specs.
-
-    Tests for  create, list, delete, show, associate,
-    disassociate, set/unset key V2 APIs.
-    """
-
-    @classmethod
-    def resource_setup(cls):
-        super(QosSpecsV2TestJSON, cls).resource_setup()
-        # Create admin qos client
-        # Create a test shared qos-specs for tests
-        cls.qos_name = utils.rand_name(cls.__name__ + '-QoS')
-        cls.qos_consumer = 'front-end'
-
-        cls.created_qos = cls.create_test_qos_specs(cls.qos_name,
-                                                    cls.qos_consumer,
-                                                    read_iops_sec='2000')
-
-    def _create_delete_test_qos_with_given_consumer(self, consumer):
-        name = utils.rand_name('qos')
-        qos = {'name': name, 'consumer': consumer}
-        body = self.create_test_qos_specs(name, consumer)
-        for key in ['name', 'consumer']:
-            self.assertEqual(qos[key], body[key])
-
-        self.volume_qos_client.delete_qos(body['id'])
-        self.volume_qos_client.wait_for_resource_deletion(body['id'])
-
-        # validate the deletion
-        list_qos = self.volume_qos_client.list_qos()['qos_specs']
-        self.assertNotIn(body, list_qos)
-
-    def _create_test_volume_type(self):
-        vol_type_name = utils.rand_name("volume-type")
-        vol_type = self.volume_types_client.create_volume_type(
-            name=vol_type_name)['volume_type']
-        self.addCleanup(self.volume_types_client.delete_volume_type,
-                        vol_type['id'])
-        return vol_type
-
-    def _test_associate_qos(self, vol_type_id):
-        self.volume_qos_client.associate_qos(
-            self.created_qos['id'], vol_type_id)
-
-    def _test_get_association_qos(self):
-        body = self.volume_qos_client.show_association_qos(
-            self.created_qos['id'])['qos_associations']
-
-        associations = []
-        for association in body:
-            associations.append(association['id'])
-
-        return associations
-
-    @test.idempotent_id('7e15f883-4bef-49a9-95eb-f94209a1ced1')
-    def test_create_delete_qos_with_front_end_consumer(self):
-        """Tests the creation and deletion of QoS specs
-
-        With consumer as front end
-        """
-        self._create_delete_test_qos_with_given_consumer('front-end')
-
-    @test.idempotent_id('b115cded-8f58-4ee4-aab5-9192cfada08f')
-    def test_create_delete_qos_with_back_end_consumer(self):
-        """Tests the creation and deletion of QoS specs
-
-        With consumer as back-end
-        """
-        self._create_delete_test_qos_with_given_consumer('back-end')
-
-    @test.idempotent_id('f88d65eb-ea0d-487d-af8d-71f4011575a4')
-    def test_create_delete_qos_with_both_consumer(self):
-        """Tests the creation and deletion of QoS specs
-
-        With consumer as both front end and back end
-        """
-        self._create_delete_test_qos_with_given_consumer('both')
-
-    @test.idempotent_id('7aa214cc-ac1a-4397-931f-3bb2e83bb0fd')
-    def test_get_qos(self):
-        """Tests the detail of a given qos-specs"""
-        body = self.volume_qos_client.show_qos(
-            self.created_qos['id'])['qos_specs']
-        self.assertEqual(self.qos_name, body['name'])
-        self.assertEqual(self.qos_consumer, body['consumer'])
-
-    @test.idempotent_id('75e04226-bcf7-4595-a34b-fdf0736f38fc')
-    def test_list_qos(self):
-        """Tests the list of all qos-specs"""
-        body = self.volume_qos_client.list_qos()['qos_specs']
-        self.assertIn(self.created_qos, body)
-
-    @test.idempotent_id('ed00fd85-4494-45f2-8ceb-9e2048919aed')
-    def test_set_unset_qos_key(self):
-        """Test the addition of a specs key to qos-specs"""
-        args = {'iops_bytes': '500'}
-        body = self.volume_qos_client.set_qos_key(
-            self.created_qos['id'],
-            iops_bytes='500')['qos_specs']
-        self.assertEqual(args, body)
-        body = self.volume_qos_client.show_qos(
-            self.created_qos['id'])['qos_specs']
-        self.assertEqual(args['iops_bytes'], body['specs']['iops_bytes'])
-
-        # test the deletion of a specs key from qos-specs
-        keys = ['iops_bytes']
-        self.volume_qos_client.unset_qos_key(self.created_qos['id'], keys)
-        operation = 'qos-key-unset'
-        self.volume_qos_client.wait_for_qos_operations(self.created_qos['id'],
-                                                       operation, keys)
-        body = self.volume_qos_client.show_qos(
-            self.created_qos['id'])['qos_specs']
-        self.assertNotIn(keys[0], body['specs'])
-
-    @test.idempotent_id('1dd93c76-6420-485d-a771-874044c416ac')
-    def test_associate_disassociate_qos(self):
-        """Test the following operations :
-
-        1. associate_qos
-        2. get_association_qos
-        3. disassociate_qos
-        4. disassociate_all_qos
-        """
-
-        # create a test volume-type
-        vol_type = []
-        for _ in range(0, 3):
-            vol_type.append(self._create_test_volume_type())
-
-        # associate the qos-specs with volume-types
-        for i in range(0, 3):
-            self._test_associate_qos(vol_type[i]['id'])
-
-        # get the association of the qos-specs
-        associations = self._test_get_association_qos()
-
-        for i in range(0, 3):
-            self.assertIn(vol_type[i]['id'], associations)
-
-        # disassociate a volume-type with qos-specs
-        self.volume_qos_client.disassociate_qos(
-            self.created_qos['id'], vol_type[0]['id'])
-        operation = 'disassociate'
-        self.volume_qos_client.wait_for_qos_operations(self.created_qos['id'],
-                                                       operation,
-                                                       vol_type[0]['id'])
-        associations = self._test_get_association_qos()
-        self.assertNotIn(vol_type[0]['id'], associations)
-
-        # disassociate all volume-types from qos-specs
-        self.volume_qos_client.disassociate_all_qos(
-            self.created_qos['id'])
-        operation = 'disassociate-all'
-        self.volume_qos_client.wait_for_qos_operations(self.created_qos['id'],
-                                                       operation)
-        associations = self._test_get_association_qos()
-        self.assertEmpty(associations)
-
-
-class QosSpecsV1TestJSON(QosSpecsV2TestJSON):
-    _api_version = 1
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index 688baf5..d0fa07e 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -30,70 +30,53 @@
             raise cls.skipException("Cinder snapshot feature disabled")
 
     @classmethod
-    def setup_clients(cls):
-        super(SnapshotV2MetadataTestJSON, cls).setup_clients()
-        cls.client = cls.snapshots_client
-
-    @classmethod
     def resource_setup(cls):
         super(SnapshotV2MetadataTestJSON, cls).resource_setup()
         # Create a volume
         cls.volume = cls.create_volume()
         # Create a snapshot
         cls.snapshot = cls.create_snapshot(volume_id=cls.volume['id'])
-        cls.snapshot_id = cls.snapshot['id']
 
     def tearDown(self):
         # Update the metadata to {}
-        self.client.update_snapshot_metadata(self.snapshot_id, metadata={})
+        self.snapshots_client.update_snapshot_metadata(
+            self.snapshot['id'], metadata={})
         super(SnapshotV2MetadataTestJSON, self).tearDown()
 
     @test.idempotent_id('a2f20f99-e363-4584-be97-bc33afb1a56c')
-    def test_create_get_delete_snapshot_metadata(self):
+    def test_crud_snapshot_metadata(self):
         # Create metadata for the snapshot
         metadata = {"key1": "value1",
                     "key2": "value2",
                     "key3": "value3"}
-        expected = {"key2": "value2",
-                    "key3": "value3"}
-        body = self.client.create_snapshot_metadata(
-            self.snapshot_id, metadata)['metadata']
-        # Get the metadata of the snapshot
-        body = self.client.show_snapshot_metadata(
-            self.snapshot_id)['metadata']
-        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
-
-        # Delete one item metadata of the snapshot
-        self.client.delete_snapshot_metadata_item(
-            self.snapshot_id, "key1")
-        body = self.client.show_snapshot_metadata(
-            self.snapshot_id)['metadata']
-        self.assertThat(body.items(), matchers.ContainsAll(expected.items()))
-        self.assertNotIn("key1", body)
-
-    @test.idempotent_id('bd2363bc-de92-48a4-bc98-28943c6e4be1')
-    def test_update_snapshot_metadata(self):
-        # Update metadata for the snapshot
-        metadata = {"key1": "value1",
-                    "key2": "value2",
-                    "key3": "value3"}
         update = {"key3": "value3_update",
                   "key4": "value4"}
-        # Create metadata for the snapshot
-        body = self.client.create_snapshot_metadata(
-            self.snapshot_id, metadata)['metadata']
-        # Get the metadata of the snapshot
-        body = self.client.show_snapshot_metadata(
-            self.snapshot_id)['metadata']
-        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+        expect = {"key4": "value4"}
+        # Create metadata
+        body = self.snapshots_client.create_snapshot_metadata(
+            self.snapshot['id'], metadata)['metadata']
 
-        # Update metadata item
-        body = self.client.update_snapshot_metadata(
-            self.snapshot_id, metadata=update)['metadata']
         # Get the metadata of the snapshot
-        body = self.client.show_snapshot_metadata(
-            self.snapshot_id)['metadata']
-        self.assertEqual(update, body)
+        body = self.snapshots_client.show_snapshot_metadata(
+            self.snapshot['id'])['metadata']
+        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()),
+                        'Create snapshot metadata failed')
+
+        # Update metadata
+        body = self.snapshots_client.update_snapshot_metadata(
+            self.snapshot['id'], metadata=update)['metadata']
+        body = self.snapshots_client.show_snapshot_metadata(
+            self.snapshot['id'])['metadata']
+        self.assertEqual(update, body, 'Update snapshot metadata failed')
+
+        # Delete one item metadata of the snapshot
+        self.snapshots_client.delete_snapshot_metadata_item(
+            self.snapshot['id'], "key3")
+        body = self.snapshots_client.show_snapshot_metadata(
+            self.snapshot['id'])['metadata']
+        self.assertThat(body.items(), matchers.ContainsAll(expect.items()),
+                        'Delete one item metadata of the snapshot failed')
+        self.assertNotIn("key3", body)
 
     @test.idempotent_id('e8ff85c5-8f97-477f-806a-3ac364a949ed')
     def test_update_snapshot_metadata_item(self):
@@ -106,18 +89,18 @@
                   "key2": "value2",
                   "key3": "value3_update"}
         # Create metadata for the snapshot
-        body = self.client.create_snapshot_metadata(
-            self.snapshot_id, metadata)['metadata']
+        body = self.snapshots_client.create_snapshot_metadata(
+            self.snapshot['id'], metadata)['metadata']
         # Get the metadata of the snapshot
-        body = self.client.show_snapshot_metadata(
-            self.snapshot_id)['metadata']
+        body = self.snapshots_client.show_snapshot_metadata(
+            self.snapshot['id'])['metadata']
         self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
         # Update metadata item
-        body = self.client.update_snapshot_metadata_item(
-            self.snapshot_id, "key3", meta=update_item)['meta']
+        body = self.snapshots_client.update_snapshot_metadata_item(
+            self.snapshot['id'], "key3", meta=update_item)['meta']
         # Get the metadata of the snapshot
-        body = self.client.show_snapshot_metadata(
-            self.snapshot_id)['metadata']
+        body = self.snapshots_client.show_snapshot_metadata(
+            self.snapshot['id'])['metadata']
         self.assertThat(body.items(), matchers.ContainsAll(expect.items()))
 
 
diff --git a/tempest/api/volume/test_volume_absolute_limits.py b/tempest/api/volume/test_volume_absolute_limits.py
new file mode 100644
index 0000000..35e0d56
--- /dev/null
+++ b/tempest/api/volume/test_volume_absolute_limits.py
@@ -0,0 +1,52 @@
+# Copyright 2016 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 tempest.api.volume import base
+from tempest import config
+from tempest import test
+
+
+CONF = config.CONF
+
+
+class AbsoluteLimitsV2Tests(base.BaseVolumeTest):
+
+    # avoid existing volumes of pre-defined tenant
+    force_tenant_isolation = True
+
+    @classmethod
+    def resource_setup(cls):
+        super(AbsoluteLimitsV2Tests, cls).resource_setup()
+        # Create a shared volume for tests
+        cls.volume = cls.create_volume()
+
+    @test.idempotent_id('8e943f53-e9d6-4272-b2e9-adcf2f7c29ad')
+    def test_get_volume_absolute_limits(self):
+        # get volume limit for a tenant
+        absolute_limits = \
+            self.volume_limits_client.show_limits(
+            )['limits']['absolute']
+
+        # verify volume limits and defaults per tenants
+        self.assertEqual(absolute_limits['totalGigabytesUsed'],
+                         CONF.volume.volume_size)
+        self.assertEqual(absolute_limits['totalVolumesUsed'], 1)
+        self.assertEqual(absolute_limits['totalSnapshotsUsed'], 0)
+        self.assertEqual(absolute_limits['totalBackupsUsed'], 0)
+        self.assertEqual(absolute_limits['totalBackupGigabytesUsed'], 0)
+
+
+class AbsoluteLimitsV1Tests(AbsoluteLimitsV2Tests):
+    _api_version = 1
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
index e529538..c125bb8 100644
--- a/tempest/api/volume/test_volume_metadata.py
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -26,60 +26,46 @@
         super(VolumesV2MetadataTest, cls).resource_setup()
         # Create a volume
         cls.volume = cls.create_volume()
-        cls.volume_id = cls.volume['id']
 
     def tearDown(self):
         # Update the metadata to {}
-        self.volumes_client.update_volume_metadata(self.volume_id, {})
+        self.volumes_client.update_volume_metadata(self.volume['id'], {})
         super(VolumesV2MetadataTest, self).tearDown()
 
     @test.idempotent_id('6f5b125b-f664-44bf-910f-751591fe5769')
-    def test_create_get_delete_volume_metadata(self):
+    def test_crud_volume_metadata(self):
         # Create metadata for the volume
         metadata = {"key1": "value1",
                     "key2": "value2",
                     "key3": "value3",
                     "key4": "<value&special_chars>"}
+        update = {"key4": "value4",
+                  "key1": "value1_update"}
+        expected = {"key4": "value4"}
 
-        body = self.volumes_client.create_volume_metadata(self.volume_id,
+        body = self.volumes_client.create_volume_metadata(self.volume['id'],
                                                           metadata)['metadata']
         # Get the metadata of the volume
         body = self.volumes_client.show_volume_metadata(
-            self.volume_id)['metadata']
-        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
-        # Delete one item metadata of the volume
-        self.volumes_client.delete_volume_metadata_item(
-            self.volume_id, "key1")
-        body = self.volumes_client.show_volume_metadata(
-            self.volume_id)['metadata']
-        self.assertNotIn("key1", body)
-        del metadata["key1"]
-        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+            self.volume['id'])['metadata']
+        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()),
+                        'Create metadata for the volume failed')
 
-    @test.idempotent_id('774d2918-9beb-4f30-b3d1-2a4e8179ec0a')
-    def test_update_volume_metadata(self):
-        # Update metadata for the volume
-        metadata = {"key1": "value1",
-                    "key2": "value2",
-                    "key3": "value3"}
-
-        update = {"key4": "value4",
-                  "key1": "value1_update"}
-
-        # Create metadata for the volume
-        body = self.volumes_client.create_volume_metadata(
-            self.volume_id, metadata)['metadata']
-        # Get the metadata of the volume
-        body = self.volumes_client.show_volume_metadata(
-            self.volume_id)['metadata']
-        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
         # Update metadata
         body = self.volumes_client.update_volume_metadata(
-            self.volume_id, update)['metadata']
-        # Get the metadata of the volume
+            self.volume['id'], update)['metadata']
         body = self.volumes_client.show_volume_metadata(
-            self.volume_id)['metadata']
-        self.assertEqual(update, body)
+            self.volume['id'])['metadata']
+        self.assertEqual(update, body, 'Update metadata failed')
+
+        # Delete one item metadata of the volume
+        self.volumes_client.delete_volume_metadata_item(
+            self.volume['id'], "key1")
+        body = self.volumes_client.show_volume_metadata(
+            self.volume['id'])['metadata']
+        self.assertNotIn("key1", body)
+        self.assertThat(body.items(), matchers.ContainsAll(expected.items()),
+                        'Delete one item metadata of the volume failed')
 
     @test.idempotent_id('862261c5-8df4-475a-8c21-946e50e36a20')
     def test_update_volume_metadata_item(self):
@@ -93,14 +79,15 @@
                   "key3": "value3_update"}
         # Create metadata for the volume
         body = self.volumes_client.create_volume_metadata(
-            self.volume_id, metadata)['metadata']
-        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+            self.volume['id'], metadata)['metadata']
+        self.assertThat(body.items(),
+                        matchers.ContainsAll(metadata.items()))
         # Update metadata item
         body = self.volumes_client.update_volume_metadata_item(
-            self.volume_id, "key3", update_item)['meta']
+            self.volume['id'], "key3", update_item)['meta']
         # Get the metadata of the volume
         body = self.volumes_client.show_volume_metadata(
-            self.volume_id)['metadata']
+            self.volume['id'])['metadata']
         self.assertThat(body.items(), matchers.ContainsAll(expect.items()))
 
 
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 7046dcf..a8889e0 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -16,11 +16,9 @@
 from testtools import matchers
 
 from tempest.api.volume import base
-from tempest import config
+from tempest.common import waiters
 from tempest import test
 
-CONF = config.CONF
-
 
 class VolumesV2TransfersTest(base.BaseVolumeTest):
 
@@ -35,24 +33,19 @@
         cls.alt_tenant_id = cls.alt_client.tenant_id
         cls.adm_client = cls.os_adm.volumes_client
 
-    def _delete_volume(self, volume_id):
-        # Delete the specified volume using admin creds
-        self.adm_client.delete_volume(volume_id)
-        self.adm_client.wait_for_resource_deletion(volume_id)
-
     @test.idempotent_id('4d75b645-a478-48b1-97c8-503f64242f1a')
     def test_create_get_list_accept_volume_transfer(self):
         # Create a volume first
         volume = self.create_volume()
-        self.addCleanup(self._delete_volume, volume['id'])
+        self.addCleanup(self.delete_volume, self.adm_client, volume['id'])
 
         # Create a volume transfer
         transfer = self.client.create_volume_transfer(
             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,20 +59,21 @@
         # 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):
         # Create a volume first
         volume = self.create_volume()
-        self.addCleanup(self._delete_volume, volume['id'])
+        self.addCleanup(self.delete_volume, self.adm_client, volume['id'])
 
         # Create a volume transfer
         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 +85,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..d8d6b9a 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -17,8 +17,9 @@
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
 from tempest import test
-import testtools
 
 CONF = config.CONF
 
@@ -29,48 +30,42 @@
     def setup_clients(cls):
         super(VolumesV2ActionsTest, cls).setup_clients()
         cls.client = cls.volumes_client
-        cls.image_client = cls.os.image_client
+        if CONF.service_available.glance:
+            # Check if glance v1 is available to determine which client to use.
+            if CONF.image_feature_enabled.api_v1:
+                cls.image_client = cls.os.image_client
+            elif CONF.image_feature_enabled.api_v2:
+                cls.image_client = cls.os.image_client_v2
+            else:
+                raise exceptions.InvalidConfiguration(
+                    'Either api_v1 or api_v2 must be True in '
+                    '[image-feature-enabled].')
 
     @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(
-            name=srv_name,
-            wait_until='ACTIVE')
-
         # Create a test shared volume for attach/detach tests
         cls.volume = cls.create_volume()
-        cls.client.wait_for_volume_status(cls.volume['id'], 'available')
-
-    @classmethod
-    def resource_cleanup(cls):
-        # Delete the test instance
-        cls.servers_client.delete_server(cls.server['id'])
-        waiters.wait_for_server_termination(cls.servers_client,
-                                            cls.server['id'])
-
-        super(VolumesV2ActionsTest, cls).resource_cleanup()
 
     @test.idempotent_id('fff42874-7db5-4487-a8e1-ddda5fb5288d')
-    @test.stresstest(class_setup_per='process')
     @test.attr(type='smoke')
     @test.services('compute')
     def test_attach_detach_volume_to_instance(self):
+        # Create a server
+        server = self.create_server(wait_until='ACTIVE')
         # 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')
+                                  instance_uuid=server['id'],
+                                  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,
-                          'Update bootable status of a volume is not enabled.')
     def test_volume_bootable(self):
         # Verify that a volume bootable flag is retrieved
         for bool_bootable in [True, False]:
@@ -79,30 +74,36 @@
             fetched_volume = self.client.show_volume(
                 self.volume['id'])['volume']
             # Get Volume information
-            bool_flag = self._is_true(fetched_volume['bootable'])
-            self.assertEqual(bool_bootable, bool_flag)
+            # NOTE(masayukig): 'bootable' is "true" or "false" in the current
+            # cinder implementation. So we need to cast boolean values to str
+            # and make it lower to compare here.
+            self.assertEqual(str(bool_bootable).lower(),
+                             fetched_volume['bootable'])
 
     @test.idempotent_id('9516a2c8-9135-488c-8dd6-5677a7e5f371')
-    @test.stresstest(class_setup_per='process')
     @test.services('compute')
     def test_get_volume_attachment(self):
+        # Create a server
+        server = self.create_server(wait_until='ACTIVE')
         # 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')
-        # 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,
+                                  instance_uuid=server['id'],
+                                  mountpoint='/dev/%s' %
+                                             CONF.compute.volume_device_name)
+        waiters.wait_for_volume_status(self.client,
+                                       self.volume['id'], 'in-use')
+        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(self.server['id'], attachment['server_id'])
+        attachment = volume['attachments'][0]
+
+        self.assertEqual('/dev/%s' %
+                         CONF.compute.volume_device_name,
+                         attachment['device'])
+        self.assertEqual(server['id'], attachment['server_id'])
         self.assertEqual(self.volume['id'], attachment['id'])
         self.assertEqual(self.volume['id'], attachment['volume_id'])
 
@@ -113,14 +114,17 @@
         # it is shared with the other tests. After it is uploaded in Glance,
         # there is no way to delete it from Cinder, so we delete it from Glance
         # using the Glance image_client and from Cinder via tearDownClass.
-        image_name = data_utils.rand_name('Image')
+        image_name = data_utils.rand_name(self.__class__.__name__ + '-Image')
         body = self.client.upload_volume(
             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.image_client.wait_for_image_status(image_id, 'active')
-        self.client.wait_for_volume_status(self.volume['id'], 'available')
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.image_client.delete_image,
+                        image_id)
+        waiters.wait_for_image_status(self.image_client, image_id, 'active')
+        waiters.wait_for_volume_status(self.client,
+                                       self.volume['id'], 'available')
 
     @test.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
     def test_reserve_unreserve_volume(self):
@@ -135,29 +139,20 @@
         body = self.client.show_volume(self.volume['id'])['volume']
         self.assertIn('available', body['status'])
 
-    def _is_true(self, val):
-        return val in ['true', 'True', True]
-
     @test.idempotent_id('fff74e1e-5bd3-4b33-9ea9-24c103bc3f59')
     def test_volume_readonly_update(self):
-        # Update volume readonly true
-        readonly = True
-        self.client.update_volume_readonly(self.volume['id'],
-                                           readonly=readonly)
-        # Get Volume information
-        fetched_volume = self.client.show_volume(self.volume['id'])['volume']
-        bool_flag = self._is_true(fetched_volume['metadata']['readonly'])
-        self.assertEqual(True, bool_flag)
-
-        # Update volume readonly false
-        readonly = False
-        self.client.update_volume_readonly(self.volume['id'],
-                                           readonly=readonly)
-
-        # Get Volume information
-        fetched_volume = self.client.show_volume(self.volume['id'])['volume']
-        bool_flag = self._is_true(fetched_volume['metadata']['readonly'])
-        self.assertEqual(False, bool_flag)
+        for readonly in [True, False]:
+            # Update volume readonly
+            self.client.update_volume_readonly(self.volume['id'],
+                                               readonly=readonly)
+            # Get Volume information
+            fetched_volume = self.client.show_volume(
+                self.volume['id'])['volume']
+            # NOTE(masayukig): 'readonly' is "True" or "False" in the current
+            # cinder implementation. So we need to cast boolean values to str
+            # to compare here.
+            self.assertEqual(str(readonly),
+                             fetched_volume['metadata']['readonly'])
 
 
 class VolumesV1ActionsTest(VolumesV2ActionsTest):
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
new file mode 100644
index 0000000..03a3774
--- /dev/null
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -0,0 +1,127 @@
+# Copyright 2016 Red Hat, Inc.
+# 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.volume import base
+from tempest.common.utils import data_utils
+from tempest.common import waiters
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class VolumesBackupsV2Test(base.BaseVolumeTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesBackupsV2Test, cls).skip_checks()
+        if not CONF.volume_feature_enabled.backup:
+            raise cls.skipException("Cinder backup feature disabled")
+        if not CONF.service_available.swift:
+            skip_msg = ("%s skipped as swift is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
+
+    def restore_backup(self, backup_id):
+        # Restore a backup
+        restored_volume = self.backups_client.restore_backup(
+            backup_id)['restore']
+
+        # Delete backup
+        self.addCleanup(self.volumes_client.delete_volume,
+                        restored_volume['volume_id'])
+        self.assertEqual(backup_id, restored_volume['backup_id'])
+        waiters.wait_for_backup_status(self.backups_client,
+                                       backup_id, 'available')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       restored_volume['volume_id'],
+                                       'available')
+        return restored_volume
+
+    @test.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
+    def test_volume_backup_create_get_detailed_list_restore_delete(self):
+        # Create backup
+        volume = self.create_volume()
+        self.addCleanup(self.volumes_client.delete_volume,
+                        volume['id'])
+        backup_name = data_utils.rand_name(
+            self.__class__.__name__ + '-Backup')
+        description = data_utils.rand_name("volume-backup-description")
+        backup = self.create_backup(volume_id=volume['id'],
+                                    name=backup_name,
+                                    description=description)
+        self.assertEqual(backup_name, backup['name'])
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume['id'], 'available')
+
+        # Get a given backup
+        backup = self.backups_client.show_backup(backup['id'])['backup']
+        self.assertEqual(backup_name, backup['name'])
+        self.assertEqual(description, backup['description'])
+
+        # Get all backups with detail
+        backups = self.backups_client.list_backups(
+            detail=True)['backups']
+        self.assertIn((backup['name'], backup['id']),
+                      [(m['name'], m['id']) for m in backups])
+
+        self.restore_backup(backup['id'])
+
+    @test.idempotent_id('07af8f6d-80af-44c9-a5dc-c8427b1b62e6')
+    @test.services('compute')
+    def test_backup_create_attached_volume(self):
+        """Test backup create using force flag.
+
+        Cinder allows to create a volume backup, whether the volume status
+        is "available" or "in-use".
+        """
+        # Create a server
+        volume = self.create_volume()
+        self.addCleanup(self.volumes_client.delete_volume,
+                        volume['id'])
+        server = self.create_server(wait_until='ACTIVE')
+        # Attach volume to instance
+        self.attach_volume(server['id'], volume['id'])
+        # Create backup using force flag
+        backup_name = data_utils.rand_name(
+            self.__class__.__name__ + '-Backup')
+        backup = self.create_backup(volume_id=volume['id'],
+                                    name=backup_name, force=True)
+        self.assertEqual(backup_name, backup['name'])
+
+    @test.idempotent_id('2a8ba340-dff2-4511-9db7-646f07156b15')
+    def test_bootable_volume_backup_and_restore(self):
+        # Create volume from image
+        img_uuid = CONF.compute.image_ref
+        volume = self.create_volume(imageRef=img_uuid)
+
+        volume_details = self.volumes_client.show_volume(
+            volume['id'])['volume']
+        self.assertEqual('true', volume_details['bootable'])
+
+        # Create a backup
+        backup = self.create_backup(volume_id=volume['id'])
+
+        # Restore the backup
+        restored_volume_id = self.restore_backup(backup['id'])['volume_id']
+
+        # Verify the restored backup volume is bootable
+        restored_volume_info = self.volumes_client.show_volume(
+            restored_volume_id)['volume']
+
+        self.assertEqual('true', restored_volume_info['bootable'])
+
+
+class VolumesBackupsV1Test(VolumesBackupsV2Test):
+    _api_version = 1
diff --git a/tempest/api/volume/test_volumes_clone.py b/tempest/api/volume/test_volumes_clone.py
new file mode 100644
index 0000000..2cedb4e
--- /dev/null
+++ b/tempest/api/volume/test_volumes_clone.py
@@ -0,0 +1,66 @@
+# Copyright 2016 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 tempest.api.volume import base
+from tempest import config
+from tempest import test
+
+
+CONF = config.CONF
+
+
+class VolumesV2CloneTest(base.BaseVolumeTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesV2CloneTest, cls).skip_checks()
+        if not CONF.volume_feature_enabled.clone:
+            raise cls.skipException("Cinder volume clones are disabled")
+
+    @test.idempotent_id('9adae371-a257-43a5-9555-dc7c88e66e0e')
+    def test_create_from_volume(self):
+        # Creates a volume from another volume passing a size different from
+        # the source volume.
+        src_size = CONF.volume.volume_size
+
+        src_vol = self.create_volume(size=src_size)
+        # Destination volume bigger than source
+        dst_vol = self.create_volume(source_volid=src_vol['id'],
+                                     size=src_size + 1)
+
+        volume = self.volumes_client.show_volume(dst_vol['id'])['volume']
+        # Should allow
+        self.assertEqual(volume['source_volid'], src_vol['id'])
+        self.assertEqual(int(volume['size']), src_size + 1)
+
+    @test.idempotent_id('cbbcd7c6-5a6c-481a-97ac-ca55ab715d16')
+    def test_create_from_bootable_volume(self):
+        # Create volume from image
+        img_uuid = CONF.compute.image_ref
+        src_vol = self.create_volume(imageRef=img_uuid)
+
+        # Create a volume from the bootable volume
+        cloned_vol = self.create_volume(source_volid=src_vol['id'])
+        cloned_vol_details = self.volumes_client.show_volume(
+            cloned_vol['id'])['volume']
+
+        # Verify cloned volume creation as expected
+        self.assertEqual('true', cloned_vol_details['bootable'])
+        self.assertEqual(src_vol['id'], cloned_vol_details['source_volid'])
+        self.assertEqual(src_vol['size'], cloned_vol_details['size'])
+
+
+class VolumesV1CloneTest(VolumesV2CloneTest):
+    _api_version = 1
diff --git a/tempest/api/volume/test_volumes_clone_negative.py b/tempest/api/volume/test_volumes_clone_negative.py
new file mode 100644
index 0000000..5c54e1e
--- /dev/null
+++ b/tempest/api/volume/test_volumes_clone_negative.py
@@ -0,0 +1,48 @@
+# Copyright 2016 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 tempest.api.volume import base
+from tempest import config
+from tempest.lib import exceptions
+from tempest import test
+
+
+CONF = config.CONF
+
+
+class VolumesV2CloneNegativeTest(base.BaseVolumeTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesV2CloneNegativeTest, cls).skip_checks()
+        if not CONF.volume_feature_enabled.clone:
+            raise cls.skipException("Cinder volume clones are disabled")
+
+    @test.idempotent_id('9adae371-a257-43a5-459a-dc7c88e66e0e')
+    def test_create_from_volume_decreasing_size(self):
+        # Creates a volume from another volume passing a size different from
+        # the source volume.
+        src_size = CONF.volume.volume_size + 1
+        src_vol = self.create_volume(size=src_size)
+
+        # Destination volume smaller than source
+        self.assertRaises(exceptions.BadRequest,
+                          self.volumes_client.create_volume,
+                          size=src_size - 1,
+                          source_volid=src_vol['id'])
+
+
+class VolumesV1CloneNegativeTest(VolumesV2CloneNegativeTest):
+    _api_version = 1
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index ed1e5c5..c3d6dbb 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -14,27 +14,22 @@
 #    under the License.
 
 from tempest.api.volume import base
-from tempest import config
+from tempest.common import waiters
 from tempest import test
 
-CONF = config.CONF
-
 
 class VolumesV2ExtendTest(base.BaseVolumeTest):
 
-    @classmethod
-    def setup_clients(cls):
-        super(VolumesV2ExtendTest, cls).setup_clients()
-        cls.client = cls.volumes_client
-
     @test.idempotent_id('9a36df71-a257-43a5-9555-dc7c88e66e0e')
     def test_volume_extend(self):
         # Extend Volume Test.
         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')
-        volume = self.client.show_volume(self.volume['id'])['volume']
+        self.volumes_client.extend_volume(self.volume['id'],
+                                          new_size=extend_size)
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       self.volume['id'], 'available')
+        volume = self.volumes_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..65e461c 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
 
@@ -26,44 +27,31 @@
 
 class VolumesV2GetTest(base.BaseVolumeTest):
 
-    @classmethod
-    def setup_clients(cls):
-        super(VolumesV2GetTest, cls).setup_clients()
-        cls.client = cls.volumes_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(VolumesV2GetTest, cls).resource_setup()
-
-        cls.name_field = cls.special_fields['name_field']
-        cls.descrip_field = cls.special_fields['descrip_field']
-
-    def _delete_volume(self, volume_id):
-        self.client.delete_volume(volume_id)
-        self.client.wait_for_resource_deletion(volume_id)
-
     def _volume_create_get_update_delete(self, **kwargs):
+        name_field = self.special_fields['name_field']
+        descrip_field = self.special_fields['descrip_field']
+
         # Create a volume, Get it's details and Delete the volume
-        volume = {}
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'Test'}
         # Create a volume
-        kwargs[self.name_field] = v_name
+        kwargs[name_field] = v_name
         kwargs['metadata'] = metadata
-        volume = self.client.create_volume(**kwargs)['volume']
+        volume = self.volumes_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')
-        self.assertIn(self.name_field, volume)
-        self.assertEqual(volume[self.name_field], v_name,
+        self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+        waiters.wait_for_volume_status(self.volumes_client, volume['id'],
+                                       'available')
+        self.assertIn(name_field, volume)
+        self.assertEqual(volume[name_field], v_name,
                          "The created volume name is not equal "
                          "to the requested name")
-        self.assertTrue(volume['id'] is not None,
-                        "Field volume id is empty or not found.")
+
         # Get Volume information
-        fetched_volume = self.client.show_volume(volume['id'])['volume']
+        fetched_volume = self.volumes_client.show_volume(
+            volume['id'])['volume']
         self.assertEqual(v_name,
-                         fetched_volume[self.name_field],
+                         fetched_volume[name_field],
                          'The fetched Volume name is different '
                          'from the created Volume')
         self.assertEqual(volume['id'],
@@ -77,57 +65,62 @@
 
         if 'imageRef' in kwargs:
             self.assertEqual('true', fetched_volume['bootable'])
-        if 'imageRef' not in kwargs:
+        else:
             self.assertEqual('false', fetched_volume['bootable'])
 
         # Update Volume
         # Test volume update when display_name is same with original value
-        params = {self.name_field: v_name}
-        self.client.update_volume(volume['id'], **params)
+        params = {name_field: v_name}
+        self.volumes_client.update_volume(volume['id'], **params)
         # Test volume update when display_name is new
-        new_v_name = data_utils.rand_name('new-Volume')
+        new_v_name = data_utils.rand_name(
+            self.__class__.__name__ + '-new-Volume')
         new_desc = 'This is the new description of volume'
-        params = {self.name_field: new_v_name,
-                  self.descrip_field: new_desc}
-        update_volume = self.client.update_volume(
+        params = {name_field: new_v_name,
+                  descrip_field: new_desc}
+        update_volume = self.volumes_client.update_volume(
             volume['id'], **params)['volume']
         # Assert response body for update_volume method
-        self.assertEqual(new_v_name, update_volume[self.name_field])
-        self.assertEqual(new_desc, update_volume[self.descrip_field])
+        self.assertEqual(new_v_name, update_volume[name_field])
+        self.assertEqual(new_desc, update_volume[descrip_field])
         # Assert response body for show_volume method
-        updated_volume = self.client.show_volume(volume['id'])['volume']
+        updated_volume = self.volumes_client.show_volume(
+            volume['id'])['volume']
         self.assertEqual(volume['id'], updated_volume['id'])
-        self.assertEqual(new_v_name, updated_volume[self.name_field])
-        self.assertEqual(new_desc, updated_volume[self.descrip_field])
+        self.assertEqual(new_v_name, updated_volume[name_field])
+        self.assertEqual(new_desc, updated_volume[descrip_field])
         self.assertThat(updated_volume['metadata'].items(),
                         matchers.ContainsAll(metadata.items()),
                         'The fetched Volume metadata misses data '
                         'from the created Volume')
+
         # Test volume create when display_name is none and display_description
         # contains specific characters,
         # then test volume update if display_name is duplicated
-        new_volume = {}
         new_v_desc = data_utils.rand_name('@#$%^* description')
-        params = {self.descrip_field: new_v_desc,
-                  'availability_zone': volume['availability_zone']}
-        new_volume = self.client.create_volume(**params)['volume']
+        params = {descrip_field: new_v_desc,
+                  'availability_zone': volume['availability_zone'],
+                  'size': CONF.volume.volume_size}
+        new_volume = self.volumes_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')
+        self.addCleanup(self.delete_volume, self.volumes_client,
+                        new_volume['id'])
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       new_volume['id'], 'available')
 
-        params = {self.name_field: volume[self.name_field],
-                  self.descrip_field: volume[self.descrip_field]}
-        self.client.update_volume(new_volume['id'], **params)
+        params = {name_field: volume[name_field],
+                  descrip_field: volume[descrip_field]}
+        self.volumes_client.update_volume(new_volume['id'], **params)
 
         if 'imageRef' in kwargs:
             self.assertEqual('true', updated_volume['bootable'])
-        if 'imageRef' not in kwargs:
+        else:
             self.assertEqual('false', updated_volume['bootable'])
 
     @test.attr(type='smoke')
     @test.idempotent_id('27fb0e9f-fb64-41dd-8bdb-1ffa762f0d51')
     def test_volume_create_get_update_delete(self):
-        self._volume_create_get_update_delete()
+        self._volume_create_get_update_delete(size=CONF.volume.volume_size)
 
     @test.attr(type='smoke')
     @test.idempotent_id('54a01030-c7fc-447c-86ee-c1182beae638')
@@ -145,7 +138,8 @@
                           'Cinder volume clones are disabled')
     def test_volume_create_get_update_delete_as_clone(self):
         origin = self.create_volume()
-        self._volume_create_get_update_delete(source_volid=origin['id'])
+        self._volume_create_get_update_delete(source_volid=origin['id'],
+                                              size=CONF.volume.volume_size)
 
 
 class VolumesV1GetTest(VolumesV2GetTest):
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 38a5a80..5e3f49f 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -32,9 +32,15 @@
     VOLUME_FIELDS = ('id', 'name')
 
     def assertVolumesIn(self, fetched_list, expected_list, fields=None):
+        """Check out the list.
+
+        This function is aim at check out whether all of the volumes in
+        expected_list are in fetched_list.
+        """
         if fields:
-            expected_list = map(operator.itemgetter(*fields), expected_list)
-            fetched_list = map(operator.itemgetter(*fields), fetched_list)
+            fieldsgetter = operator.itemgetter(*fields)
+            expected_list = map(fieldsgetter, expected_list)
+            fetched_list = [fieldsgetter(item) for item in fetched_list]
 
         missing_vols = [v for v in expected_list if v not in fetched_list]
         if len(missing_vols) == 0:
@@ -49,40 +55,25 @@
                              [str_vol(v) for v in fetched_list]))
 
     @classmethod
-    def setup_clients(cls):
-        super(VolumesV2ListTestJSON, cls).setup_clients()
-        cls.client = cls.volumes_client
-
-    @classmethod
     def resource_setup(cls):
         super(VolumesV2ListTestJSON, cls).resource_setup()
         cls.name = cls.VOLUME_FIELDS[1]
-
         # Create 3 test volumes
         cls.volume_list = []
-        cls.volume_id_list = []
         cls.metadata = {'Type': 'work'}
-        for i in range(3):
+        for _ in range(3):
             volume = cls.create_volume(metadata=cls.metadata)
-            volume = cls.client.show_volume(volume['id'])['volume']
+            volume = cls.volumes_client.show_volume(volume['id'])['volume']
             cls.volume_list.append(volume)
-            cls.volume_id_list.append(volume['id'])
-
-    @classmethod
-    def resource_cleanup(cls):
-        # Delete the created volumes
-        for volid in cls.volume_id_list:
-            cls.client.delete_volume(volid)
-            cls.client.wait_for_resource_deletion(volid)
-        super(VolumesV2ListTestJSON, cls).resource_cleanup()
 
     def _list_by_param_value_and_assert(self, params, with_detail=False):
         """list or list_details with given params and validates result"""
         if with_detail:
             fetched_vol_list = \
-                self.client.list_volumes(detail=True, params=params)['volumes']
+                self.volumes_client.list_volumes(detail=True,
+                                                 params=params)['volumes']
         else:
-            fetched_vol_list = self.client.list_volumes(
+            fetched_vol_list = self.volumes_client.list_volumes(
                 params=params)['volumes']
 
         # Validating params of fetched volumes
@@ -108,7 +99,7 @@
     def test_volume_list(self):
         # Get a list of Volumes
         # Fetch all volumes
-        fetched_list = self.client.list_volumes()['volumes']
+        fetched_list = self.volumes_client.list_volumes()['volumes']
         self.assertVolumesIn(fetched_list, self.volume_list,
                              fields=self.VOLUME_FIELDS)
 
@@ -116,14 +107,15 @@
     def test_volume_list_with_details(self):
         # Get a list of Volumes with details
         # Fetch all Volumes
-        fetched_list = self.client.list_volumes(detail=True)['volumes']
+        fetched_list = self.volumes_client.list_volumes(detail=True)['volumes']
         self.assertVolumesIn(fetched_list, self.volume_list)
 
     @test.idempotent_id('a28e8da4-0b56-472f-87a8-0f4d3f819c02')
     def test_volume_list_by_name(self):
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
         params = {self.name: volume[self.name]}
-        fetched_vol = self.client.list_volumes(params=params)['volumes']
+        fetched_vol = self.volumes_client.list_volumes(
+            params=params)['volumes']
         self.assertEqual(1, len(fetched_vol), str(fetched_vol))
         self.assertEqual(fetched_vol[0][self.name],
                          volume[self.name])
@@ -132,7 +124,7 @@
     def test_volume_list_details_by_name(self):
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
         params = {self.name: volume[self.name]}
-        fetched_vol = self.client.list_volumes(
+        fetched_vol = self.volumes_client.list_volumes(
             detail=True, params=params)['volumes']
         self.assertEqual(1, len(fetched_vol), str(fetched_vol))
         self.assertEqual(fetched_vol[0][self.name],
@@ -141,7 +133,8 @@
     @test.idempotent_id('39654e13-734c-4dab-95ce-7613bf8407ce')
     def test_volumes_list_by_status(self):
         params = {'status': 'available'}
-        fetched_list = self.client.list_volumes(params=params)['volumes']
+        fetched_list = self.volumes_client.list_volumes(
+            params=params)['volumes']
         self._list_by_param_value_and_assert(params)
         self.assertVolumesIn(fetched_list, self.volume_list,
                              fields=self.VOLUME_FIELDS)
@@ -149,18 +142,42 @@
     @test.idempotent_id('2943f712-71ec-482a-bf49-d5ca06216b9f')
     def test_volumes_list_details_by_status(self):
         params = {'status': 'available'}
-        fetched_list = self.client.list_volumes(
+        fetched_list = self.volumes_client.list_volumes(
             detail=True, params=params)['volumes']
         for volume in fetched_list:
             self.assertEqual('available', volume['status'])
         self.assertVolumesIn(fetched_list, self.volume_list)
 
+    @test.idempotent_id('2016a942-3020-40d7-95ce-7613bf8407ce')
+    def test_volumes_list_by_bootable(self):
+        """Check out volumes.
+
+        This test function is aim at check out whether all of the volumes
+        in volume_list are not a bootable volume.
+        """
+        params = {'bootable': 'false'}
+        fetched_list = self.volumes_client.list_volumes(
+            params=params)['volumes']
+        self._list_by_param_value_and_assert(params)
+        self.assertVolumesIn(fetched_list, self.volume_list,
+                             fields=self.VOLUME_FIELDS)
+
+    @test.idempotent_id('2016a939-72ec-482a-bf49-d5ca06216b9f')
+    def test_volumes_list_details_by_bootable(self):
+        params = {'bootable': 'false'}
+        fetched_list = self.volumes_client.list_volumes(
+            detail=True, params=params)['volumes']
+        for volume in fetched_list:
+            self.assertEqual('false', volume['bootable'])
+        self.assertVolumesIn(fetched_list, self.volume_list)
+
     @test.idempotent_id('c0cfa863-3020-40d7-b587-e35f597d5d87')
     def test_volumes_list_by_availability_zone(self):
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
         zone = volume['availability_zone']
         params = {'availability_zone': zone}
-        fetched_list = self.client.list_volumes(params=params)['volumes']
+        fetched_list = self.volumes_client.list_volumes(
+            params=params)['volumes']
         self._list_by_param_value_and_assert(params)
         self.assertVolumesIn(fetched_list, self.volume_list,
                              fields=self.VOLUME_FIELDS)
@@ -170,7 +187,7 @@
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
         zone = volume['availability_zone']
         params = {'availability_zone': zone}
-        fetched_list = self.client.list_volumes(
+        fetched_list = self.volumes_client.list_volumes(
             detail=True, params=params)['volumes']
         for volume in fetched_list:
             self.assertEqual(zone, volume['availability_zone'])
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index ad6f556..bcdbd22 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -13,24 +13,15 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
-from tempest.common import waiters
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 
 class VolumesV2NegativeTest(base.BaseVolumeTest):
 
     @classmethod
-    def setup_clients(cls):
-        super(VolumesV2NegativeTest, cls).setup_clients()
-        cls.client = cls.volumes_client
-
-    @classmethod
     def resource_setup(cls):
         super(VolumesV2NegativeTest, cls).resource_setup()
 
@@ -44,108 +35,114 @@
     @test.idempotent_id('f131c586-9448-44a4-a8b0-54ca838aa43e')
     def test_volume_get_nonexistent_volume_id(self):
         # Should not be able to get a non-existent volume
-        self.assertRaises(lib_exc.NotFound, self.client.show_volume,
-                          str(uuid.uuid4()))
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
+                          data_utils.rand_uuid())
 
     @test.attr(type=['negative'])
     @test.idempotent_id('555efa6e-efcd-44ef-8a3b-4a7ca4837a29')
     def test_volume_delete_nonexistent_volume_id(self):
         # Should not be able to delete a non-existent Volume
-        self.assertRaises(lib_exc.NotFound, self.client.delete_volume,
-                          str(uuid.uuid4()))
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
+                          data_utils.rand_uuid())
 
     @test.attr(type=['negative'])
     @test.idempotent_id('1ed83a8a-682d-4dfb-a30e-ee63ffd6c049')
     def test_create_volume_with_invalid_size(self):
         # Should not be able to create volume with invalid size
         # in request
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.create_volume,
                           size='#$%', display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('9387686f-334f-4d31-a439-33494b9e2683')
-    def test_create_volume_with_out_passing_size(self):
+    def test_create_volume_without_passing_size(self):
         # Should not be able to create volume without passing size
         # in request
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.create_volume,
                           size='', display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('41331caa-eaf4-4001-869d-bc18c1869360')
     def test_create_volume_with_size_zero(self):
         # Should not be able to create volume with size zero
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.create_volume,
                           size='0', display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('8b472729-9eba-446e-a83b-916bdb34bef7')
     def test_create_volume_with_size_negative(self):
         # Should not be able to create volume with size negative
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.create_volume,
                           size='-1', display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('10254ed8-3849-454e-862e-3ab8e6aa01d2')
     def test_create_volume_with_nonexistent_volume_type(self):
         # Should not be able to create volume with non-existent volume type
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.NotFound, self.client.create_volume,
-                          size='1', volume_type=str(uuid.uuid4()),
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
+                          size='1', volume_type=data_utils.rand_uuid(),
                           display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('0c36f6ae-4604-4017-b0a9-34fdc63096f9')
     def test_create_volume_with_nonexistent_snapshot_id(self):
         # Should not be able to create volume with non-existent snapshot
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.NotFound, self.client.create_volume,
-                          size='1', snapshot_id=str(uuid.uuid4()),
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
+                          size='1', snapshot_id=data_utils.rand_uuid(),
                           display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('47c73e08-4be8-45bb-bfdf-0c4e79b88344')
     def test_create_volume_with_nonexistent_source_volid(self):
         # Should not be able to create volume with non-existent source volume
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.NotFound, self.client.create_volume,
-                          size='1', source_volid=str(uuid.uuid4()),
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
+                          size='1', source_volid=data_utils.rand_uuid(),
                           display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('0186422c-999a-480e-a026-6a665744c30c')
     def test_update_volume_with_nonexistent_volume_id(self):
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.NotFound, self.client.update_volume,
-                          volume_id=str(uuid.uuid4()), display_name=v_name,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
+                          volume_id=data_utils.rand_uuid(),
+                          display_name=v_name,
                           metadata=metadata)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('e66e40d6-65e6-4e75-bdc7-636792fa152d')
     def test_update_volume_with_invalid_volume_id(self):
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.NotFound, self.client.update_volume,
-                          volume_id='#$%%&^&^', display_name=v_name,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
+                          volume_id=data_utils.rand_name('invalid'),
+                          display_name=v_name,
                           metadata=metadata)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('72aeca85-57a5-4c1f-9057-f320f9ea575b')
     def test_update_volume_with_empty_volume_id(self):
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.NotFound, self.client.update_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           volume_id='', display_name=v_name,
                           metadata=metadata)
 
@@ -153,43 +150,39 @@
     @test.idempotent_id('30799cfd-7ee4-446c-b66c-45b383ed211b')
     def test_get_invalid_volume_id(self):
         # Should not be able to get volume with invalid id
-        self.assertRaises(lib_exc.NotFound, self.client.show_volume,
-                          '#$%%&^&^')
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
+                          data_utils.rand_name('invalid'))
 
     @test.attr(type=['negative'])
     @test.idempotent_id('c6c3db06-29ad-4e91-beb0-2ab195fe49e3')
     def test_get_volume_without_passing_volume_id(self):
         # Should not be able to get volume when empty ID is passed
-        self.assertRaises(lib_exc.NotFound, self.client.show_volume, '')
+        self.assertRaises(lib_exc.NotFound,
+                          self.volumes_client.show_volume, '')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('1f035827-7c32-4019-9240-b4ec2dbd9dfd')
     def test_delete_invalid_volume_id(self):
         # Should not be able to delete volume when invalid ID is passed
-        self.assertRaises(lib_exc.NotFound, self.client.delete_volume,
-                          '!@#$%^&*()')
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
+                          data_utils.rand_name('invalid'))
 
     @test.attr(type=['negative'])
     @test.idempotent_id('441a1550-5d44-4b30-af0f-a6d402f52026')
     def test_delete_volume_without_passing_volume_id(self):
         # Should not be able to delete volume when empty ID is passed
-        self.assertRaises(lib_exc.NotFound, self.client.delete_volume, '')
+        self.assertRaises(lib_exc.NotFound,
+                          self.volumes_client.delete_volume, '')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('f5e56b0a-5d02-43c1-a2a7-c9b792c2e3f6')
     @test.services('compute')
     def test_attach_volumes_with_nonexistent_volume_id(self):
-        srv_name = data_utils.rand_name('Instance')
-        server = self.create_server(
-            name=srv_name,
-            wait_until='ACTIVE')
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, server['id'])
-        self.addCleanup(self.servers_client.delete_server, server['id'])
+        server = self.create_server(wait_until='ACTIVE')
 
         self.assertRaises(lib_exc.NotFound,
-                          self.client.attach_volume,
-                          str(uuid.uuid4()),
+                          self.volumes_client.attach_volume,
+                          data_utils.rand_uuid(),
                           instance_uuid=server['id'],
                           mountpoint=self.mountpoint)
 
@@ -197,7 +190,7 @@
     @test.idempotent_id('9f9c24e4-011d-46b5-b992-952140ce237a')
     def test_detach_volumes_with_invalid_volume_id(self):
         self.assertRaises(lib_exc.NotFound,
-                          self.client.detach_volume,
+                          self.volumes_client.detach_volume,
                           'xxx')
 
     @test.attr(type=['negative'])
@@ -205,7 +198,8 @@
     def test_volume_extend_with_size_smaller_than_original_size(self):
         # Extend volume with smaller size than original size.
         extend_size = 0
-        self.assertRaises(lib_exc.BadRequest, self.client.extend_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.extend_volume,
                           self.volume['id'], new_size=extend_size)
 
     @test.attr(type=['negative'])
@@ -213,7 +207,8 @@
     def test_volume_extend_with_non_number_size(self):
         # Extend volume when size is non number.
         extend_size = 'abc'
-        self.assertRaises(lib_exc.BadRequest, self.client.extend_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.extend_volume,
                           self.volume['id'], new_size=extend_size)
 
     @test.attr(type=['negative'])
@@ -221,7 +216,8 @@
     def test_volume_extend_with_None_size(self):
         # Extend volume with None size.
         extend_size = None
-        self.assertRaises(lib_exc.BadRequest, self.client.extend_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.extend_volume,
                           self.volume['id'], new_size=extend_size)
 
     @test.attr(type=['negative'])
@@ -229,65 +225,68 @@
     def test_volume_extend_with_nonexistent_volume_id(self):
         # Extend volume size when volume is nonexistent.
         extend_size = int(self.volume['size']) + 1
-        self.assertRaises(lib_exc.NotFound, self.client.extend_volume,
-                          str(uuid.uuid4()), new_size=extend_size)
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
+                          data_utils.rand_uuid(), new_size=extend_size)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('aff8ba64-6d6f-4f2e-bc33-41a08ee9f115')
     def test_volume_extend_without_passing_volume_id(self):
         # Extend volume size when passing volume id is None.
         extend_size = int(self.volume['size']) + 1
-        self.assertRaises(lib_exc.NotFound, self.client.extend_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
                           None, new_size=extend_size)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('ac6084c0-0546-45f9-b284-38a367e0e0e2')
     def test_reserve_volume_with_nonexistent_volume_id(self):
         self.assertRaises(lib_exc.NotFound,
-                          self.client.reserve_volume,
-                          str(uuid.uuid4()))
+                          self.volumes_client.reserve_volume,
+                          data_utils.rand_uuid())
 
     @test.attr(type=['negative'])
     @test.idempotent_id('eb467654-3dc1-4a72-9b46-47c29d22654c')
     def test_unreserve_volume_with_nonexistent_volume_id(self):
         self.assertRaises(lib_exc.NotFound,
-                          self.client.unreserve_volume,
-                          str(uuid.uuid4()))
+                          self.volumes_client.unreserve_volume,
+                          data_utils.rand_uuid())
 
     @test.attr(type=['negative'])
     @test.idempotent_id('449c4ed2-ecdd-47bb-98dc-072aeccf158c')
     def test_reserve_volume_with_negative_volume_status(self):
         # Mark volume as reserved.
-        self.client.reserve_volume(self.volume['id'])
+        self.volumes_client.reserve_volume(self.volume['id'])
         # Mark volume which is marked as reserved before
         self.assertRaises(lib_exc.BadRequest,
-                          self.client.reserve_volume,
+                          self.volumes_client.reserve_volume,
                           self.volume['id'])
         # Unmark volume as reserved.
-        self.client.unreserve_volume(self.volume['id'])
+        self.volumes_client.unreserve_volume(self.volume['id'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('0f4aa809-8c7b-418f-8fb3-84c7a5dfc52f')
     def test_list_volumes_with_nonexistent_name(self):
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         params = {self.name_field: v_name}
-        fetched_volume = self.client.list_volumes(params=params)['volumes']
+        fetched_volume = self.volumes_client.list_volumes(
+            params=params)['volumes']
         self.assertEqual(0, len(fetched_volume))
 
     @test.attr(type=['negative'])
     @test.idempotent_id('9ca17820-a0e7-4cbd-a7fa-f4468735e359')
     def test_list_volumes_detail_with_nonexistent_name(self):
-        v_name = data_utils.rand_name('Volume')
+        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         params = {self.name_field: v_name}
         fetched_volume = \
-            self.client.list_volumes(detail=True, params=params)['volumes']
+            self.volumes_client.list_volumes(
+                detail=True, params=params)['volumes']
         self.assertEqual(0, len(fetched_volume))
 
     @test.attr(type=['negative'])
     @test.idempotent_id('143b279b-7522-466b-81be-34a87d564a7c')
     def test_list_volumes_with_invalid_status(self):
         params = {'status': 'null'}
-        fetched_volume = self.client.list_volumes(params=params)['volumes']
+        fetched_volume = self.volumes_client.list_volumes(
+            params=params)['volumes']
         self.assertEqual(0, len(fetched_volume))
 
     @test.attr(type=['negative'])
@@ -295,10 +294,10 @@
     def test_list_volumes_detail_with_invalid_status(self):
         params = {'status': 'null'}
         fetched_volume = \
-            self.client.list_volumes(detail=True, params=params)['volumes']
+            self.volumes_client.list_volumes(detail=True,
+                                             params=params)['volumes']
         self.assertEqual(0, len(fetched_volume))
 
 
 class VolumesV1NegativeTest(VolumesV2NegativeTest):
     _api_version = 1
-    _name = 'display_name'
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index c79235a..6f85891 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -30,64 +30,72 @@
     def resource_setup(cls):
         super(VolumesV2SnapshotTestJSON, cls).resource_setup()
         cls.volume_origin = cls.create_volume()
-
         cls.name_field = cls.special_fields['name_field']
         cls.descrip_field = cls.special_fields['descrip_field']
 
-    def _detach(self, volume_id):
-        """Detach volume."""
-        self.volumes_client.detach_volume(volume_id)
-        self.volumes_client.wait_for_volume_status(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."""
-
-        if with_detail:
-            fetched_snap_list = self.snapshots_client.list_snapshots(
-                detail=True, **params)['snapshots']
-        else:
-            fetched_snap_list = self.snapshots_client.list_snapshots(
-                **params)['snapshots']
-
-        # Validating params of fetched snapshots
-        for snap in fetched_snap_list:
-            for key in params:
-                msg = "Failed to list snapshots %s by %s" % \
-                      ('details' if with_detail else '', key)
-                self.assertEqual(params[key], snap[key], msg)
+    def cleanup_snapshot(self, snapshot):
+        # Delete the snapshot
+        self.snapshots_client.delete_snapshot(snapshot['id'])
+        self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
+        self.snapshots.remove(snapshot)
 
     @test.idempotent_id('b467b54c-07a4-446d-a1cf-651dedcc3ff1')
     @test.services('compute')
     def test_snapshot_create_with_volume_in_use(self):
         # Create a snapshot when volume status is in-use
         # Create a test instance
-        server_name = data_utils.rand_name('instance')
-        server = self.create_server(
-            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,
-                        self.volume_origin['id'], 'available')
-        self.addCleanup(self.servers_client.detach_volume, server['id'],
-                        self.volume_origin['id'])
+        server = self.create_server(wait_until='ACTIVE')
+        self.attach_volume(server['id'], self.volume_origin['id'])
+
         # Snapshot a volume even if it's attached to an instance
         snapshot = self.create_snapshot(self.volume_origin['id'],
                                         force=True)
         # Delete the snapshot
         self.cleanup_snapshot(snapshot)
 
+    @test.idempotent_id('8567b54c-4455-446d-a1cf-651ddeaa3ff2')
+    @test.services('compute')
+    def test_snapshot_delete_with_volume_in_use(self):
+        # Create a test instance
+        server = self.create_server(wait_until='ACTIVE')
+        self.attach_volume(server['id'], self.volume_origin['id'])
+
+        # Snapshot a volume attached to an instance
+        snapshot1 = self.create_snapshot(self.volume_origin['id'], force=True)
+        snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
+        snapshot3 = self.create_snapshot(self.volume_origin['id'], force=True)
+
+        # Delete the snapshots. Some snapshot implementations can take
+        # different paths according to order they are deleted.
+        self.cleanup_snapshot(snapshot1)
+        self.cleanup_snapshot(snapshot3)
+        self.cleanup_snapshot(snapshot2)
+
+    @test.idempotent_id('5210a1de-85a0-11e6-bb21-641c676a5d61')
+    @test.services('compute')
+    def test_snapshot_create_offline_delete_online(self):
+
+        # Create a snapshot while it is not attached
+        snapshot1 = self.create_snapshot(self.volume_origin['id'])
+
+        # Create a server and attach it
+        server = self.create_server(wait_until='ACTIVE')
+        self.attach_volume(server['id'], self.volume_origin['id'])
+
+        # Now that the volume is attached, create another snapshots
+        snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
+        snapshot3 = self.create_snapshot(self.volume_origin['id'], force=True)
+
+        # Delete the snapshots. Some snapshot implementations can take
+        # different paths according to order they are deleted.
+        self.cleanup_snapshot(snapshot3)
+        self.cleanup_snapshot(snapshot1)
+        self.cleanup_snapshot(snapshot2)
+
     @test.idempotent_id('2a8abbe4-d871-46db-b049-c41f5af8216e')
     def test_snapshot_create_get_list_update_delete(self):
         # Create a snapshot
-        s_name = data_utils.rand_name('snap')
-        params = {self.name_field: s_name}
-        snapshot = self.create_snapshot(self.volume_origin['id'], **params)
+        snapshot = self.create_snapshot(self.volume_origin['id'])
 
         # Get the snap and check for some of its details
         snap_get = self.snapshots_client.show_snapshot(
@@ -103,7 +111,8 @@
         self.assertIn(tracking_data, snaps_data)
 
         # Updates snapshot with new values
-        new_s_name = data_utils.rand_name('new-snap')
+        new_s_name = data_utils.rand_name(
+            self.__class__.__name__ + '-new-snap')
         new_desc = 'This is the new description of snapshot.'
         params = {self.name_field: new_s_name,
                   self.descrip_field: new_desc}
@@ -121,66 +130,29 @@
         # Delete the snapshot
         self.cleanup_snapshot(snapshot)
 
-    @test.idempotent_id('59f41f43-aebf-48a9-ab5d-d76340fab32b')
-    def test_snapshots_list_with_params(self):
-        """list snapshots with params."""
-        # Create a snapshot
-        display_name = data_utils.rand_name('snap')
-        params = {self.name_field: display_name}
-        snapshot = self.create_snapshot(self.volume_origin['id'], **params)
-        self.addCleanup(self.cleanup_snapshot, snapshot)
-
-        # Verify list snapshots by display_name filter
-        params = {self.name_field: snapshot[self.name_field]}
-        self._list_by_param_values_and_assert(**params)
-
-        # Verify list snapshots by status filter
-        params = {'status': 'available'}
-        self._list_by_param_values_and_assert(**params)
-
-        # Verify list snapshots by status and display name filter
-        params = {'status': 'available',
-                  self.name_field: snapshot[self.name_field]}
-        self._list_by_param_values_and_assert(**params)
-
-    @test.idempotent_id('220a1022-1fcd-4a74-a7bd-6b859156cda2')
-    def test_snapshots_list_details_with_params(self):
-        """list snapshot details with params."""
-        # Create a snapshot
-        display_name = data_utils.rand_name('snap')
-        params = {self.name_field: display_name}
-        snapshot = self.create_snapshot(self.volume_origin['id'], **params)
-        self.addCleanup(self.cleanup_snapshot, snapshot)
-
-        # Verify list snapshot details by display_name filter
-        params = {self.name_field: snapshot[self.name_field]}
-        self._list_by_param_values_and_assert(with_detail=True, **params)
-        # Verify list snapshot details by status filter
-        params = {'status': 'available'}
-        self._list_by_param_values_and_assert(with_detail=True, **params)
-        # Verify list snapshot details by status and display name filter
-        params = {'status': 'available',
-                  self.name_field: snapshot[self.name_field]}
-        self._list_by_param_values_and_assert(with_detail=True, **params)
-
     @test.idempotent_id('677863d1-3142-456d-b6ac-9924f667a7f4')
     def test_volume_from_snapshot(self):
-        # Create a temporary snap using wrapper method from base, then
-        # create a snap based volume and deletes it
-        snapshot = self.create_snapshot(self.volume_origin['id'])
-        # 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')
-        self.volumes_client.delete_volume(volume['id'])
-        self.volumes_client.wait_for_resource_deletion(volume['id'])
-        self.cleanup_snapshot(snapshot)
+        # Creates a volume a snapshot passing a size different from the source
+        src_size = CONF.volume.volume_size
 
-    def cleanup_snapshot(self, snapshot):
-        # Delete the snapshot
-        self.snapshots_client.delete_snapshot(snapshot['id'])
-        self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
-        self.snapshots.remove(snapshot)
+        src_vol = self.create_volume(size=src_size)
+        src_snap = self.create_snapshot(src_vol['id'])
+        # Destination volume bigger than source snapshot
+        dst_vol = self.create_volume(snapshot_id=src_snap['id'],
+                                     size=src_size + 1)
+        # NOTE(zhufl): dst_vol is created based on snapshot, so dst_vol
+        # should be deleted before deleting snapshot, otherwise deleting
+        # snapshot will end with status 'error-deleting'. This depends on
+        # the implementation mechanism of vendors, generally speaking,
+        # some verdors will use "virtual disk clone" which will promote
+        # disk clone speed, and in this situation the "disk clone"
+        # is just a relationship between volume and snapshot.
+        self.addCleanup(self.delete_volume, self.volumes_client, dst_vol['id'])
+
+        volume = self.volumes_client.show_volume(dst_vol['id'])['volume']
+        # Should allow
+        self.assertEqual(volume['snapshot_id'], src_snap['id'])
+        self.assertEqual(int(volume['size']), src_size + 1)
 
 
 class VolumesV1SnapshotTestJSON(VolumesV2SnapshotTestJSON):
diff --git a/tempest/api/volume/test_volumes_snapshots_list.py b/tempest/api/volume/test_volumes_snapshots_list.py
new file mode 100644
index 0000000..b831252
--- /dev/null
+++ b/tempest/api/volume/test_volumes_snapshots_list.py
@@ -0,0 +1,110 @@
+#    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.volume import base
+from tempest import config
+from tempest.lib import decorators
+from tempest import test
+
+CONF = config.CONF
+
+
+class VolumesV2SnapshotListTestJSON(base.BaseVolumeTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesV2SnapshotListTestJSON, cls).skip_checks()
+        if not CONF.volume_feature_enabled.snapshot:
+            raise cls.skipException("Cinder volume snapshots are disabled")
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumesV2SnapshotListTestJSON, cls).resource_setup()
+        cls.volume_origin = cls.create_volume()
+        cls.name_field = cls.special_fields['name_field']
+        # Create snapshots with params
+        for _ in range(2):
+            cls.snapshot = cls.create_snapshot(cls.volume_origin['id'])
+
+    def _list_by_param_values_and_assert(self, with_detail=False, **params):
+        """list or list_details with given params and validates result."""
+
+        fetched_snap_list = self.snapshots_client.list_snapshots(
+            detail=with_detail, **params)['snapshots']
+
+        # Validating params of fetched snapshots
+        for snap in fetched_snap_list:
+            for key in params:
+                msg = "Failed to list snapshots %s by %s" % \
+                      ('details' if with_detail else '', key)
+                self.assertEqual(params[key], snap[key], msg)
+
+    def _list_snapshots_by_param_limit(self, limit, expected_elements):
+        """list snapshots by limit param"""
+        # Get snapshots list using limit parameter
+        fetched_snap_list = self.snapshots_client.list_snapshots(
+            limit=limit)['snapshots']
+        # Validating filtered snapshots length equals to expected_elements
+        self.assertEqual(expected_elements, len(fetched_snap_list))
+
+    @test.idempotent_id('59f41f43-aebf-48a9-ab5d-d76340fab32b')
+    def test_snapshots_list_with_params(self):
+        """list snapshots with params."""
+        # Verify list snapshots by display_name filter
+        params = {self.name_field: self.snapshot[self.name_field]}
+        self._list_by_param_values_and_assert(**params)
+
+        # Verify list snapshots by status filter
+        params = {'status': 'available'}
+        self._list_by_param_values_and_assert(**params)
+
+        # Verify list snapshots by status and display name filter
+        params = {'status': 'available',
+                  self.name_field: self.snapshot[self.name_field]}
+        self._list_by_param_values_and_assert(**params)
+
+    @test.idempotent_id('220a1022-1fcd-4a74-a7bd-6b859156cda2')
+    def test_snapshots_list_details_with_params(self):
+        """list snapshot details with params."""
+        # Verify list snapshot details by display_name filter
+        params = {self.name_field: self.snapshot[self.name_field]}
+        self._list_by_param_values_and_assert(with_detail=True, **params)
+        # Verify list snapshot details by status filter
+        params = {'status': 'available'}
+        self._list_by_param_values_and_assert(with_detail=True, **params)
+        # Verify list snapshot details by status and display name filter
+        params = {'status': 'available',
+                  self.name_field: self.snapshot[self.name_field]}
+        self._list_by_param_values_and_assert(with_detail=True, **params)
+
+    @test.idempotent_id('db4d8e0a-7a2e-41cc-a712-961f6844e896')
+    def test_snapshot_list_param_limit(self):
+        # List returns limited elements
+        self._list_snapshots_by_param_limit(limit=1, expected_elements=1)
+
+    @test.idempotent_id('a1427f61-420e-48a5-b6e3-0b394fa95400')
+    def test_snapshot_list_param_limit_equals_infinite(self):
+        # List returns all elements when request limit exceeded
+        # snapshots number
+        snap_list = self.snapshots_client.list_snapshots()['snapshots']
+        self._list_snapshots_by_param_limit(limit=100000,
+                                            expected_elements=len(snap_list))
+
+    @decorators.skip_because(bug='1540893')
+    @test.idempotent_id('e3b44b7f-ae87-45b5-8a8c-66110eb24d0a')
+    def test_snapshot_list_param_limit_equals_zero(self):
+        # List returns zero elements
+        self._list_snapshots_by_param_limit(limit=0, expected_elements=0)
+
+
+class VolumesV1SnapshotLimitTestJSON(VolumesV2SnapshotListTestJSON):
+    _api_version = 1
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index d46c9b5..1f5bb0d 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -10,13 +10,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-from tempest_lib import exceptions as lib_exc
-
 from tempest.api.volume 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
@@ -34,20 +31,35 @@
     @test.idempotent_id('e3e466af-70ab-4f4b-a967-ab04e3532ea7')
     def test_create_snapshot_with_nonexistent_volume_id(self):
         # Create a snapshot with nonexistent volume id
-        s_name = data_utils.rand_name('snap')
+        s_name = data_utils.rand_name(self.__class__.__name__ + '-snap')
         self.assertRaises(lib_exc.NotFound,
                           self.snapshots_client.create_snapshot,
-                          volume_id=str(uuid.uuid4()), display_name=s_name)
+                          volume_id=data_utils.rand_uuid(),
+                          display_name=s_name)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('bb9da53e-d335-4309-9c15-7e76fd5e4d6d')
     def test_create_snapshot_without_passing_volume_id(self):
         # Create a snapshot without passing volume id
-        s_name = data_utils.rand_name('snap')
+        s_name = data_utils.rand_name(self.__class__.__name__ + '-snap')
         self.assertRaises(lib_exc.NotFound,
                           self.snapshots_client.create_snapshot,
                           volume_id=None, display_name=s_name)
 
+    @test.idempotent_id('677863d1-34f9-456d-b6ac-9924f667a7f4')
+    def test_volume_from_snapshot_decreasing_size(self):
+        # Creates a volume a snapshot passing a size different from the source
+        src_size = CONF.volume.volume_size + 1
+
+        src_vol = self.create_volume(size=src_size)
+        src_snap = self.create_snapshot(src_vol['id'])
+
+        # Destination volume smaller than source
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.create_volume,
+                          size=src_size - 1,
+                          snapshot_id=src_snap['id'])
+
 
 class VolumesV1SnapshotNegativeTestJSON(VolumesV2SnapshotNegativeTestJSON):
     _api_version = 1
diff --git a/tempest/api/volume/v2/test_image_metadata.py b/tempest/api/volume/v2/test_image_metadata.py
new file mode 100644
index 0000000..1e7bb30
--- /dev/null
+++ b/tempest/api/volume/v2/test_image_metadata.py
@@ -0,0 +1,64 @@
+# Copyright 2016 Red Hat, Inc.
+# 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 testtools import matchers
+
+from tempest.api.volume import base
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class VolumesV2ImageMetadata(base.BaseVolumeTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumesV2ImageMetadata, cls).resource_setup()
+        # Create a volume from image ID
+        cls.volume = cls.create_volume(imageRef=CONF.compute.image_ref)
+
+    @test.idempotent_id('03efff0b-5c75-4822-8f10-8789ac15b13e')
+    @test.services('image')
+    def test_update_image_metadata(self):
+        # Update image metadata
+        image_metadata = {'image_id': '5137a025-3c5f-43c1-bc64-5f41270040a5',
+                          'image_name': 'image',
+                          'kernel_id': '6ff710d2-942b-4d6b-9168-8c9cc2404ab1',
+                          'ramdisk_id': 'somedisk'}
+        self.volumes_client.update_volume_image_metadata(self.volume['id'],
+                                                         **image_metadata)
+
+        # Fetch image metadata from the volume
+        volume_image_metadata = self.volumes_client.show_volume(
+            self.volume['id'])['volume']['volume_image_metadata']
+
+        # Verify image metadata was updated
+        self.assertThat(volume_image_metadata.items(),
+                        matchers.ContainsAll(image_metadata.items()))
+
+        # Delete one item from image metadata of the volume
+        self.volumes_client.delete_volume_image_metadata(self.volume['id'],
+                                                         'ramdisk_id')
+        del image_metadata['ramdisk_id']
+
+        # Fetch the new image metadata from the volume
+        volume_image_metadata = self.volumes_client.show_volume(
+            self.volume['id'])['volume']['volume_image_metadata']
+
+        # Verify image metadata was updated after item deletion
+        self.assertThat(volume_image_metadata.items(),
+                        matchers.ContainsAll(image_metadata.items()))
+        self.assertNotIn('ramdisk_id', volume_image_metadata)
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 6568627..28ba941 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -14,9 +14,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import random
 from six.moves.urllib import parse
 
 from tempest.api.volume import base
+from tempest.lib import decorators
 from tempest import test
 
 
@@ -31,32 +33,19 @@
     """
 
     @classmethod
-    def setup_clients(cls):
-        super(VolumesV2ListTestJSON, cls).setup_clients()
-        cls.client = cls.volumes_client
-
-    @classmethod
     def resource_setup(cls):
         super(VolumesV2ListTestJSON, cls).resource_setup()
 
         # Create 3 test volumes
-        cls.volume_list = []
-        cls.volume_id_list = []
         cls.metadata = {'Type': 'work'}
-        for i in range(3):
+        # NOTE(zhufl): When using pre-provisioned credentials, the project
+        # may have volumes other than those created below.
+        existing_volumes = cls.volumes_client.list_volumes()['volumes']
+        cls.volume_id_list = [vol['id'] for vol in existing_volumes]
+        for _ in range(3):
             volume = cls.create_volume(metadata=cls.metadata)
-            volume = cls.client.show_volume(volume['id'])['volume']
-            cls.volume_list.append(volume)
             cls.volume_id_list.append(volume['id'])
 
-    @classmethod
-    def resource_cleanup(cls):
-        # Delete the created volumes
-        for volid in cls.volume_id_list:
-            cls.client.delete_volume(volid)
-            cls.client.wait_for_resource_deletion(volid)
-        super(VolumesV2ListTestJSON, cls).resource_cleanup()
-
     @test.idempotent_id('2a7064eb-b9c3-429b-b888-33928fc5edd3')
     def test_volume_list_details_with_multiple_params(self):
         # List volumes detail using combined condition
@@ -69,7 +58,7 @@
                       'sort_dir': sort_dir,
                       'sort_key': sort_key
                       }
-            fetched_volume = self.client.list_volumes(
+            fetched_volume = self.volumes_client.list_volumes(
                 detail=True, params=params)['volumes']
             self.assertEqual(limit, len(fetched_volume),
                              "The count of volumes is %s, expected:%s " %
@@ -79,11 +68,13 @@
             val0 = fetched_volume[0][sort_key]
             val1 = fetched_volume[1][sort_key]
             if sort_dir == 'asc':
-                self.assertTrue(val0 < val1,
-                                "%s < %s" % (val0, val1))
+                self.assertLess(val0, val1,
+                                "list is not in asc order with sort_key: %s."
+                                " %s" % (sort_key, fetched_volume))
             elif sort_dir == 'desc':
-                self.assertTrue(val0 > val1,
-                                "%s > %s" % (val0, val1))
+                self.assertGreater(val0, val1,
+                                   "list is not in desc order with sort_key: "
+                                   "%s. %s" % (sort_key, fetched_volume))
 
         _list_details_with_multiple_params()
         _list_details_with_multiple_params(sort_dir='desc')
@@ -173,9 +164,9 @@
 
             # If cannot follow make sure it's because we have finished
             else:
-                self.assertListEqual([], remaining or [],
-                                     'No more pages reported, but still '
-                                     'missing ids %s' % remaining)
+                self.assertEqual([], remaining or [],
+                                 'No more pages reported, but still '
+                                 'missing ids %s' % remaining)
                 break
 
     @test.idempotent_id('e9138a2c-f67b-4796-8efa-635c196d01de')
@@ -185,3 +176,29 @@
     @test.idempotent_id('af55e775-8e4b-4feb-8719-215c43b0238c')
     def test_volume_list_pagination(self):
         self._test_pagination('volumes', ids=self.volume_id_list, detail=False)
+
+    @test.idempotent_id('46eff077-100b-427f-914e-3db2abcdb7e2')
+    @decorators.skip_because(bug='1572765')
+    def test_volume_list_with_detail_param_marker(self):
+        # Choosing a random volume from a list of volumes for 'marker'
+        # parameter
+        random_volume = random.choice(self.volume_id_list)
+
+        params = {'marker': random_volume}
+
+        # Running volume list using marker parameter
+        vol_with_marker = self.volumes_client.list_volumes(
+            detail=True, params=params)['volumes']
+
+        # Fetching the index of the random volume from volume_id_list
+        index_marker = self.volume_id_list.index(random_volume)
+
+        # The expected list with marker parameter
+        verify_volume_list = self.volume_id_list[:index_marker]
+
+        failed_msg = "Failed to list volume details by marker"
+
+        # Validating the expected list is the same like the observed list
+        self.assertEqual(verify_volume_list,
+                         map(lambda x: x['id'],
+                             vol_with_marker[::-1]), failed_msg)
diff --git a/tempest/api/baremetal/__init__.py b/tempest/api/volume/v3/__init__.py
similarity index 100%
rename from tempest/api/baremetal/__init__.py
rename to tempest/api/volume/v3/__init__.py
diff --git a/tempest/api/volume/v3/base.py b/tempest/api/volume/v3/base.py
new file mode 100644
index 0000000..31fc1eb
--- /dev/null
+++ b/tempest/api/volume/v3/base.py
@@ -0,0 +1,65 @@
+# Copyright 2016 Andrew Kerr
+# 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.volume import api_microversion_fixture
+from tempest.api.volume import base
+from tempest import config
+from tempest.lib.common import api_version_utils
+
+CONF = config.CONF
+
+
+class VolumesV3Test(api_version_utils.BaseMicroversionTest,
+                    base.BaseVolumeTest):
+    """Base test case class for all v3 Cinder API tests."""
+
+    _api_version = 3
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesV3Test, cls).skip_checks()
+        api_version_utils.check_skip_with_microversion(
+            cls.min_microversion, cls.max_microversion,
+            CONF.volume.min_microversion, CONF.volume.max_microversion)
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumesV3Test, cls).resource_setup()
+        cls.request_microversion = (
+            api_version_utils.select_request_microversion(
+                cls.min_microversion,
+                CONF.volume.min_microversion))
+
+    @classmethod
+    def setup_clients(cls):
+        super(VolumesV3Test, cls).setup_clients()
+        cls.messages_client = cls.os.volume_v3_messages_client
+
+    def setUp(self):
+        super(VolumesV3Test, self).setUp()
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            self.request_microversion))
+
+
+class VolumesV3AdminTest(VolumesV3Test,
+                         base.BaseVolumeAdminTest):
+    """Base test case class for all v3 Volume Admin API tests."""
+
+    credentials = ['primary', 'admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(VolumesV3AdminTest, cls).setup_clients()
+        cls.admin_messages_client = cls.os_adm.volume_v3_messages_client
+        cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
diff --git a/tempest/api_schema/request/__init__.py b/tempest/api_schema/request/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api_schema/request/__init__.py
+++ /dev/null
diff --git a/tempest/api_schema/request/compute/__init__.py b/tempest/api_schema/request/compute/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api_schema/request/compute/__init__.py
+++ /dev/null
diff --git a/tempest/api_schema/request/compute/flavors.py b/tempest/api_schema/request/compute/flavors.py
deleted file mode 100644
index adaaf27..0000000
--- a/tempest/api_schema/request/compute/flavors.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# (c) 2014 Deutsche Telekom AG
-#    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_flavor_details = {
-    "name": "get-flavor-details",
-    "http-method": "GET",
-    "url": "flavors/%s",
-    "resources": [
-        {"name": "flavor", "expected_result": 404}
-    ]
-}
-
-common_flavor_list = {
-    "name": "list-flavors-with-detail",
-    "http-method": "GET",
-    "url": "flavors/detail",
-    "json-schema": {
-        "type": "object",
-        "properties": {
-        }
-    }
-}
-
-common_admin_flavor_create = {
-    "name": "flavor-create",
-    "http-method": "POST",
-    "admin_client": True,
-    "url": "flavors",
-    "default_result_code": 400,
-    "json-schema": {
-        "type": "object",
-        "properties": {
-           "flavor": {
-               "type": "object",
-               "properties": {
-                   "name": {"type": "string",
-                            "exclude_tests": ["gen_str_min_length"]},
-                   "ram": {"type": "integer", "minimum": 1},
-                   "vcpus": {"type": "integer", "minimum": 1},
-                   "disk": {"type": "integer"},
-                   "id": {"type": "integer",
-                          "exclude_tests": ["gen_none", "gen_string"]
-                          },
-                   }
-               }
-        }
-    }
-}
diff --git a/tempest/api_schema/request/compute/v2/__init__.py b/tempest/api_schema/request/compute/v2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api_schema/request/compute/v2/__init__.py
+++ /dev/null
diff --git a/tempest/api_schema/request/compute/v2/flavors.py b/tempest/api_schema/request/compute/v2/flavors.py
deleted file mode 100644
index bc459ad..0000000
--- a/tempest/api_schema/request/compute/v2/flavors.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# (c) 2014 Deutsche Telekom AG
-#    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.api_schema.request.compute import flavors
-
-flavors_details = copy.deepcopy(flavors.common_flavor_details)
-
-flavor_list = copy.deepcopy(flavors.common_flavor_list)
-
-flavor_create = copy.deepcopy(flavors.common_admin_flavor_create)
-
-flavor_list["json-schema"]["properties"] = {
-    "minRam": {
-        "type": "integer",
-        "results": {
-            "gen_none": 400,
-            "gen_string": 400
-        }
-    },
-    "minDisk": {
-        "type": "integer",
-        "results": {
-            "gen_none": 400,
-            "gen_string": 400
-        }
-    }
-}
diff --git a/tempest/api_schema/response/compute/v2_2/keypairs.py b/tempest/api_schema/response/compute/v2_2/keypairs.py
deleted file mode 100644
index 5d8d24d..0000000
--- a/tempest/api_schema/response/compute/v2_2/keypairs.py
+++ /dev/null
@@ -1,41 +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 copy
-
-from tempest.api_schema.response.compute.v2_1 import keypairs
-
-get_keypair = copy.deepcopy(keypairs.get_keypair)
-get_keypair['response_body']['properties']['keypair'][
-    'properties'].update({'type': {'type': 'string'}})
-get_keypair['response_body']['properties']['keypair'][
-    'required'].append('type')
-
-create_keypair = copy.deepcopy(keypairs.create_keypair)
-create_keypair['status_code'] = [201]
-create_keypair['response_body']['properties']['keypair'][
-    'properties'].update({'type': {'type': 'string'}})
-create_keypair['response_body']['properties']['keypair'][
-    'required'].append('type')
-
-delete_keypair = {
-    'status_code': [204],
-}
-
-list_keypairs = copy.deepcopy(keypairs.list_keypairs)
-list_keypairs['response_body']['properties']['keypairs'][
-    'items']['properties']['keypair'][
-    'properties'].update({'type': {'type': 'string'}})
-list_keypairs['response_body']['properties']['keypairs'][
-    'items']['properties']['keypair']['required'].append('type')
diff --git a/tempest/clients.py b/tempest/clients.py
index 2a8a4ae..18116f3 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -13,367 +13,55 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import copy
-
 from oslo_log import log as logging
-from tempest_lib.services.compute.agents_client import AgentsClient
-from tempest_lib.services.compute.aggregates_client import AggregatesClient
-from tempest_lib.services.compute.availability_zone_client import \
-    AvailabilityZoneClient
-from tempest_lib.services.compute.baremetal_nodes_client import \
-    BaremetalNodesClient
-from tempest_lib.services.compute.certificates_client import \
-    CertificatesClient
-from tempest_lib.services.compute.extensions_client import \
-    ExtensionsClient
-from tempest_lib.services.compute.fixed_ips_client import FixedIPsClient
-from tempest_lib.services.compute.flavors_client import FlavorsClient
-from tempest_lib.services.compute.floating_ip_pools_client import \
-    FloatingIPPoolsClient
-from tempest_lib.services.compute.floating_ips_bulk_client import \
-    FloatingIPsBulkClient
-from tempest_lib.services.compute.floating_ips_client import \
-    FloatingIPsClient as ComputeFloatingIPsClient
-from tempest_lib.services.compute.hosts_client import HostsClient
-from tempest_lib.services.compute.hypervisor_client import \
-    HypervisorClient
-from tempest_lib.services.compute.images_client import ImagesClient \
-    as ComputeImagesClient
-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.limits_client import LimitsClient
-from tempest_lib.services.compute.migrations_client import MigrationsClient
-from tempest_lib.services.compute.networks_client import NetworksClient \
-    as ComputeNetworksClient
-from tempest_lib.services.compute.quota_classes_client import \
-    QuotaClassesClient
-from tempest_lib.services.compute.quotas_client import QuotasClient
-from tempest_lib.services.compute.security_group_default_rules_client import \
-    SecurityGroupDefaultRulesClient
-from tempest_lib.services.compute.security_group_rules_client import \
-    SecurityGroupRulesClient as ComputeSecurityGroupRulesClient
-from tempest_lib.services.compute.security_groups_client import \
-    SecurityGroupsClient as ComputeSecurityGroupsClient
-from tempest_lib.services.compute.server_groups_client import \
-    ServerGroupsClient
-from tempest_lib.services.compute.servers_client import ServersClient
-from tempest_lib.services.compute.services_client import ServicesClient
-from tempest_lib.services.compute.snapshots_client import \
-    SnapshotsClient as ComputeSnapshotsClient
-from tempest_lib.services.compute.tenant_networks_client import \
-    TenantNetworksClient
-from tempest_lib.services.compute.tenant_usages_client import \
-    TenantUsagesClient
-from tempest_lib.services.compute.versions_client import VersionsClient
-from tempest_lib.services.compute.volumes_client import \
-    VolumesClient as ComputeVolumesClient
-from tempest_lib.services.identity.v2.token_client import TokenClient
-from tempest_lib.services.identity.v3.token_client import V3TokenClient
-from tempest_lib.services.network.agents_client import AgentsClient \
-    as NetworkAgentsClient
-from tempest_lib.services.network.extensions_client import \
-    ExtensionsClient as NetworkExtensionsClient
-from tempest_lib.services.network.floating_ips_client import FloatingIPsClient
-from tempest_lib.services.network.metering_label_rules_client import \
-    MeteringLabelRulesClient
-from tempest_lib.services.network.metering_labels_client import \
-    MeteringLabelsClient
-from tempest_lib.services.network.networks_client import NetworksClient
-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_groups_client import \
-    SecurityGroupsClient
-from tempest_lib.services.network.subnetpools_client import SubnetpoolsClient
-from tempest_lib.services.network.subnets_client import SubnetsClient
 
-from tempest.common import negative_rest_client
 from tempest import config
-from tempest import exceptions
-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 \
-    DatabaseFlavorsClient
-from tempest.services.database.json.limits_client import \
-    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.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
-from tempest.services.identity.v3.json.credentials_client import \
-    CredentialsClient as CredentialsV3Client
-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
-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.services_client import \
-    ServicesClient as IdentityServicesV3Client
-from tempest.services.identity.v3.json.users_clients import 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.object_storage.account_client import AccountClient
-from tempest.services.object_storage.container_client import ContainerClient
-from tempest.services.object_storage.object_client import ObjectClient
-from tempest.services.orchestration.json.orchestration_client import \
-    OrchestrationClient
-from tempest.services.telemetry.json.alarming_client import AlarmingClient
-from tempest.services.telemetry.json.telemetry_client import \
-    TelemetryClient
-from tempest.services.volume.v1.json.admin.hosts_client import \
-    HostsClient as VolumeHostsClient
-from tempest.services.volume.v1.json.admin.quotas_client import \
-    QuotasClient as VolumeQuotasClient
-from tempest.services.volume.v1.json.admin.services_client import \
-    ServicesClient as VolumeServicesClient
-from tempest.services.volume.v1.json.admin.types_client import \
-    TypesClient as VolumeTypesClient
-from tempest.services.volume.v1.json.availability_zone_client import \
-    AvailabilityZoneClient as VolumeAvailabilityZoneClient
-from tempest.services.volume.v1.json.backups_client import BackupsClient
-from tempest.services.volume.v1.json.extensions_client import \
-    ExtensionsClient as VolumeExtensionsClient
-from tempest.services.volume.v1.json.qos_client import QosSpecsClient
-from tempest.services.volume.v1.json.snapshots_client import SnapshotsClient
-from tempest.services.volume.v1.json.volumes_client import VolumesClient
-from tempest.services.volume.v2.json.admin.hosts_client import \
-    HostsClient as VolumeHostsV2Client
-from tempest.services.volume.v2.json.admin.quotas_client import \
-    QuotasClient as VolumeQuotasV2Client
-from tempest.services.volume.v2.json.admin.services_client import \
-    ServicesClient as VolumeServicesV2Client
-from tempest.services.volume.v2.json.admin.types_client import \
-    TypesClient as VolumeTypesV2Client
-from tempest.services.volume.v2.json.availability_zone_client import \
-    AvailabilityZoneClient as VolumeAvailabilityZoneV2Client
-from tempest.services.volume.v2.json.backups_client import \
-    BackupsClient as BackupsV2Client
-from tempest.services.volume.v2.json.extensions_client import \
-    ExtensionsClient as VolumeExtensionsV2Client
-from tempest.services.volume.v2.json.qos_client import \
-    QosSpecsClient as QosSpecsV2Client
-from tempest.services.volume.v2.json.snapshots_client import \
-    SnapshotsClient as SnapshotsV2Client
-from tempest.services.volume.v2.json.volumes_client import \
-    VolumesClient as VolumesV2Client
+from tempest.lib import auth
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services import clients
+from tempest.services import object_storage
+from tempest.services import orchestration
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
-class Manager(manager.Manager):
+class Manager(clients.ServiceClients):
     """Top level manager for OpenStack tempest clients"""
 
-    default_params = {
-        'disable_ssl_certificate_validation':
-            CONF.identity.disable_ssl_certificate_validation,
-        'ca_certs': CONF.identity.ca_certificates_file,
-        'trace_requests': CONF.debug.trace_requests
-    }
+    default_params = config.service_client_config()
 
-    # NOTE: Tempest uses timeout values of compute API if project specific
-    # timeout values don't exist.
+    # TODO(jordanP): remove this once no Tempest plugin use that class
+    # variable.
     default_params_with_timeout_values = {
         'build_interval': CONF.compute.build_interval,
         'build_timeout': CONF.compute.build_timeout
     }
     default_params_with_timeout_values.update(default_params)
 
-    def __init__(self, credentials, service=None, api_microversions=None):
+    def __init__(self, credentials, scope='project'):
         """Initialization of Manager class.
 
         Setup all services clients and make them available for tests cases.
         :param credentials: type Credentials or TestResources
-        :param service: Service name
-        :param api_microversions: This is dict of services catalog type
-               and their microversion which will be set on respective
-               services clients.
-               {<service catalog type>: request_microversion}
-               Example :
-                {'compute': request_microversion}
-                    - request_microversion will be set on all compute
-                      service clients.
-                OR
-                {'compute': request_microversion,
-                 'volume': request_microversion}
-                    - request_microversion of compute will be set on all
-                      compute service clients.
-                    - request_microversion of volume will be set on all
-                      volume service clients.
+        :param scope: default scope for tokens produced by the auth provider
         """
-        super(Manager, self).__init__(credentials=credentials)
-        self.api_microversions = api_microversions or {}
+        _, identity_uri = get_auth_provider_class(credentials)
+        super(Manager, self).__init__(
+            credentials=credentials, identity_uri=identity_uri, scope=scope,
+            region=CONF.identity.region,
+            client_parameters=self._prepare_configuration())
+        # TODO(andreaf) When clients are initialised without the right
+        # parameters available, the calls below will trigger a KeyError.
+        # We should catch that and raise a better error.
         self._set_compute_clients()
-        self._set_database_clients()
         self._set_identity_clients()
         self._set_volume_clients()
         self._set_object_storage_clients()
+        self._set_image_clients()
+        self._set_network_clients()
 
-        self.baremetal_client = BaremetalClient(
-            self.auth_provider,
-            CONF.baremetal.catalog_type,
-            CONF.identity.region,
-            endpoint_type=CONF.baremetal.endpoint_type,
-            **self.default_params_with_timeout_values)
-        self.network_agents_client = NetworkAgentsClient(
-            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.network_extensions_client = NetworkExtensionsClient(
-            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.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,
-            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.subnetpools_client = SubnetpoolsClient(
-            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.subnets_client = SubnetsClient(
-            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.ports_client = PortsClient(
-            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.network_quotas_client = NetworkQuotasClient(
-            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.floating_ips_client = FloatingIPsClient(
-            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.metering_labels_client = MeteringLabelsClient(
-            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.metering_label_rules_client = MeteringLabelRulesClient(
-            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,
-            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_groups_client = SecurityGroupsClient(
-            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)
-        if CONF.service_available.ceilometer:
-            self.telemetry_client = TelemetryClient(
-                self.auth_provider,
-                CONF.telemetry.catalog_type,
-                CONF.identity.region,
-                endpoint_type=CONF.telemetry.endpoint_type,
-                **self.default_params_with_timeout_values)
-        if CONF.service_available.aodh:
-            self.alarming_client = AlarmingClient(
-                self.auth_provider,
-                CONF.alarming.catalog_type,
-                CONF.identity.region,
-                endpoint_type=CONF.alarming.endpoint_type,
-                **self.default_params_with_timeout_values)
-        if CONF.service_available.glance:
-            self.image_client = ImagesClient(
-                self.auth_provider,
-                CONF.image.catalog_type,
-                CONF.image.region or CONF.identity.region,
-                endpoint_type=CONF.image.endpoint_type,
-                build_interval=CONF.image.build_interval,
-                build_timeout=CONF.image.build_timeout,
-                **self.default_params)
-            self.image_client_v2 = ImagesClientV2(
-                self.auth_provider,
-                CONF.image.catalog_type,
-                CONF.image.region or CONF.identity.region,
-                endpoint_type=CONF.image.endpoint_type,
-                build_interval=CONF.image.build_interval,
-                build_timeout=CONF.image.build_timeout,
-                **self.default_params)
-        self.orchestration_client = OrchestrationClient(
+        self.orchestration_client = orchestration.OrchestrationClient(
             self.auth_provider,
             CONF.orchestration.catalog_type,
             CONF.orchestration.region or CONF.identity.region,
@@ -381,257 +69,257 @@
             build_interval=CONF.orchestration.build_interval,
             build_timeout=CONF.orchestration.build_timeout,
             **self.default_params)
-        self.data_processing_client = DataProcessingClient(
-            self.auth_provider,
-            CONF.data_processing.catalog_type,
-            CONF.identity.region,
-            endpoint_type=CONF.data_processing.endpoint_type,
-            **self.default_params_with_timeout_values)
-        self.negative_client = negative_rest_client.NegativeRestClient(
-            self.auth_provider, service, **self.default_params)
 
-        self._set_api_microversions()
+    def _prepare_configuration(self):
+        """Map values from CONF into Manager parameters
+
+        This uses `config.service_client_config` for all services to collect
+        most configuration items needed to init the clients.
+        """
+        # NOTE(andreaf) Once all service clients in Tempest are migrated
+        # to tempest.lib, their configuration will be picked up from the
+        # registry, and this method will become redundant.
+
+        configuration = {}
+
+        # Setup the parameters for all Tempest services which are not in lib.
+        # NOTE(andreaf) Since client.py is an internal module of Tempest,
+        # it doesn't have to consider plugin configuration.
+        for service in clients._tempest_internal_modules():
+            try:
+                # NOTE(andreaf) Use the unversioned service name to fetch
+                # the configuration since configuration is not versioned.
+                service_for_config = service.split('.')[0]
+                if service_for_config not in configuration:
+                    configuration[service_for_config] = (
+                        config.service_client_config(service_for_config))
+            except lib_exc.UnknownServiceClient:
+                LOG.warning(
+                    'Could not load configuration for service %s', service)
+
+        return configuration
+
+    def _set_network_clients(self):
+        self.network_agents_client = self.network.AgentsClient()
+        self.network_extensions_client = self.network.ExtensionsClient()
+        self.networks_client = self.network.NetworksClient()
+        self.subnetpools_client = self.network.SubnetpoolsClient()
+        self.subnets_client = self.network.SubnetsClient()
+        self.ports_client = self.network.PortsClient()
+        self.network_quotas_client = self.network.QuotasClient()
+        self.floating_ips_client = self.network.FloatingIPsClient()
+        self.metering_labels_client = self.network.MeteringLabelsClient()
+        self.metering_label_rules_client = (
+            self.network.MeteringLabelRulesClient())
+        self.routers_client = self.network.RoutersClient()
+        self.security_group_rules_client = (
+            self.network.SecurityGroupRulesClient())
+        self.security_groups_client = self.network.SecurityGroupsClient()
+        self.network_versions_client = self.network.NetworkVersionsClient()
+        self.service_providers_client = self.network.ServiceProvidersClient()
+
+    def _set_image_clients(self):
+        if CONF.service_available.glance:
+            self.image_client = self.image_v1.ImagesClient()
+            self.image_member_client = self.image_v1.ImageMembersClient()
+            self.image_client_v2 = self.image_v2.ImagesClient()
+            self.image_member_client_v2 = self.image_v2.ImageMembersClient()
+            self.namespaces_client = self.image_v2.NamespacesClient()
+            self.resource_types_client = self.image_v2.ResourceTypesClient()
+            self.namespace_objects_client = \
+                self.image_v2.NamespaceObjectsClient()
+            self.schemas_client = self.image_v2.SchemasClient()
+            self.namespace_properties_client = \
+                self.image_v2.NamespacePropertiesClient()
+            self.namespace_tags_client = \
+                self.image_v2.NamespaceTagsClient()
 
     def _set_compute_clients(self):
-        params = {
-            'service': CONF.compute.catalog_type,
-            'region': CONF.compute.region or CONF.identity.region,
-            'endpoint_type': CONF.compute.endpoint_type,
-            'build_interval': CONF.compute.build_interval,
-            'build_timeout': CONF.compute.build_timeout
-        }
-        params.update(self.default_params)
-
-        self.agents_client = AgentsClient(self.auth_provider, **params)
-        self.compute_networks_client = ComputeNetworksClient(
-            self.auth_provider, **params)
-        self.migrations_client = MigrationsClient(self.auth_provider,
-                                                  **params)
+        self.agents_client = self.compute.AgentsClient()
+        self.compute_networks_client = self.compute.NetworksClient()
+        self.migrations_client = self.compute.MigrationsClient()
         self.security_group_default_rules_client = (
-            SecurityGroupDefaultRulesClient(self.auth_provider, **params))
-        self.certificates_client = CertificatesClient(self.auth_provider,
-                                                      **params)
-        self.servers_client = ServersClient(
-            self.auth_provider,
-            enable_instance_password=CONF.compute_feature_enabled
-                .enable_instance_password,
-            **params)
-        self.server_groups_client = ServerGroupsClient(
-            self.auth_provider, **params)
-        self.limits_client = LimitsClient(self.auth_provider, **params)
-        self.compute_images_client = ComputeImagesClient(self.auth_provider,
-                                                         **params)
-        self.keypairs_client = KeyPairsClient(self.auth_provider, **params)
-        self.quotas_client = QuotasClient(self.auth_provider, **params)
-        self.quota_classes_client = QuotaClassesClient(self.auth_provider,
-                                                       **params)
-        self.flavors_client = FlavorsClient(self.auth_provider, **params)
-        self.extensions_client = ExtensionsClient(self.auth_provider,
-                                                  **params)
-        self.floating_ip_pools_client = FloatingIPPoolsClient(
-            self.auth_provider, **params)
-        self.floating_ips_bulk_client = FloatingIPsBulkClient(
-            self.auth_provider, **params)
-        self.compute_floating_ips_client = ComputeFloatingIPsClient(
-            self.auth_provider, **params)
-        self.compute_security_group_rules_client = \
-            ComputeSecurityGroupRulesClient(self.auth_provider, **params)
-        self.compute_security_groups_client = ComputeSecurityGroupsClient(
-            self.auth_provider, **params)
-        self.interfaces_client = InterfacesClient(self.auth_provider,
-                                                  **params)
-        self.fixed_ips_client = FixedIPsClient(self.auth_provider,
-                                               **params)
-        self.availability_zone_client = AvailabilityZoneClient(
-            self.auth_provider, **params)
-        self.aggregates_client = AggregatesClient(self.auth_provider,
-                                                  **params)
-        self.services_client = ServicesClient(self.auth_provider, **params)
-        self.tenant_usages_client = TenantUsagesClient(self.auth_provider,
-                                                       **params)
-        self.hosts_client = HostsClient(self.auth_provider, **params)
-        self.hypervisor_client = HypervisorClient(self.auth_provider,
-                                                  **params)
-        self.instance_usages_audit_log_client = \
-            InstanceUsagesAuditLogClient(self.auth_provider, **params)
-        self.tenant_networks_client = \
-            TenantNetworksClient(self.auth_provider, **params)
-        self.baremetal_nodes_client = BaremetalNodesClient(
-            self.auth_provider, **params)
+            self.compute.SecurityGroupDefaultRulesClient())
+        self.certificates_client = self.compute.CertificatesClient()
+        eip = CONF.compute_feature_enabled.enable_instance_password
+        self.servers_client = self.compute.ServersClient(
+            enable_instance_password=eip)
+        self.server_groups_client = self.compute.ServerGroupsClient()
+        self.limits_client = self.compute.LimitsClient()
+        self.compute_images_client = self.compute.ImagesClient()
+        self.keypairs_client = self.compute.KeyPairsClient()
+        self.quotas_client = self.compute.QuotasClient()
+        self.quota_classes_client = self.compute.QuotaClassesClient()
+        self.flavors_client = self.compute.FlavorsClient()
+        self.extensions_client = self.compute.ExtensionsClient()
+        self.floating_ip_pools_client = self.compute.FloatingIPPoolsClient()
+        self.floating_ips_bulk_client = self.compute.FloatingIPsBulkClient()
+        self.compute_floating_ips_client = self.compute.FloatingIPsClient()
+        self.compute_security_group_rules_client = (
+            self.compute.SecurityGroupRulesClient())
+        self.compute_security_groups_client = (
+            self.compute.SecurityGroupsClient())
+        self.interfaces_client = self.compute.InterfacesClient()
+        self.fixed_ips_client = self.compute.FixedIPsClient()
+        self.availability_zone_client = self.compute.AvailabilityZoneClient()
+        self.aggregates_client = self.compute.AggregatesClient()
+        self.services_client = self.compute.ServicesClient()
+        self.tenant_usages_client = self.compute.TenantUsagesClient()
+        self.baremetal_nodes_client = self.compute.BaremetalNodesClient()
+        self.hosts_client = self.compute.HostsClient()
+        self.hypervisor_client = self.compute.HypervisorClient()
+        self.instance_usages_audit_log_client = (
+            self.compute.InstanceUsagesAuditLogClient())
+        self.tenant_networks_client = self.compute.TenantNetworksClient()
 
         # NOTE: The following client needs special timeout values because
         # the API is a proxy for the other component.
-        params_volume = copy.deepcopy(params)
-        params_volume.update({
-            'build_interval': CONF.volume.build_interval,
-            'build_timeout': CONF.volume.build_timeout
-        })
-        self.volumes_extensions_client = ComputeVolumesClient(
-            self.auth_provider, **params_volume)
-        self.compute_versions_client = VersionsClient(self.auth_provider,
-                                                      **params_volume)
-        self.snapshots_extensions_client = ComputeSnapshotsClient(
-            self.auth_provider, **params_volume)
-
-    def _set_database_clients(self):
-        self.database_flavors_client = DatabaseFlavorsClient(
-            self.auth_provider,
-            CONF.database.catalog_type,
-            CONF.identity.region,
-            **self.default_params_with_timeout_values)
-        self.database_limits_client = DatabaseLimitsClient(
-            self.auth_provider,
-            CONF.database.catalog_type,
-            CONF.identity.region,
-            **self.default_params_with_timeout_values)
-        self.database_versions_client = DatabaseVersionsClient(
-            self.auth_provider,
-            CONF.database.catalog_type,
-            CONF.identity.region,
-            **self.default_params_with_timeout_values)
+        params_volume = {}
+        for _key in ('build_interval', 'build_timeout'):
+            _value = self.parameters['volume'].get(_key)
+            if _value:
+                params_volume[_key] = _value
+        self.volumes_extensions_client = self.compute.VolumesClient(
+            **params_volume)
+        self.compute_versions_client = self.compute.VersionsClient(
+            **params_volume)
+        self.snapshots_extensions_client = self.compute.SnapshotsClient(
+            **params_volume)
 
     def _set_identity_clients(self):
-        params = {
-            'service': CONF.identity.catalog_type,
-            'region': CONF.identity.region
-        }
-        params.update(self.default_params_with_timeout_values)
-
         # 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.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)
+        params_v2_admin = {
+            'endpoint_type': CONF.identity.v2_admin_endpoint_type}
+        self.endpoints_client = self.identity_v2.EndpointsClient(
+            **params_v2_admin)
+        self.identity_client = self.identity_v2.IdentityClient(
+            **params_v2_admin)
+        self.tenants_client = self.identity_v2.TenantsClient(
+            **params_v2_admin)
+        self.roles_client = self.identity_v2.RolesClient(**params_v2_admin)
+        self.users_client = self.identity_v2.UsersClient(**params_v2_admin)
+        self.identity_services_client = self.identity_v2.ServicesClient(
+            **params_v2_admin)
 
         # Clients below use the public endpoint type of Keystone API v2
-        params_v2_public = params.copy()
-        params_v2_public['endpoint_type'] = (
-            CONF.identity.v2_public_endpoint_type)
-        self.identity_public_client = IdentityClient(self.auth_provider,
-                                                     **params_v2_public)
-        self.tenants_public_client = TenantsClient(self.auth_provider,
-                                                   **params_v2_public)
-        self.users_public_client = UsersClient(self.auth_provider,
-                                               **params_v2_public)
+        params_v2_public = {
+            'endpoint_type': CONF.identity.v2_public_endpoint_type}
+        self.identity_public_client = self.identity_v2.IdentityClient(
+            **params_v2_public)
+        self.tenants_public_client = self.identity_v2.TenantsClient(
+            **params_v2_public)
+        self.users_public_client = self.identity_v2.UsersClient(
+            **params_v2_public)
 
-        # Clients below use the endpoint type of Keystone API v3
-        params_v3 = params.copy()
-        params_v3['endpoint_type'] = CONF.identity.v3_endpoint_type
-        self.identity_v3_client = IdentityV3Client(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.identity_services_client = IdentityServicesV3Client(
-            self.auth_provider, **params_v3)
-        self.policies_client = PoliciesV3Client(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)
+        # Clients below use the endpoint type of Keystone API v3, which is set
+        # in endpoint_type
+        params_v3 = {'endpoint_type': CONF.identity.v3_endpoint_type}
+        self.domains_client = self.identity_v3.DomainsClient(**params_v3)
+        self.identity_v3_client = self.identity_v3.IdentityClient(**params_v3)
+        self.trusts_client = self.identity_v3.TrustsClient(**params_v3)
+        self.users_v3_client = self.identity_v3.UsersClient(**params_v3)
+        self.endpoints_v3_client = self.identity_v3.EndPointsClient(
+            **params_v3)
+        self.roles_v3_client = self.identity_v3.RolesClient(**params_v3)
+        self.inherited_roles_client = self.identity_v3.InheritedRolesClient(
+            **params_v3)
+        self.role_assignments_client = self.identity_v3.RoleAssignmentsClient(
+            **params_v3)
+        self.identity_services_v3_client = self.identity_v3.ServicesClient(
+            **params_v3)
+        self.policies_client = self.identity_v3.PoliciesClient(**params_v3)
+        self.projects_client = self.identity_v3.ProjectsClient(**params_v3)
+        self.regions_client = self.identity_v3.RegionsClient(**params_v3)
+        self.credentials_client = self.identity_v3.CredentialsClient(
+            **params_v3)
+        self.groups_client = self.identity_v3.GroupsClient(**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
         # API version is marked as enabled
         if CONF.identity_feature_enabled.api_v2:
             if CONF.identity.uri:
-                self.token_client = TokenClient(
-                    CONF.identity.uri, **self.default_params)
+                self.token_client = self.identity_v2.TokenClient(
+                    auth_url=CONF.identity.uri)
             else:
                 msg = 'Identity v2 API enabled, but no identity.uri set'
-                raise exceptions.InvalidConfiguration(msg)
+                raise lib_exc.InvalidConfiguration(msg)
         if CONF.identity_feature_enabled.api_v3:
             if CONF.identity.uri_v3:
-                self.token_v3_client = V3TokenClient(
-                    CONF.identity.uri_v3, **self.default_params)
+                self.token_v3_client = self.identity_v3.V3TokenClient(
+                    auth_url=CONF.identity.uri_v3)
             else:
                 msg = 'Identity v3 API enabled, but no identity.uri_v3 set'
-                raise exceptions.InvalidConfiguration(msg)
+                raise lib_exc.InvalidConfiguration(msg)
 
     def _set_volume_clients(self):
-        params = {
-            'service': CONF.volume.catalog_type,
-            'region': CONF.volume.region or CONF.identity.region,
-            'endpoint_type': CONF.volume.endpoint_type,
-            'build_interval': CONF.volume.build_interval,
-            'build_timeout': CONF.volume.build_timeout
-        }
-        params.update(self.default_params)
 
-        self.volume_qos_client = QosSpecsClient(self.auth_provider,
-                                                **params)
-        self.volume_qos_v2_client = QosSpecsV2Client(
-            self.auth_provider, **params)
-        self.volume_services_client = VolumeServicesClient(
-            self.auth_provider, **params)
-        self.volume_services_v2_client = VolumeServicesV2Client(
-            self.auth_provider, **params)
-        self.backups_client = BackupsClient(self.auth_provider, **params)
-        self.backups_v2_client = BackupsV2Client(self.auth_provider,
-                                                 **params)
-        self.snapshots_client = SnapshotsClient(self.auth_provider,
-                                                **params)
-        self.snapshots_v2_client = SnapshotsV2Client(self.auth_provider,
-                                                     **params)
-        self.volumes_client = VolumesClient(
-            self.auth_provider, default_volume_size=CONF.volume.volume_size,
-            **params)
-        self.volumes_v2_client = VolumesV2Client(
-            self.auth_provider, default_volume_size=CONF.volume.volume_size,
-            **params)
-        self.volume_types_client = VolumeTypesClient(self.auth_provider,
-                                                     **params)
-        self.volume_types_v2_client = VolumeTypesV2Client(
-            self.auth_provider, **params)
-        self.volume_hosts_client = VolumeHostsClient(self.auth_provider,
-                                                     **params)
-        self.volume_hosts_v2_client = VolumeHostsV2Client(
-            self.auth_provider, **params)
-        self.volume_quotas_client = VolumeQuotasClient(self.auth_provider,
-                                                       **params)
-        self.volume_quotas_v2_client = VolumeQuotasV2Client(self.auth_provider,
-                                                            **params)
-        self.volumes_extension_client = VolumeExtensionsClient(
-            self.auth_provider, **params)
-        self.volumes_v2_extension_client = VolumeExtensionsV2Client(
-            self.auth_provider, **params)
+        self.volume_qos_client = self.volume_v1.QosSpecsClient()
+        self.volume_qos_v2_client = self.volume_v2.QosSpecsClient()
+        self.volume_services_client = self.volume_v1.ServicesClient()
+        self.volume_services_v2_client = self.volume_v2.ServicesClient()
+        self.backups_client = self.volume_v1.BackupsClient()
+        self.backups_v2_client = self.volume_v2.BackupsClient()
+        self.encryption_types_client = self.volume_v1.EncryptionTypesClient()
+        self.encryption_types_v2_client = \
+            self.volume_v2.EncryptionTypesClient()
+        self.snapshot_manage_v2_client = self.volume_v2.SnapshotManageClient()
+        self.snapshots_client = self.volume_v1.SnapshotsClient()
+        self.snapshots_v2_client = self.volume_v2.SnapshotsClient()
+        self.volumes_client = self.volume_v1.VolumesClient()
+        self.volumes_v2_client = self.volume_v2.VolumesClient()
+        self.volume_v3_messages_client = self.volume_v3.MessagesClient()
+        self.volume_types_client = self.volume_v1.TypesClient()
+        self.volume_types_v2_client = self.volume_v2.TypesClient()
+        self.volume_hosts_client = self.volume_v1.HostsClient()
+        self.volume_hosts_v2_client = self.volume_v2.HostsClient()
+        self.volume_quotas_client = self.volume_v1.QuotasClient()
+        self.volume_quotas_v2_client = self.volume_v2.QuotasClient()
+        self.volumes_extension_client = self.volume_v1.ExtensionsClient()
+        self.volumes_v2_extension_client = self.volume_v2.ExtensionsClient()
         self.volume_availability_zone_client = \
-            VolumeAvailabilityZoneClient(self.auth_provider, **params)
+            self.volume_v1.AvailabilityZoneClient()
         self.volume_v2_availability_zone_client = \
-            VolumeAvailabilityZoneV2Client(self.auth_provider, **params)
+            self.volume_v2.AvailabilityZoneClient()
+        self.volume_limits_client = self.volume_v1.LimitsClient()
+        self.volume_v2_limits_client = self.volume_v2.LimitsClient()
+        self.volume_capabilities_v2_client = \
+            self.volume_v2.CapabilitiesClient()
+        self.volume_scheduler_stats_v2_client = \
+            self.volume_v2.SchedulerStatsClient()
 
     def _set_object_storage_clients(self):
-        params = {
-            'service': CONF.object_storage.catalog_type,
-            'region': CONF.object_storage.region or CONF.identity.region,
-            'endpoint_type': CONF.object_storage.endpoint_type
-        }
-        params.update(self.default_params_with_timeout_values)
+        # Mandatory parameters (always defined)
+        params = self.parameters['object-storage']
 
-        self.account_client = AccountClient(self.auth_provider, **params)
-        self.container_client = ContainerClient(self.auth_provider, **params)
-        self.object_client = ObjectClient(self.auth_provider, **params)
+        self.account_client = object_storage.AccountClient(self.auth_provider,
+                                                           **params)
+        self.capabilities_client = object_storage.CapabilitiesClient(
+            self.auth_provider, **params)
+        self.container_client = object_storage.ContainerClient(
+            self.auth_provider, **params)
+        self.object_client = object_storage.ObjectClient(self.auth_provider,
+                                                         **params)
 
-    def _set_api_microversions(self):
-        service_clients = [x for x in self.__dict__ if x.endswith('_client')]
-        for client in service_clients:
-            client_obj = getattr(self, client)
-            microversion = self.api_microversions.get(client_obj.service)
-            if microversion:
-                if hasattr(client_obj, 'set_api_microversion'):
-                    client_obj.set_api_microversion(microversion)
-                else:
-                    LOG.debug("Need to implement set_api_microversion on %s"
-                              % client)
+
+def get_auth_provider_class(credentials):
+    if isinstance(credentials, auth.KeystoneV3Credentials):
+        return auth.KeystoneV3AuthProvider, CONF.identity.uri_v3
+    else:
+        return auth.KeystoneV2AuthProvider, CONF.identity.uri
+
+
+def get_auth_provider(credentials, pre_auth=False, scope='project'):
+    # kwargs for auth provider match the common ones used by service clients
+    default_params = config.service_client_config()
+    if credentials is None:
+        raise lib_exc.InvalidCredentials(
+            'Credentials must be specified')
+    auth_provider_class, auth_url = get_auth_provider_class(
+        credentials)
+    _auth_provider = auth_provider_class(credentials, auth_url,
+                                         scope=scope,
+                                         **default_params)
+    if pre_auth:
+        _auth_provider.set_auth()
+    return _auth_provider
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 9e98d90..172d9e1 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -22,7 +22,7 @@
 credentials for created users, so each user will be in separate tenant and
 have the username, tenant_name, password and roles.
 
-**Usage:** ``tempest-account-generator [-h] [OPTIONS] accounts_file.yaml``.
+**Usage:** ``tempest account-generator [-h] [OPTIONS] accounts_file.yaml``.
 
 Positional Arguments
 --------------------
@@ -38,15 +38,17 @@
 of your cloud to operate properly. The corresponding info can be given either
 through CLI options or environment variables.
 
-You're probably familiar with these, but just to remind::
+You're probably familiar with these, but just to remind:
 
-    +----------+------------------+----------------------+
-    | Param    | CLI              | Environment Variable |
-    +----------+------------------+----------------------+
-    | Username | --os-username    | OS_USERNAME          |
-    | Password | --os-password    | OS_PASSWORD          |
-    | Tenant   | --os-tenant-name | OS_TENANT_NAME       |
-    +----------+------------------+----------------------+
+======== ======================== ====================
+Param    CLI                      Environment Variable
+======== ======================== ====================
+Username --os-username            OS_USERNAME
+Password --os-password            OS_PASSWORD
+Project  --os-project-name        OS_PROJECT_NAME
+Tenant   --os-tenant-name (depr.) OS_TENANT_NAME
+Domain   --os-domain-name         OS_DOMAIN_NAME
+======== ======================== ====================
 
 Optional Arguments
 ------------------
@@ -63,8 +65,14 @@
 **--os-password <auth-password>** (Optional) Password used for authentication
 with the OpenStack Identity service. Defaults to env[OS_PASSWORD].
 
-**--os-tenant-name <auth-tenant-name>** (Optional) Tenant to request
-authorization on. Defaults to env[OS_TENANT_NAME].
+**--os-project-name <auth-project-name>** (Optional) Project to request
+authorization on. Defaults to env[OS_PROJECT_NAME].
+
+**--os-tenant-name <auth-tenant-name>** (Optional, deprecated) Tenant to
+request authorization on. Defaults to env[OS_TENANT_NAME].
+
+**--os-domain-name <auth-domain-name>** (Optional) Domain the user and project
+belong to. Defaults to env[OS_DOMAIN_NAME].
 
 **--tag TAG** (Optional) Resources tag. Each created resource (user, project)
 will have the prefix with the given TAG in its name. Using tag is recommended
@@ -79,31 +87,24 @@
 **--with-admin** (Optional) Creates admin for each concurrent group
 (default: False).
 
-To see help on specific argument, please do: ``tempest-account-generator
+**-i VERSION**, **--identity-version VERSION** (Optional) Provisions accounts
+using the specified version of the identity API. (default: '3').
+
+To see help on specific argument, please do: ``tempest account-generator
 [OPTIONS] <accounts_file.yaml> -h``.
 """
 import argparse
-import netaddr
 import os
 import traceback
 
 from cliff import command
 from oslo_log import log as logging
-import tempest_lib.auth
-from tempest_lib.common.utils import data_utils
-import tempest_lib.exceptions
-from tempest_lib.services.network import networks_client
-from tempest_lib.services.network import subnets_client
 import yaml
 
-from tempest.common import identity
+from tempest.common import credentials_factory
+from tempest.common import dynamic_creds
 from tempest import config
-from tempest import exceptions as exc
-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.network.json import network_client
+
 
 LOG = None
 CONF = config.CONF
@@ -120,288 +121,91 @@
     LOG = logging.getLogger(__name__)
 
 
-def get_admin_clients(opts):
-    _creds = tempest_lib.auth.KeystoneV2Credentials(
-        username=opts.os_username,
-        password=opts.os_password,
-        tenant_name=opts.os_tenant_name)
-    auth_params = {
-        'disable_ssl_certificate_validation':
-            CONF.identity.disable_ssl_certificate_validation,
-        'ca_certs': CONF.identity.ca_certificates_file,
-        'trace_requests': CONF.debug.trace_requests
-    }
-    _auth = tempest_lib.auth.KeystoneV2AuthProvider(
-        _creds, CONF.identity.uri, **auth_params)
-    params = {
-        'disable_ssl_certificate_validation':
-            CONF.identity.disable_ssl_certificate_validation,
-        'ca_certs': CONF.identity.ca_certificates_file,
-        'trace_requests': CONF.debug.trace_requests,
-        'build_interval': CONF.compute.build_interval,
-        'build_timeout': CONF.compute.build_timeout
-    }
-    identity_admin = identity_client.IdentityClient(
-        _auth,
-        CONF.identity.catalog_type,
-        CONF.identity.region,
-        endpoint_type='adminURL',
-        **params
-    )
-    tenants_admin = tenants_client.TenantsClient(
-        _auth,
-        CONF.identity.catalog_type,
-        CONF.identity.region,
-        endpoint_type='adminURL',
-        **params
-    )
-    roles_admin = roles_client.RolesClient(
-        _auth,
-        CONF.identity.catalog_type,
-        CONF.identity.region,
-        endpoint_type='adminURL',
-        **params
-    )
-    users_admin = users_client.UsersClient(
-        _auth,
-        CONF.identity.catalog_type,
-        CONF.identity.region,
-        endpoint_type='adminURL',
-        **params
-    )
-    network_admin = None
-    networks_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(
-            _auth,
-            CONF.network.catalog_type,
-            CONF.network.region or CONF.identity.region,
-            endpoint_type='adminURL',
-            **params)
-        networks_admin = networks_client.NetworksClient(
-            _auth,
-            CONF.network.catalog_type,
-            CONF.network.region or CONF.identity.region,
-            endpoint_type='adminURL',
-            **params)
-        subnets_admin = subnets_client.SubnetsClient(
-            _auth,
-            CONF.network.catalog_type,
-            CONF.network.region or CONF.identity.region,
-            endpoint_type='adminURL',
-            **params)
-    return (identity_admin, tenants_admin, roles_admin, users_admin,
-            neutron_iso_networks, network_admin, networks_admin, subnets_admin)
+def get_credential_provider(opts):
+    identity_version = "".join(['v', str(opts.identity_version)])
+    # NOTE(andreaf) For now tempest.conf controls whether resources will
+    # actually be created. Once we remove the dependency from tempest.conf
+    # we will need extra CLI option(s) to control this.
+    network_resources = {'router': True,
+                         'network': True,
+                         'subnet': True,
+                         'dhcp': True}
+    admin_creds_dict = {'username': opts.os_username,
+                        'password': opts.os_password}
+    _project_name = opts.os_project_name or opts.os_tenant_name
+    if opts.identity_version == 3:
+        admin_creds_dict['project_name'] = _project_name
+        admin_creds_dict['domain_name'] = opts.os_domain_name or 'Default'
+    elif opts.identity_version == 2:
+        admin_creds_dict['tenant_name'] = _project_name
+    admin_creds = credentials_factory.get_credentials(
+        fill_in=False, identity_version=identity_version, **admin_creds_dict)
+    return dynamic_creds.DynamicCredentialProvider(
+        identity_version=identity_version,
+        name=opts.tag,
+        network_resources=network_resources,
+        neutron_available=CONF.service_available.neutron,
+        create_networks=CONF.auth.create_isolated_networks,
+        identity_admin_role=CONF.identity.admin_role,
+        identity_admin_domain_scope=CONF.identity.admin_domain_scope,
+        project_network_cidr=CONF.network.project_network_cidr,
+        project_network_mask_bits=CONF.network.project_network_mask_bits,
+        public_network_id=CONF.network.public_network_id,
+        admin_creds=admin_creds,
+        **credentials_factory.get_dynamic_provider_params())
 
 
-def create_resources(opts, resources):
-    (identity_admin, tenants_admin, roles_admin, users_admin,
-     neutron_iso_networks, network_admin, networks_admin,
-     subnets_admin) = get_admin_clients(opts)
-    roles = roles_admin.list_roles()['roles']
-    for u in resources['users']:
-        u['role_ids'] = []
-        for r in u.get('roles', ()):
-            try:
-                role = filter(lambda r_: r_['name'] == r, roles)[0]
-            except IndexError:
-                msg = "Role: %s doesn't exist" % r
-                raise exc.InvalidConfiguration(msg)
-            u['role_ids'] += [role['id']]
-    existing = [x['name'] for x in tenants_admin.list_tenants()['tenants']]
-    for tenant in resources['tenants']:
-        if tenant not in existing:
-            tenants_admin.create_tenant(tenant)
-        else:
-            LOG.warning("Tenant '%s' already exists in this environment"
-                        % tenant)
-    LOG.info('Tenants created')
-    for u in resources['users']:
-        try:
-            tenant = identity.get_tenant_by_name(tenants_admin, u['tenant'])
-        except tempest_lib.exceptions.NotFound:
-            LOG.error("Tenant: %s - not found" % u['tenant'])
-            continue
-        while True:
-            try:
-                identity.get_user_by_username(tenants_admin,
-                                              tenant['id'], u['name'])
-            except tempest_lib.exceptions.NotFound:
-                users_admin.create_user(
-                    u['name'], u['pass'], tenant['id'],
-                    "%s@%s" % (u['name'], tenant['id']),
-                    enabled=True)
-                break
-            else:
-                LOG.warning("User '%s' already exists in this environment. "
-                            "New name generated" % u['name'])
-                u['name'] = random_user_name(opts.tag, u['prefix'])
-
-    LOG.info('Users created')
-    if neutron_iso_networks:
-        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'])
-            u['network'] = network_name
-            u['router'] = router_name
-        LOG.info('Networks created')
-    for u in resources['users']:
-        try:
-            tenant = identity.get_tenant_by_name(tenants_admin, u['tenant'])
-        except tempest_lib.exceptions.NotFound:
-            LOG.error("Tenant: %s - not found" % u['tenant'])
-            continue
-        try:
-            user = identity.get_user_by_username(tenants_admin,
-                                                 tenant['id'], u['name'])
-        except tempest_lib.exceptions.NotFound:
-            LOG.error("User: %s - not found" % u['user'])
-            continue
-        for r in u['role_ids']:
-            try:
-                roles_admin.assign_user_role(tenant['id'], user['id'], r)
-            except tempest_lib.exceptions.Conflict:
-                # don't care if it's already assigned
-                pass
-    LOG.info('Roles assigned')
-    LOG.info('Resources deployed successfully!')
-
-
-def create_network_resources(network_admin_client, networks_admin_client,
-                             subnets_admin_client, tenant_id, name):
-
-    def _create_network(name):
-        resp_body = networks_admin_client.create_network(
-            name=name, tenant_id=tenant_id)
-        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
-        for subnet_cidr in base_cidr.subnet(mask_bits):
-            try:
-                resp_body = subnets_admin_client.\
-                    create_subnet(
-                        network_id=network_id, cidr=str(subnet_cidr),
-                        name=subnet_name,
-                        tenant_id=tenant_id,
-                        enable_dhcp=True,
-                        ip_version=4)
-                break
-            except tempest_lib.exceptions.BadRequest as e:
-                if 'overlaps with another subnet' not in str(e):
-                    raise
-        else:
-            message = 'Available CIDR for subnet creation could not be found'
-            raise Exception(message)
-        return resp_body['subnet']
-
-    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,
-            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,
-                                                  subnet_id=subnet_id)
-
-    network_name = name + "-network"
-    network = _create_network(network_name)
-    subnet_name = name + "-subnet"
-    subnet = _create_subnet(subnet_name, network['id'])
-    router_name = name + "-router"
-    router = _create_router(router_name)
-    _add_router_interface(router['id'], subnet['id'])
-    return network_name, router_name
-
-
-def random_user_name(tag, prefix):
-    if tag:
-        return data_utils.rand_name('-'.join((tag, prefix)))
-    else:
-        return data_utils.rand_name(prefix)
-
-
-def generate_resources(opts):
-    spec = [{'number': 1,
-             'prefix': 'primary',
-             'roles': (CONF.auth.tempest_roles +
-                       [CONF.object_storage.operator_role])},
-            {'number': 1,
-             'prefix': 'alt',
-             'roles': (CONF.auth.tempest_roles +
-                       [CONF.object_storage.operator_role])}]
+def generate_resources(cred_provider, admin):
+    # Create the list of resources to be provisioned for each process
+    # NOTE(andreaf) get_credentials expects a string for types or a list for
+    # roles. Adding all required inputs to the spec list.
+    spec = ['primary', 'alt']
     if CONF.service_available.swift:
-        spec.append({'number': 1,
-                     'prefix': 'swift_operator',
-                     'roles': (CONF.auth.tempest_roles +
-                               [CONF.object_storage.operator_role])})
-        spec.append({'number': 1,
-                     'prefix': 'swift_reseller_admin',
-                     'roles': (CONF.auth.tempest_roles +
-                               [CONF.object_storage.reseller_admin_role])})
+        spec.append([CONF.object_storage.operator_role])
+        spec.append([CONF.object_storage.reseller_admin_role])
     if CONF.service_available.heat:
-        spec.append({'number': 1,
-                     'prefix': 'stack_owner',
-                     'roles': (CONF.auth.tempest_roles +
-                               [CONF.orchestration.stack_owner_role])})
-    if opts.admin:
-        spec.append({
-            'number': 1,
-            'prefix': 'admin',
-            'roles': (CONF.auth.tempest_roles +
-                      [CONF.identity.admin_role])
-        })
-    resources = {'tenants': [],
-                 'users': []}
-    for count in range(opts.concurrency):
-        for user_group in spec:
-            users = [random_user_name(opts.tag, user_group['prefix'])
-                     for _ in range(user_group['number'])]
-            for user in users:
-                tenant = '-'.join((user, 'tenant'))
-                resources['tenants'].append(tenant)
-                resources['users'].append({
-                    'tenant': tenant,
-                    'name': user,
-                    'pass': data_utils.rand_password(),
-                    'prefix': user_group['prefix'],
-                    'roles': user_group['roles']
-                })
+        spec.append([CONF.orchestration.stack_owner_role,
+                     CONF.object_storage.operator_role])
+    if admin:
+        spec.append('admin')
+    resources = []
+    for cred_type in spec:
+        resources.append((cred_type, cred_provider.get_credentials(
+            credential_type=cred_type)))
     return resources
 
 
-def dump_accounts(opts, resources):
+def dump_accounts(resources, identity_version, account_file):
     accounts = []
-    for user in resources['users']:
+    for resource in resources:
+        cred_type, test_resource = resource
         account = {
-            'username': user['name'],
-            'tenant_name': user['tenant'],
-            'password': user['pass'],
-            'roles': user['roles']
+            'username': test_resource.username,
+            'password': test_resource.password
         }
-        if 'network' in user or 'router' in user:
+        if identity_version == 3:
+            account['project_name'] = test_resource.project_name
+            account['domain_name'] = test_resource.domain_name
+        else:
+            account['project_name'] = test_resource.tenant_name
+
+        # If the spec includes 'admin' credentials are defined via type,
+        # else they are defined via list of roles.
+        if cred_type == 'admin':
+            account['types'] = [cred_type]
+        elif cred_type not in ['primary', 'alt']:
+            account['roles'] = cred_type
+
+        if test_resource.network:
             account['resources'] = {}
-        if 'network' in user:
-            account['resources']['network'] = user['network']
-        if 'router' in user:
-            account['resources']['router'] = user['router']
+        if test_resource.network:
+            account['resources']['network'] = test_resource.network['name']
         accounts.append(account)
-    if os.path.exists(opts.accounts):
-        os.rename(opts.accounts, '.'.join((opts.accounts, 'bak')))
-    with open(opts.accounts, 'w') as f:
-        yaml.dump(accounts, f, default_flow_style=False)
-    LOG.info('%s generated successfully!' % opts.accounts)
+    if os.path.exists(account_file):
+        os.rename(account_file, '.'.join((account_file, 'bak')))
+    with open(account_file, 'w') as f:
+        yaml.safe_dump(accounts, f, default_flow_style=False)
+    LOG.info('%s generated successfully!', account_file)
 
 
 def _parser_add_args(parser):
@@ -418,10 +222,18 @@
                         metavar='<auth-password>',
                         default=os.environ.get('OS_PASSWORD'),
                         help='Defaults to env[OS_PASSWORD].')
+    parser.add_argument('--os-project-name',
+                        metavar='<auth-project-name>',
+                        default=os.environ.get('OS_PROJECT_NAME'),
+                        help='Defaults to env[OS_PROJECT_NAME].')
     parser.add_argument('--os-tenant-name',
                         metavar='<auth-tenant-name>',
                         default=os.environ.get('OS_TENANT_NAME'),
                         help='Defaults to env[OS_TENANT_NAME].')
+    parser.add_argument('--os-domain-name',
+                        metavar='<auth-domain-name>',
+                        default=os.environ.get('OS_DOMAIN_NAME'),
+                        help='Defaults to env[OS_DOMAIN_NAME].')
     parser.add_argument('--tag',
                         default='',
                         required=False,
@@ -430,22 +242,29 @@
     parser.add_argument('-r', '--concurrency',
                         default=1,
                         type=int,
-                        required=True,
+                        required=False,
                         dest='concurrency',
                         help='Concurrency count')
     parser.add_argument('--with-admin',
                         action='store_true',
                         dest='admin',
                         help='Creates admin for each concurrent group')
+    parser.add_argument('-i', '--identity-version',
+                        default=3,
+                        choices=[2, 3],
+                        type=int,
+                        required=False,
+                        dest='identity_version',
+                        help='Version of the Identity API to use')
     parser.add_argument('accounts',
                         metavar='accounts_file.yaml',
                         help='Output accounts yaml file')
 
 
 def get_options():
-    usage_string = ('tempest-account-generator [-h] <ARG> ...\n\n'
+    usage_string = ('tempest account-generator [-h] <ARG> ...\n\n'
                     'To see help on specific argument, do:\n'
-                    'tempest-account-generator <ARG> -h')
+                    'tempest account-generator <ARG> -h')
     parser = argparse.ArgumentParser(
         description=DESCRIPTION,
         formatter_class=argparse.ArgumentDefaultsHelpFormatter,
@@ -466,12 +285,11 @@
 
     def take_action(self, parsed_args):
         try:
-            return main(parsed_args)
+            main(parsed_args)
         except Exception:
             LOG.exception("Failure generating test accounts.")
             traceback.print_exc()
             raise
-        return 0
 
     def get_description(self):
         return DESCRIPTION
@@ -480,14 +298,21 @@
 def main(opts=None):
     setup_logging()
     if not opts:
-        LOG.warn("Use of: 'tempest-account-generator' is deprecated, "
-                 "please use: 'tempest account-generator'")
+        LOG.warning("Use of: 'tempest-account-generator' is deprecated, "
+                    "please use: 'tempest account-generator'")
         opts = get_options()
     if opts.config_file:
         config.CONF.set_config_path(opts.config_file)
-    resources = generate_resources(opts)
-    create_resources(opts, resources)
-    dump_accounts(opts, resources)
+    if opts.os_tenant_name:
+        LOG.warning("'os-tenant-name' and 'OS_TENANT_NAME' are both "
+                    "deprecated, please use 'os-project-name' or "
+                    "'OS_PROJECT_NAME' instead")
+    resources = []
+    for count in range(opts.concurrency):
+        # Use N different cred_providers to obtain different sets of creds
+        cred_provider = get_credential_provider(opts)
+        resources.extend(generate_resources(cred_provider, opts.admin))
+    dump_accounts(resources, opts.identity_version, opts.accounts)
 
 if __name__ == "__main__":
     main()
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index 5a52043..ec76103 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -71,9 +71,6 @@
 
 class TempestCleanup(command.Command):
 
-    def __init__(self, app, cmd):
-        super(TempestCleanup, self).__init__(app, cmd)
-
     def take_action(self, parsed_args):
         try:
             self.init(parsed_args)
@@ -83,7 +80,6 @@
             LOG.exception("Failure during cleanup")
             traceback.print_exc()
             raise
-        return 0
 
     def init(self, parsed_args):
         cleanup_service.init_conf()
@@ -110,14 +106,13 @@
         self._load_json()
 
     def _cleanup(self):
-        print ("Begin cleanup")
+        print("Begin cleanup")
         is_dry_run = self.options.dry_run
         is_preserve = not self.options.delete_tempest_conf_objects
         is_save_state = False
 
         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
@@ -129,7 +124,7 @@
                   'is_save_state': is_save_state}
         tenant_service = cleanup_service.TenantService(admin_mgr, **kwargs)
         tenants = tenant_service.list()
-        print ("Process %s tenants" % len(tenants))
+        print("Process %s tenants" % len(tenants))
 
         # Loop through list of tenants and clean them up.
         for tenant in tenants:
@@ -146,21 +141,21 @@
             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()
 
     def _remove_admin_user_roles(self):
         tenant_ids = self.admin_role_added
-        LOG.debug("Removing admin user roles where needed for tenants: %s"
-                  % tenant_ids)
+        LOG.debug("Removing admin user roles where needed for tenants: %s",
+                  tenant_ids)
         for tenant_id in tenant_ids:
             self._remove_admin_role(tenant_id)
 
     def _clean_tenant(self, tenant):
-        print ("Cleaning tenant:  %s " % tenant['name'])
+        print("Cleaning tenant:  %s " % tenant['name'])
         is_dry_run = self.options.dry_run
         dry_run_data = self.dry_run_data
         is_preserve = not self.options.delete_tempest_conf_objects
@@ -191,7 +186,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,
@@ -234,42 +229,44 @@
     def _add_admin(self, tenant_id):
         rl_cl = self.admin_mgr.roles_client
         needs_role = True
-        roles = rl_cl.list_user_roles(tenant_id, self.admin_id)['roles']
+        roles = rl_cl.list_user_roles_on_project(tenant_id,
+                                                 self.admin_id)['roles']
         for role in roles:
             if role['id'] == self.admin_role_id:
                 needs_role = False
                 LOG.debug("User already had admin privilege for this tenant")
         if needs_role:
-            LOG.debug("Adding admin privilege for : %s" % tenant_id)
-            rl_cl.assign_user_role(tenant_id, self.admin_id,
-                                   self.admin_role_id)
+            LOG.debug("Adding admin privilege for : %s", tenant_id)
+            rl_cl.create_user_role_on_project(tenant_id, self.admin_id,
+                                              self.admin_role_id)
             self.admin_role_added.append(tenant_id)
 
     def _remove_admin_role(self, tenant_id):
-        LOG.debug("Remove admin user role for tenant: %s" % tenant_id)
+        LOG.debug("Remove admin user role for tenant: %s", tenant_id)
         # Must initialize AdminManager for each user role
         # Otherwise authentication exception is thrown, weird
         id_cl = credentials.AdminManager().identity_client
         if (self._tenant_exists(tenant_id)):
             try:
-                id_cl.delete_user_role(tenant_id, self.admin_id,
-                                       self.admin_role_id)
+                id_cl.delete_role_from_user_on_project(tenant_id,
+                                                       self.admin_id,
+                                                       self.admin_role_id)
             except Exception as ex:
                 LOG.exception("Failed removing role from tenant which still"
-                              "exists, exception: %s" % ex)
+                              "exists, exception: %s", ex)
 
     def _tenant_exists(self, tenant_id):
         tn_cl = self.admin_mgr.tenants_client
         try:
             t = tn_cl.show_tenant(tenant_id)
-            LOG.debug("Tenant is: %s" % str(t))
+            LOG.debug("Tenant is: %s", str(t))
             return True
         except Exception as ex:
-            LOG.debug("Tenant no longer exists? %s" % ex)
+            LOG.debug("Tenant no longer exists? %s", ex)
             return False
 
     def _init_state(self):
-        print ("Initializing saved state.")
+        print("Initializing saved state.")
         data = {}
         admin_mgr = self.admin_mgr
         kwargs = {'data': data,
@@ -281,21 +278,20 @@
             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 "
-                          "flag prior to running tempest. Exception: %s" % ex)
+                          "flag prior to running tempest. Exception: %s", ex)
             sys.exit(ex)
         except Exception as ex:
-            LOG.exception("Exception parsing saved state json : %s" % ex)
+            LOG.exception("Exception parsing saved state json : %s", ex)
             sys.exit(ex)
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 5ec8008..a632726 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -18,6 +18,7 @@
 
 from tempest.common import credentials_factory as credentials
 from tempest.common import identity
+from tempest.common.utils import net_info
 from tempest import config
 from tempest import test
 
@@ -33,7 +34,6 @@
 CONF_TENANTS = None
 CONF_USERS = None
 
-IS_CEILOMETER = None
 IS_CINDER = None
 IS_GLANCE = None
 IS_HEAT = None
@@ -51,14 +51,12 @@
     global CONF_PUB_ROUTER
     global CONF_TENANTS
     global CONF_USERS
-    global IS_CEILOMETER
     global IS_CINDER
     global IS_GLANCE
     global IS_HEAT
     global IS_NEUTRON
     global IS_NOVA
 
-    IS_CEILOMETER = CONF.service_available.ceilometer
     IS_CINDER = CONF.service_available.cinder
     IS_GLANCE = CONF.service_available.glance
     IS_HEAT = CONF.service_available.heat
@@ -70,25 +68,22 @@
     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_USERS = [CONF.auth.admin_username, CONF.identity.username,
-                  CONF.identity.alt_username]
+    CONF_TENANTS = [CONF.auth.admin_project_name]
+    CONF_USERS = [CONF.auth.admin_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']:
@@ -149,7 +144,7 @@
     def list(self):
         client = self.client
         snaps = client.list_snapshots()['snapshots']
-        LOG.debug("List count, %s Snapshots" % len(snaps))
+        LOG.debug("List count, %s Snapshots", len(snaps))
         return snaps
 
     def delete(self):
@@ -176,7 +171,7 @@
         client = self.client
         servers_body = client.list_servers()
         servers = servers_body['servers']
-        LOG.debug("List count, %s Servers" % len(servers))
+        LOG.debug("List count, %s Servers", len(servers))
         return servers
 
     def delete(self):
@@ -198,7 +193,7 @@
     def list(self):
         client = self.server_groups_client
         sgs = client.list_server_groups()['server_groups']
-        LOG.debug("List count, %s Server Groups" % len(sgs))
+        LOG.debug("List count, %s Server Groups", len(sgs))
         return sgs
 
     def delete(self):
@@ -223,7 +218,7 @@
     def list(self):
         client = self.client
         stacks = client.list_stacks()['stacks']
-        LOG.debug("List count, %s Stacks" % len(stacks))
+        LOG.debug("List count, %s Stacks", len(stacks))
         return stacks
 
     def delete(self):
@@ -248,7 +243,7 @@
     def list(self):
         client = self.client
         keypairs = client.list_keypairs()['keypairs']
-        LOG.debug("List count, %s Keypairs" % len(keypairs))
+        LOG.debug("List count, %s Keypairs", len(keypairs))
         return keypairs
 
     def delete(self):
@@ -275,7 +270,7 @@
         client = self.client
         secgrps = client.list_security_groups()['security_groups']
         secgrp_del = [grp for grp in secgrps if grp['name'] != 'default']
-        LOG.debug("List count, %s Security Groups" % len(secgrp_del))
+        LOG.debug("List count, %s Security Groups", len(secgrp_del))
         return secgrp_del
 
     def delete(self):
@@ -300,7 +295,7 @@
     def list(self):
         client = self.client
         floating_ips = client.list_floating_ips()['floating_ips']
-        LOG.debug("List count, %s Floating IPs" % len(floating_ips))
+        LOG.debug("List count, %s Floating IPs", len(floating_ips))
         return floating_ips
 
     def delete(self):
@@ -325,7 +320,7 @@
     def list(self):
         client = self.client
         vols = client.list_volumes()['volumes']
-        LOG.debug("List count, %s Volumes" % len(vols))
+        LOG.debug("List count, %s Volumes", len(vols))
         return vols
 
     def delete(self):
@@ -355,7 +350,8 @@
             LOG.exception("Delete Volume Quotas exception.")
 
     def dry_run(self):
-        quotas = self.client.show_quota_usage(self.tenant_id)['quota_set']
+        quotas = self.client.show_quota_set(
+            self.tenant_id, params={'usage': True})['quota_set']
         self.data['volume_quotas'] = quotas
 
 
@@ -382,7 +378,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 +385,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)):
@@ -406,7 +402,7 @@
         if self.is_preserve:
             networks = [network for network in networks
                         if network['id'] not in CONF_NETWORKS]
-        LOG.debug("List count, %s Networks" % networks)
+        LOG.debug("List count, %s Networks", networks)
         return networks
 
     def delete(self):
@@ -429,7 +425,7 @@
         client = self.floating_ips_client
         flips = client.list_floatingips(**self.tenant_filter)
         flips = flips['floatingips']
-        LOG.debug("List count, %s Network Floating IPs" % len(flips))
+        LOG.debug("List count, %s Network Floating IPs", len(flips))
         return flips
 
     def delete(self):
@@ -449,25 +445,26 @@
 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:
             routers = [router for router in routers
                        if router['id'] != CONF_PUB_ROUTER]
 
-        LOG.debug("List count, %s Routers" % len(routers))
+        LOG.debug("List count, %s Routers", len(routers))
         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']
-                         if port["device_owner"] == "network:router_interface"]
+                         in ports_client.list_ports(device_id=rid)['ports']
+                         if net_info.is_router_interface_port(port)]
                 for port in ports:
                     client.remove_router_interface(rid, port_id=port['id'])
                 client.delete_router(rid)
@@ -486,7 +483,7 @@
         hms = client.list_health_monitors()
         hms = hms['health_monitors']
         hms = self._filter_by_tenant_id(hms)
-        LOG.debug("List count, %s Health Monitors" % len(hms))
+        LOG.debug("List count, %s Health Monitors", len(hms))
         return hms
 
     def delete(self):
@@ -510,7 +507,7 @@
         members = client.list_members()
         members = members['members']
         members = self._filter_by_tenant_id(members)
-        LOG.debug("List count, %s Members" % len(members))
+        LOG.debug("List count, %s Members", len(members))
         return members
 
     def delete(self):
@@ -534,7 +531,7 @@
         vips = client.list_vips()
         vips = vips['vips']
         vips = self._filter_by_tenant_id(vips)
-        LOG.debug("List count, %s VIPs" % len(vips))
+        LOG.debug("List count, %s VIPs", len(vips))
         return vips
 
     def delete(self):
@@ -558,7 +555,7 @@
         pools = client.list_pools()
         pools = pools['pools']
         pools = self._filter_by_tenant_id(pools)
-        LOG.debug("List count, %s Pools" % len(pools))
+        LOG.debug("List count, %s Pools", len(pools))
         return pools
 
     def delete(self):
@@ -582,7 +579,7 @@
         rules = client.list_metering_label_rules()
         rules = rules['metering_label_rules']
         rules = self._filter_by_tenant_id(rules)
-        LOG.debug("List count, %s Metering Label Rules" % len(rules))
+        LOG.debug("List count, %s Metering Label Rules", len(rules))
         return rules
 
     def delete(self):
@@ -606,7 +603,7 @@
         labels = client.list_metering_labels()
         labels = labels['metering_labels']
         labels = self._filter_by_tenant_id(labels)
-        LOG.debug("List count, %s Metering Labels" % len(labels))
+        LOG.debug("List count, %s Metering Labels", len(labels))
         return labels
 
     def delete(self):
@@ -635,7 +632,7 @@
         if self.is_preserve:
             ports = self._filter_by_conf_networks(ports)
 
-        LOG.debug("List count, %s Ports" % len(ports))
+        LOG.debug("List count, %s Ports", len(ports))
         return ports
 
     def delete(self):
@@ -663,7 +660,7 @@
 
         if self.is_preserve:
             secgroups = self._filter_by_conf_networks(secgroups)
-        LOG.debug("List count, %s securtiy_groups" % len(secgroups))
+        LOG.debug("List count, %s security_groups", len(secgroups))
         return secgroups
 
     def delete(self):
@@ -688,7 +685,7 @@
         subnets = subnets['subnets']
         if self.is_preserve:
             subnets = self._filter_by_conf_networks(subnets)
-        LOG.debug("List count, %s Subnets" % len(subnets))
+        LOG.debug("List count, %s Subnets", len(subnets))
         return subnets
 
     def delete(self):
@@ -705,32 +702,6 @@
         self.data['subnets'] = subnets
 
 
-# Telemetry services
-class TelemetryAlarmService(BaseService):
-    def __init__(self, manager, **kwargs):
-        super(TelemetryAlarmService, self).__init__(kwargs)
-        self.client = manager.telemetry_client
-
-    def list(self):
-        client = self.client
-        alarms = client.list_alarms()
-        LOG.debug("List count, %s Alarms" % len(alarms))
-        return alarms
-
-    def delete(self):
-        client = self.client
-        alarms = self.list()
-        for alarm in alarms:
-            try:
-                client.delete_alarm(alarm['id'])
-            except Exception:
-                LOG.exception("Delete Alarms exception.")
-
-    def dry_run(self):
-        alarms = self.list()
-        self.data['alarms'] = alarms
-
-
 # begin global services
 class FlavorService(BaseService):
     def __init__(self, manager, **kwargs):
@@ -748,7 +719,7 @@
         if self.is_preserve:
             flavors = [flavor for flavor in flavors
                        if flavor['id'] not in CONF_FLAVORS]
-        LOG.debug("List count, %s Flavors after reconcile" % len(flavors))
+        LOG.debug("List count, %s Flavors after reconcile", len(flavors))
         return flavors
 
     def delete(self):
@@ -785,7 +756,7 @@
         if self.is_preserve:
             images = [image for image in images
                       if image['id'] not in CONF_IMAGES]
-        LOG.debug("List count, %s Images after reconcile" % len(images))
+        LOG.debug("List count, %s Images after reconcile", len(images))
         return images
 
     def delete(self):
@@ -835,7 +806,7 @@
             users = [user for user in users if user['name'] !=
                      CONF.auth.admin_username]
 
-        LOG.debug("List count, %s Users after reconcile" % len(users))
+        LOG.debug("List count, %s Users after reconcile", len(users))
         return users
 
     def delete(self):
@@ -872,7 +843,7 @@
                          (role['id'] not in
                           self.saved_state_json['roles'].keys()
                           and role['name'] != CONF.identity.admin_role)]
-                LOG.debug("List count, %s Roles after reconcile" % len(roles))
+                LOG.debug("List count, %s Roles after reconcile", len(roles))
             return roles
         except Exception:
             LOG.exception("Cannot retrieve Roles.")
@@ -908,13 +879,13 @@
         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']
                        not in CONF_TENANTS]
 
-        LOG.debug("List count, %s Tenants after reconcile" % len(tenants))
+        LOG.debug("List count, %s Tenants after reconcile", len(tenants))
         return tenants
 
     def delete(self):
@@ -940,7 +911,7 @@
 
     def __init__(self, manager, **kwargs):
         super(DomainService, self).__init__(kwargs)
-        self.client = manager.identity_v3_client
+        self.client = manager.domains_client
 
     def list(self):
         client = self.client
@@ -949,7 +920,7 @@
             domains = [domain for domain in domains if domain['id']
                        not in self.saved_state_json['domains'].keys()]
 
-        LOG.debug("List count, %s Domains after reconcile" % len(domains))
+        LOG.debug("List count, %s Domains after reconcile", len(domains))
         return domains
 
     def delete(self):
@@ -975,8 +946,8 @@
 
 def get_tenant_cleanup_services():
     tenant_services = []
-    if IS_CEILOMETER:
-        tenant_services.append(TelemetryAlarmService)
+    # TODO(gmann): Tempest should provide some plugin hook for cleanup
+    # script extension to plugin tests also.
     if IS_NOVA:
         tenant_services.append(ServerService)
         tenant_services.append(KeyPairService)
diff --git a/etc/config-generator.tempest.conf b/tempest/cmd/config-generator.tempest.conf
similarity index 100%
rename from etc/config-generator.tempest.conf
rename to tempest/cmd/config-generator.tempest.conf
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
index ac67ce4..7634d9e 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -14,12 +14,15 @@
 
 import os
 import shutil
-import subprocess
 import sys
 
 from cliff import command
+from oslo_config import generator
 from oslo_log import log as logging
 from six import moves
+from testrepository import commands
+
+from tempest.cmd import workspace
 
 LOG = logging.getLogger(__name__)
 
@@ -37,38 +40,28 @@
 def get_tempest_default_config_dir():
     """Get default config directory of tempest
 
-    Returns the correct default config dir to support both cases of
-    tempest being or not installed in a virtualenv.
-    Cases considered:
-    - no virtual env, python2: real_prefix and base_prefix not set
-    - no virtual env, python3: real_prefix not set, base_prefix set and
-      identical to prefix
-    - virtualenv, python2: real_prefix and prefix are set and different
-    - virtualenv, python3: real_prefix not set, base_prefix and prefix are
-      set and identical
-    - pyvenv, any python version: real_prefix not set, base_prefix and prefix
-      are set and different
+    There are 3 dirs that get tried in priority order. First is /etc/tempest,
+    if that doesn't exist it looks for a tempest dir in the XDG_CONFIG_HOME
+    dir (defaulting to ~/.config/tempest) and last it tries for a
+    ~/.tempest/etc directory. If none of these exist a ~/.tempest/etc
+    directory will be created.
 
     :return: default config dir
     """
-    real_prefix = getattr(sys, 'real_prefix', None)
-    base_prefix = getattr(sys, 'base_prefix', None)
-    prefix = sys.prefix
-    if (real_prefix is None and
-            (base_prefix is None or base_prefix == prefix)):
-        # Probably not running in a virtual environment.
-        # NOTE(andreaf) we cannot distinguish this case from the case of
-        # a virtual environment created with virtualenv, and running python3.
-        # Also if it appears we are not in virtual env and fail to find
-        # global config: '/etc/tempest', fall back to
-        # '[sys.prefix]/etc/tempest'
-        global_conf_dir = '/etc/tempest'
-        if os.path.isdir(global_conf_dir):
-            return global_conf_dir
-        else:
-            return os.path.join(prefix, 'etc/tempest')
+    global_conf_dir = '/etc/tempest'
+    xdg_config = os.environ.get('XDG_CONFIG_HOME',
+                                os.path.expanduser('~/.config'))
+    user_xdg_global_path = os.path.join(xdg_config, 'tempest')
+    user_global_path = os.path.join(os.path.expanduser('~'), '.tempest/etc')
+    if os.path.isdir(global_conf_dir):
+        return global_conf_dir
+    elif os.path.isdir(user_xdg_global_path):
+        return user_xdg_global_path
+    elif os.path.isdir(user_global_path):
+        return user_global_path
     else:
-        return os.path.join(prefix, 'etc/tempest')
+        os.makedirs(user_global_path)
+        return user_global_path
 
 
 class TempestInit(command.Command):
@@ -76,8 +69,19 @@
 
     def get_parser(self, prog_name):
         parser = super(TempestInit, self).get_parser(prog_name)
-        parser.add_argument('dir', nargs='?', default=os.getcwd())
+        parser.add_argument('dir', nargs='?', default=os.getcwd(),
+                            help="The path to the workspace directory. If you "
+                            "omit this argument, the workspace directory is "
+                            "your current directory")
         parser.add_argument('--config-dir', '-c', default=None)
+        parser.add_argument('--show-global-config-dir', '-s',
+                            action='store_true', dest='show_global_dir',
+                            help="Print the global config dir location, "
+                                 "then exit")
+        parser.add_argument('--name', help="The workspace name", default=None)
+        parser.add_argument('--workspace-path', default=None,
+                            help="The path to the workspace file, the default "
+                                 "is ~/.tempest/workspace.yaml")
         return parser
 
     def generate_testr_conf(self, local_path):
@@ -88,35 +92,53 @@
         with open(testr_conf_path, 'w+') as testr_conf_file:
             testr_conf_file.write(testr_conf)
 
-    def update_local_conf(self, conf_path, lock_dir, log_dir):
-        config_parse = moves.configparser.SafeConfigParser()
+    def get_configparser(self, conf_path):
+        config_parse = moves.configparser.ConfigParser()
         config_parse.optionxform = str
+        # get any existing values if a config file already exists
+        if os.path.isfile(conf_path):
+            # use read() for Python 2 and 3 compatibility
+            config_parse.read(conf_path)
+        return config_parse
+
+    def update_local_conf(self, conf_path, lock_dir, log_dir):
+        config_parse = self.get_configparser(conf_path)
+        # Set local lock_dir in tempest conf
+        if not config_parse.has_section('oslo_concurrency'):
+            config_parse.add_section('oslo_concurrency')
+        config_parse.set('oslo_concurrency', 'lock_path', lock_dir)
+        # Set local log_dir in tempest conf
+        config_parse.set('DEFAULT', 'log_dir', log_dir)
+        # Set default log filename to tempest.log
+        config_parse.set('DEFAULT', 'log_file', 'tempest.log')
+
+        # write out a new file with the updated configurations
         with open(conf_path, 'w+') as conf_file:
-            config_parse.readfp(conf_file)
-            # Set local lock_dir in tempest conf
-            if not config_parse.has_section('oslo_concurrency'):
-                config_parse.add_section('oslo_concurrency')
-            config_parse.set('oslo_concurrency', 'lock_path', lock_dir)
-            # Set local log_dir in tempest conf
-            config_parse.set('DEFAULT', 'log_dir', log_dir)
-            # Set default log filename to tempest.log
-            config_parse.set('DEFAULT', 'log_file', 'tempest.log')
+            config_parse.write(conf_file)
 
     def copy_config(self, etc_dir, config_dir):
-        shutil.copytree(config_dir, etc_dir)
+        if os.path.isdir(config_dir):
+            shutil.copytree(config_dir, etc_dir)
+        else:
+            LOG.warning("Global config dir %s can't be found", config_dir)
 
-    def generate_sample_config(self, local_dir, config_dir):
-        conf_generator = os.path.join(config_dir,
+    def generate_sample_config(self, local_dir):
+        conf_generator = os.path.join(os.path.dirname(__file__),
                                       'config-generator.tempest.conf')
-
-        subprocess.call(['oslo-config-generator', '--config-file',
-                         conf_generator],
-                        cwd=local_dir)
+        output_file = os.path.join(local_dir, 'etc/tempest.conf.sample')
+        if os.path.isfile(conf_generator):
+            generator.main(['--config-file', conf_generator, '--output-file',
+                            output_file])
+        else:
+            LOG.warning("Skipping sample config generation because global "
+                        "config file %s can't be found", conf_generator)
 
     def create_working_dir(self, local_dir, config_dir):
+        # make sure we are working with abspath however tempest init is called
+        local_dir = os.path.abspath(local_dir)
         # Create local dir if missing
         if not os.path.isdir(local_dir):
-            LOG.debug('Creating local working dir: %s' % local_dir)
+            LOG.debug('Creating local working dir: %s', local_dir)
             os.mkdir(local_dir)
         elif not os.listdir(local_dir) == []:
             raise OSError("Directory you are trying to initialize already "
@@ -129,24 +151,33 @@
         testr_dir = os.path.join(local_dir, '.testrepository')
         # Create lock dir
         if not os.path.isdir(lock_dir):
-            LOG.debug('Creating lock dir: %s' % lock_dir)
+            LOG.debug('Creating lock dir: %s', lock_dir)
             os.mkdir(lock_dir)
         # Create log dir
         if not os.path.isdir(log_dir):
-            LOG.debug('Creating log dir: %s' % log_dir)
+            LOG.debug('Creating log dir: %s', log_dir)
             os.mkdir(log_dir)
         # Create and copy local etc dir
         self.copy_config(etc_dir, config_dir)
         # Generate the sample config file
-        self.generate_sample_config(local_dir, config_dir)
+        self.generate_sample_config(local_dir)
         # Update local confs to reflect local paths
         self.update_local_conf(config_path, lock_dir, log_dir)
         # Generate a testr conf file
         self.generate_testr_conf(local_dir)
         # setup local testr working dir
         if not os.path.isdir(testr_dir):
-            subprocess.call(['testr', 'init'], cwd=local_dir)
+            commands.run_argv(['testr', 'init', '-d', local_dir], sys.stdin,
+                              sys.stdout, sys.stderr)
 
     def take_action(self, parsed_args):
+        workspace_manager = workspace.WorkspaceManager(
+            parsed_args.workspace_path)
+        name = parsed_args.name or parsed_args.dir.split(os.path.sep)[-1]
         config_dir = parsed_args.config_dir or get_tempest_default_config_dir()
+        if parsed_args.show_global_dir:
+            print("Global config dir is located at: %s" % config_dir)
+            sys.exit(0)
         self.create_working_dir(parsed_args.dir, config_dir)
+        workspace_manager.register_new_workspace(
+            name, parsed_args.dir, init=True)
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
deleted file mode 100755
index e26a014..0000000
--- a/tempest/cmd/javelin.py
+++ /dev/null
@@ -1,1169 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-"""Javelin is a tool for creating, verifying, and deleting a small set of
-resources in a declarative way.
-
-Javelin is meant to be used as a way to validate quickly that resources can
-survive an upgrade process.
-
-Authentication
---------------
-
-Javelin will be creating (and removing) users and tenants so it needs the admin
-credentials of your cloud to operate properly. The corresponding info can be
-given the usual way, either through CLI options or environment variables.
-
-You're probably familiar with these, but just in case::
-
-    +----------+------------------+----------------------+
-    | Param    | CLI              | Environment Variable |
-    +----------+------------------+----------------------+
-    | Username | --os-username    | OS_USERNAME          |
-    | Password | --os-password    | OS_PASSWORD          |
-    | Tenant   | --os-tenant-name | OS_TENANT_NAME       |
-    +----------+------------------+----------------------+
-
-
-Runtime Arguments
------------------
-
-**-m/--mode**: (Required) Has to be one of 'check', 'create' or 'destroy'. It
-indicates which actions javelin is going to perform.
-
-**-r/--resources**: (Required) The path to a YAML file describing the resources
-used by Javelin.
-
-**-d/--devstack-base**: (Required) The path to the devstack repo used to
-retrieve artefacts (like images) that will be referenced in the resource files.
-
-**-c/--config-file**: (Optional) The path to a valid Tempest config file
-describing your cloud. Javelin may use this to determine if certain services
-are enabled and modify its behavior accordingly.
-
-
-Resource file
--------------
-
-The resource file is a valid YAML file describing the resources that will be
-created, checked and destroyed by javelin. Here's a canonical example of a
-resource file::
-
-  tenants:
-    - javelin
-    - discuss
-
-  users:
-    - name: javelin
-      pass: gungnir
-      tenant: javelin
-    - name: javelin2
-      pass: gungnir2
-      tenant: discuss
-
-  # resources that we want to create
-  images:
-    - name: javelin_cirros
-      owner: javelin
-      file: cirros-0.3.2-x86_64-blank.img
-      disk_format: ami
-      container_format: ami
-      aki: cirros-0.3.2-x86_64-vmlinuz
-      ari: cirros-0.3.2-x86_64-initrd
-
-  servers:
-    - name: peltast
-      owner: javelin
-      flavor: m1.small
-      image: javelin_cirros
-      floating_ip_pool: public
-    - name: hoplite
-      owner: javelin
-      flavor: m1.medium
-      image: javelin_cirros
-
-
-An important piece of the resource definition is the *owner* field, which is
-the user (that we've created) that is the owner of that resource. All
-operations on that resource will happen as that regular user to ensure that
-admin level access does not mask issues.
-
-The check phase will act like a unit test, using well known assert methods to
-verify that the correct resources exist.
-
-"""
-
-import argparse
-import collections
-import datetime
-import os
-import sys
-import unittest
-
-import netaddr
-from oslo_log import log as logging
-from oslo_utils import timeutils
-import six
-from tempest_lib import auth
-from tempest_lib import exceptions as lib_exc
-from tempest_lib.services.compute import flavors_client
-from tempest_lib.services.compute import floating_ips_client
-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 subnets_client
-import yaml
-
-from tempest.common import identity
-from tempest.common import waiters
-from tempest import config
-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.object_storage import container_client
-from tempest.services.object_storage import object_client
-from tempest.services.telemetry.json import alarming_client
-from tempest.services.telemetry.json import telemetry_client
-from tempest.services.volume.v1.json import volumes_client
-
-CONF = config.CONF
-OPTS = {}
-USERS = {}
-RES = collections.defaultdict(list)
-
-LOG = None
-
-JAVELIN_START = datetime.datetime.utcnow()
-
-
-class OSClient(object):
-    _creds = None
-    identity = None
-    servers = None
-
-    def __init__(self, user, pw, tenant):
-        default_params = {
-            'disable_ssl_certificate_validation':
-                CONF.identity.disable_ssl_certificate_validation,
-            'ca_certs': CONF.identity.ca_certificates_file,
-            'trace_requests': CONF.debug.trace_requests
-        }
-        default_params_with_timeout_values = {
-            'build_interval': CONF.compute.build_interval,
-            'build_timeout': CONF.compute.build_timeout
-        }
-        default_params_with_timeout_values.update(default_params)
-
-        compute_params = {
-            'service': CONF.compute.catalog_type,
-            'region': CONF.compute.region or CONF.identity.region,
-            'endpoint_type': CONF.compute.endpoint_type,
-            'build_interval': CONF.compute.build_interval,
-            'build_timeout': CONF.compute.build_timeout
-        }
-        compute_params.update(default_params)
-
-        object_storage_params = {
-            'service': CONF.object_storage.catalog_type,
-            'region': CONF.object_storage.region or CONF.identity.region,
-            'endpoint_type': CONF.object_storage.endpoint_type
-        }
-        object_storage_params.update(default_params)
-
-        _creds = auth.KeystoneV2Credentials(
-            username=user,
-            password=pw,
-            tenant_name=tenant)
-        auth_provider_params = {
-            'disable_ssl_certificate_validation':
-                CONF.identity.disable_ssl_certificate_validation,
-            'ca_certs': CONF.identity.ca_certificates_file,
-            'trace_requests': CONF.debug.trace_requests
-        }
-        _auth = auth.KeystoneV2AuthProvider(
-            _creds, CONF.identity.uri, **auth_provider_params)
-        self.identity = identity_client.IdentityClient(
-            _auth,
-            CONF.identity.catalog_type,
-            CONF.identity.region,
-            endpoint_type='adminURL',
-            **default_params_with_timeout_values)
-        self.tenants = tenants_client.TenantsClient(
-            _auth,
-            CONF.identity.catalog_type,
-            CONF.identity.region,
-            endpoint_type='adminURL',
-            **default_params_with_timeout_values)
-        self.roles = roles_client.RolesClient(
-            _auth,
-            CONF.identity.catalog_type,
-            CONF.identity.region,
-            endpoint_type='adminURL',
-            **default_params_with_timeout_values)
-        self.users = users_client.UsersClient(
-            _auth,
-            CONF.identity.catalog_type,
-            CONF.identity.region,
-            endpoint_type='adminURL',
-            **default_params_with_timeout_values)
-        self.servers = servers_client.ServersClient(_auth,
-                                                    **compute_params)
-        self.flavors = flavors_client.FlavorsClient(_auth,
-                                                    **compute_params)
-        self.floating_ips = floating_ips_client.FloatingIPsClient(
-            _auth, **compute_params)
-        self.secgroups = security_groups_client.SecurityGroupsClient(
-            _auth, **compute_params)
-        self.secrules = security_group_rules_client.SecurityGroupRulesClient(
-            _auth, **compute_params)
-        self.objects = object_client.ObjectClient(_auth,
-                                                  **object_storage_params)
-        self.containers = container_client.ContainerClient(
-            _auth, **object_storage_params)
-        self.images = images_client.ImagesClientV2(
-            _auth,
-            CONF.image.catalog_type,
-            CONF.image.region or CONF.identity.region,
-            endpoint_type=CONF.image.endpoint_type,
-            build_interval=CONF.image.build_interval,
-            build_timeout=CONF.image.build_timeout,
-            **default_params)
-        self.telemetry = telemetry_client.TelemetryClient(
-            _auth,
-            CONF.telemetry.catalog_type,
-            CONF.identity.region,
-            endpoint_type=CONF.telemetry.endpoint_type,
-            **default_params_with_timeout_values)
-        self.alarming = alarming_client.AlarmingClient(
-            _auth,
-            CONF.alarm.catalog_type,
-            CONF.identity.region,
-            endpoint_type=CONF.alarm.endpoint_type,
-            **default_params_with_timeout_values)
-        self.volumes = volumes_client.VolumesClient(
-            _auth,
-            CONF.volume.catalog_type,
-            CONF.volume.region or CONF.identity.region,
-            endpoint_type=CONF.volume.endpoint_type,
-            build_interval=CONF.volume.build_interval,
-            build_timeout=CONF.volume.build_timeout,
-            **default_params)
-        self.networks = network_client.NetworkClient(
-            _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.subnets = subnets_client.SubnetsClient(
-            _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)
-
-
-def load_resources(fname):
-    """Load the expected resources from a yaml file."""
-    return yaml.load(open(fname, 'r'))
-
-
-def keystone_admin():
-    return OSClient(OPTS.os_username, OPTS.os_password, OPTS.os_tenant_name)
-
-
-def client_for_user(name):
-    LOG.debug("Entering client_for_user")
-    if name in USERS:
-        user = USERS[name]
-        LOG.debug("Created client for user %s" % user)
-        return OSClient(user['name'], user['pass'], user['tenant'])
-    else:
-        LOG.error("%s not found in USERS: %s" % (name, USERS))
-
-
-###################
-#
-# TENANTS
-#
-###################
-
-
-def create_tenants(tenants):
-    """Create tenants from resource definition.
-
-    Don't create the tenants if they already exist.
-    """
-    admin = keystone_admin()
-    body = admin.tenants.list_tenants()['tenants']
-    existing = [x['name'] for x in body]
-    for tenant in tenants:
-        if tenant not in existing:
-            admin.tenants.create_tenant(tenant)['tenant']
-        else:
-            LOG.warning("Tenant '%s' already exists in this environment"
-                        % tenant)
-
-
-def destroy_tenants(tenants):
-    admin = keystone_admin()
-    for tenant in tenants:
-        tenant_id = identity.get_tenant_by_name(admin.tenant, tenant)['id']
-        admin.tenants.delete_tenant(tenant_id)
-
-##############
-#
-# USERS
-#
-##############
-
-
-def _users_for_tenant(users, tenant):
-    u_for_t = []
-    for user in users:
-        for n in user:
-            if user[n]['tenant'] == tenant:
-                u_for_t.append(user[n])
-    return u_for_t
-
-
-def _tenants_from_users(users):
-    tenants = set()
-    for user in users:
-        for n in user:
-            tenants.add(user[n]['tenant'])
-    return tenants
-
-
-def _assign_swift_role(user, swift_role):
-    admin = keystone_admin()
-    roles = admin.roles.list_roles()
-    role = next(r for r in roles if r['name'] == swift_role)
-    LOG.debug(USERS[user])
-    try:
-        admin.roles.assign_user_role(
-            USERS[user]['tenant_id'],
-            USERS[user]['id'],
-            role['id'])
-    except lib_exc.Conflict:
-        # don't care if it's already assigned
-        pass
-
-
-def create_users(users):
-    """Create tenants from resource definition.
-
-    Don't create the tenants if they already exist.
-    """
-    global USERS
-    LOG.info("Creating users")
-    admin = keystone_admin()
-    for u in users:
-        try:
-            tenant = identity.get_tenant_by_name(admin.tenants, u['tenant'])
-        except lib_exc.NotFound:
-            LOG.error("Tenant: %s - not found" % u['tenant'])
-            continue
-        try:
-            identity.get_user_by_username(admin.tenants,
-                                          tenant['id'], u['name'])
-            LOG.warning("User '%s' already exists in this environment"
-                        % u['name'])
-        except lib_exc.NotFound:
-            admin.users.create_user(
-                u['name'], u['pass'], tenant['id'],
-                "%s@%s" % (u['name'], tenant['id']),
-                enabled=True)
-
-
-def destroy_users(users):
-    admin = keystone_admin()
-    for user in users:
-        tenant_id = identity.get_tenant_by_name(admin.tenants,
-                                                user['tenant'])['id']
-        user_id = identity.get_user_by_username(admin.tenants,
-                                                tenant_id, user['name'])['id']
-        admin.users.delete_user(user_id)
-
-
-def collect_users(users):
-    global USERS
-    LOG.info("Collecting users")
-    admin = keystone_admin()
-    for u in users:
-        tenant = identity.get_tenant_by_name(admin.tenants, u['tenant'])
-        u['tenant_id'] = tenant['id']
-        USERS[u['name']] = u
-        body = identity.get_user_by_username(admin.tenants,
-                                             tenant['id'], u['name'])
-        USERS[u['name']]['id'] = body['id']
-
-
-class JavelinCheck(unittest.TestCase):
-    def __init__(self, users, resources):
-        super(JavelinCheck, self).__init__()
-        self.users = users
-        self.res = resources
-
-    def runTest(self, *args):
-        pass
-
-    def _ping_ip(self, ip_addr, count, namespace=None):
-        if namespace is None:
-            ping_cmd = "ping -c1 " + ip_addr
-        else:
-            ping_cmd = "sudo ip netns exec %s ping -c1 %s" % (namespace,
-                                                              ip_addr)
-        for current in range(count):
-            return_code = os.system(ping_cmd)
-            if return_code is 0:
-                break
-        self.assertNotEqual(current, count - 1,
-                            "Server is not pingable at %s" % ip_addr)
-
-    def check(self):
-        self.check_users()
-        self.check_objects()
-        self.check_servers()
-        self.check_volumes()
-        self.check_telemetry()
-        self.check_secgroups()
-
-        # validate neutron is enabled and ironic disabled:
-        # Tenant network isolation is not supported when using ironic.
-        # "admin" has set up a neutron flat network environment within a shared
-        # fixed network for all tenants to use.
-        # In this case, network/subnet/router creation can be skipped and the
-        # server booted the same as nova network.
-        if (CONF.service_available.neutron and
-                not CONF.baremetal.driver_enabled):
-            self.check_networking()
-
-    def check_users(self):
-        """Check that the users we expect to exist, do.
-
-        We don't use the resource list for this because we need to validate
-        that things like tenantId didn't drift across versions.
-        """
-        LOG.info("checking users")
-        for name, user in six.iteritems(self.users):
-            client = keystone_admin()
-            found = client.users.show_user(user['id'])['user']
-            self.assertEqual(found['name'], user['name'])
-            self.assertEqual(found['tenantId'], user['tenant_id'])
-
-            # also ensure we can auth with that user, and do something
-            # on the cloud. We don't care about the results except that it
-            # remains authorized.
-            client = client_for_user(user['name'])
-            client.servers.list_servers()
-
-    def check_objects(self):
-        """Check that the objects created are still there."""
-        if not self.res.get('objects'):
-            return
-        LOG.info("checking objects")
-        for obj in self.res['objects']:
-            client = client_for_user(obj['owner'])
-            r, contents = client.objects.get_object(
-                obj['container'], obj['name'])
-            source = _file_contents(obj['file'])
-            self.assertEqual(contents, source)
-
-    def check_servers(self):
-        """Check that the servers are still up and running."""
-        if not self.res.get('servers'):
-            return
-        LOG.info("checking servers")
-        for server in self.res['servers']:
-            client = client_for_user(server['owner'])
-            found = _get_server_by_name(client, server['name'])
-            self.assertIsNotNone(
-                found,
-                "Couldn't find expected server %s" % server['name'])
-
-            found = client.servers.show_server(found['id'])['server']
-            # validate neutron is enabled and ironic disabled:
-            if (CONF.service_available.neutron and
-                    not CONF.baremetal.driver_enabled):
-                _floating_is_alive = False
-                for network_name, body in found['addresses'].items():
-                    for addr in body:
-                        ip = addr['addr']
-                        # Use floating IP, fixed IP or other type to
-                        # reach the server.
-                        # This is useful in multi-node environment.
-                        if CONF.validation.connect_method == 'floating':
-                            if addr.get('OS-EXT-IPS:type',
-                                        'floating') == 'floating':
-                                self._ping_ip(ip, 60)
-                                _floating_is_alive = True
-                        elif CONF.validation.connect_method == 'fixed':
-                            if addr.get('OS-EXT-IPS:type',
-                                        'fixed') == 'fixed':
-                                namespace = _get_router_namespace(client,
-                                                                  network_name)
-                                self._ping_ip(ip, 60, namespace)
-                        else:
-                            self._ping_ip(ip, 60)
-                # If CONF.validation.connect_method is floating, validate
-                # that the floating IP is attached to the server and the
-                # the server is pingable.
-                if CONF.validation.connect_method == 'floating':
-                    self.assertTrue(_floating_is_alive,
-                                    "Server %s has no floating IP." %
-                                    server['name'])
-            else:
-                addr = found['addresses']['private'][0]['addr']
-                self._ping_ip(addr, 60)
-
-    def check_secgroups(self):
-        """Check that the security groups still exist."""
-        LOG.info("Checking security groups")
-        for secgroup in self.res['secgroups']:
-            client = client_for_user(secgroup['owner'])
-            found = _get_resource_by_name(client.secgroups, 'security_groups',
-                                          secgroup['name'])
-            self.assertIsNotNone(
-                found,
-                "Couldn't find expected secgroup %s" % secgroup['name'])
-
-    def check_telemetry(self):
-        """Check that ceilometer provides a sane sample.
-
-        Confirm that there is more than one sample and that they have the
-        expected metadata.
-
-        If in check mode confirm that the oldest sample available is from
-        before the upgrade.
-        """
-        if not self.res.get('telemetry'):
-            return
-        LOG.info("checking telemetry")
-        for server in self.res['servers']:
-            client = client_for_user(server['owner'])
-            body = client.telemetry.list_samples(
-                'instance',
-                query=('metadata.display_name', 'eq', server['name'])
-            )
-            self.assertTrue(len(body) >= 1, 'expecting at least one sample')
-            self._confirm_telemetry_sample(server, body[-1])
-
-    def check_volumes(self):
-        """Check that the volumes are still there and attached."""
-        if not self.res.get('volumes'):
-            return
-        LOG.info("checking volumes")
-        for volume in self.res['volumes']:
-            client = client_for_user(volume['owner'])
-            vol_body = _get_volume_by_name(client, volume['name'])
-            self.assertIsNotNone(
-                vol_body,
-                "Couldn't find expected volume %s" % volume['name'])
-
-            # Verify that a volume's attachment retrieved
-            server_id = _get_server_by_name(client, volume['server'])['id']
-            attachment = client.volumes.get_attachment_from_volume(vol_body)
-            self.assertEqual(vol_body['id'], attachment['volume_id'])
-            self.assertEqual(server_id, attachment['server_id'])
-
-    def _confirm_telemetry_sample(self, server, sample):
-        """Check this sample matches the expected resource metadata."""
-        # Confirm display_name
-        self.assertEqual(server['name'],
-                         sample['resource_metadata']['display_name'])
-        # Confirm instance_type of flavor
-        flavor = sample['resource_metadata'].get(
-            'flavor.name',
-            sample['resource_metadata'].get('instance_type')
-        )
-        self.assertEqual(server['flavor'], flavor)
-        # Confirm the oldest sample was created before upgrade.
-        if OPTS.mode == 'check':
-            oldest_timestamp = timeutils.normalize_time(
-                timeutils.parse_isotime(sample['timestamp']))
-            self.assertTrue(
-                oldest_timestamp < JAVELIN_START,
-                'timestamp should come before start of second javelin run'
-            )
-
-    def check_networking(self):
-        """Check that the networks are still there."""
-        for res_type in ('networks', 'subnets', 'routers'):
-            for res in self.res[res_type]:
-                client = client_for_user(res['owner'])
-                found = _get_resource_by_name(client.networks, res_type,
-                                              res['name'])
-                self.assertIsNotNone(
-                    found,
-                    "Couldn't find expected resource %s" % res['name'])
-
-
-#######################
-#
-# OBJECTS
-#
-#######################
-
-
-def _file_contents(fname):
-    with open(fname, 'r') as f:
-        return f.read()
-
-
-def create_objects(objects):
-    if not objects:
-        return
-    LOG.info("Creating objects")
-    for obj in objects:
-        LOG.debug("Object %s" % obj)
-        swift_role = obj.get('swift_role', 'Member')
-        _assign_swift_role(obj['owner'], swift_role)
-        client = client_for_user(obj['owner'])
-        client.containers.create_container(obj['container'])
-        client.objects.create_object(
-            obj['container'], obj['name'],
-            _file_contents(obj['file']))
-
-
-def destroy_objects(objects):
-    for obj in objects:
-        client = client_for_user(obj['owner'])
-        r, body = client.objects.delete_object(obj['container'], obj['name'])
-        if not (200 <= int(r['status']) < 299):
-            raise ValueError("unable to destroy object: [%s] %s" % (r, body))
-
-
-#######################
-#
-# IMAGES
-#
-#######################
-
-
-def _resolve_image(image, imgtype):
-    name = image[imgtype]
-    fname = os.path.join(OPTS.devstack_base, image['imgdir'], name)
-    return name, fname
-
-
-def _get_image_by_name(client, name):
-    body = client.images.list_images()
-    for image in body:
-        if name == image['name']:
-            return image
-    return None
-
-
-def create_images(images):
-    if not images:
-        return
-    LOG.info("Creating images")
-    for image in images:
-        client = client_for_user(image['owner'])
-
-        # DEPRECATED: 'format' was used for ami images
-        # Use 'disk_format' and 'container_format' instead
-        if 'format' in image:
-            LOG.warning("Deprecated: 'format' is deprecated for images "
-                        "description. Please use 'disk_format' and 'container_"
-                        "format' instead.")
-            image['disk_format'] = image['format']
-            image['container_format'] = image['format']
-
-        # only upload a new image if the name isn't there
-        if _get_image_by_name(client, image['name']):
-            LOG.info("Image '%s' already exists" % image['name'])
-            continue
-
-        # special handling for 3 part image
-        extras = {}
-        if image['disk_format'] == 'ami':
-            name, fname = _resolve_image(image, 'aki')
-            aki = client.images.create_image(
-                'javelin_' + name, 'aki', 'aki')
-            client.images.store_image_file(aki.get('id'), open(fname, 'r'))
-            extras['kernel_id'] = aki.get('id')
-
-            name, fname = _resolve_image(image, 'ari')
-            ari = client.images.create_image(
-                'javelin_' + name, 'ari', 'ari')
-            client.images.store_image_file(ari.get('id'), open(fname, 'r'))
-            extras['ramdisk_id'] = ari.get('id')
-
-        _, fname = _resolve_image(image, 'file')
-        body = client.images.create_image(
-            image['name'], image['container_format'],
-            image['disk_format'], **extras)
-        image_id = body.get('id')
-        client.images.store_image_file(image_id, open(fname, 'r'))
-
-
-def destroy_images(images):
-    if not images:
-        return
-    LOG.info("Destroying images")
-    for image in images:
-        client = client_for_user(image['owner'])
-
-        response = _get_image_by_name(client, image['name'])
-        if not response:
-            LOG.info("Image '%s' does not exist" % image['name'])
-            continue
-        client.images.delete_image(response['id'])
-
-
-#######################
-#
-# NETWORKS
-#
-#######################
-
-def _get_router_namespace(client, network):
-    network_id = _get_resource_by_name(client.networks,
-                                       'networks', network)['id']
-    n_body = client.networks.list_routers()
-    for router in n_body['routers']:
-        router_id = router['id']
-        r_body = client.networks.list_router_interfaces(router_id)
-        for port in r_body['ports']:
-            if port['network_id'] == network_id:
-                return "qrouter-%s" % router_id
-
-
-def _get_resource_by_name(client, resource, name):
-    get_resources = getattr(client, 'list_%s' % resource)
-    if get_resources is None:
-        raise AttributeError("client doesn't have method list_%s" % resource)
-    # Until all tempest client methods are changed to return only one value,
-    # 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:
-        body = body[1]
-    if isinstance(body, dict):
-        body = body[resource]
-    for res in body:
-        if name == res['name']:
-            return res
-    raise ValueError('%s not found in %s resources' % (name, resource))
-
-
-def create_networks(networks):
-    LOG.info("Creating networks")
-    for network in networks:
-        client = client_for_user(network['owner'])
-
-        # only create a network if the name isn't here
-        body = client.networks.list_networks()
-        if any(item['name'] == network['name'] for item in body['networks']):
-            LOG.warning("Duplicated network name: %s" % network['name'])
-            continue
-
-        client.networks.create_network(name=network['name'])
-
-
-def destroy_networks(networks):
-    LOG.info("Destroying subnets")
-    for network in networks:
-        client = client_for_user(network['owner'])
-        network_id = _get_resource_by_name(client.networks, 'networks',
-                                           network['name'])['id']
-        client.networks.delete_network(network_id)
-
-
-def create_subnets(subnets):
-    LOG.info("Creating subnets")
-    for subnet in subnets:
-        client = client_for_user(subnet['owner'])
-
-        network = _get_resource_by_name(client.networks, 'networks',
-                                        subnet['network'])
-        ip_version = netaddr.IPNetwork(subnet['range']).version
-        # ensure we don't overlap with another subnet in the network
-        try:
-            client.networks.create_subnet(network_id=network['id'],
-                                          cidr=subnet['range'],
-                                          name=subnet['name'],
-                                          ip_version=ip_version)
-        except lib_exc.BadRequest as e:
-            is_overlapping_cidr = 'overlaps with another subnet' in str(e)
-            if not is_overlapping_cidr:
-                raise
-
-
-def destroy_subnets(subnets):
-    LOG.info("Destroying subnets")
-    for subnet in subnets:
-        client = client_for_user(subnet['owner'])
-        subnet_id = _get_resource_by_name(client.subnets,
-                                          'subnets', subnet['name'])['id']
-        client.subnets.delete_subnet(subnet_id)
-
-
-def create_routers(routers):
-    LOG.info("Creating routers")
-    for router in routers:
-        client = client_for_user(router['owner'])
-
-        # only create a router if the name isn't here
-        body = client.networks.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'])
-
-
-def destroy_routers(routers):
-    LOG.info("Destroying routers")
-    for router in routers:
-        client = client_for_user(router['owner'])
-        router_id = _get_resource_by_name(client.networks,
-                                          'routers', router['name'])['id']
-        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)
-
-
-def add_router_interface(routers):
-    for router in routers:
-        client = client_for_user(router['owner'])
-        router_id = _get_resource_by_name(client.networks,
-                                          'routers', router['name'])['id']
-
-        for subnet in router['subnet']:
-            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)
-        # 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(
-                    router_id, set_enable_snat=True,
-                    external_gateway_info={"network_id": ext_net})
-            else:
-                raise ValueError('public_network_id is not configured.')
-
-
-#######################
-#
-# SERVERS
-#
-#######################
-
-def _get_server_by_name(client, name):
-    body = client.servers.list_servers()
-    for server in body['servers']:
-        if name == server['name']:
-            return server
-    return None
-
-
-def _get_flavor_by_name(client, name):
-    body = client.flavors.list_flavors()['flavors']
-    for flavor in body:
-        if name == flavor['name']:
-            return flavor
-    return None
-
-
-def create_servers(servers):
-    if not servers:
-        return
-    LOG.info("Creating servers")
-    for server in servers:
-        client = client_for_user(server['owner'])
-
-        if _get_server_by_name(client, server['name']):
-            LOG.info("Server '%s' already exists" % server['name'])
-            continue
-
-        image_id = _get_image_by_name(client, server['image'])['id']
-        flavor_id = _get_flavor_by_name(client, server['flavor'])['id']
-        # validate neutron is enabled and ironic disabled
-        kwargs = dict()
-        if (CONF.service_available.neutron and
-                not CONF.baremetal.driver_enabled and server.get('networks')):
-            get_net_id = lambda x: (_get_resource_by_name(
-                client.networks, 'networks', x)['id'])
-            kwargs['networks'] = [{'uuid': get_net_id(network)}
-                                  for network in server['networks']]
-        body = client.servers.create_server(
-            name=server['name'], imageRef=image_id, flavorRef=flavor_id,
-            **kwargs)['server']
-        server_id = body['id']
-        client.servers.wait_for_server_status(server_id, 'ACTIVE')
-        # create security group(s) after server spawning
-        for secgroup in server['secgroups']:
-            client.servers.add_security_group(server_id, name=secgroup)
-        if CONF.validation.connect_method == 'floating':
-            floating_ip_pool = server.get('floating_ip_pool')
-            floating_ip = client.floating_ips.create_floating_ip(
-                pool_name=floating_ip_pool)['floating_ip']
-            client.floating_ips.associate_floating_ip_to_server(
-                floating_ip['ip'], server_id)
-
-
-def destroy_servers(servers):
-    if not servers:
-        return
-    LOG.info("Destroying servers")
-    for server in servers:
-        client = client_for_user(server['owner'])
-
-        response = _get_server_by_name(client, server['name'])
-        if not response:
-            LOG.info("Server '%s' does not exist" % server['name'])
-            continue
-
-        # TODO(EmilienM): disassociate floating IP from server and release it.
-        client.servers.delete_server(response['id'])
-        waiters.wait_for_server_termination(client.servers, response['id'],
-                                            ignore_error=True)
-
-
-def create_secgroups(secgroups):
-    LOG.info("Creating security groups")
-    for secgroup in secgroups:
-        client = client_for_user(secgroup['owner'])
-
-        # only create a security group if the name isn't here
-        # i.e. a security group may be used by another server
-        # only create a router if the name isn't here
-        body = client.secgroups.list_security_groups()['security_groups']
-        if any(item['name'] == secgroup['name'] for item in body):
-            LOG.warning("Security group '%s' already exists" %
-                        secgroup['name'])
-            continue
-
-        body = client.secgroups.create_security_group(
-            name=secgroup['name'],
-            description=secgroup['description'])['security_group']
-        secgroup_id = body['id']
-        # for each security group, create the rules
-        for rule in secgroup['rules']:
-            ip_proto, from_port, to_port, cidr = rule.split()
-            client.secrules.create_security_group_rule(
-                parent_group_id=secgroup_id, ip_protocol=ip_proto,
-                from_port=from_port, to_port=to_port, cidr=cidr)
-
-
-def destroy_secgroups(secgroups):
-    LOG.info("Destroying security groups")
-    for secgroup in secgroups:
-        client = client_for_user(secgroup['owner'])
-        sg_id = _get_resource_by_name(client.secgroups,
-                                      'security_groups',
-                                      secgroup['name'])
-        # sg rules are deleted automatically
-        client.secgroups.delete_security_group(sg_id['id'])
-
-
-#######################
-#
-# VOLUMES
-#
-#######################
-
-def _get_volume_by_name(client, name):
-    body = client.volumes.list_volumes()['volumes']
-    for volume in body:
-        if name == volume['display_name']:
-            return volume
-    return None
-
-
-def create_volumes(volumes):
-    if not volumes:
-        return
-    LOG.info("Creating volumes")
-    for volume in volumes:
-        client = client_for_user(volume['owner'])
-
-        # only create a volume if the name isn't here
-        if _get_volume_by_name(client, volume['name']):
-            LOG.info("volume '%s' already exists" % volume['name'])
-            continue
-
-        size = volume['gb']
-        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')
-
-
-def destroy_volumes(volumes):
-    for volume in volumes:
-        client = client_for_user(volume['owner'])
-        volume_id = _get_volume_by_name(client, volume['name'])['id']
-        client.volumes.detach_volume(volume_id)
-        client.volumes.delete_volume(volume_id)
-
-
-def attach_volumes(volumes):
-    for volume in volumes:
-        client = client_for_user(volume['owner'])
-        server_id = _get_server_by_name(client, volume['server'])['id']
-        volume_id = _get_volume_by_name(client, volume['name'])['id']
-        device = volume['device']
-        client.volumes.attach_volume(volume_id,
-                                     instance_uuid=server_id,
-                                     mountpoint=device)
-
-
-#######################
-#
-# MAIN LOGIC
-#
-#######################
-
-def create_resources():
-    LOG.info("Creating Resources")
-    # first create keystone level resources, and we need to be admin
-    # for this.
-    create_tenants(RES['tenants'])
-    create_users(RES['users'])
-    collect_users(RES['users'])
-
-    # next create resources in a well known order
-    create_objects(RES['objects'])
-    create_images(RES['images'])
-
-    # validate neutron is enabled and ironic is disabled
-    if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
-        create_networks(RES['networks'])
-        create_subnets(RES['subnets'])
-        create_routers(RES['routers'])
-        add_router_interface(RES['routers'])
-
-    create_secgroups(RES['secgroups'])
-    create_volumes(RES['volumes'])
-
-    # Only attempt attaching the volumes if servers are defined in the
-    # resource file
-    if 'servers' in RES:
-        create_servers(RES['servers'])
-        attach_volumes(RES['volumes'])
-
-
-def destroy_resources():
-    LOG.info("Destroying Resources")
-    # Destroy in inverse order of create
-    destroy_servers(RES['servers'])
-    destroy_images(RES['images'])
-    destroy_objects(RES['objects'])
-    destroy_volumes(RES['volumes'])
-    if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
-        destroy_routers(RES['routers'])
-        destroy_subnets(RES['subnets'])
-        destroy_networks(RES['networks'])
-    destroy_secgroups(RES['secgroups'])
-    destroy_users(RES['users'])
-    destroy_tenants(RES['tenants'])
-    LOG.warning("Destroy mode incomplete")
-
-
-def get_options():
-    global OPTS
-    parser = argparse.ArgumentParser(
-        description='Create and validate a fixed set of OpenStack resources')
-    parser.add_argument('-m', '--mode',
-                        metavar='<create|check|destroy>',
-                        required=True,
-                        help=('One of (create, check, destroy)'))
-    parser.add_argument('-r', '--resources',
-                        required=True,
-                        metavar='resourcefile.yaml',
-                        help='Resources definition yaml file')
-
-    parser.add_argument(
-        '-d', '--devstack-base',
-        required=True,
-        metavar='/opt/stack/old',
-        help='Devstack base directory for retrieving artifacts')
-    parser.add_argument(
-        '-c', '--config-file',
-        metavar='/etc/tempest.conf',
-        help='path to javelin2(tempest) config file')
-
-    # auth bits, letting us also just source the devstack openrc
-    parser.add_argument('--os-username',
-                        metavar='<auth-user-name>',
-                        default=os.environ.get('OS_USERNAME'),
-                        help=('Defaults to env[OS_USERNAME].'))
-    parser.add_argument('--os-password',
-                        metavar='<auth-password>',
-                        default=os.environ.get('OS_PASSWORD'),
-                        help=('Defaults to env[OS_PASSWORD].'))
-    parser.add_argument('--os-tenant-name',
-                        metavar='<auth-tenant-name>',
-                        default=os.environ.get('OS_TENANT_NAME'),
-                        help=('Defaults to env[OS_TENANT_NAME].'))
-
-    OPTS = parser.parse_args()
-    if OPTS.mode not in ('create', 'check', 'destroy'):
-        print("ERROR: Unknown mode -m %s\n" % OPTS.mode)
-        parser.print_help()
-        sys.exit(1)
-    if OPTS.config_file:
-        config.CONF.set_config_path(OPTS.config_file)
-
-
-def setup_logging():
-    global LOG
-    logging.setup(CONF, __name__)
-    LOG = logging.getLogger(__name__)
-
-
-def main():
-    print("Javelin is deprecated and will be removed from Tempest in the "
-          "future.")
-    global RES
-    get_options()
-    setup_logging()
-    RES.update(load_resources(OPTS.resources))
-
-    if OPTS.mode == 'create':
-        create_resources()
-        # Make sure the resources we just created actually work
-        checker = JavelinCheck(USERS, RES)
-        checker.check()
-    elif OPTS.mode == 'check':
-        collect_users(RES['users'])
-        checker = JavelinCheck(USERS, RES)
-        checker.check()
-    elif OPTS.mode == 'destroy':
-        collect_users(RES['users'])
-        destroy_resources()
-    else:
-        LOG.error('Unknown mode %s' % OPTS.mode)
-        return 1
-    LOG.info('javelin2 successfully finished')
-    return 0
-
-if __name__ == "__main__":
-    sys.exit(main())
diff --git a/tempest/cmd/list_plugins.py b/tempest/cmd/list_plugins.py
index 1f1ff1a..86732da 100644
--- a/tempest/cmd/list_plugins.py
+++ b/tempest/cmd/list_plugins.py
@@ -19,24 +19,20 @@
 """
 
 from cliff import command
-from oslo_log import log as logging
 import prettytable
 
-from tempest.test_discover.plugins import TempestTestPluginManager
-
-LOG = logging.getLogger(__name__)
+from tempest.test_discover import plugins as plg
 
 
 class TempestListPlugins(command.Command):
     def take_action(self, parsed_args):
         self._list_plugins()
-        return 0
 
     def get_description(self):
         return 'List all tempest plugins'
 
     def _list_plugins(self):
-        plugins = TempestTestPluginManager()
+        plugins = plg.TempestTestPluginManager()
 
         output = prettytable.PrettyTable(["Name", "EntryPoint"])
         for plugin in plugins.ext_plugins.extensions:
diff --git a/tempest/cmd/main.py b/tempest/cmd/main.py
index acd97a8..1090c41 100644
--- a/tempest/cmd/main.py
+++ b/tempest/cmd/main.py
@@ -11,11 +11,11 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-import logging
 import sys
 
 from cliff import app
 from cliff import commandmanager
+from oslo_log import log as logging
 from pbr import version
 
 
@@ -26,7 +26,7 @@
     def __init__(self):
         super(Main, self).__init__(
             description='Tempest cli application',
-            version=version.VersionInfo('tempest').version_string(),
+            version=version.VersionInfo('tempest').version_string_with_vcs(),
             command_manager=commandmanager.CommandManager('tempest.cm'),
             deferred_help=True,
             )
diff --git a/tempest/cmd/resources.yaml b/tempest/cmd/resources.yaml
deleted file mode 100644
index 2d6664c..0000000
--- a/tempest/cmd/resources.yaml
+++ /dev/null
@@ -1,96 +0,0 @@
-# This is a yaml description for the most basic definitions
-# of what should exist across the resource boundary. Perhaps
-# one day this will grow into a Heat resource template, but as
-# Heat isn't a known working element in the upgrades, we do
-# this much simpler thing for now.
-
-tenants:
-  - javelin
-  - discuss
-
-users:
-  - name: javelin
-    pass: gungnir
-    tenant: javelin
-  - name: javelin2
-    pass: gungnir2
-    tenant: discuss
-
-secgroups:
-  - name: angon
-    owner: javelin
-    description: angon
-    rules:
-      - 'icmp -1 -1 0.0.0.0/0'
-      - 'tcp 22 22 0.0.0.0/0'
-  - name: baobab
-    owner: javelin
-    description: baobab
-    rules:
-      - 'tcp 80 80 0.0.0.0/0'
-
-# resources that we want to create
-images:
-  - name: javelin_cirros
-    owner: javelin
-    imgdir: files/images/cirros-0.3.2-x86_64-uec
-    file: cirros-0.3.2-x86_64-blank.img
-    format: ami
-    aki: cirros-0.3.2-x86_64-vmlinuz
-    ari: cirros-0.3.2-x86_64-initrd
-volumes:
-  - name: assegai
-    server: peltast
-    owner: javelin
-    gb: 1
-    device: /dev/vdb
-  - name: pifpouf
-    server: hoplite
-    owner: javelin
-    gb: 2
-    device: /dev/vdb
-networks:
-  - name: world1
-    owner: javelin
-  - name: world2
-    owner: javelin
-subnets:
-  - name: subnet1
-    range: 10.1.0.0/24
-    network: world1
-    owner: javelin
-  - name: subnet2
-    range: 192.168.1.0/24
-    network: world2
-    owner: javelin
-routers:
-  - name: connector
-    owner: javelin
-    gateway: true
-    subnet:
-      - subnet1
-      - subnet2
-servers:
-  - name: peltast
-    owner: javelin
-    flavor: m1.small
-    image: javelin_cirros
-    networks:
-      - world1
-    secgroups:
-      - angon
-      - baobab
-  - name: hoplite
-    owner: javelin
-    flavor: m1.medium
-    image: javelin_cirros
-    networks:
-      - world2
-    secgroups:
-      - angon
-objects:
-  - container: jc1
-    name: javelin1
-    owner: javelin
-    file: /etc/hosts
-telemetry: true
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
new file mode 100644
index 0000000..54b844a
--- /dev/null
+++ b/tempest/cmd/run.py
@@ -0,0 +1,289 @@
+# 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.
+
+"""
+Runs tempest tests
+
+This command is used for running the tempest tests
+
+Test Selection
+==============
+Tempest run has several options:
+
+ * **--regex/-r**: This is a selection regex like what testr uses. It will run
+                   any tests that match on re.match() with the regex
+ * **--smoke/-s**: Run all the tests tagged as smoke
+
+There are also the **--blacklist-file** and **--whitelist-file** options that
+let you pass a filepath to tempest run with the file format being a line
+separated regex, with '#' used to signify the start of a comment on a line.
+For example::
+
+    # Regex file
+    ^regex1 # Match these tests
+    .*regex2 # Match those tests
+
+The blacklist file will be used to construct a negative lookahead regex and
+the whitelist file will simply OR all the regexes in the file. The whitelist
+and blacklist file options are mutually exclusive so you can't use them
+together. However, you can combine either with a normal regex or the *--smoke*
+flag. When used with a blacklist file the generated regex will be combined to
+something like::
+
+    ^((?!black_regex1|black_regex2).)*$cli_regex1
+
+When combined with a whitelist file all the regexes from the file and the CLI
+regexes will be ORed.
+
+You can also use the **--list-tests** option in conjunction with selection
+arguments to list which tests will be run.
+
+Test Execution
+==============
+There are several options to control how the tests are executed. By default
+tempest will run in parallel with a worker for each CPU present on the machine.
+If you want to adjust the number of workers use the **--concurrency** option
+and if you want to run tests serially use **--serial/-t**
+
+Running with Workspaces
+-----------------------
+Tempest run enables you to run your tempest tests from any setup tempest
+workspace it relies on you having setup a tempest workspace with either the
+``tempest init`` or ``tempest workspace`` commands. Then using the
+``--workspace`` CLI option you can specify which one of your workspaces you
+want to run tempest from. Using this option you don't have to run Tempest
+directly with you current working directory being the workspace, Tempest will
+take care of managing everything to be executed from there.
+
+Running from Anywhere
+---------------------
+Tempest run provides you with an option to execute tempest from anywhere on
+your system. You are required to provide a config file in this case with the
+``--config-file`` option. When run tempest will create a .testrepository
+directory and a .testr.conf file in your current working directory. This way
+you can use testr commands directly to inspect the state of the previous run.
+
+Test Output
+===========
+By default tempest run's output to STDOUT will be generated using the
+subunit-trace output filter. But, if you would prefer a subunit v2 stream be
+output to STDOUT use the **--subunit** flag
+
+"""
+
+import io
+import os
+import sys
+import threading
+
+from cliff import command
+from os_testr import regex_builder
+from os_testr import subunit_trace
+import six
+from testrepository.commands import run_argv
+
+from tempest.cmd import init
+from tempest.cmd import workspace
+from tempest import config
+
+
+CONF = config.CONF
+
+
+class TempestRun(command.Command):
+
+    def _set_env(self, config_file=None):
+        if config_file:
+            CONF.set_config_path(os.path.abspath(config_file))
+        # NOTE(mtreinish): This is needed so that testr doesn't gobble up any
+        # stacktraces on failure.
+        if 'TESTR_PDB' in os.environ:
+            return
+        else:
+            os.environ["TESTR_PDB"] = ""
+        # NOTE(dims): most of our .testr.conf try to test for PYTHON
+        # environment variable and fall back to "python", under python3
+        # if it does not exist. we should set it to the python3 executable
+        # to deal with this situation better for now.
+        if six.PY3 and 'PYTHON' not in os.environ:
+            os.environ['PYTHON'] = sys.executable
+
+    def _create_testrepository(self):
+        if not os.path.isdir('.testrepository'):
+            returncode = run_argv(['testr', 'init'], sys.stdin, sys.stdout,
+                                  sys.stderr)
+            if returncode:
+                sys.exit(returncode)
+
+    def _create_testr_conf(self):
+        top_level_path = os.path.dirname(os.path.dirname(__file__))
+        discover_path = os.path.join(top_level_path, 'test_discover')
+        file_contents = init.TESTR_CONF % (top_level_path, discover_path)
+        with open('.testr.conf', 'w+') as testr_conf_file:
+                testr_conf_file.write(file_contents)
+
+    def take_action(self, parsed_args):
+        returncode = 0
+        if parsed_args.config_file:
+            self._set_env(parsed_args.config_file)
+        else:
+            self._set_env()
+        # Workspace execution mode
+        if parsed_args.workspace:
+            workspace_mgr = workspace.WorkspaceManager(
+                parsed_args.workspace_path)
+            path = workspace_mgr.get_workspace(parsed_args.workspace)
+            if not path:
+                sys.exit(
+                    "The %r workspace isn't registered in "
+                    "%r. Use 'tempest init' to "
+                    "register the workspace." %
+                    (parsed_args.workspace, workspace_mgr.path))
+            os.chdir(path)
+            # NOTE(mtreinish): tempest init should create a .testrepository dir
+            # but since workspaces can be imported let's sanity check and
+            # ensure that one is created
+            self._create_testrepository()
+        # Local execution mode
+        elif os.path.isfile('.testr.conf'):
+            # If you're running in local execution mode and there is not a
+            # testrepository dir create one
+            self._create_testrepository()
+        # local execution with config file mode
+        elif parsed_args.config_file:
+            self._create_testr_conf()
+            self._create_testrepository()
+        else:
+            print("No .testr.conf file was found for local execution")
+            sys.exit(2)
+
+        regex = self._build_regex(parsed_args)
+        if parsed_args.list_tests:
+            argv = ['tempest', 'list-tests', regex]
+            returncode = run_argv(argv, sys.stdin, sys.stdout, sys.stderr)
+        else:
+            options = self._build_options(parsed_args)
+            returncode = self._run(regex, options)
+        sys.exit(returncode)
+
+    def get_description(self):
+        return 'Run tempest'
+
+    def get_parser(self, prog_name):
+        parser = super(TempestRun, self).get_parser(prog_name)
+        parser = self._add_args(parser)
+        return parser
+
+    def _add_args(self, parser):
+        # workspace args
+        parser.add_argument('--workspace', default=None,
+                            help='Name of tempest workspace to use for running'
+                                 ' tests. You can see a list of workspaces '
+                                 'with tempest workspace list')
+        parser.add_argument('--workspace-path', default=None,
+                            dest='workspace_path',
+                            help="The path to the workspace file, the default "
+                                 "is ~/.tempest/workspace.yaml")
+        # Configuration flags
+        parser.add_argument('--config-file', default=None, dest='config_file',
+                            help='Configuration file to run tempest with')
+        # test selection args
+        regex = parser.add_mutually_exclusive_group()
+        regex.add_argument('--smoke', '-s', action='store_true',
+                           help="Run the smoke tests only")
+        regex.add_argument('--regex', '-r', default='',
+                           help='A normal testr selection regex used to '
+                                'specify a subset of tests to run')
+        list_selector = parser.add_mutually_exclusive_group()
+        list_selector.add_argument('--whitelist-file', '--whitelist_file',
+                                   help="Path to a whitelist file, this file "
+                                        "contains a separate regex on each "
+                                        "newline.")
+        list_selector.add_argument('--blacklist-file', '--blacklist_file',
+                                   help='Path to a blacklist file, this file '
+                                        'contains a separate regex exclude on '
+                                        'each newline')
+        # list only args
+        parser.add_argument('--list-tests', '-l', action='store_true',
+                            help='List tests',
+                            default=False)
+        # execution args
+        parser.add_argument('--concurrency', '-w',
+                            help="The number of workers to use, defaults to "
+                                 "the number of cpus")
+        parallel = parser.add_mutually_exclusive_group()
+        parallel.add_argument('--parallel', dest='parallel',
+                              action='store_true',
+                              help='Run tests in parallel (this is the'
+                                   ' default)')
+        parallel.add_argument('--serial', '-t', dest='parallel',
+                              action='store_false',
+                              help='Run tests serially')
+        # output args
+        parser.add_argument("--subunit", action='store_true',
+                            help='Enable subunit v2 output')
+
+        parser.set_defaults(parallel=True)
+        return parser
+
+    def _build_regex(self, parsed_args):
+        regex = ''
+        if parsed_args.smoke:
+            regex = 'smoke'
+        elif parsed_args.regex:
+            regex = parsed_args.regex
+        if parsed_args.whitelist_file or parsed_args.blacklist_file:
+            regex = regex_builder.construct_regex(parsed_args.blacklist_file,
+                                                  parsed_args.whitelist_file,
+                                                  regex, False)
+        return regex
+
+    def _build_options(self, parsed_args):
+        options = []
+        if parsed_args.subunit:
+            options.append("--subunit")
+        if parsed_args.parallel:
+            options.append("--parallel")
+        if parsed_args.concurrency:
+            options.append("--concurrency=%s" % parsed_args.concurrency)
+        return options
+
+    def _run(self, regex, options):
+        returncode = 0
+        argv = ['tempest', 'run', regex] + options
+        if '--subunit' in options:
+            returncode = run_argv(argv, sys.stdin, sys.stdout, sys.stderr)
+        else:
+            argv.append('--subunit')
+            stdin = io.StringIO()
+            stdout_r, stdout_w = os.pipe()
+            subunit_w = os.fdopen(stdout_w, 'wt')
+            subunit_r = os.fdopen(stdout_r)
+            returncodes = {}
+
+            def run_argv_thread():
+                returncodes['testr'] = run_argv(argv, stdin, subunit_w,
+                                                sys.stderr)
+                subunit_w.close()
+
+            run_thread = threading.Thread(target=run_argv_thread)
+            run_thread.start()
+            returncodes['subunit-trace'] = subunit_trace.trace(
+                subunit_r, sys.stdout, post_fails=True, print_failures=True)
+            run_thread.join()
+            subunit_r.close()
+            # python version of pipefail
+            if returncodes['testr']:
+                returncode = returncodes['testr']
+            elif returncodes['subunit-trace']:
+                returncode = returncodes['subunit-trace']
+        return returncode
diff --git a/tempest/cmd/run_stress.py b/tempest/cmd/run_stress.py
deleted file mode 100644
index 943fe5b..0000000
--- a/tempest/cmd/run_stress.py
+++ /dev/null
@@ -1,163 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright 2013 Quanta Research Cambridge, Inc.
-#
-#    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 argparse
-import inspect
-import sys
-try:
-    from unittest import loader
-except ImportError:
-    # unittest in python 2.6 does not contain loader, so uses unittest2
-    from unittest2 import loader
-import traceback
-
-from cliff import command
-from oslo_log import log as logging
-from oslo_serialization import jsonutils as json
-from testtools import testsuite
-
-from tempest.stress import driver
-
-LOG = logging.getLogger(__name__)
-
-
-def discover_stress_tests(path="./", filter_attr=None, call_inherited=False):
-    """Discovers all tempest tests and create action out of them"""
-    LOG.info("Start test discovery")
-    tests = []
-    testloader = loader.TestLoader()
-    list = testloader.discover(path)
-    for func in (testsuite.iterate_tests(list)):
-        attrs = []
-        try:
-            method_name = getattr(func, '_testMethodName')
-            full_name = "%s.%s.%s" % (func.__module__,
-                                      func.__class__.__name__,
-                                      method_name)
-            test_func = getattr(func, method_name)
-            # NOTE(mkoderer): this contains a list of all type attributes
-            attrs = getattr(test_func, "__testtools_attrs")
-        except Exception:
-            next
-        if 'stress' in attrs:
-            if filter_attr is not None and filter_attr not in attrs:
-                continue
-            class_setup_per = getattr(test_func, "st_class_setup_per")
-
-            action = {'action':
-                      "tempest.stress.actions.unit_test.UnitTest",
-                      'kwargs': {"test_method": full_name,
-                                 "class_setup_per": class_setup_per
-                                 }
-                      }
-            if (not call_inherited and
-                getattr(test_func, "st_allow_inheritance") is not True):
-                class_structure = inspect.getmro(test_func.im_class)
-                if test_func.__name__ not in class_structure[0].__dict__:
-                    continue
-            tests.append(action)
-    return tests
-
-
-class TempestRunStress(command.Command):
-
-    def get_parser(self, prog_name):
-        pa = super(TempestRunStress, self).get_parser(prog_name)
-        pa = add_arguments(pa)
-        return pa
-
-    def take_action(self, pa):
-        try:
-            action(pa)
-        except Exception:
-            LOG.exception("Failure in the stress test framework")
-            traceback.print_exc()
-            raise
-        return 0
-
-    def get_description(self):
-        return 'Run tempest stress tests'
-
-
-def add_arguments(parser):
-    parser.add_argument('-d', '--duration', default=300, type=int,
-                        help="Duration of test in secs")
-    parser.add_argument('-s', '--serial', action='store_true',
-                        help="Trigger running tests serially")
-    parser.add_argument('-S', '--stop', action='store_true',
-                        default=False, help="Stop on first error")
-    parser.add_argument('-n', '--number', type=int,
-                        help="How often an action is executed for each "
-                        "process")
-    group = parser.add_mutually_exclusive_group(required=True)
-    group.add_argument('-a', '--all', action='store_true',
-                       help="Execute all stress tests")
-    parser.add_argument('-T', '--type',
-                        help="Filters tests of a certain type (e.g. gate)")
-    parser.add_argument('-i', '--call-inherited', action='store_true',
-                        default=False,
-                        help="Call also inherited function with stress "
-                        "attribute")
-    group.add_argument('-t', "--tests", nargs='?',
-                       help="Name of the file with test description")
-    return parser
-
-
-def action(ns):
-    result = 0
-    if not ns.all:
-        tests = json.load(open(ns.tests, 'r'))
-    else:
-        tests = discover_stress_tests(filter_attr=ns.type,
-                                      call_inherited=ns.call_inherited)
-
-    if ns.serial:
-        # Duration is total time
-        duration = ns.duration / len(tests)
-        for test in tests:
-            step_result = driver.stress_openstack([test],
-                                                  duration,
-                                                  ns.number,
-                                                  ns.stop)
-            # NOTE(mkoderer): we just save the last result code
-            if (step_result != 0):
-                result = step_result
-                if ns.stop:
-                    return result
-    else:
-        result = driver.stress_openstack(tests,
-                                         ns.duration,
-                                         ns.number,
-                                         ns.stop)
-    return result
-
-
-def main():
-    LOG.warning("Deprecated: Use 'tempest run-stress' instead. "
-                "The old entrypoint will be removed in a future release.")
-    parser = argparse.ArgumentParser(description='Run stress tests')
-    pa = add_arguments(parser)
-    ns = pa.parse_args()
-    return action(ns)
-
-
-if __name__ == "__main__":
-    try:
-        sys.exit(main())
-    except Exception:
-        LOG.exception("Failure in the stress test framework")
-        traceback.print_exc()
-        sys.exit(1)
diff --git a/tempest/cmd/subunit_describe_calls.py b/tempest/cmd/subunit_describe_calls.py
new file mode 100644
index 0000000..0f868a9
--- /dev/null
+++ b/tempest/cmd/subunit_describe_calls.py
@@ -0,0 +1,316 @@
+# Copyright 2016 Rackspace
+#
+# 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.
+
+"""
+subunit-describe-calls is a parser for subunit streams to determine what REST
+API calls are made inside of a test and in what order they are called.
+
+Runtime Arguments
+-----------------
+
+**--subunit, -s**: (Optional) The path to the subunit file being parsed,
+defaults to stdin
+
+**--non-subunit-name, -n**: (Optional) The file_name that the logs are being
+stored in
+
+**--output-file, -o**: (Optional) The path where the JSON output will be
+written to. This contains more information than is present in stdout.
+
+**--ports, -p**: (Optional) The path to a JSON file describing the ports being
+used by different services
+
+Usage
+-----
+
+subunit-describe-calls will take in either stdin subunit v1 or v2 stream or a
+file path which contains either a subunit v1 or v2 stream passed via the
+--subunit parameter. This is then parsed checking for details contained in the
+file_bytes of the --non-subunit-name parameter (the default is pythonlogging
+which is what Tempest uses to store logs). By default the OpenStack Kilo
+release port defaults (http://bit.ly/22jpF5P) are used unless a file is
+provided via the --ports option. The resulting output is dumped in JSON output
+to the path provided in the --output-file option.
+
+Ports file JSON structure
+^^^^^^^^^^^^^^^^^^^^^^^^^
+::
+
+  {
+      "<port number>": "<name of service>",
+      ...
+  }
+
+
+Output file JSON structure
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+::
+
+  {
+      "full_test_name[with_id_and_tags]": [
+          {
+              "name": "The ClassName.MethodName that made the call",
+              "verb": "HTTP Verb",
+              "service": "Name of the service",
+              "url": "A shortened version of the URL called",
+              "status_code": "The status code of the response",
+              "request_headers": "The headers of the request",
+              "request_body": "The body of the request",
+              "response_headers": "The headers of the response",
+              "response_body": "The body of the response"
+          }
+      ]
+  }
+"""
+import argparse
+import collections
+import io
+import json
+import os
+import re
+import sys
+
+import subunit
+import testtools
+
+
+class UrlParser(testtools.TestResult):
+    uuid_re = re.compile(r'(^|[^0-9a-f])[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-'
+                         '[0-9a-f]{4}-[0-9a-f]{12}([^0-9a-f]|$)')
+    id_re = re.compile(r'(^|[^0-9a-z])[0-9a-z]{8}[0-9a-z]{4}[0-9a-z]{4}'
+                       '[0-9a-z]{4}[0-9a-z]{12}([^0-9a-z]|$)')
+    ip_re = re.compile(r'(^|[^0-9])[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]'
+                       '{1,3}([^0-9]|$)')
+    url_re = re.compile(r'.*INFO.*Request \((?P<name>.*)\): (?P<code>[\d]{3}) '
+                        '(?P<verb>\w*) (?P<url>.*) .*')
+    port_re = re.compile(r'.*:(?P<port>\d+).*')
+    path_re = re.compile(r'http[s]?://[^/]*/(?P<path>.*)')
+    request_re = re.compile(r'.* Request - Headers: (?P<headers>.*)')
+    response_re = re.compile(r'.* Response - Headers: (?P<headers>.*)')
+    body_re = re.compile(r'.*Body: (?P<body>.*)')
+
+    # Based on mitaka defaults:
+    # http://docs.openstack.org/mitaka/config-reference/
+    # firewalls-default-ports.html
+    services = {
+        "8776": "Block Storage",
+        "8774": "Nova",
+        "8773": "Nova-API", "8775": "Nova-API",
+        "8386": "Sahara",
+        "35357": "Keystone", "5000": "Keystone",
+        "9292": "Glance", "9191": "Glance",
+        "9696": "Neutron",
+        "6000": "Swift", "6001": "Swift", "6002": "Swift",
+        "8004": "Heat", "8000": "Heat", "8003": "Heat",
+        "8777": "Ceilometer",
+        "80": "Horizon",
+        "8080": "Swift",
+        "443": "SSL",
+        "873": "rsync",
+        "3260": "iSCSI",
+        "3306": "MySQL",
+        "5672": "AMQP"}
+
+    def __init__(self, services=None):
+        super(UrlParser, self).__init__()
+        self.test_logs = {}
+        self.services = services or self.services
+
+    def addSuccess(self, test, details=None):
+        output = test.shortDescription() or test.id()
+        calls = self.parse_details(details)
+        self.test_logs.update({output: calls})
+
+    def addSkip(self, test, err, details=None):
+        output = test.shortDescription() or test.id()
+        calls = self.parse_details(details)
+        self.test_logs.update({output: calls})
+
+    def addError(self, test, err, details=None):
+        output = test.shortDescription() or test.id()
+        calls = self.parse_details(details)
+        self.test_logs.update({output: calls})
+
+    def addFailure(self, test, err, details=None):
+        output = test.shortDescription() or test.id()
+        calls = self.parse_details(details)
+        self.test_logs.update({output: calls})
+
+    def stopTestRun(self):
+        super(UrlParser, self).stopTestRun()
+
+    def startTestRun(self):
+        super(UrlParser, self).startTestRun()
+
+    def parse_details(self, details):
+        if details is None:
+            return
+
+        calls = []
+        for _, detail in details.items():
+            in_request = False
+            in_response = False
+            current_call = {}
+            for line in detail.as_text().split("\n"):
+                url_match = self.url_re.match(line)
+                request_match = self.request_re.match(line)
+                response_match = self.response_re.match(line)
+                body_match = self.body_re.match(line)
+
+                if url_match is not None:
+                    if current_call != {}:
+                        calls.append(current_call.copy())
+                        current_call = {}
+                        in_request, in_response = False, False
+                    current_call.update({
+                        "name": url_match.group("name"),
+                        "verb": url_match.group("verb"),
+                        "status_code": url_match.group("code"),
+                        "service": self.get_service(url_match.group("url")),
+                        "url": self.url_path(url_match.group("url"))})
+                elif request_match is not None:
+                    in_request, in_response = True, False
+                    current_call.update(
+                        {"request_headers": request_match.group("headers")})
+                elif in_request and body_match is not None:
+                    in_request = False
+                    current_call.update(
+                        {"request_body": body_match.group(
+                            "body")})
+                elif response_match is not None:
+                    in_request, in_response = False, True
+                    current_call.update(
+                        {"response_headers": response_match.group(
+                            "headers")})
+                elif in_response and body_match is not None:
+                    in_response = False
+                    current_call.update(
+                        {"response_body": body_match.group("body")})
+            if current_call != {}:
+                calls.append(current_call.copy())
+
+        return calls
+
+    def get_service(self, url):
+        match = self.port_re.match(url)
+        if match is not None:
+            return self.services.get(match.group("port"), "Unknown")
+        return "Unknown"
+
+    def url_path(self, url):
+        match = self.path_re.match(url)
+        if match is not None:
+            path = match.group("path")
+            path = self.uuid_re.sub(r'\1<uuid>\2', path)
+            path = self.ip_re.sub(r'\1<ip>\2', path)
+            path = self.id_re.sub(r'\1<id>\2', path)
+            return path
+        return url
+
+
+class FileAccumulator(testtools.StreamResult):
+
+    def __init__(self, non_subunit_name='pythonlogging'):
+        super(FileAccumulator, self).__init__()
+        self.route_codes = collections.defaultdict(io.BytesIO)
+        self.non_subunit_name = non_subunit_name
+
+    def status(self, **kwargs):
+        if kwargs.get('file_name') != self.non_subunit_name:
+            return
+        file_bytes = kwargs.get('file_bytes')
+        if not file_bytes:
+            return
+        route_code = kwargs.get('route_code')
+        stream = self.route_codes[route_code]
+        stream.write(file_bytes)
+
+
+class ArgumentParser(argparse.ArgumentParser):
+    def __init__(self):
+        desc = "Outputs all HTTP calls a given test made that were logged."
+        super(ArgumentParser, self).__init__(description=desc)
+
+        self.prog = "subunit-describe-calls"
+
+        self.add_argument(
+            "-s", "--subunit", metavar="<subunit file>",
+            nargs="?", type=argparse.FileType('rb'), default=sys.stdin,
+            help="The path to the subunit output file.")
+
+        self.add_argument(
+            "-n", "--non-subunit-name", metavar="<non subunit name>",
+            default="pythonlogging",
+            help="The name used in subunit to describe the file contents.")
+
+        self.add_argument(
+            "-o", "--output-file", metavar="<output file>", default=None,
+            help="The output file name for the json.")
+
+        self.add_argument(
+            "-p", "--ports", metavar="<ports file>", default=None,
+            help="A JSON file describing the ports for each service.")
+
+
+def parse(stream, non_subunit_name, ports):
+    if ports is not None and os.path.exists(ports):
+        ports = json.loads(open(ports).read())
+
+    url_parser = UrlParser(ports)
+    suite = subunit.ByteStreamToStreamResult(
+        stream, non_subunit_name=non_subunit_name)
+    result = testtools.StreamToExtendedDecorator(url_parser)
+    accumulator = FileAccumulator(non_subunit_name)
+    result = testtools.StreamResultRouter(result)
+    result.add_rule(accumulator, 'test_id', test_id=None)
+    result.startTestRun()
+    suite.run(result)
+
+    for bytes_io in accumulator.route_codes.values():  # v1 processing
+        bytes_io.seek(0)
+        suite = subunit.ProtocolTestCase(bytes_io)
+        suite.run(url_parser)
+    result.stopTestRun()
+
+    return url_parser
+
+
+def output(url_parser, output_file):
+    if output_file is not None:
+        with open(output_file, "w") as outfile:
+            outfile.write(json.dumps(url_parser.test_logs))
+        return
+
+    for test_name, items in url_parser.test_logs.iteritems():
+        sys.stdout.write('{0}\n'.format(test_name))
+        if not items:
+            sys.stdout.write('\n')
+            continue
+        for item in items:
+            sys.stdout.write('\t- {0} {1} request for {2} to {3}\n'.format(
+                item.get('status_code'), item.get('verb'),
+                item.get('service'), item.get('url')))
+        sys.stdout.write('\n')
+
+
+def entry_point():
+    cl_args = ArgumentParser().parse_args()
+    parser = parse(cl_args.subunit, cl_args.non_subunit_name, cl_args.ports)
+    output(parser, cl_args.output_file)
+
+
+if __name__ == "__main__":
+    entry_point()
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 5e5e127..0a1881c 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -15,8 +15,8 @@
 #    under the License.
 
 import argparse
-import httplib2
 import os
+import re
 import sys
 import traceback
 
@@ -29,6 +29,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
@@ -45,7 +46,7 @@
     conf_dir = os.environ.get('TEMPEST_CONFIG_DIR', default_config_dir)
     conf_file = os.environ.get('TEMPEST_CONFIG', default_config_file)
     path = os.path.join(conf_dir, conf_file)
-    fd = open(path, 'rw')
+    fd = open(path, 'r+')
     return fd
 
 
@@ -77,9 +78,16 @@
                             not CONF.image_feature_enabled.api_v2, update)
 
 
+def _remove_version_project(url_path):
+    # The regex matches strings like /v2.0, /v3/, /v2.1/project-id/
+    return re.sub(r'/v\d+(\.\d+)?(/[^/]+)?', '', url_path)
+
+
 def _get_unversioned_endpoint(base_url):
     endpoint_parts = urlparse.urlparse(base_url)
-    endpoint = endpoint_parts.scheme + '://' + endpoint_parts.netloc
+    new_path = _remove_version_project(endpoint_parts.path)
+    endpoint_parts = endpoint_parts._replace(path=new_path)
+    endpoint = urlparse.urlunparse(endpoint_parts)
     return endpoint
 
 
@@ -89,15 +97,25 @@
         'keystone': os.identity_client,
         'cinder': os.volumes_client,
     }
-    client_dict[service].skip_path()
+    if service != 'keystone':
+        # Since keystone may be listening on a path, do not remove the path.
+        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:
@@ -129,6 +147,10 @@
             contains_version('v2.', versions)):
         print_and_or_update('api_v2', 'volume-feature-enabled',
                             not CONF.volume_feature_enabled.api_v2, update)
+    if (CONF.volume_feature_enabled.api_v3 !=
+            contains_version('v3.', versions)):
+        print_and_or_update('api_v3', 'volume-feature-enabled',
+                            not CONF.volume_feature_enabled.api_v3, update)
 
 
 def verify_api_versions(os, service, update):
@@ -147,7 +169,7 @@
         'nova': os.extensions_client,
         'cinder': os.volumes_extension_client,
         'neutron': os.network_extensions_client,
-        'swift': os.account_client,
+        'swift': os.capabilities_client,
     }
     # NOTE (e0ne): Use Cinder API v2 by default because v1 is deprecated
     if CONF.volume_feature_enabled.api_v2:
@@ -179,7 +201,7 @@
     if service != 'swift':
         resp = extensions_client.list_extensions()
     else:
-        __, resp = extensions_client.list_extensions()
+        __, resp = extensions_client.list_capabilities()
     # For Nova, Cinder and Neutron we use the alias name rather than the
     # 'name' field because the alias is considered to be the canonical
     # name.
@@ -264,12 +286,8 @@
         'object_storage': 'swift',
         'compute': 'nova',
         'orchestration': 'heat',
-        'metering': 'ceilometer',
-        'telemetry': 'ceilometer',
-        'data_processing': 'sahara',
         'baremetal': 'ironic',
         'identity': 'keystone',
-        'database': 'trove'
     }
     # Get catalog list for endpoints to use for validation
     _token, auth_data = os.auth_provider.get_auth()
@@ -351,17 +369,23 @@
     replace = opts.replace_ext
     global CONF_PARSER
 
-    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 = moves.configparser.ConfigParser()
         CONF_PARSER.optionxform = str
         CONF_PARSER.readfp(conf_file)
-    icreds = credentials.get_credentials_provider('verify_tempest_config')
+
+    # Indicate not to create network resources as part of getting credentials
+    net_resources = {
+        'network': False,
+        'router': False,
+        'subnet': False,
+        'dhcp': False
+    }
+    icreds = credentials.get_credentials_provider(
+        'verify_tempest_config', network_resources=net_resources)
     try:
-        os = clients.Manager(icreds.get_primary_creds())
+        os = clients.Manager(icreds.get_primary_creds().credentials)
         services = check_service_availability(os, update)
         results = {}
         for service in ['nova', 'cinder', 'neutron', 'swift']:
@@ -378,8 +402,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()
 
@@ -394,12 +419,11 @@
 
     def take_action(self, parsed_args):
         try:
-            return main(parsed_args)
+            main(parsed_args)
         except Exception:
             LOG.exception("Failure verifying configuration.")
             traceback.print_exc()
             raise
-        return 0
 
 if __name__ == "__main__":
     main()
diff --git a/tempest/cmd/workspace.py b/tempest/cmd/workspace.py
new file mode 100644
index 0000000..d2dc00d
--- /dev/null
+++ b/tempest/cmd/workspace.py
@@ -0,0 +1,229 @@
+# Copyright 2016 Rackspace
+#
+# 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.
+
+"""
+Manages Tempest workspaces
+
+This command is used for managing tempest workspaces
+
+Commands
+========
+
+list
+----
+Outputs the name and path of all known tempest workspaces
+
+register
+--------
+Registers a new tempest workspace via a given --name and --path
+
+rename
+------
+Renames a tempest workspace from --old-name to --new-name
+
+move
+----
+Changes the path of a given tempest workspace --name to --path
+
+remove
+------
+Deletes the entry for a given tempest workspace --name
+
+General Options
+===============
+
+ **--workspace_path**: Allows the user to specify a different location for the
+                       workspace.yaml file containing the workspace definitions
+                       instead of ~/.tempest/workspace.yaml
+"""
+
+import os
+import sys
+
+from cliff import command
+from oslo_concurrency import lockutils
+import prettytable
+import yaml
+
+from tempest import config
+
+CONF = config.CONF
+
+
+class WorkspaceManager(object):
+    def __init__(self, path=None):
+        lockutils.get_lock_path(CONF)
+        self.path = path or os.path.join(
+            os.path.expanduser("~"), ".tempest", "workspace.yaml")
+        if not os.path.isdir(os.path.dirname(self.path)):
+            os.makedirs(self.path.rsplit(os.path.sep, 1)[0])
+        self.workspaces = {}
+
+    @lockutils.synchronized('workspaces', external=True)
+    def get_workspace(self, name):
+        """Returns the workspace that has the given name
+
+        If the workspace isn't registered then `None` is returned.
+        """
+        self._populate()
+        return self.workspaces.get(name)
+
+    @lockutils.synchronized('workspaces', external=True)
+    def rename_workspace(self, old_name, new_name):
+        self._populate()
+        self._name_exists(old_name)
+        self._workspace_name_exists(new_name)
+        self.workspaces[new_name] = self.workspaces.pop(old_name)
+        self._write_file()
+
+    @lockutils.synchronized('workspaces', external=True)
+    def move_workspace(self, name, path):
+        self._populate()
+        path = os.path.abspath(os.path.expanduser(path))
+        self._name_exists(name)
+        self._validate_path(path)
+        self.workspaces[name] = path
+        self._write_file()
+
+    def _name_exists(self, name):
+        if name not in self.workspaces:
+            print("A workspace was not found with name: {0}".format(name))
+            sys.exit(1)
+
+    @lockutils.synchronized('workspaces', external=True)
+    def remove_workspace(self, name):
+        self._populate()
+        self._name_exists(name)
+        self.workspaces.pop(name)
+        self._write_file()
+
+    @lockutils.synchronized('workspaces', external=True)
+    def list_workspaces(self):
+        self._populate()
+        self._validate_workspaces()
+        return self.workspaces
+
+    def _workspace_name_exists(self, name):
+        if name in self.workspaces:
+            print("A workspace already exists with name: {0}.".format(
+                name))
+            sys.exit(1)
+
+    def _validate_path(self, path):
+        if not os.path.exists(path):
+            print("Path does not exist.")
+            sys.exit(1)
+
+    @lockutils.synchronized('workspaces', external=True)
+    def register_new_workspace(self, name, path, init=False):
+        """Adds the new workspace and writes out the new workspace config"""
+        self._populate()
+        path = os.path.abspath(os.path.expanduser(path))
+        # This only happens when register is called from outside of init
+        if not init:
+            self._validate_path(path)
+        self._workspace_name_exists(name)
+        self.workspaces[name] = path
+        self._write_file()
+
+    def _validate_workspaces(self):
+        if self.workspaces is not None:
+            self.workspaces = {n: p for n, p in self.workspaces.items()
+                               if os.path.exists(p)}
+            self._write_file()
+
+    def _write_file(self):
+        with open(self.path, 'w') as f:
+            f.write(yaml.dump(self.workspaces))
+
+    def _populate(self):
+        if not os.path.isfile(self.path):
+            return
+        with open(self.path, 'r') as f:
+            self.workspaces = yaml.safe_load(f) or {}
+
+
+class TempestWorkspace(command.Command):
+    def take_action(self, parsed_args):
+        self.manager = WorkspaceManager(parsed_args.workspace_path)
+        if getattr(parsed_args, 'register', None):
+            self.manager.register_new_workspace(
+                parsed_args.name, parsed_args.path)
+        elif getattr(parsed_args, 'rename', None):
+            self.manager.rename_workspace(
+                parsed_args.old_name, parsed_args.new_name)
+        elif getattr(parsed_args, 'move', None):
+            self.manager.move_workspace(
+                parsed_args.name, parsed_args.path)
+        elif getattr(parsed_args, 'remove', None):
+            self.manager.remove_workspace(
+                parsed_args.name)
+        else:
+            self._print_workspaces()
+        sys.exit(0)
+
+    def get_description(self):
+        return 'Tempest workspace actions'
+
+    def get_parser(self, prog_name):
+        parser = super(TempestWorkspace, self).get_parser(prog_name)
+
+        parser.add_argument(
+            '--workspace-path', required=False, default=None,
+            help="The path to the workspace file, the default is "
+                 "~/.tempest/workspace.yaml")
+
+        subparsers = parser.add_subparsers()
+
+        list_parser = subparsers.add_parser(
+            'list', help='Outputs the name and path of all known tempest '
+            'workspaces')
+        list_parser.set_defaults(list=True)
+
+        register_parser = subparsers.add_parser(
+            'register', help='Registers a new tempest workspace via a given '
+            '--name and --path')
+        register_parser.add_argument('--name', required=True)
+        register_parser.add_argument('--path', required=True)
+        register_parser.set_defaults(register=True)
+
+        update_parser = subparsers.add_parser(
+            'rename', help='Renames a tempest workspace from --old-name to '
+            '--new-name')
+        update_parser.add_argument('--old-name', required=True)
+        update_parser.add_argument('--new-name', required=True)
+        update_parser.set_defaults(rename=True)
+
+        move_parser = subparsers.add_parser(
+            'move', help='Changes the path of a given tempest workspace '
+            '--name to --path')
+        move_parser.add_argument('--name', required=True)
+        move_parser.add_argument('--path', required=True)
+        move_parser.set_defaults(move=True)
+
+        remove_parser = subparsers.add_parser(
+            'remove', help='Deletes the entry for a given tempest workspace '
+            '--name')
+        remove_parser.add_argument('--name', required=True)
+        remove_parser.set_defaults(remove=True)
+
+        return parser
+
+    def _print_workspaces(self):
+        output = prettytable.PrettyTable(["Name", "Path"])
+        if self.manager.list_workspaces() is not None:
+            for name, path in self.manager.list_workspaces().items():
+                output.add_row([name, path])
+
+        print(output)
diff --git a/tempest/common/api_version_request.py b/tempest/common/api_version_request.py
deleted file mode 100644
index d8a5b56..0000000
--- a/tempest/common/api_version_request.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# Copyright 2014 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 re
-
-from tempest import exceptions
-
-
-# Define the minimum and maximum version of the API across all of the
-# REST API. The format of the version is:
-# X.Y where:
-#
-# - X will only be changed if a significant backwards incompatible API
-# change is made which affects the API as whole. That is, something
-# that is only very very rarely incremented.
-#
-# - Y when you make any change to the API. Note that this includes
-# semantic changes which may not affect the input or output formats or
-# even originate in the API code layer. We are not distinguishing
-# between backwards compatible and backwards incompatible changes in
-# the versioning system. It must be made clear in the documentation as
-# to what is a backwards compatible change and what is a backwards
-# incompatible one.
-
-class APIVersionRequest(object):
-    """This class represents an API Version Request.
-
-    This class provides convenience methods for manipulation
-    and comparison of version numbers that we need to do to
-    implement microversions.
-    """
-
-    # NOTE: This 'latest' version is a magic number, we assume any
-    # projects(Nova, etc.) never achieve this number.
-    latest_ver_major = 99999
-    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
-        """
-        # NOTE(gmann): 'version_string' as String "None" will be considered as
-        # invalid version string.
-        self.ver_major = 0
-        self.ver_minor = 0
-
-        if version_string is not None:
-            match = re.match(r"^([1-9]\d*)\.([1-9]\d*|0)$",
-                             version_string)
-            if match:
-                self.ver_major = int(match.group(1))
-                self.ver_minor = int(match.group(2))
-            elif version_string == 'latest':
-                self.ver_major = self.latest_ver_major
-                self.ver_minor = self.latest_ver_minor
-            else:
-                raise exceptions.InvalidAPIVersionString(
-                    version=version_string)
-
-    def __str__(self):
-        """Debug/Logging representation of object."""
-        return ("API Version Request: %s" % self.get_string())
-
-    def is_null(self):
-        return self.ver_major == 0 and self.ver_minor == 0
-
-    def _format_type_error(self, other):
-        return TypeError("'%(other)s' should be an instance of '%(cls)s'" %
-                         {"other": other, "cls": self.__class__})
-
-    def __lt__(self, other):
-        if not isinstance(other, APIVersionRequest):
-            raise self._format_type_error(other)
-
-        return ((self.ver_major, self.ver_minor) <
-                (other.ver_major, other.ver_minor))
-
-    def __eq__(self, other):
-        if not isinstance(other, APIVersionRequest):
-            raise self._format_type_error(other)
-
-        return ((self.ver_major, self.ver_minor) ==
-                (other.ver_major, other.ver_minor))
-
-    def __gt__(self, other):
-        if not isinstance(other, APIVersionRequest):
-            raise self._format_type_error(other)
-
-        return ((self.ver_major, self.ver_minor) >
-                (other.ver_major, other.ver_minor))
-
-    def __le__(self, other):
-        return self < other or self == other
-
-    def __ne__(self, other):
-        return not self.__eq__(other)
-
-    def __ge__(self, other):
-        return self > other or self == other
-
-    def matches(self, min_version, max_version):
-        """Matches the version object.
-
-        Returns whether the version object represents a version
-        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
-
-        If min_version is null then there is no minimum limit.
-        If max_version is null then there is no maximum limit.
-        If self is null then raise ValueError
-        """
-
-        if self.is_null():
-            raise ValueError
-        if max_version.is_null() and min_version.is_null():
-            return True
-        elif max_version.is_null():
-            return min_version <= self
-        elif min_version.is_null():
-            return self <= max_version
-        else:
-            return min_version <= self <= max_version
-
-    def get_string(self):
-        """Version string representation.
-
-        Converts object to string representation which if used to create
-        an APIVersionRequest object results in the same version request.
-        """
-        if self.is_null():
-            return None
-        if (self.ver_major == self.latest_ver_major and
-            self.ver_minor == self.latest_ver_minor):
-            return 'latest'
-        return "%s.%s" % (self.ver_major, self.ver_minor)
diff --git a/tempest/common/api_version_utils.py b/tempest/common/api_version_utils.py
deleted file mode 100644
index c3d977f..0000000
--- a/tempest/common/api_version_utils.py
+++ /dev/null
@@ -1,95 +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 testtools
-
-from tempest.common import api_version_request
-from tempest import exceptions
-
-
-class BaseMicroversionTest(object):
-    """Mixin class for API microversion test class."""
-
-    # NOTE: Basically, each microversion is small API change and we
-    # can use the same tests for most microversions in most cases.
-    # So it is nice to define the test class as possible to run
-    # for all microversions. We need to define microversion range
-    # (min_microversion, max_microversion) on each test class if necessary.
-    min_microversion = None
-    max_microversion = 'latest'
-
-
-def check_skip_with_microversion(test_min_version, test_max_version,
-                                 cfg_min_version, cfg_max_version):
-    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)
-    config_max_version = api_version_request.APIVersionRequest(cfg_max_version)
-    if ((min_version > max_version) or
-       (config_min_version > config_max_version)):
-        msg = ("Test Class versions [%s - %s]. "
-               "Configuration versions [%s - %s]."
-               % (min_version.get_string(),
-                  max_version.get_string(),
-                  config_min_version.get_string(),
-                  config_max_version.get_string()))
-        raise exceptions.InvalidAPIVersionRange(msg)
-
-    # NOTE: Select tests which are in range of configuration like
-    #               config min           config max
-    # ----------------+--------------------------+----------------
-    # ...don't-select|
-    #            ...select...  ...select...  ...select...
-    #                                             |don't-select...
-    # ......................select............................
-    if (max_version < config_min_version or
-        config_max_version < min_version):
-        msg = ("The microversion range[%s - %s] of this test is out of the "
-               "configuration range[%s - %s]."
-               % (min_version.get_string(),
-                  max_version.get_string(),
-                  config_min_version.get_string(),
-                  config_max_version.get_string()))
-        raise testtools.TestCase.skipException(msg)
-
-
-def select_request_microversion(test_min_version, cfg_min_version):
-    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
-    return max_version.get_string()
-
-
-def assert_version_header_matches_request(api_microversion_header_name,
-                                          api_microversion,
-                                          response_header):
-    """Checks API microversion in resposne header
-
-    Verify whether microversion is present in response header
-    and with specified 'api_microversion' value.
-
-    @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
-            expected to be present.
-    """
-    api_microversion_header_name = api_microversion_header_name.lower()
-    if (api_microversion_header_name not in response_header or
-        api_microversion != response_header[api_microversion_header_name]):
-        msg = ("Microversion header '%s' with value '%s' does not match in "
-               "response - %s. " % (api_microversion_header_name,
-                                    api_microversion,
-                                    response_header))
-        raise exceptions.InvalidHTTPResponseHeader(msg)
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 73505e6..4f2fe67 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -15,12 +15,12 @@
 
 from oslo_log import log as logging
 from oslo_utils import excutils
-from tempest_lib.common.utils import data_utils
 
 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
 
@@ -30,7 +30,8 @@
 def create_test_server(clients, validatable=False, validation_resources=None,
                        tenant_network=None, wait_until=None,
                        volume_backed=False, name=None, flavor=None,
-                       image_id=None, **kwargs):
+                       image_id=None, delete_vol_on_termination=True,
+                       **kwargs):
     """Common wrapper utility returning a test server.
 
     This method is a common wrapper returning a test server that can be
@@ -39,20 +40,25 @@
     :param clients: Client manager which provides OpenStack Tempest clients.
     :param validatable: Whether the server will be pingable or sshable.
     :param validation_resources: Resources created for the connection to the
-    server. Include a keypair, a security group and an IP.
+        server. Include a keypair, a security group and an IP.
     :param tenant_network: Tenant network to be used for creating a server.
     :param wait_until: Server status to wait for the server to reach after
-    its creation.
+        its creation.
     :param volume_backed: Whether the instance is volume backed or not.
+    :param name: Name of the server to be provisioned. If not defined a random
+        string ending with '-instance' will be generated.
+    :param flavor: Flavor of the server to be provisioned. If not defined,
+        CONF.compute.flavor_ref will be used instead.
+    :param image_id: ID of the image to be used to provision the server. If not
+        defined, CONF.compute.image_ref will be used instead.
+    :param delete_vol_on_termination: Controls whether the backing volume
+        should be deleted when the server is deleted. Only applies to volume
+        backed servers.
     :returns: a tuple
     """
 
     # 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:
@@ -95,22 +101,23 @@
                 wait_until = 'ACTIVE'
 
     if volume_backed:
-        volume_name = data_utils.rand_name('volume')
+        volume_name = data_utils.rand_name(__name__ + '-volume')
         volumes_client = clients.volumes_v2_client
         if CONF.volume_feature_enabled.api_v1:
             volumes_client = clients.volumes_client
         volume = volumes_client.create_volume(
             display_name=volume_name,
-            imageRef=image_id)
-        volumes_client.wait_for_volume_status(volume['volume']['id'],
-                                              'available')
+            imageRef=image_id,
+            size=CONF.volume.volume_size)
+        waiters.wait_for_volume_status(volumes_client,
+                                       volume['volume']['id'], 'available')
 
         bd_map_v2 = [{
             'uuid': volume['volume']['id'],
             'source_type': 'volume',
             'destination_type': 'volume',
             'boot_index': 0,
-            'delete_on_termination': True}]
+            'delete_on_termination': delete_vol_on_termination}]
         kwargs['block_device_mapping_v2'] = bd_map_v2
 
         # Since this is boot from volume an image does not need
@@ -122,14 +129,13 @@
                                                 **kwargs)
 
     # handle the case of multiple servers
-    servers = []
     if multiple_create_request:
         # Get servers created which name match with name param.
         body_servers = clients.servers_client.list_servers()
         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 +158,39 @@
 
             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
+
+
+def shelve_server(servers_client, server_id, force_shelve_offload=False):
+    """Common wrapper utility to shelve server.
+
+    This method is a common wrapper to make server in 'SHELVED'
+    or 'SHELVED_OFFLOADED' state.
+
+    :param servers_clients: Compute servers client instance.
+    :param server_id: Server to make in shelve state
+    :param force_shelve_offload: Forcefully offload shelve server if it
+                                 is configured not to offload server
+                                 automatically after offload time.
+    """
+    servers_client.shelve_server(server_id)
+
+    offload_time = CONF.compute.shelved_offload_time
+    if offload_time >= 0:
+        waiters.wait_for_server_status(servers_client, server_id,
+                                       'SHELVED_OFFLOADED',
+                                       extra_timeout=offload_time)
+    else:
+        waiters.wait_for_server_status(servers_client, server_id, 'SHELVED')
+        if force_shelve_offload:
+            servers_client.shelve_offload_server(server_id)
+            waiters.wait_for_server_status(servers_client, server_id,
+                                           'SHELVED_OFFLOADED')
diff --git a/tempest/common/cred_client.py b/tempest/common/cred_client.py
deleted file mode 100644
index 5aa794c..0000000
--- a/tempest/common/cred_client.py
+++ /dev/null
@@ -1,188 +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 abc
-
-from oslo_log import log as logging
-import six
-from tempest_lib import auth
-from tempest_lib import exceptions as lib_exc
-
-from tempest.services.identity.v2.json import identity_client as v2_identity
-
-LOG = logging.getLogger(__name__)
-
-
-@six.add_metaclass(abc.ABCMeta)
-class CredsClient(object):
-    """This class is a wrapper around the identity clients
-
-     to provide a single interface for managing credentials in both v2 and v3
-     cases. It's not bound to created credentials, only to a specific set of
-     admin credentials used for generating credentials.
-    """
-
-    def __init__(self, identity_client, projects_client, users_client,
-                 roles_client=None):
-        # The client implies version and credentials
-        self.identity_client = identity_client
-        self.users_client = users_client
-        self.projects_client = projects_client
-        # this is temporary until the v3 clients are
-        # separated, then using *only* each client will become mandatory
-        self.roles_client = roles_client or identity_client
-
-    def create_user(self, username, password, project, email):
-        user = self.users_client.create_user(
-            username, password, project['id'], email)
-        if 'user' in user:
-            user = user['user']
-        return user
-
-    def delete_user(self, user_id):
-        self.users_client.delete_user(user_id)
-
-    @abc.abstractmethod
-    def create_project(self, name, description):
-        pass
-
-    def _check_role_exists(self, role_name):
-        try:
-            roles = self._list_roles()
-            role = next(r for r in roles if r['name'] == role_name)
-        except StopIteration:
-            return None
-        return role
-
-    def create_user_role(self, role_name):
-        if not self._check_role_exists(role_name):
-            self.roles_client.create_role(name=role_name)
-
-    def assign_user_role(self, user, project, role_name):
-        role = self._check_role_exists(role_name)
-        if not role:
-            msg = 'No "%s" role found' % role_name
-            raise lib_exc.NotFound(msg)
-        try:
-            self._assign_user_role(project, user, role)
-        except lib_exc.Conflict:
-            LOG.debug("Role %s already assigned on project %s for user %s" % (
-                role['id'], project['id'], user['id']))
-
-    @abc.abstractmethod
-    def get_credentials(self, user, project, password):
-        """Produces a Credentials object from the details provided
-
-        :param user: a user dict
-        :param project: a project dict
-        :param password: the password as a string
-        :return: a Credentials object with all the available credential details
-        """
-        pass
-
-    def _list_roles(self):
-        roles = self.roles_client.list_roles()['roles']
-        return roles
-
-
-class V2CredsClient(CredsClient):
-
-    def __init__(self, identity_client, projects_client, users_client,
-                 roles_client):
-        super(V2CredsClient, self).__init__(identity_client,
-                                            projects_client,
-                                            users_client,
-                                            roles_client)
-
-    def create_project(self, name, description):
-        tenant = self.projects_client.create_tenant(
-            name=name, description=description)['tenant']
-        return tenant
-
-    def delete_project(self, project_id):
-        self.projects_client.delete_tenant(project_id)
-
-    def get_credentials(self, user, project, password):
-        # User and project already include both ID and name here,
-        # so there's no need to use the fill_in mode
-        return auth.get_credentials(
-            auth_url=None,
-            fill_in=False,
-            identity_version='v2',
-            username=user['name'], user_id=user['id'],
-            tenant_name=project['name'], tenant_id=project['id'],
-            password=password)
-
-    def _assign_user_role(self, project, user, role):
-        self.roles_client.assign_user_role(project['id'], user['id'],
-                                           role['id'])
-
-
-class V3CredsClient(CredsClient):
-
-    def __init__(self, identity_client, projects_client, users_client,
-                 domain_name):
-        super(V3CredsClient, self).__init__(identity_client,
-                                            projects_client, users_client)
-        try:
-            # Domain names must be unique, in any case a list is returned,
-            # selecting the first (and only) element
-            self.creds_domain = self.identity_client.list_domains(
-                params={'name': domain_name})['domains'][0]
-        except lib_exc.NotFound:
-            # TODO(andrea) we could probably create the domain on the fly
-            msg = "Requested domain %s could not be found" % domain_name
-            raise lib_exc.InvalidCredentials(msg)
-
-    def create_project(self, name, description):
-        project = self.projects_client.create_project(
-            name=name, description=description,
-            domain_id=self.creds_domain['id'])['project']
-        return project
-
-    def delete_project(self, project_id):
-        self.projects_client.delete_project(project_id)
-
-    def get_credentials(self, user, project, password):
-        # User, project and domain already include both ID and name here,
-        # so there's no need to use the fill_in mode.
-        return auth.get_credentials(
-            auth_url=None,
-            fill_in=False,
-            identity_version='v3',
-            username=user['name'], user_id=user['id'],
-            project_name=project['name'], project_id=project['id'],
-            password=password,
-            project_domain_id=self.creds_domain['id'],
-            project_domain_name=self.creds_domain['name'])
-
-    def _list_roles(self):
-        roles = self.identity_client.list_roles()['roles']
-        return roles
-
-    def _assign_user_role(self, project, user, role):
-        self.roles_client.assign_user_role_on_project(project['id'],
-                                                      user['id'],
-                                                      role['id'])
-
-
-def get_creds_client(identity_client,
-                     projects_client,
-                     users_client,
-                     roles_client=None,
-                     project_domain_name=None):
-    if isinstance(identity_client, v2_identity.IdentityClient):
-        return V2CredsClient(identity_client, projects_client, users_client,
-                             roles_client)
-    else:
-        return V3CredsClient(identity_client, projects_client, users_client,
-                             project_domain_name)
diff --git a/tempest/common/cred_provider.py b/tempest/common/cred_provider.py
deleted file mode 100644
index 9dd89ea..0000000
--- a/tempest/common/cred_provider.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# Copyright (c) 2014 Deutsche Telekom AG
-# Copyright (c) 2014 Hewlett-Packard 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.
-
-import abc
-
-import six
-from tempest_lib import auth
-
-from tempest import exceptions
-
-
-@six.add_metaclass(abc.ABCMeta)
-class CredentialProvider(object):
-    def __init__(self, identity_version, name=None, network_resources=None,
-                 credentials_domain=None, admin_role=None):
-        """A CredentialProvider supplies credentials to test classes.
-
-        :param identity_version: Identity version of the credentials provided
-        :param name: Name of the calling test. Included in provisioned
-                     credentials when credentials are provisioned on the fly
-        :param network_resources: Network resources required for the
-                                  credentials
-        :param credentials_domain: Domain credentials belong to
-        :param admin_role: Name of the role of the admin account
-        """
-        self.identity_version = identity_version
-        self.name = name or "test_creds"
-        self.network_resources = network_resources
-        self.credentials_domain = credentials_domain or 'Default'
-        self.admin_role = admin_role
-        if not auth.is_identity_version_supported(self.identity_version):
-            raise exceptions.InvalidIdentityVersion(
-                identity_version=self.identity_version)
-
-    @abc.abstractmethod
-    def get_primary_creds(self):
-        return
-
-    @abc.abstractmethod
-    def get_admin_creds(self):
-        return
-
-    @abc.abstractmethod
-    def get_alt_creds(self):
-        return
-
-    @abc.abstractmethod
-    def clear_creds(self):
-        return
-
-    @abc.abstractmethod
-    def is_multi_user(self):
-        return
-
-    @abc.abstractmethod
-    def is_multi_tenant(self):
-        return
-
-    @abc.abstractmethod
-    def get_creds_by_roles(self, roles, force_new=False):
-        return
-
-    @abc.abstractmethod
-    def is_role_available(self, role):
-        return
-
-
-class TestResources(object):
-    """Readonly Credentials, with network resources added."""
-
-    def __init__(self, credentials):
-        self._credentials = credentials
-        self.network = None
-        self.subnet = None
-        self.router = None
-
-    def __getattr__(self, item):
-        return getattr(self._credentials, item)
-
-    def set_resources(self, **kwargs):
-        for key in kwargs.keys():
-            if hasattr(self, key):
-                setattr(self, key, kwargs[key])
-
-    @property
-    def credentials(self):
-        return self._credentials
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 24c1198..e6b46ed 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -12,21 +12,20 @@
 #    limitations under the License.
 
 from oslo_concurrency import lockutils
-from tempest_lib import auth
 
 from tempest import clients
-from tempest.common import cred_provider
 from tempest.common import dynamic_creds
 from tempest.common import preprov_creds
 from tempest import config
-from tempest import exceptions
+from tempest.lib import auth
+from tempest.lib import exceptions
 
 CONF = config.CONF
 
 
 """This module provides factories of credential and credential providers
 
-Credentials providers and clients are (going to be) part of tempest-lib,
+Credentials providers and clients are (going to be) part of tempest.lib,
 and so they may not hold any dependency to tempest configuration.
 
 Methods in this module collect the relevant configuration details and pass
@@ -40,19 +39,19 @@
 
 
 # Subset of the parameters of credential providers that depend on configuration
-def _get_common_provider_params():
+def get_common_provider_params():
     return {
         'credentials_domain': CONF.auth.default_credentials_domain_name,
         'admin_role': CONF.identity.admin_role
     }
 
 
-def _get_dynamic_provider_params():
-    return _get_common_provider_params()
+def get_dynamic_provider_params():
+    return get_common_provider_params()
 
 
-def _get_preprov_provider_params():
-    _common_params = _get_common_provider_params()
+def get_preprov_provider_params():
+    _common_params = get_common_provider_params()
     reseller_admin_role = CONF.object_storage.reseller_admin_role
     return dict(_common_params, **dict([
         ('accounts_lock_dir', lockutils.get_lock_path(CONF)),
@@ -62,89 +61,6 @@
     ]))
 
 
-class LegacyCredentialProvider(cred_provider.CredentialProvider):
-
-    def __init__(self, identity_version):
-        """Credentials provider which returns credentials from tempest.conf
-
-        Credentials provider which always returns the first and second
-        configured accounts as primary and alt users.
-        Credentials from tempest.conf are deprecated, and this credential
-        provider is also accordingly.
-
-        This credential provider can be used in case of serial test execution
-        to preserve the current behaviour of the serial tempest run.
-
-        :param identity_version: Version of the identity API
-        :return: CredentialProvider
-        """
-        super(LegacyCredentialProvider, self).__init__(
-            identity_version=identity_version)
-        self._creds = {}
-
-    def _unique_creds(self, cred_arg=None):
-        """Verify that the configured credentials are valid and distinct """
-        try:
-            user = self.get_primary_creds()
-            alt_user = self.get_alt_creds()
-            return getattr(user, cred_arg) != getattr(alt_user, cred_arg)
-        except exceptions.InvalidCredentials as ic:
-            msg = "At least one of the configured credentials is " \
-                  "not valid: %s" % ic.message
-            raise exceptions.InvalidConfiguration(msg)
-
-    def is_multi_user(self):
-        return self._unique_creds('username')
-
-    def is_multi_tenant(self):
-        return self._unique_creds('tenant_id')
-
-    def get_primary_creds(self):
-        if self._creds.get('primary'):
-            return self._creds.get('primary')
-        primary_credential = get_configured_credentials(
-            credential_type='user', fill_in=False,
-            identity_version=self.identity_version)
-        self._creds['primary'] = cred_provider.TestResources(
-            primary_credential)
-        return self._creds['primary']
-
-    def get_alt_creds(self):
-        if self._creds.get('alt'):
-            return self._creds.get('alt')
-        alt_credential = get_configured_credentials(
-            credential_type='alt_user', fill_in=False,
-            identity_version=self.identity_version)
-        self._creds['alt'] = cred_provider.TestResources(
-            alt_credential)
-        return self._creds['alt']
-
-    def clear_creds(self):
-        self._creds = {}
-
-    def get_admin_creds(self):
-        if self._creds.get('admin'):
-            return self._creds.get('admin')
-        creds = get_configured_credentials(
-            "identity_admin", fill_in=False)
-        self._creds['admin'] = cred_provider.TestResources(creds)
-        return self._creds['admin']
-
-    def get_creds_by_roles(self, roles, force_new=False):
-        msg = "Credentials being specified through the config file can not be"\
-              " used with tests that specify using credentials by roles. "\
-              "Either exclude/skip the tests doing this or use either an "\
-              "test_accounts_file or dynamic credentials."
-        raise exceptions.InvalidConfiguration(msg)
-
-    def is_role_available(self, role):
-        # NOTE(andreaf) LegacyCredentialProvider does not support credentials
-        # by role, so returning always False.
-        # Test that rely on credentials by role should use this to skip
-        # when this is credential provider is used
-        return False
-
-
 # Return the right implementation of CredentialProvider based on config
 # Dropping interface and password, as they are never used anyways
 # TODO(andreaf) Drop them from the CredentialsProvider interface completely
@@ -157,24 +73,33 @@
     # the test should be skipped else it would fail.
     identity_version = identity_version or CONF.identity.auth_version
     if CONF.auth.use_dynamic_credentials or force_tenant_isolation:
-        admin_creds = get_configured_credentials(
-            'identity_admin', fill_in=True, identity_version=identity_version)
+        admin_creds = get_configured_admin_credentials(
+            fill_in=True, identity_version=identity_version)
         return dynamic_creds.DynamicCredentialProvider(
             name=name,
             network_resources=network_resources,
             identity_version=identity_version,
             admin_creds=admin_creds,
-            **_get_dynamic_provider_params())
+            identity_admin_domain_scope=CONF.identity.admin_domain_scope,
+            identity_admin_role=CONF.identity.admin_role,
+            extra_roles=CONF.auth.tempest_roles,
+            neutron_available=CONF.service_available.neutron,
+            project_network_cidr=CONF.network.project_network_cidr,
+            project_network_mask_bits=CONF.network.project_network_mask_bits,
+            public_network_id=CONF.network.public_network_id,
+            create_networks=(CONF.auth.create_isolated_networks and not
+                             CONF.network.shared_physical_network),
+            resource_prefix=CONF.resources_prefix,
+            **get_dynamic_provider_params())
     else:
         if CONF.auth.test_accounts_file:
             # Most params are not relevant for pre-created accounts
             return preprov_creds.PreProvisionedCredentialProvider(
                 name=name, identity_version=identity_version,
-                **_get_preprov_provider_params())
+                **get_preprov_provider_params())
         else:
-            # Dynamic credentials are disabled, and the account file is not
-            # defined - we fall back on credentials configured in tempest.conf
-            return LegacyCredentialProvider(identity_version=identity_version)
+            raise exceptions.InvalidConfiguration(
+                'A valid credential provider is needed')
 
 
 # We want a helper function here to check and see if admin credentials
@@ -191,13 +116,13 @@
     elif CONF.auth.test_accounts_file:
         check_accounts = preprov_creds.PreProvisionedCredentialProvider(
             identity_version=identity_version, name='check_admin',
-            **_get_preprov_provider_params())
+            **get_preprov_provider_params())
         if not check_accounts.admin_available():
             is_admin = False
     else:
         try:
-            get_configured_credentials('identity_admin', fill_in=False,
-                                       identity_version=identity_version)
+            get_configured_admin_credentials(fill_in=False,
+                                             identity_version=identity_version)
         except exceptions.InvalidConfiguration:
             is_admin = False
     return is_admin
@@ -216,9 +141,11 @@
     if CONF.auth.test_accounts_file:
         check_accounts = preprov_creds.PreProvisionedCredentialProvider(
             identity_version=identity_version, name='check_alt',
-            **_get_preprov_provider_params())
+            **get_preprov_provider_params())
     else:
-        check_accounts = LegacyCredentialProvider(identity_version)
+        raise exceptions.InvalidConfiguration(
+            'A valid credential provider is needed')
+
     try:
         if not check_accounts.is_multi_user():
             return False
@@ -246,54 +173,51 @@
 
 # Read credentials from configuration, builds a Credentials object
 # based on the specified or configured version
-def get_configured_credentials(credential_type, fill_in=True,
-                               identity_version=None):
+def get_configured_admin_credentials(fill_in=True, identity_version=None):
     identity_version = identity_version or CONF.identity.auth_version
 
     if identity_version not in ('v2', 'v3'):
         raise exceptions.InvalidConfiguration(
             'Unsupported auth version: %s' % identity_version)
 
-    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')
     # Read the parts of credentials from config
     params = DEFAULT_PARAMS.copy()
-    section, prefix = CREDENTIAL_TYPES[credential_type]
     for attr in conf_attributes:
-        _section = getattr(CONF, section)
-        if prefix is None:
-            params[attr] = getattr(_section, attr)
-        else:
-            params[attr] = getattr(_section, prefix + "_" + attr)
+        params[attr] = getattr(CONF.auth, 'admin_' + attr)
     # 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,
                                   identity_version=identity_version, **params)
     if not fill_in:
         if not credentials.is_valid():
-            msg = ("The %s credentials are incorrectly set in the config file."
-                   " Double check that all required values are assigned" %
-                   credential_type)
-            raise exceptions.InvalidConfiguration(msg)
+            msg = ("The admin credentials are incorrectly set in the config "
+                   "file for identity version %s. Double check that all "
+                   "required values are assigned.")
+            raise exceptions.InvalidConfiguration(msg % identity_version)
     return credentials
 
 
 # Wrapper around auth.get_credentials to use the configured identity version
-# is none is specified
+# if none is specified
 def get_credentials(fill_in=True, identity_version=None, **kwargs):
     params = dict(DEFAULT_PARAMS, **kwargs)
     identity_version = identity_version or CONF.identity.auth_version
     # In case of "v3" add the domain from config if not specified
+    # To honour the "default_credentials_domain_name", if not domain
+    # field is specified at all, add it the credential dict.
     if identity_version == 'v3':
         domain_fields = set(x for x in auth.KeystoneV3Credentials.ATTRIBUTES
                             if 'domain' in x)
         if not domain_fields.intersection(kwargs.keys()):
             domain_name = CONF.auth.default_credentials_domain_name
-            params['user_domain_name'] = domain_name
+            # NOTE(andreaf) Setting domain_name implicitly sets user and
+            # project domain names, if they are None
+            params['domain_name'] = domain_name
 
         auth_url = CONF.identity.uri_v3
     else:
@@ -306,19 +230,9 @@
 # === Credential / client managers
 
 
-class ConfiguredUserManager(clients.Manager):
-    """Manager that uses user credentials for its managed client objects"""
-
-    def __init__(self, service=None):
-        super(ConfiguredUserManager, self).__init__(
-            credentials=get_configured_credentials('user'),
-            service=service)
-
-
 class AdminManager(clients.Manager):
     """Manager that uses admin credentials for its managed client objects"""
 
-    def __init__(self, service=None):
+    def __init__(self):
         super(AdminManager, self).__init__(
-            credentials=get_configured_credentials('identity_admin'),
-            service=service)
+            credentials=get_configured_admin_credentials())
diff --git a/tempest/common/custom_matchers.py b/tempest/common/custom_matchers.py
index 8ba33ed..ed11b21 100644
--- a/tempest/common/custom_matchers.py
+++ b/tempest/common/custom_matchers.py
@@ -14,7 +14,6 @@
 
 import re
 
-import six
 from testtools import helpers
 
 
@@ -28,7 +27,7 @@
     checked in each test code.
     """
 
-    def __init__(self, target, method):
+    def __init__(self, target, method, policies=None):
         """Initialization of ExistsAllResponseHeaders
 
         param: target Account/Container/Object
@@ -36,14 +35,34 @@
         """
         self.target = target
         self.method = method
+        self.policies = policies or []
+
+    def _content_length_required(self, resp):
+        # Verify whether given HTTP response must contain content-length.
+        # Take into account the exceptions defined in RFC 7230.
+        if resp.status in range(100, 200) or resp.status == 204:
+            return False
+
+        return True
 
     def match(self, actual):
         """Check headers
 
-        param: actual HTTP response headers
+        param: actual HTTP response object containing headers and status
         """
-        # Check common headers for all HTTP methods
-        if 'content-length' not in actual:
+        # Check common headers for all HTTP methods.
+        #
+        # Please note that for 1xx and 204 responses Content-Length presence
+        # is not checked intensionally. According to RFC 7230 a server MUST
+        # NOT send the header in such responses. Thus, clients should not
+        # depend on this header. However, the standard does not require them
+        # to validate the server's behavior. We leverage that to not refuse
+        # any implementation violating it like Swift [1] or some versions of
+        # Ceph RadosGW [2].
+        # [1] https://bugs.launchpad.net/swift/+bug/1537811
+        # [2] http://tracker.ceph.com/issues/13582
+        if ('content-length' not in actual and
+                self._content_length_required(actual)):
             return NonExistentHeader('content-length')
         if 'content-type' not in actual:
             return NonExistentHeader('content-type')
@@ -65,11 +84,63 @@
                     return NonExistentHeader('x-account-container-count')
                 if 'x-account-object-count' not in actual:
                     return NonExistentHeader('x-account-object-count')
+                if int(actual['x-account-container-count']) > 0:
+                    acct_header = "x-account-storage-policy-"
+                    matched_policy_count = 0
+
+                    # Loop through the policies and look for account
+                    # usage data.  There should be at least 1 set
+                    for policy in self.policies:
+                        front_header = acct_header + policy['name'].lower()
+
+                        usage_policies = [
+                            front_header + '-bytes-used',
+                            front_header + '-object-count',
+                            front_header + '-container-count'
+                        ]
+
+                        # There should be 3 usage values for a give storage
+                        # policy in an account bytes, object count, and
+                        # container count
+                        policy_hdrs = sum(1 for use_hdr in usage_policies
+                                          if use_hdr in actual)
+
+                        # If there are less than 3 headers here then 1 is
+                        # missing, let's figure out which one and report
+                        if policy_hdrs == 3:
+                            matched_policy_count = matched_policy_count + 1
+                        else:
+                            if policy_hdrs > 0 and policy_hdrs < 3:
+                                for use_hdr in usage_policies:
+                                    if use_hdr not in actual:
+                                        return NonExistentHeader(use_hdr)
+
+                    # Only flag an error if actual policies have been read and
+                    # no usage has been found
+                    if self.policies and matched_policy_count == 0:
+                        return GenericError("No storage policy usage headers")
+
             elif self.target == 'Container':
                 if 'x-container-bytes-used' not in actual:
                     return NonExistentHeader('x-container-bytes-used')
                 if 'x-container-object-count' not in actual:
                     return NonExistentHeader('x-container-object-count')
+                if 'x-storage-policy' not in actual:
+                    return NonExistentHeader('x-storage-policy')
+                else:
+                    policy_name = actual['x-storage-policy']
+
+                    # loop through the policies and ensure that
+                    # the value in the container header matches
+                    # one of the storage policies
+                    for policy in self.policies:
+                        if policy['name'] == policy_name:
+                            break
+                    else:
+                        # Ensure that there are actual policies stored
+                        if self.policies:
+                            return InvalidHeaderValue('x-storage-policy',
+                                                      policy_name)
             elif self.target == 'Object':
                 if 'etag' not in actual:
                     return NonExistentHeader('etag')
@@ -95,6 +166,19 @@
         return None
 
 
+class GenericError(object):
+    """Informs an error message of a generic error during header evaluation"""
+
+    def __init__(self, body):
+        self.body = body
+
+    def describe(self):
+        return "%s" % self.body
+
+    def get_details(self):
+        return {}
+
+
 class NonExistentHeader(object):
     """Informs an error message in the case of missing a certain header"""
 
@@ -108,6 +192,20 @@
         return {}
 
 
+class InvalidHeaderValue(object):
+    """Informs an error message when a header contains a bad value"""
+
+    def __init__(self, header, value):
+        self.header = header
+        self.value = value
+
+    def describe(self):
+        return "InvalidValue (%s, %s)" % (self.header, self.value)
+
+    def get_details(self):
+        return {}
+
+
 class AreAllWellFormatted(object):
     """Specific matcher to check the correctness of formats of values
 
@@ -118,7 +216,7 @@
     """
 
     def match(self, actual):
-        for key, value in six.iteritems(actual):
+        for key, value in actual.items():
             if key in ('content-length', 'x-account-bytes-used',
                        'x-account-container-count', 'x-account-object-count',
                        'x-container-bytes-used', 'x-container-object-count')\
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index 1810c57..632a876 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -15,23 +15,25 @@
 import netaddr
 from oslo_log import log as logging
 import six
-from tempest_lib import exceptions as lib_exc
 
 from tempest import clients
-from tempest.common import cred_client
-from tempest.common import cred_provider
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest import exceptions
+from tempest.lib.common import cred_client
+from tempest.lib.common import cred_provider
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 
-CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
 class DynamicCredentialProvider(cred_provider.CredentialProvider):
 
     def __init__(self, identity_version, name=None, network_resources=None,
-                 credentials_domain=None, admin_role=None, admin_creds=None):
+                 credentials_domain=None, admin_role=None, admin_creds=None,
+                 identity_admin_domain_scope=False,
+                 identity_admin_role='admin', extra_roles=None,
+                 neutron_available=False, create_networks=True,
+                 project_network_cidr=None, project_network_mask_bits=None,
+                 public_network_id=None, resource_prefix=None):
         """Creates credentials dynamically for tests
 
         A credential provider that, based on an initial set of
@@ -48,6 +50,23 @@
         :param dict network_resources: network resources to be created for
                                        the created credentials
         :param Credentials admin_creds: initial admin credentials
+        :param bool identity_admin_domain_scope: Set to true if admin should be
+                                                 scoped to the domain. By
+                                                 default this is False and the
+                                                 admin role is scoped to the
+                                                 project.
+        :param str identity_admin_role: The role name to use for admin
+        :param list extra_roles: A list of strings for extra roles that should
+                                 be assigned to all created users
+        :param bool neutron_available: Whether we are running in an environemnt
+                                       with neutron
+        :param bool create_networks: Whether dynamic project networks should be
+                                     created or not
+        :param project_network_cidr: The CIDR to use for created project
+                                     networks
+        :param project_network_mask_bits: The network mask bits to use for
+                                          created project networks
+        :param public_network_id: The id for the public network to use
         """
         super(DynamicCredentialProvider, self).__init__(
             identity_version=identity_version, admin_role=admin_role,
@@ -56,13 +75,23 @@
         self.network_resources = network_resources
         self._creds = {}
         self.ports = []
+        self.resource_prefix = resource_prefix or ''
+        self.neutron_available = neutron_available
+        self.create_networks = create_networks
+        self.project_network_cidr = project_network_cidr
+        self.project_network_mask_bits = project_network_mask_bits
+        self.public_network_id = public_network_id
         self.default_admin_creds = admin_creds
+        self.identity_admin_domain_scope = identity_admin_domain_scope
+        self.identity_admin_role = identity_admin_role or 'admin'
+        self.extra_roles = extra_roles or []
         (self.identity_admin_client,
          self.tenants_admin_client,
          self.users_admin_client,
          self.roles_admin_client,
-         self.network_admin_client,
+         self.domains_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()
@@ -78,6 +107,7 @@
             self.tenants_admin_client,
             self.users_admin_client,
             self.roles_admin_client,
+            self.domains_admin_client,
             self.creds_domain_name)
 
     def _get_admin_clients(self):
@@ -90,49 +120,61 @@
         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, os.network_client, os.networks_client,
+                    os.roles_client, None,
+                    os.networks_client, os.routers_client, os.subnets_client,
+                    os.ports_client, os.security_groups_client)
+        else:
+            # We use a dedicated client manager for identity client in case we
+            # need a different token scope for them.
+            scope = 'domain' if self.identity_admin_domain_scope else 'project'
+            identity_os = clients.Manager(self.default_admin_creds,
+                                          scope=scope)
+            return (identity_os.identity_v3_client,
+                    identity_os.projects_client,
+                    identity_os.users_v3_client, identity_os.roles_v3_client,
+                    identity_os.domains_client,
+                    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, None, os.network_client,
-                    os.networks_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.
+    def _create_creds(self, admin=False, roles=None):
+        """Create credentials with random name.
 
-        If the name contains a '.' is the full class path of something, and
-        we don't really care. If it isn't, it's probably a meaningful name,
-        so use it.
+        Creates project and user. When admin flag is True create user
+        with admin role. Assign user with additional roles (for example
+        _member_) and roles requested by caller.
 
-        For logging purposes, -user and -tenant are long and redundant,
-        don't use them. The user# will be sufficient to figure it out.
+        :param admin: Flag if to assign to the user admin role
+        :type admin: bool
+        :param roles: Roles to assign for the user
+        :type roles: list
+        :return: Readonly Credentials with network resources
         """
-        if '.' in self.name:
-            root = ""
-        else:
-            root = self.name
+        root = self.name
 
-        project_name = data_utils.rand_name(root) + suffix
+        project_name = data_utils.rand_name(root, prefix=self.resource_prefix)
         project_desc = project_name + "-desc"
         project = self.creds_client.create_project(
             name=project_name, description=project_desc)
 
-        username = data_utils.rand_name(root) + suffix
+        # NOTE(andreaf) User and project can be distinguished from the context,
+        # having the same ID in both makes it easier to match them and debug.
+        username = project_name
         user_password = data_utils.rand_password()
-        email = data_utils.rand_name(root) + suffix + "@example.com"
+        email = data_utils.rand_name(
+            root, prefix=self.resource_prefix) + "@example.com"
         user = self.creds_client.create_user(
             username, user_password, project, email)
-        if 'user' in user:
-            user = user['user']
         role_assigned = False
         if admin:
-            self.creds_client.assign_user_role(user, project,
-                                               self.admin_role)
+            self.creds_client.assign_user_role(user, project, self.admin_role)
             role_assigned = True
+            if (self.identity_version == 'v3' and
+                    self.identity_admin_domain_scope):
+                self.creds_client.assign_user_role_on_domain(
+                    user, self.identity_admin_role)
         # Add roles specified in config file
-        for conf_role in CONF.auth.tempest_roles:
+        for conf_role in self.extra_roles:
             self.creds_client.assign_user_role(user, project, conf_role)
             role_assigned = True
         # Add roles requested by caller
@@ -144,13 +186,30 @@
         # it must beassigned a role on the project. So we need to ensure that
         # our newly created user has a role on the newly created project.
         if self.identity_version == 'v3' and not role_assigned:
-            self.creds_client.create_user_role('Member')
+            try:
+                self.creds_client.create_user_role('Member')
+            except lib_exc.Conflict:
+                LOG.warning('Member role already exists, ignoring conflict.')
             self.creds_client.assign_user_role(user, project, 'Member')
 
         creds = self.creds_client.get_credentials(user, project, user_password)
         return cred_provider.TestResources(creds)
 
     def _create_network_resources(self, tenant_id):
+        """The function creates network resources in the given tenant.
+
+        The function checks if network_resources class member is empty,
+        In case it is, it will create a network, a subnet and a router for
+        the tenant according to the given tenant id parameter.
+        Otherwise it will create a network resource according
+        to the values from network_resources dict.
+
+        :param tenant_id: The tenant id to create resources for.
+        :type tenant_id: str
+        :raises: InvalidConfiguration, Exception
+        :returns: network resources(network,subnet,router)
+        :rtype: tuple
+        """
         network = None
         subnet = None
         router = None
@@ -159,35 +218,43 @@
             if self.network_resources['router']:
                 if (not self.network_resources['subnet'] or
                     not self.network_resources['network']):
-                    raise exceptions.InvalidConfiguration(
+                    raise lib_exc.InvalidConfiguration(
                         'A router requires a subnet and network')
             elif self.network_resources['subnet']:
                 if not self.network_resources['network']:
-                    raise exceptions.InvalidConfiguration(
+                    raise lib_exc.InvalidConfiguration(
                         'A subnet requires a network')
             elif self.network_resources['dhcp']:
-                raise exceptions.InvalidConfiguration('DHCP requires a subnet')
+                raise lib_exc.InvalidConfiguration('DHCP requires a subnet')
 
-        data_utils.rand_name_root = data_utils.rand_name(self.name)
+        rand_name_root = data_utils.rand_name(
+            self.name, prefix=self.resource_prefix)
         if not self.network_resources or self.network_resources['network']:
-            network_name = data_utils.rand_name_root + "-network"
+            network_name = rand_name_root + "-network"
             network = self._create_network(network_name, tenant_id)
         try:
             if not self.network_resources or self.network_resources['subnet']:
-                subnet_name = data_utils.rand_name_root + "-subnet"
+                subnet_name = rand_name_root + "-subnet"
                 subnet = self._create_subnet(subnet_name, tenant_id,
                                              network['id'])
             if not self.network_resources or self.network_resources['router']:
-                router_name = data_utils.rand_name_root + "-router"
+                router_name = rand_name_root + "-router"
                 router = self._create_router(router_name, tenant_id)
                 self._add_router_interface(router['id'], subnet['id'])
         except Exception:
-            if router:
-                self._clear_isolated_router(router['id'], router['name'])
-            if subnet:
-                self._clear_isolated_subnet(subnet['id'], subnet['name'])
-            if network:
-                self._clear_isolated_network(network['id'], network['name'])
+            try:
+                if router:
+                    self._clear_isolated_router(router['id'], router['name'])
+                if subnet:
+                    self._clear_isolated_subnet(subnet['id'], subnet['name'])
+                if network:
+                    self._clear_isolated_network(network['id'],
+                                                 network['name'])
+            except Exception as cleanup_exception:
+                msg = "There was an exception trying to setup network " \
+                      "resources for tenant %s, and this error happened " \
+                      "trying to clean them up: %s"
+                LOG.warning(msg, tenant_id, cleanup_exception)
             raise
         return network, subnet, router
 
@@ -197,8 +264,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(self.project_network_cidr)
+        mask_bits = self.project_network_mask_bits
         for subnet_cidr in base_cidr.subnet(mask_bits):
             try:
                 if self.network_resources:
@@ -227,15 +294,15 @@
 
     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,
+            network_id=self.public_network_id)
+        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):
@@ -249,17 +316,15 @@
                 credentials = self._create_creds(roles=credential_type)
             self._creds[str(credential_type)] = credentials
             # Maintained until tests are ported
-            LOG.info("Acquired dynamic creds:\n credentials: %s"
-                     % credentials)
-            if (CONF.service_available.neutron and
-                not CONF.baremetal.driver_enabled and
-                CONF.auth.create_isolated_networks):
+            LOG.info("Acquired dynamic creds:\n credentials: %s", credentials)
+            if (self.neutron_available and
+                self.create_networks):
                 network, subnet, router = self._create_network_resources(
                     credentials.tenant_id)
                 credentials.set_resources(network=network, subnet=subnet,
                                           router=router)
                 LOG.info("Created isolated network resources for : \n"
-                         + " credentials: %s" % credentials)
+                         + " credentials: %s", credentials)
         return credentials
 
     def get_primary_creds(self):
@@ -286,11 +351,11 @@
         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' %
+            LOG.warning('router with name: %s not found for delete',
                         router_name)
 
     def _clear_isolated_subnet(self, subnet_id, subnet_name):
@@ -298,7 +363,7 @@
         try:
             client.delete_subnet(subnet_id)
         except lib_exc.NotFound:
-            LOG.warning('subnet with name: %s not found for delete' %
+            LOG.warning('subnet with name: %s not found for delete',
                         subnet_name)
 
     def _clear_isolated_network(self, network_id, network_name):
@@ -306,7 +371,7 @@
         try:
             net_client.delete_network(network_id)
         except lib_exc.NotFound:
-            LOG.warning('network with name: %s not found for delete' %
+            LOG.warning('network with name: %s not found for delete',
                         network_name)
 
     def _cleanup_default_secgroup(self, tenant):
@@ -318,11 +383,11 @@
             try:
                 nsg_client.delete_security_group(secgroup['id'])
             except lib_exc.NotFound:
-                LOG.warning('Security group %s, id %s not found for clean-up' %
-                            (secgroup['name'], secgroup['id']))
+                LOG.warning('Security group %s, id %s not found for clean-up',
+                            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,
@@ -335,11 +400,11 @@
             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:
-                    LOG.warning('router with name: %s not found for delete' %
+                    LOG.warning('router with name: %s not found for delete',
                                 creds.router['name'])
                 self._clear_isolated_router(creds.router['id'],
                                             creds.router['name'])
@@ -360,14 +425,23 @@
             try:
                 self.creds_client.delete_user(creds.user_id)
             except lib_exc.NotFound:
-                LOG.warning("user with name: %s not found for delete" %
+                LOG.warning("user with name: %s not found for delete",
                             creds.username)
+            # NOTE(zhufl): Only when neutron's security_group ext is
+            # enabled, _cleanup_default_secgroup will not raise error. But
+            # here cannot use test.is_extension_enabled for it will cause
+            # "circular dependency". So here just use try...except to
+            # ensure tenant deletion without big changes.
             try:
-                if CONF.service_available.neutron:
+                if self.neutron_available:
                     self._cleanup_default_secgroup(creds.tenant_id)
+            except lib_exc.NotFound:
+                LOG.warning("failed to cleanup tenant %s's secgroup",
+                            creds.tenant_name)
+            try:
                 self.creds_client.delete_project(creds.tenant_id)
             except lib_exc.NotFound:
-                LOG.warning("tenant with name: %s not found for delete" %
+                LOG.warning("tenant with name: %s not found for delete",
                             creds.tenant_name)
         self._creds = {}
 
diff --git a/tempest/common/fixed_network.py b/tempest/common/fixed_network.py
index 3fc1365..f50edbd 100644
--- a/tempest/common/fixed_network.py
+++ b/tempest/common/fixed_network.py
@@ -13,9 +13,8 @@
 import copy
 from oslo_log import log as logging
 
-from tempest_lib.common.utils import misc as misc_utils
-
 from tempest import exceptions
+from tempest.lib.common.utils import test_utils
 
 LOG = logging.getLogger(__name__)
 
@@ -32,7 +31,7 @@
         list returns a 404, there are no found networks, or the found network
         is invalid
     """
-    caller = misc_utils.find_test_caller()
+    caller = test_utils.find_test_caller()
 
     if not name:
         raise exceptions.InvalidTestResource(type='network', name=name)
@@ -85,7 +84,7 @@
            tenant network is available in the creds provider
     :returns: a dict with 'id' and 'name' of the network
     """
-    caller = misc_utils.find_test_caller()
+    caller = test_utils.find_test_caller()
     net_creds = creds_provider.get_primary_creds()
     network = getattr(net_creds, 'network', None)
     if not network or not network.get('name'):
@@ -123,5 +122,5 @@
             params.update({"networks": [{'uuid': network['id']}]})
         else:
             LOG.warning('The provided network dict: %s was invalid and did '
-                        'not contain an id' % network)
+                        'not contain an id', network)
     return params
diff --git a/tempest/common/generator/__init__.py b/tempest/common/generator/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/common/generator/__init__.py
+++ /dev/null
diff --git a/tempest/common/generator/base_generator.py b/tempest/common/generator/base_generator.py
deleted file mode 100644
index 3a51f2e..0000000
--- a/tempest/common/generator/base_generator.py
+++ /dev/null
@@ -1,176 +0,0 @@
-# Copyright 2014 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 copy
-import functools
-
-import jsonschema
-import six
-
-
-def _check_for_expected_result(name, schema):
-    expected_result = None
-    if "results" in schema:
-        if name in schema["results"]:
-            expected_result = schema["results"][name]
-    return expected_result
-
-
-def generator_type(*args, **kwargs):
-    def wrapper(func):
-        func.types = args
-        for key in kwargs:
-            setattr(func, key, kwargs[key])
-        return func
-    return wrapper
-
-
-def simple_generator(fn):
-    """Decorator for simple generators that return one value"""
-    @functools.wraps(fn)
-    def wrapped(self, schema):
-        result = fn(self, schema)
-        if result is not None:
-            expected_result = _check_for_expected_result(fn.__name__, schema)
-            return (fn.__name__, result, expected_result)
-        return
-    return wrapped
-
-
-class BasicGeneratorSet(object):
-    _instance = None
-
-    schema = {
-        "type": "object",
-        "properties": {
-            "name": {"type": "string"},
-            "http-method": {
-                "enum": ["GET", "PUT", "HEAD",
-                         "POST", "PATCH", "DELETE", 'COPY']
-            },
-            "admin_client": {"type": "boolean"},
-            "url": {"type": "string"},
-            "default_result_code": {"type": "integer"},
-            "json-schema": {},
-            "resources": {
-                "type": "array",
-                "items": {
-                    "oneOf": [
-                        {"type": "string"},
-                        {
-                            "type": "object",
-                            "properties": {
-                                "name": {"type": "string"},
-                                "expected_result": {"type": "integer"}
-                            }
-                        }
-                    ]
-                }
-            },
-            "results": {
-                "type": "object",
-                "properties": {}
-            }
-        },
-        "required": ["name", "http-method", "url"],
-        "additionalProperties": False,
-    }
-
-    def __init__(self):
-        self.types_dict = {}
-        for m in dir(self):
-            if callable(getattr(self, m)) and not'__' in m:
-                method = getattr(self, m)
-                if hasattr(method, "types"):
-                    for type in method.types:
-                        if type not in self.types_dict:
-                            self.types_dict[type] = []
-                        self.types_dict[type].append(method)
-
-    def validate_schema(self, schema):
-        if "json-schema" in schema:
-            jsonschema.Draft4Validator.check_schema(schema['json-schema'])
-        jsonschema.validate(schema, self.schema)
-
-    def generate_scenarios(self, schema, path=None):
-        """Generate scenario (all possible test cases) out of the given schema
-
-        :param schema: a dict style schema (see ``BasicGeneratorSet.schema``)
-        :param path: the schema path if the given schema is a subschema
-        """
-        schema_type = schema['type']
-        scenarios = []
-
-        if schema_type == 'object':
-            properties = schema["properties"]
-            for attribute, definition in six.iteritems(properties):
-                current_path = copy.copy(path)
-                if path is not None:
-                    current_path.append(attribute)
-                else:
-                    current_path = [attribute]
-                scenarios.extend(
-                    self.generate_scenarios(definition, current_path))
-        elif isinstance(schema_type, list):
-            if "integer" in schema_type:
-                schema_type = "integer"
-            else:
-                raise Exception("non-integer list types not supported")
-        for generator in self.types_dict[schema_type]:
-            if hasattr(generator, "needed_property"):
-                prop = generator.needed_property
-                if (prop not in schema or
-                    schema[prop] is None or
-                    schema[prop] is False):
-                    continue
-
-            name = generator.__name__
-            if ("exclude_tests" in schema and
-               name in schema["exclude_tests"]):
-                continue
-            if path is not None:
-                name = "%s_%s" % ("_".join(path), name)
-            scenarios.append({
-                "_negtest_name": name,
-                "_negtest_generator": generator,
-                "_negtest_schema": schema,
-                "_negtest_path": path})
-        return scenarios
-
-    def generate_payload(self, test, schema):
-        """Generates one jsonschema out of the given test.
-
-        It's mandatory to use generate_scenarios before to register all needed
-        variables to the test.
-
-        :param test: A test object (scenario) with all _negtest variables on it
-        :param schema: schema for the test
-        """
-        generator = test._negtest_generator
-        ret = generator(test._negtest_schema)
-        path = copy.copy(test._negtest_path)
-        expected_result = None
-
-        if ret is not None:
-            generator_result = generator(test._negtest_schema)
-            invalid_snippet = generator_result[1]
-            expected_result = generator_result[2]
-            element = path.pop()
-            if len(path) > 0:
-                schema_snip = reduce(dict.get, path, schema)
-                schema_snip[element] = invalid_snippet
-            else:
-                schema[element] = invalid_snippet
-        return expected_result
diff --git a/tempest/common/generator/negative_generator.py b/tempest/common/generator/negative_generator.py
deleted file mode 100644
index 67ace54..0000000
--- a/tempest/common/generator/negative_generator.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright 2014 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 copy
-
-import tempest.common.generator.base_generator as base
-import tempest.common.generator.valid_generator as valid
-
-
-class NegativeTestGenerator(base.BasicGeneratorSet):
-    @base.generator_type("string")
-    @base.simple_generator
-    def gen_int(self, _):
-        return 4
-
-    @base.generator_type("integer")
-    @base.simple_generator
-    def gen_string(self, _):
-        return "XXXXXX"
-
-    @base.generator_type("integer", "string")
-    def gen_none(self, schema):
-        # Note(mkoderer): it's not using the decorator otherwise it'd be
-        # filtered
-        expected_result = base._check_for_expected_result('gen_none', schema)
-        return ('gen_none', None, expected_result)
-
-    @base.generator_type("string")
-    @base.simple_generator
-    def gen_str_min_length(self, schema):
-        min_length = schema.get("minLength", 0)
-        if min_length > 0:
-            return "x" * (min_length - 1)
-
-    @base.generator_type("string", needed_property="maxLength")
-    @base.simple_generator
-    def gen_str_max_length(self, schema):
-        max_length = schema.get("maxLength", -1)
-        return "x" * (max_length + 1)
-
-    @base.generator_type("integer", needed_property="minimum")
-    @base.simple_generator
-    def gen_int_min(self, schema):
-        minimum = schema["minimum"]
-        if "exclusiveMinimum" not in schema:
-            minimum -= 1
-        return minimum
-
-    @base.generator_type("integer", needed_property="maximum")
-    @base.simple_generator
-    def gen_int_max(self, schema):
-        maximum = schema["maximum"]
-        if "exclusiveMaximum" not in schema:
-            maximum += 1
-        return maximum
-
-    @base.generator_type("object", needed_property="additionalProperties")
-    @base.simple_generator
-    def gen_obj_add_attr(self, schema):
-        valid_schema = valid.ValidTestGenerator().generate_valid(schema)
-        new_valid = copy.deepcopy(valid_schema)
-        new_valid["$$$$$$$$$$"] = "xxx"
-        return new_valid
diff --git a/tempest/common/generator/valid_generator.py b/tempest/common/generator/valid_generator.py
deleted file mode 100644
index 3070489..0000000
--- a/tempest/common/generator/valid_generator.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2014 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 six
-
-import tempest.common.generator.base_generator as base
-
-
-class ValidTestGenerator(base.BasicGeneratorSet):
-    @base.generator_type("string")
-    @base.simple_generator
-    def generate_valid_string(self, schema):
-        size = schema.get("minLength", 1)
-        # TODO(dkr mko): handle format and pattern
-        return "x" * size
-
-    @base.generator_type("integer")
-    @base.simple_generator
-    def generate_valid_integer(self, schema):
-        # TODO(dkr mko): handle multipleOf
-        if "minimum" in schema:
-            minimum = schema["minimum"]
-            if "exclusiveMinimum" not in schema:
-                return minimum
-            else:
-                return minimum + 1
-        if "maximum" in schema:
-            maximum = schema["maximum"]
-            if "exclusiveMaximum" not in schema:
-                return maximum
-            else:
-                return maximum - 1
-        return 0
-
-    @base.generator_type("object")
-    @base.simple_generator
-    def generate_valid_object(self, schema):
-        obj = {}
-        for k, v in six.iteritems(schema["properties"]):
-            obj[k] = self.generate_valid(v)
-        return obj
-
-    def generate(self, schema):
-        schema_type = schema["type"]
-        if isinstance(schema_type, list):
-            if "integer" in schema_type:
-                schema_type = "integer"
-            else:
-                raise Exception("non-integer list types not supported")
-        result = []
-        if schema_type not in self.types_dict:
-            raise TypeError("generator (%s) doesn't support type: %s"
-                            % (self.__class__.__name__, schema_type))
-        for generator in self.types_dict[schema_type]:
-            ret = generator(schema)
-            if ret is not None:
-                if isinstance(ret, list):
-                    result.extend(ret)
-                elif isinstance(ret, tuple):
-                    result.append(ret)
-                else:
-                    raise Exception("generator (%s) returns invalid result: %s"
-                                    % (generator, ret))
-        return result
-
-    def generate_valid(self, schema):
-        return self.generate(schema)[0][1]
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
deleted file mode 100644
index baf796d..0000000
--- a/tempest/common/glance_http.py
+++ /dev/null
@@ -1,342 +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.
-
-# Originally copied from python-glanceclient
-
-import copy
-import hashlib
-import posixpath
-import re
-import socket
-import struct
-
-import OpenSSL
-from oslo_log import log as logging
-import six
-from six import moves
-from six.moves import http_client as httplib
-from six.moves.urllib import parse as urlparse
-
-from tempest import exceptions as exc
-
-LOG = logging.getLogger(__name__)
-USER_AGENT = 'tempest'
-CHUNKSIZE = 1024 * 64  # 64kB
-TOKEN_CHARS_RE = re.compile('^[-A-Za-z0-9+/=]*$')
-
-
-class HTTPClient(object):
-
-    def __init__(self, auth_provider, filters, **kwargs):
-        self.auth_provider = auth_provider
-        self.filters = filters
-        self.endpoint = auth_provider.base_url(filters)
-        endpoint_parts = urlparse.urlparse(self.endpoint)
-        self.endpoint_scheme = endpoint_parts.scheme
-        self.endpoint_hostname = endpoint_parts.hostname
-        self.endpoint_port = endpoint_parts.port
-
-        self.connection_class = self._get_connection_class(
-            self.endpoint_scheme)
-        self.connection_kwargs = self._get_connection_kwargs(
-            self.endpoint_scheme, **kwargs)
-
-    @staticmethod
-    def _get_connection_class(scheme):
-        if scheme == 'https':
-            return VerifiedHTTPSConnection
-        else:
-            return httplib.HTTPConnection
-
-    @staticmethod
-    def _get_connection_kwargs(scheme, **kwargs):
-        _kwargs = {'timeout': float(kwargs.get('timeout', 600))}
-
-        if scheme == 'https':
-            _kwargs['ca_certs'] = kwargs.get('ca_certs', None)
-            _kwargs['cert_file'] = kwargs.get('cert_file', None)
-            _kwargs['key_file'] = kwargs.get('key_file', None)
-            _kwargs['insecure'] = kwargs.get('insecure', False)
-            _kwargs['ssl_compression'] = kwargs.get('ssl_compression', True)
-
-        return _kwargs
-
-    def _get_connection(self):
-        _class = self.connection_class
-        try:
-            return _class(self.endpoint_hostname, self.endpoint_port,
-                          **self.connection_kwargs)
-        except httplib.InvalidURL:
-            raise exc.EndpointNotFound
-
-    def _http_request(self, url, method, **kwargs):
-        """Send an http request with the specified characteristics.
-
-        Wrapper around httplib.HTTP(S)Connection.request to handle tasks such
-        as setting headers and error handling.
-        """
-        # Copy the kwargs so we can reuse the original in case of redirects
-        kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {}))
-        kwargs['headers'].setdefault('User-Agent', USER_AGENT)
-
-        self._log_request(method, url, kwargs['headers'])
-
-        conn = self._get_connection()
-
-        try:
-            url_parts = urlparse.urlparse(url)
-            conn_url = posixpath.normpath(url_parts.path)
-            LOG.debug('Actual Path: {path}'.format(path=conn_url))
-            if kwargs['headers'].get('Transfer-Encoding') == 'chunked':
-                conn.putrequest(method, conn_url)
-                for header, value in kwargs['headers'].items():
-                    conn.putheader(header, value)
-                conn.endheaders()
-                chunk = kwargs['body'].read(CHUNKSIZE)
-                # Chunk it, baby...
-                while chunk:
-                    conn.send('%x\r\n%s\r\n' % (len(chunk), chunk))
-                    chunk = kwargs['body'].read(CHUNKSIZE)
-                conn.send('0\r\n\r\n')
-            else:
-                conn.request(method, conn_url, **kwargs)
-            resp = conn.getresponse()
-        except socket.gaierror as e:
-            message = ("Error finding address for %(url)s: %(e)s" %
-                       {'url': url, 'e': e})
-            raise exc.EndpointNotFound(message)
-        except (socket.error, socket.timeout) as e:
-            message = ("Error communicating with %(endpoint)s %(e)s" %
-                       {'endpoint': self.endpoint, 'e': e})
-            raise exc.TimeoutException(message)
-
-        body_iter = ResponseBodyIterator(resp)
-        # Read body into string if it isn't obviously image data
-        if resp.getheader('content-type', None) != 'application/octet-stream':
-            body_str = ''.join([body_chunk for body_chunk in body_iter])
-            body_iter = six.StringIO(body_str)
-            self._log_response(resp, None)
-        else:
-            self._log_response(resp, body_iter)
-
-        return resp, body_iter
-
-    def _log_request(self, method, url, headers):
-        LOG.info('Request: ' + method + ' ' + url)
-        if headers:
-            headers_out = headers
-            if 'X-Auth-Token' in headers and headers['X-Auth-Token']:
-                token = headers['X-Auth-Token']
-                if len(token) > 64 and TOKEN_CHARS_RE.match(token):
-                    headers_out = headers.copy()
-                    headers_out['X-Auth-Token'] = "<Token omitted>"
-                LOG.info('Request Headers: ' + str(headers_out))
-
-    def _log_response(self, resp, body):
-        status = str(resp.status)
-        LOG.info("Response Status: " + status)
-        if resp.getheaders():
-            LOG.info('Response Headers: ' + str(resp.getheaders()))
-        if body:
-            str_body = str(body)
-            length = len(body)
-            LOG.info('Response Body: ' + str_body[:2048])
-            if length >= 2048:
-                self.LOG.debug("Large body (%d) md5 summary: %s", length,
-                               hashlib.md5(str_body).hexdigest())
-
-    def raw_request(self, method, url, **kwargs):
-        kwargs.setdefault('headers', {})
-        kwargs['headers'].setdefault('Content-Type',
-                                     'application/octet-stream')
-        if 'body' in kwargs:
-            if (hasattr(kwargs['body'], 'read')
-                    and method.lower() in ('post', 'put')):
-                # We use 'Transfer-Encoding: chunked' because
-                # body size may not always be known in advance.
-                kwargs['headers']['Transfer-Encoding'] = 'chunked'
-
-        # Decorate the request with auth
-        req_url, kwargs['headers'], kwargs['body'] = \
-            self.auth_provider.auth_request(
-                method=method, url=url, headers=kwargs['headers'],
-                body=kwargs.get('body', None), filters=self.filters)
-        return self._http_request(req_url, method, **kwargs)
-
-
-class OpenSSLConnectionDelegator(object):
-    """An OpenSSL.SSL.Connection delegator.
-
-    Supplies an additional 'makefile' method which httplib requires
-    and is not present in OpenSSL.SSL.Connection.
-
-    Note: Since it is not possible to inherit from OpenSSL.SSL.Connection
-    a delegator must be used.
-    """
-    def __init__(self, *args, **kwargs):
-        self.connection = OpenSSL.SSL.Connection(*args, **kwargs)
-
-    def __getattr__(self, name):
-        return getattr(self.connection, name)
-
-    def makefile(self, *args, **kwargs):
-        # Ensure the socket is closed when this file is closed
-        kwargs['close'] = True
-        return socket._fileobject(self.connection, *args, **kwargs)
-
-
-class VerifiedHTTPSConnection(httplib.HTTPSConnection):
-    """Extended HTTPSConnection which uses OpenSSL library for enhanced SSL
-
-    Note: Much of this functionality can eventually be replaced
-          with native Python 3.3 code.
-    """
-    def __init__(self, host, port=None, key_file=None, cert_file=None,
-                 ca_certs=None, timeout=None, insecure=False,
-                 ssl_compression=True):
-        httplib.HTTPSConnection.__init__(self, host, port,
-                                         key_file=key_file,
-                                         cert_file=cert_file)
-        self.key_file = key_file
-        self.cert_file = cert_file
-        self.timeout = timeout
-        self.insecure = insecure
-        self.ssl_compression = ssl_compression
-        self.ca_certs = ca_certs
-        self.setcontext()
-
-    @staticmethod
-    def host_matches_cert(host, x509):
-        """Verify that the x509 certificate we have received from 'host'
-
-        Identifies the server we are connecting to, ie that the certificate's
-        Common Name or a Subject Alternative Name matches 'host'.
-        """
-        # First see if we can match the CN
-        if x509.get_subject().commonName == host:
-            return True
-
-        # Also try Subject Alternative Names for a match
-        san_list = None
-        for i in moves.xrange(x509.get_extension_count()):
-            ext = x509.get_extension(i)
-            if ext.get_short_name() == 'subjectAltName':
-                san_list = str(ext)
-                for san in ''.join(san_list.split()).split(','):
-                    if san == "DNS:%s" % host:
-                        return True
-
-        # Server certificate does not match host
-        msg = ('Host "%s" does not match x509 certificate contents: '
-               'CommonName "%s"' % (host, x509.get_subject().commonName))
-        if san_list is not None:
-            msg = msg + ', subjectAltName "%s"' % san_list
-        raise exc.SSLCertificateError(msg)
-
-    def verify_callback(self, connection, x509, errnum,
-                        depth, preverify_ok):
-        if x509.has_expired():
-            msg = "SSL Certificate expired on '%s'" % x509.get_notAfter()
-            raise exc.SSLCertificateError(msg)
-
-        if depth == 0 and preverify_ok is True:
-            # We verify that the host matches against the last
-            # certificate in the chain
-            return self.host_matches_cert(self.host, x509)
-        else:
-            # Pass through OpenSSL's default result
-            return preverify_ok
-
-    def setcontext(self):
-        """Set up the OpenSSL context."""
-        self.context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
-
-        if self.ssl_compression is False:
-            self.context.set_options(0x20000)  # SSL_OP_NO_COMPRESSION
-
-        if self.insecure is not True:
-            self.context.set_verify(OpenSSL.SSL.VERIFY_PEER,
-                                    self.verify_callback)
-        else:
-            self.context.set_verify(OpenSSL.SSL.VERIFY_NONE,
-                                    self.verify_callback)
-
-        if self.cert_file:
-            try:
-                self.context.use_certificate_file(self.cert_file)
-            except Exception as e:
-                msg = 'Unable to load cert from "%s" %s' % (self.cert_file, e)
-                raise exc.SSLConfigurationError(msg)
-            if self.key_file is None:
-                # We support having key and cert in same file
-                try:
-                    self.context.use_privatekey_file(self.cert_file)
-                except Exception as e:
-                    msg = ('No key file specified and unable to load key '
-                           'from "%s" %s' % (self.cert_file, e))
-                    raise exc.SSLConfigurationError(msg)
-
-        if self.key_file:
-            try:
-                self.context.use_privatekey_file(self.key_file)
-            except Exception as e:
-                msg = 'Unable to load key from "%s" %s' % (self.key_file, e)
-                raise exc.SSLConfigurationError(msg)
-
-        if self.ca_certs:
-            try:
-                self.context.load_verify_locations(self.ca_certs)
-            except Exception as e:
-                msg = 'Unable to load CA from "%s" %s' % (self.ca_certs, e)
-                raise exc.SSLConfigurationError(msg)
-        else:
-            self.context.set_default_verify_paths()
-
-    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))
-
-    def close(self):
-        if self.sock:
-            # Remove the reference to the socket but don't close it yet.
-            # Response close will close both socket and associated
-            # file. Closing socket too soon will cause response
-            # reads to fail with socket IO error 'Bad file descriptor'.
-            self.sock = None
-        httplib.HTTPSConnection.close(self)
-
-
-class ResponseBodyIterator(object):
-    """A class that acts as an iterator over an HTTP response."""
-
-    def __init__(self, resp):
-        self.resp = resp
-
-    def __iter__(self):
-        while True:
-            yield self.next()
-
-    def next(self):
-        chunk = self.resp.read(CHUNKSIZE)
-        if chunk:
-            return chunk
-        else:
-            raise StopIteration()
diff --git a/tempest/common/identity.py b/tempest/common/identity.py
index 2179363..469defe 100644
--- a/tempest/common/identity.py
+++ b/tempest/common/identity.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import exceptions as lib_exc
+from tempest.lib import exceptions as lib_exc
 
 
 def get_tenant_by_name(client, tenant_name):
diff --git a/tempest/common/image.py b/tempest/common/image.py
new file mode 100644
index 0000000..3618f7e
--- /dev/null
+++ b/tempest/common/image.py
@@ -0,0 +1,65 @@
+# 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 copy
+
+
+def get_image_meta_from_headers(resp):
+    meta = {'properties': {}}
+    for key in resp.response:
+        value = resp.response[key]
+        if key.startswith('x-image-meta-property-'):
+            _key = key[22:]
+            meta['properties'][_key] = value
+        elif key.startswith('x-image-meta-'):
+            _key = key[13:]
+            meta[_key] = value
+
+    for key in ['is_public', 'protected', 'deleted']:
+        if key in meta:
+            meta[key] = meta[key].strip().lower() in ('t', 'true', 'yes', '1')
+
+    for key in ['size', 'min_ram', 'min_disk']:
+        if key in meta:
+            try:
+                meta[key] = int(meta[key])
+            except ValueError:
+                pass
+    return meta
+
+
+def image_meta_to_headers(**metadata):
+    headers = {}
+    fields_copy = copy.deepcopy(metadata)
+
+    copy_from = fields_copy.pop('copy_from', None)
+    purge = fields_copy.pop('purge_props', None)
+
+    if purge is not None:
+        headers['x-glance-registry-purge-props'] = purge
+
+    if copy_from is not None:
+        headers['x-glance-api-copy-from'] = copy_from
+
+    for key, value in fields_copy.pop('properties', {}).items():
+        headers['x-image-meta-property-%s' % key] = str(value)
+
+    for key, value in fields_copy.pop('api', {}).items():
+        headers['x-glance-api-property-%s' % key] = str(value)
+
+    for key, value in fields_copy.items():
+        headers['x-image-meta-%s' % key] = str(value)
+
+    return headers
diff --git a/tempest/common/negative_rest_client.py b/tempest/common/negative_rest_client.py
deleted file mode 100644
index d97411c..0000000
--- a/tempest/common/negative_rest_client.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# (c) 2014 Deutsche Telekom AG
-# Copyright 2014 Red Hat, Inc.
-# 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.
-
-from tempest.common import service_client
-from tempest import config
-
-CONF = config.CONF
-
-
-class NegativeRestClient(service_client.ServiceClient):
-    """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):
-        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)
-
-    def _get_region_and_endpoint_type(self, service):
-        """Returns the region for a specific service"""
-        service_region = None
-        service_endpoint_type = None
-        for cfgname in dir(CONF._config):
-            # Find all config.FOO.catalog_type and assume FOO is a service.
-            cfg = getattr(CONF, cfgname)
-            catalog_type = getattr(cfg, 'catalog_type', None)
-            if catalog_type == service:
-                service_region = getattr(cfg, 'region', None)
-                service_endpoint_type = getattr(cfg, 'endpoint_type', None)
-        if not service_region:
-            service_region = CONF.identity.region
-        return service_region, service_endpoint_type
-
-    def _error_checker(self, method, url,
-                       headers, body, resp, resp_body):
-        pass
-
-    def send_request(self, method, url_template, resources, body=None):
-        url = url_template % tuple(resources)
-        if method == "GET":
-            resp, body = self.get(url)
-        elif method == "POST":
-            resp, body = self.post(url, body)
-        elif method == "PUT":
-            resp, body = self.put(url, body)
-        elif method == "PATCH":
-            resp, body = self.patch(url, body)
-        elif method == "HEAD":
-            resp, body = self.head(url)
-        elif method == "DELETE":
-            resp, body = self.delete(url)
-        elif method == "COPY":
-            resp, body = self.copy(url)
-        else:
-            assert False
-
-        return resp, body
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index 34af31e..a92d16a 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -18,14 +18,14 @@
 from oslo_concurrency import lockutils
 from oslo_log import log as logging
 import six
-from tempest_lib import auth
-from tempest_lib import exceptions as lib_exc
 import yaml
 
 from tempest import clients
-from tempest.common import cred_provider
 from tempest.common import fixed_network
 from tempest import exceptions
+from tempest.lib import auth
+from tempest.lib.common import cred_provider
+from tempest.lib import exceptions as lib_exc
 
 LOG = logging.getLogger(__name__)
 
@@ -33,9 +33,9 @@
 def read_accounts_yaml(path):
     try:
         with open(path, 'r') as yaml_file:
-            accounts = yaml.load(yaml_file)
+            accounts = yaml.safe_load(yaml_file)
     except IOError:
-        raise exceptions.InvalidConfiguration(
+        raise lib_exc.InvalidConfiguration(
             'The path for the test accounts file: %s '
             'could not be found' % path)
     return accounts
@@ -43,6 +43,11 @@
 
 class PreProvisionedCredentialProvider(cred_provider.CredentialProvider):
 
+    # Exclude from the hash fields specific to v2 or v3 identity API
+    # i.e. only include user*, project*, tenant* and password
+    HASH_CRED_FIELDS = (set(auth.KeystoneV2Credentials.ATTRIBUTES) &
+                        set(auth.KeystoneV3Credentials.ATTRIBUTES))
+
     def __init__(self, identity_version, test_accounts_file,
                  accounts_lock_dir, name=None, credentials_domain=None,
                  admin_role=None, object_storage_operator_role=None,
@@ -81,10 +86,8 @@
         self.test_accounts_file = test_accounts_file
         if test_accounts_file:
             accounts = read_accounts_yaml(self.test_accounts_file)
-            self.use_default_creds = False
         else:
-            accounts = {}
-            self.use_default_creds = True
+            raise lib_exc.InvalidCredentials("No accounts file specified")
         self.hash_dict = self.get_hash_dict(
             accounts, admin_role, object_storage_operator_role,
             object_storage_reseller_admin_role)
@@ -104,6 +107,7 @@
                       object_storage_operator_role=None,
                       object_storage_reseller_admin_role=None):
         hash_dict = {'roles': {}, 'creds': {}, 'networks': {}}
+
         # Loop over the accounts read from the yaml file
         for account in accounts:
             roles = []
@@ -116,7 +120,9 @@
             if 'resources' in account:
                 resources = account.pop('resources')
             temp_hash = hashlib.md5()
-            temp_hash.update(six.text_type(account).encode('utf-8'))
+            account_for_hash = dict((k, v) for (k, v) in account.items()
+                                    if k in cls.HASH_CRED_FIELDS)
+            temp_hash.update(six.text_type(account_for_hash).encode('utf-8'))
             temp_hash_key = temp_hash.hexdigest()
             hash_dict['creds'][temp_hash_key] = account
             for role in roles:
@@ -152,17 +158,14 @@
                 if resource == 'network':
                     hash_dict['networks'][temp_hash_key] = resources[resource]
                 else:
-                    LOG.warning('Unknown resource type %s, ignoring this field'
-                                % resource)
+                    LOG.warning(
+                        'Unknown resource type %s, ignoring this field',
+                        resource
+                    )
         return hash_dict
 
     def is_multi_user(self):
-        # Default credentials is not a valid option with locking Account
-        if self.use_default_creds:
-            raise lib_exc.InvalidCredentials(
-                "Account file %s doesn't exist" % self.test_accounts_file)
-        else:
-            return len(self.hash_dict['creds']) > 1
+        return len(self.hash_dict['creds']) > 1
 
     def is_multi_tenant(self):
         return self.is_multi_user()
@@ -219,9 +222,9 @@
         else:
             hashes = self.hash_dict['creds'].keys()
         # NOTE(mtreinish): admin is a special case because of the increased
-        # privlege set which could potentially cause issues on tests where that
-        # is not expected. So unless the admin role isn't specified do not
-        # allocate admin.
+        # privilege set which could potentially cause issues on tests where
+        # that is not expected. So unless the admin role isn't specified do
+        # not allocate admin.
         admin_hashes = self.hash_dict['roles'].get(self.admin_role,
                                                    None)
         if ((not roles or self.admin_role not in roles) and
@@ -237,14 +240,14 @@
         return temp_creds
 
     def _get_creds(self, roles=None):
-        if self.use_default_creds:
-            raise lib_exc.InvalidCredentials(
-                "Account file %s doesn't exist" % self.test_accounts_file)
         useable_hashes = self._get_match_hash_list(roles)
+        if len(useable_hashes) == 0:
+            msg = 'No users configured for type/roles %s' % roles
+            raise lib_exc.InvalidCredentials(msg)
         free_hash = self._get_free_hash(useable_hashes)
         clean_creds = self._sanitize_creds(
             self.hash_dict['creds'][free_hash])
-        LOG.info('%s allocated creds:\n%s' % (self.name, clean_creds))
+        LOG.info('%s allocated creds:\n%s', self.name, clean_creds)
         return self._wrap_creds_with_network(free_hash)
 
     @lockutils.synchronized('test_accounts_io', external=True)
@@ -252,7 +255,7 @@
         hash_path = os.path.join(self.accounts_dir, hash_string)
         if not os.path.isfile(hash_path):
             LOG.warning('Expected an account lock file %s to remove, but '
-                        'one did not exist' % hash_path)
+                        'one did not exist', hash_path)
         else:
             os.remove(hash_path)
             if not os.listdir(self.accounts_dir):
@@ -262,13 +265,13 @@
         for _hash in self.hash_dict['creds']:
             # Comparing on the attributes that are expected in the YAML
             init_attributes = creds.get_init_attributes()
+            # Only use the attributes initially used to calculate the hash
+            init_attributes = [x for x in init_attributes if
+                               x in self.HASH_CRED_FIELDS]
             hash_attributes = self.hash_dict['creds'][_hash].copy()
-            if ('user_domain_name' in init_attributes and 'user_domain_name'
-                    not in hash_attributes):
-                # Allow for the case of domain_name populated from config
-                domain_name = self.credentials_domain
-                hash_attributes['user_domain_name'] = domain_name
-            if all([getattr(creds, k) == hash_attributes[k] for
+            # NOTE(andreaf) Not all fields may be available on all credentials
+            # so defaulting to None for that case.
+            if all([getattr(creds, k, None) == hash_attributes.get(k, None) for
                    k in init_attributes]):
                 return _hash
         raise AttributeError('Invalid credentials %s' % creds)
@@ -277,7 +280,7 @@
         _hash = self.get_hash(creds)
         clean_creds = self._sanitize_creds(self.hash_dict['creds'][_hash])
         self.remove_hash(_hash)
-        LOG.info("%s returned allocated creds:\n%s" % (self.name, clean_creds))
+        LOG.info("%s returned allocated creds:\n%s", self.name, clean_creds)
 
     def get_primary_creds(self):
         if self._creds.get('primary'):
@@ -303,7 +306,9 @@
         if exist_creds and not force_new:
             return exist_creds
         elif exist_creds and force_new:
-            new_index = six.text_type(roles).encode('utf-8') + '-' + \
+            # NOTE(andreaf) In py3.x encode returns bytes, and b'' is bytes
+            # In py2.7 encode returns strings, and b'' is still string
+            new_index = six.text_type(roles).encode('utf-8') + b'-' + \
                 six.text_type(len(self._creds)).encode('utf-8')
             self._creds[new_index] = exist_creds
         net_creds = self._get_creds(roles=roles)
@@ -318,12 +323,9 @@
         return self.get_creds_by_roles([self.admin_role])
 
     def is_role_available(self, role):
-        if self.use_default_creds:
-            return False
-        else:
-            if self.hash_dict['roles'].get(role):
-                return True
-            return False
+        if self.hash_dict['roles'].get(role):
+            return True
+        return False
 
     def admin_available(self):
         return self.is_role_available(self.admin_role)
@@ -331,6 +333,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.
@@ -350,10 +353,20 @@
         return net_creds
 
     def _extend_credentials(self, creds_dict):
-        # In case of v3, adds a user_domain_name field to the creds
-        # dict if not defined
+        # Add or remove credential domain fields to fit the identity version
+        domain_fields = set(x for x in auth.KeystoneV3Credentials.ATTRIBUTES
+                            if 'domain' in x)
+        msg = 'Assuming they are valid in the default domain.'
         if self.identity_version == 'v3':
-            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
+            if not domain_fields.intersection(set(creds_dict.keys())):
+                msg = 'Using credentials %s for v3 API calls. ' + msg
+                LOG.warning(msg, self._sanitize_creds(creds_dict))
+                creds_dict['domain_name'] = self.credentials_domain
+        if self.identity_version == 'v2':
+            if domain_fields.intersection(set(creds_dict.keys())):
+                msg = 'Using credentials %s for v2 API calls. ' + msg
+                LOG.warning(msg, self._sanitize_creds(creds_dict))
+            # Remove all valid domain attributes
+            for attr in domain_fields.intersection(set(creds_dict.keys())):
+                creds_dict.pop(attr)
         return creds_dict
diff --git a/tempest/common/service_client.py b/tempest/common/service_client.py
deleted file mode 100644
index b3a5a09..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/__init__.py b/tempest/common/utils/__init__.py
index aad6373..b6565d1 100644
--- a/tempest/common/utils/__init__.py
+++ b/tempest/common/utils/__init__.py
@@ -15,8 +15,7 @@
 from functools import partial
 
 from tempest import config
-
-from tempest_lib.common.utils import data_utils as lib_data_utils
+from tempest.lib.common.utils import data_utils as lib_data_utils
 
 CONF = config.CONF
 
diff --git a/tempest/common/utils/file_utils.py b/tempest/common/utils/file_utils.py
deleted file mode 100644
index 43083f4..0000000
--- a/tempest/common/utils/file_utils.py
+++ /dev/null
@@ -1,23 +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.
-
-
-def have_effective_read_access(path):
-    try:
-        fh = open(path, "rb")
-    except IOError:
-        return False
-    fh.close()
-    return True
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 8f5faef..009812e 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -12,37 +12,85 @@
 
 import netaddr
 import re
+import six
+import sys
 import time
 
 from oslo_log import log as logging
-from tempest_lib.common import ssh
-import tempest_lib.exceptions
 
 from tempest import config
-from tempest import exceptions
+from tempest.lib.common import ssh
+from tempest.lib.common.utils import test_utils
+import tempest.lib.exceptions
 
 CONF = config.CONF
 
 LOG = logging.getLogger(__name__)
 
 
+def debug_ssh(function):
+    """Decorator to generate extra debug info in case off SSH failure"""
+    def wrapper(self, *args, **kwargs):
+        try:
+            return function(self, *args, **kwargs)
+        except tempest.lib.exceptions.SSHTimeout:
+            try:
+                original_exception = sys.exc_info()
+                caller = test_utils.find_test_caller() or "not found"
+                if self.server:
+                    msg = 'Caller: %s. Timeout trying to ssh to server %s'
+                    LOG.debug(msg, caller, self.server)
+                    if self.log_console and self.servers_client:
+                        try:
+                            msg = 'Console log for server %s: %s'
+                            console_log = (
+                                self.servers_client.get_console_output(
+                                    self.server['id'])['output'])
+                            LOG.debug(msg, self.server['id'], console_log)
+                        except Exception:
+                            msg = 'Could not get console_log for server %s'
+                            LOG.debug(msg, self.server['id'])
+                # re-raise the original ssh timeout exception
+                six.reraise(*original_exception)
+            finally:
+                # Delete the traceback to avoid circular references
+                _, _, trace = original_exception
+                del trace
+    return wrapper
+
+
 class RemoteClient(object):
 
-    def __init__(self, ip_address, username, password=None, pkey=None):
+    def __init__(self, ip_address, username, password=None, pkey=None,
+                 server=None, servers_client=None):
+        """Executes commands in a VM over ssh
+
+        :param ip_address: IP address to ssh to
+        :param username: ssh username
+        :param password: ssh password (optional)
+        :param pkey: ssh public key (optional)
+        :param server: server dict, used for debugging purposes
+        :param servers_client: servers client, used for debugging purposes
+        """
+        self.server = server
+        self.servers_client = servers_client
         ssh_timeout = CONF.validation.ssh_timeout
         connect_timeout = CONF.validation.connect_timeout
+        self.log_console = CONF.compute_feature_enabled.console_output
 
         self.ssh_client = ssh.Client(ip_address, username, password,
                                      ssh_timeout, pkey=pkey,
                                      channel_timeout=connect_timeout)
 
+    @debug_ssh
     def exec_command(self, cmd):
         # Shell options below add more clearness on failures,
         # path is extended for some non-cirros guest oses (centos7)
         cmd = CONF.validation.ssh_shell_prologue + " " + cmd
-        LOG.debug("Remote command: %s" % cmd)
+        LOG.debug("Remote command: %s", cmd)
         return self.ssh_client.exec_command(cmd)
 
+    @debug_ssh
     def validate_authentication(self):
         """Validate ssh connection and authentication
 
@@ -50,10 +98,10 @@
         """
         self.ssh_client.test_connection_auth()
 
-    def hostname_equals_servername(self, expected_hostname):
+    def get_hostname(self):
         # Get host name using command "hostname"
         actual_hostname = self.exec_command("hostname").rstrip()
-        return expected_hostname == actual_hostname
+        return actual_hostname
 
     def get_ram_size_in_mb(self):
         output = self.exec_command('free -m | grep Mem')
@@ -64,11 +112,22 @@
         output = self.exec_command('grep -c ^processor /proc/cpuinfo')
         return int(output)
 
-    def get_partitions(self):
-        # Return the contents of /proc/partitions
-        command = 'cat /proc/partitions'
+    def get_disks(self):
+        # Select root disk devices as shown by lsblk
+        command = 'lsblk -lb --nodeps'
         output = self.exec_command(command)
-        return output
+        selected = []
+        pos = None
+        for l in output.splitlines():
+            if pos is None and l.find("TYPE") > 0:
+                pos = l.find("TYPE")
+                # Show header line too
+                selected.append(l)
+            # lsblk lists disk type in a column right-aligned with TYPE
+            elif pos > 0 and l[pos:pos + 4] == "disk":
+                selected.append(l)
+
+        return "\n".join(selected)
 
     def get_boot_time(self):
         cmd = 'cut -f1 -d. /proc/uptime'
@@ -102,7 +161,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 +177,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 +206,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()
@@ -164,8 +228,8 @@
         supported_clients = ['udhcpc', 'dhclient']
         dhcp_client = CONF.scenario.dhcp_client
         if dhcp_client not in supported_clients:
-            raise exceptions.InvalidConfiguration('%s DHCP client unsupported'
-                                                  % dhcp_client)
+            raise tempest.lib.exceptions.InvalidConfiguration(
+                '%s DHCP client unsupported' % dhcp_client)
         if dhcp_client == 'udhcpc' and not fixed_ip:
             raise ValueError("need to set 'fixed_ip' for udhcpc client")
         return getattr(self, '_renew_lease_' + dhcp_client)(fixed_ip=fixed_ip)
@@ -181,8 +245,8 @@
         cmd_mkfs = 'sudo /usr/sbin/mke2fs -t %s /dev/%s' % (fs, dev_name)
         try:
             self.exec_command(cmd_mkfs)
-        except tempest_lib.exceptions.SSHExecCommandFailed:
+        except tempest.lib.exceptions.SSHExecCommandFailed:
             LOG.error("Couldn't mke2fs")
             cmd_why = 'sudo ls -lR /dev'
-            LOG.info("Contents of /dev: %s" % self.exec_command(cmd_why))
+            LOG.info("Contents of /dev: %s", self.exec_command(cmd_why))
             raise
diff --git a/tempest/common/utils/net_info.py b/tempest/common/utils/net_info.py
new file mode 100644
index 0000000..9b0a083
--- /dev/null
+++ b/tempest/common/utils/net_info.py
@@ -0,0 +1,25 @@
+# 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 re
+
+RE_OWNER = re.compile('^network:.*router_.*interface.*')
+
+
+def _is_owner_router_interface(owner):
+    return bool(RE_OWNER.match(owner))
+
+
+def is_router_interface_port(port):
+    """Based on the port attributes determines is it a router interface."""
+    return _is_owner_router_interface(port['device_owner'])
diff --git a/tempest/common/utils/net_utils.py b/tempest/common/utils/net_utils.py
new file mode 100644
index 0000000..867b3dd
--- /dev/null
+++ b/tempest/common/utils/net_utils.py
@@ -0,0 +1,73 @@
+# Copyright 2016 Hewlett Packard Enterprise Development Company
+#
+# 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 netaddr
+
+from tempest.lib import exceptions as lib_exc
+
+
+def get_unused_ip_addresses(ports_client, subnets_client,
+                            network_id, subnet_id, count):
+
+    """Return a list with the specified number of unused IP addresses
+
+    This method uses the given ports_client to find the specified number of
+    unused IP addresses on the given subnet using the supplied subnets_client
+    """
+
+    ports = ports_client.list_ports(network_id=network_id)['ports']
+    subnet = subnets_client.show_subnet(subnet_id)
+    ip_net = netaddr.IPNetwork(subnet['subnet']['cidr'])
+    subnet_set = netaddr.IPSet(ip_net.iter_hosts())
+    alloc_set = netaddr.IPSet()
+
+    # prune out any addresses already allocated to existing ports
+    for port in ports:
+        for fixed_ip in port.get('fixed_ips'):
+            alloc_set.add(fixed_ip['ip_address'])
+
+    # exclude gateway_ip of subnet
+    gateway_ip = subnet['subnet']['gateway_ip']
+    if gateway_ip:
+        alloc_set.add(gateway_ip)
+
+    av_set = subnet_set - alloc_set
+    addrs = []
+    for cidr in reversed(av_set.iter_cidrs()):
+        for ip in reversed(cidr):
+            addrs.append(str(ip))
+            if len(addrs) == count:
+                return addrs
+    msg = "Insufficient IP addresses available"
+    raise lib_exc.BadRequest(message=msg)
+
+
+def get_ping_payload_size(mtu, ip_version):
+    """Return the maximum size of ping payload that will fit into MTU."""
+    if not mtu:
+        return None
+    if ip_version == 4:
+        ip_header = 20
+        icmp_header = 8
+    else:
+        ip_header = 40
+        icmp_header = 4
+    res = mtu - ip_header - icmp_header
+    if res < 0:
+        raise lib_exc.BadRequest(
+            message='MTU = %(mtu)d is too low for IPv%(ip_version)d' % {
+                'mtu': mtu,
+                'ip_version': ip_version,
+            })
+    return res
diff --git a/tempest/common/validation_resources.py b/tempest/common/validation_resources.py
index 9457a60..88697c4 100644
--- a/tempest/common/validation_resources.py
+++ b/tempest/common/validation_resources.py
@@ -14,14 +14,34 @@
 from oslo_log import log as logging
 
 from tempest import config
-from tempest_lib import exceptions as lib_exc
 
 from tempest.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
+def _create_neutron_sec_group_rules(os, sec_group):
+    sec_group_rules_client = os.security_group_rules_client
+    ethertype = 'IPv4'
+    if CONF.validation.ip_version_for_ssh == 6:
+        ethertype = 'IPv6'
+
+    sec_group_rules_client.create_security_group_rule(
+        security_group_id=sec_group['id'],
+        protocol='tcp',
+        ethertype=ethertype,
+        port_range_min=22,
+        port_range_max=22,
+        direction='ingress')
+    sec_group_rules_client.create_security_group_rule(
+        security_group_id=sec_group['id'],
+        protocol='icmp',
+        ethertype=ethertype,
+        direction='ingress')
+
+
 def create_ssh_security_group(os, add_rule=False):
     security_groups_client = os.compute_security_groups_client
     security_group_rules_client = os.compute_security_group_rules_client
@@ -30,15 +50,17 @@
     security_group = security_groups_client.create_security_group(
         name=sg_name, description=sg_description)['security_group']
     if add_rule:
-        security_group_rules_client.create_security_group_rule(
-            parent_group_id=security_group['id'], ip_protocol='tcp',
-            from_port=22, to_port=22)
-        security_group_rules_client.create_security_group_rule(
-            parent_group_id=security_group['id'], ip_protocol='icmp',
-            from_port=-1, to_port=-1)
+        if CONF.service_available.neutron:
+            _create_neutron_sec_group_rules(os, security_group)
+        else:
+            security_group_rules_client.create_security_group_rule(
+                parent_group_id=security_group['id'], ip_protocol='tcp',
+                from_port=22, to_port=22)
+            security_group_rules_client.create_security_group_rule(
+                parent_group_id=security_group['id'], ip_protocol='icmp',
+                from_port=-1, to_port=-1)
     LOG.debug("SSH Validation resource security group with tcp and icmp "
-              "rules %s created"
-              % sg_name)
+              "rules %s created", sg_name)
     return security_group
 
 
@@ -50,7 +72,7 @@
             keypair_name = data_utils.rand_name('keypair')
             validation_data.update(os.keypairs_client.create_keypair(
                 name=keypair_name))
-            LOG.debug("Validation resource key %s created" % keypair_name)
+            LOG.debug("Validation resource key %s created", keypair_name)
         add_rule = False
         if validation_resources['security_group']:
             if validation_resources['security_group_rules']:
@@ -59,7 +81,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
 
 
@@ -73,11 +97,13 @@
             try:
                 keypair_client.delete_keypair(keypair_name)
             except lib_exc.NotFound:
-                LOG.warning("Keypair %s is not found when attempting to delete"
-                            % keypair_name)
+                LOG.warning(
+                    "Keypair %s is not found when attempting to delete",
+                    keypair_name
+                )
             except Exception as exc:
-                LOG.exception('Exception raised while deleting key %s'
-                              % keypair_name)
+                LOG.exception('Exception raised while deleting key %s',
+                              keypair_name)
                 if not has_exception:
                     has_exception = exc
         if 'security_group' in validation_data:
@@ -88,15 +114,15 @@
                 security_group_client.wait_for_resource_deletion(sec_id)
             except lib_exc.NotFound:
                 LOG.warning("Security group %s is not found when attempting "
-                            "to delete" % sec_id)
+                            "to delete", sec_id)
             except lib_exc.Conflict as exc:
                 LOG.exception('Conflict while deleting security '
-                              'group %s VM might not be deleted ' % sec_id)
+                              'group %s VM might not be deleted', sec_id)
                 if not has_exception:
                     has_exception = exc
             except Exception as exc:
                 LOG.exception('Exception raised while deleting security '
-                              'group %s ' % sec_id)
+                              'group %s', sec_id)
                 if not has_exception:
                     has_exception = exc
         if 'floating_ip' in validation_data:
@@ -106,10 +132,9 @@
                 floating_client.delete_floating_ip(fip_id)
             except lib_exc.NotFound:
                 LOG.warning('Floating ip %s not found while attempting to '
-                            'delete' % fip_id)
+                            'delete', fip_id)
             except Exception as exc:
-                LOG.exception('Exception raised while deleting ip %s '
-                              % fip_id)
+                LOG.exception('Exception raised while deleting ip %s', fip_id)
                 if not has_exception:
                     has_exception = exc
     if has_exception:
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 867d3f6..8303caf 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -14,11 +14,13 @@
 import time
 
 from oslo_log import log as logging
-from tempest_lib.common.utils import misc as misc_utils
-from tempest_lib import exceptions as lib_exc
 
+from tempest.common import image as common_image
 from tempest import config
 from tempest import exceptions
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.image.v1 import images_client as images_v1_client
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
@@ -51,9 +53,7 @@
                     return
                 # NOTE(afazekas): The instance is in "ready for action state"
                 # when no task in progress
-                # NOTE(afazekas): Converted to string because of the XML
-                # responses
-                if str(task_state) == "None":
+                if task_state is None:
                     # without state api extension 3 sec usually enough
                     time.sleep(CONF.compute.ready_wait)
                     return
@@ -89,10 +89,10 @@
                         'timeout': timeout})
             message += ' Current status: %s.' % server_status
             message += ' Current task state: %s.' % task_state
-            caller = misc_utils.find_test_caller()
+            caller = test_utils.find_test_caller()
             if caller:
                 message = '(%s) %s' % (caller, message)
-            raise exceptions.TimeoutException(message)
+            raise lib_exc.TimeoutException(message)
         old_status = server_status
         old_task_state = task_state
 
@@ -111,7 +111,7 @@
             raise exceptions.BuildErrorException(server_id=server_id)
 
         if int(time.time()) - start_time >= client.build_timeout:
-            raise exceptions.TimeoutException
+            raise lib_exc.TimeoutException
 
         time.sleep(client.build_interval)
 
@@ -122,42 +122,48 @@
     The client should have a show_image(image_id) method to get the image.
     The client should also have build_interval and build_timeout attributes.
     """
-    image = client.show_image(image_id)
-    # Compute image client return response wrapped in 'image' element
-    # which is not case with glance image client.
-    if 'image' in image:
-        image = image['image']
-    start = int(time.time())
+    if isinstance(client, images_v1_client.ImagesClient):
+        # The 'check_image' method is used here because the show_image method
+        # returns image details plus the image itself which is very expensive.
+        # The 'check_image' method returns just image details.
+        def _show_image_v1(image_id):
+            resp = client.check_image(image_id)
+            return common_image.get_image_meta_from_headers(resp)
 
-    while image['status'] != status:
-        time.sleep(client.build_interval)
-        image = client.show_image(image_id)
-        # Compute image client return response wrapped in 'image' element
-        # which is not case with glance image client.
+        show_image = _show_image_v1
+    else:
+        show_image = client.show_image
+
+    current_status = 'An unknown status'
+    start = int(time.time())
+    while int(time.time()) - start < client.build_timeout:
+        image = show_image(image_id)
+        # Compute image client returns response wrapped in 'image' element
+        # which is not the case with Glance image client.
         if 'image' in image:
             image = image['image']
-        status_curr = image['status']
-        if status_curr == 'ERROR':
+
+        current_status = image['status']
+        if current_status == status:
+            return
+        if current_status.lower() == 'killed':
+            raise exceptions.ImageKilledException(image_id=image_id,
+                                                  status=status)
+        if current_status.lower() == 'error':
             raise exceptions.AddImageException(image_id=image_id)
 
-        # check the status again to avoid a false negative where we hit
-        # the timeout at the same time that the image reached the expected
-        # status
-        if status_curr == status:
-            return
+        time.sleep(client.build_interval)
 
-        if int(time.time()) - start >= client.build_timeout:
-            message = ('Image %(image_id)s failed to reach %(status)s state'
-                       '(current state %(status_curr)s) '
-                       'within the required time (%(timeout)s s).' %
-                       {'image_id': image_id,
-                        'status': status,
-                        'status_curr': status_curr,
-                        'timeout': client.build_timeout})
-            caller = misc_utils.find_test_caller()
-            if caller:
-                message = '(%s) %s' % (caller, message)
-            raise exceptions.TimeoutException(message)
+    message = ('Image %(image_id)s failed to reach %(status)s state '
+               '(current state %(current_status)s) within the required '
+               'time (%(timeout)s s).' % {'image_id': image_id,
+                                          'status': status,
+                                          'current_status': current_status,
+                                          'timeout': client.build_timeout})
+    caller = test_utils.find_test_caller()
+    if caller:
+        message = '(%s) %s' % (caller, message)
+    raise lib_exc.TimeoutException(message)
 
 
 def wait_for_volume_status(client, volume_id, status):
@@ -170,7 +176,7 @@
         time.sleep(client.build_interval)
         body = client.show_volume(volume_id)['volume']
         volume_status = body['status']
-        if volume_status == 'error':
+        if volume_status == 'error' and status != 'error':
             raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
         if volume_status == 'error_restoring':
             raise exceptions.VolumeRestoreErrorException(volume_id=volume_id)
@@ -180,6 +186,25 @@
                        'within the required time (%s s).' %
                        (volume_id, status, volume_status,
                         client.build_timeout))
+            raise lib_exc.TimeoutException(message)
+
+
+def wait_for_volume_retype(client, volume_id, new_volume_type):
+    """Waits for a Volume to have a new volume type."""
+    body = client.show_volume(volume_id)['volume']
+    current_volume_type = body['volume_type']
+    start = int(time.time())
+
+    while current_volume_type != new_volume_type:
+        time.sleep(client.build_interval)
+        body = client.show_volume(volume_id)['volume']
+        current_volume_type = body['volume_type']
+
+        if int(time.time()) - start >= client.build_timeout:
+            message = ('Volume %s failed to reach %s volume type (current %s) '
+                       'within the required time (%s s).' %
+                       (volume_id, new_volume_type, current_volume_type,
+                        client.build_timeout))
             raise exceptions.TimeoutException(message)
 
 
@@ -201,33 +226,82 @@
                        'within the required time (%s s).' %
                        (snapshot_id, status, snapshot_status,
                         client.build_timeout))
-            raise exceptions.TimeoutException(message)
+            raise lib_exc.TimeoutException(message)
 
 
-def wait_for_bm_node_status(client, node_id, attr, status):
-    """Waits for a baremetal node attribute to reach given status.
-
-    The client should have a show_node(node_uuid) method to get the node.
-    """
-    _, node = client.show_node(node_id)
+def wait_for_backup_status(client, backup_id, status):
+    """Waits for a Backup to reach a given status."""
+    body = client.show_backup(backup_id)['backup']
+    backup_status = body['status']
     start = int(time.time())
 
-    while node[attr] != status:
+    while backup_status != status:
         time.sleep(client.build_interval)
-        _, node = client.show_node(node_id)
-        status_curr = node[attr]
-        if status_curr == status:
-            return
+        body = client.show_backup(backup_id)['backup']
+        backup_status = body['status']
+        if backup_status == 'error' and backup_status != status:
+            raise lib_exc.VolumeBackupException(backup_id=backup_id)
 
         if int(time.time()) - start >= client.build_timeout:
-            message = ('Node %(node_id)s failed to reach %(attr)s=%(status)s '
-                       'within the required time (%(timeout)s s).' %
-                       {'node_id': node_id,
-                        'attr': attr,
-                        'status': status,
-                        'timeout': client.build_timeout})
-            message += ' Current state of %s: %s.' % (attr, status_curr)
-            caller = misc_utils.find_test_caller()
-            if caller:
-                message = '(%s) %s' % (caller, message)
-            raise exceptions.TimeoutException(message)
+            message = ('Volume backup %s failed to reach %s status '
+                       '(current %s) within the required time (%s s).' %
+                       (backup_id, status, backup_status,
+                        client.build_timeout))
+            raise lib_exc.TimeoutException(message)
+
+
+def wait_for_qos_operations(client, qos_id, operation, args=None):
+    """Waits for a qos operations to be completed.
+
+    NOTE : operation value is required for  wait_for_qos_operations()
+    operation = 'qos-key' / 'disassociate' / 'disassociate-all'
+    args = keys[] when operation = 'qos-key'
+    args = volume-type-id disassociated when operation = 'disassociate'
+    args = None when operation = 'disassociate-all'
+    """
+    start_time = int(time.time())
+    while True:
+        if operation == 'qos-key-unset':
+            body = client.show_qos(qos_id)['qos_specs']
+            if not any(key in body['specs'] for key in args):
+                return
+        elif operation == 'disassociate':
+            body = client.show_association_qos(qos_id)['qos_associations']
+            if not any(args in body[i]['id'] for i in range(0, len(body))):
+                return
+        elif operation == 'disassociate-all':
+            body = client.show_association_qos(qos_id)['qos_associations']
+            if not body:
+                return
+        else:
+            msg = (" operation value is either not defined or incorrect.")
+            raise lib_exc.UnprocessableEntity(msg)
+
+        if int(time.time()) - start_time >= client.build_timeout:
+            raise lib_exc.TimeoutException
+        time.sleep(client.build_interval)
+
+
+def wait_for_interface_status(client, server, port_id, status):
+    """Waits for an interface to reach a given status."""
+    body = (client.show_interface(server, port_id)
+            ['interfaceAttachment'])
+    interface_status = body['port_state']
+    start = int(time.time())
+
+    while(interface_status != status):
+        time.sleep(client.build_interval)
+        body = (client.show_interface(server, port_id)
+                ['interfaceAttachment'])
+        interface_status = body['port_state']
+
+        timed_out = int(time.time()) - start >= client.build_timeout
+
+        if interface_status != status and timed_out:
+            message = ('Interface %s failed to reach %s status '
+                       '(current %s) within the required time (%s s).' %
+                       (port_id, status, interface_status,
+                        client.build_timeout))
+            raise lib_exc.TimeoutException(message)
+
+    return body
diff --git a/tempest/config.py b/tempest/config.py
index 515f736..fe8c175 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -15,14 +15,17 @@
 
 from __future__ import print_function
 
-import logging as std_logging
+import functools
 import os
 import tempfile
 
 from oslo_concurrency import lockutils
 from oslo_config import cfg
 from oslo_log import log as logging
+import testtools
 
+from tempest.lib import exceptions
+from tempest.lib.services import clients
 from tempest.test_discover import plugins
 
 
@@ -56,7 +59,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 +84,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 "
+               help="Password to use for an administrative user. This is "
+                    "needed for authenticating requests made by project "
                     "isolation to create users and projects",
                secret=True,
                deprecated_group='identity'),
@@ -155,42 +161,39 @@
                         'publicURL', 'adminURL', 'internalURL'],
                help="The endpoint type to use for OpenStack Identity "
                     "(Keystone) API v3"),
-    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.",
-               deprecated_for_removal=True),
     cfg.StrOpt('admin_role',
                default='admin',
                help="Role required to administrate keystone."),
-    cfg.StrOpt('password',
-               help="API key to use when authenticating.",
-               secret=True,
-               deprecated_for_removal=True),
-    cfg.StrOpt('domain_name',
-               help="Domain name for authentication (Keystone V3)."
-                    "The same domain applies to user and project",
-               deprecated_for_removal=True),
-    cfg.StrOpt('alt_username',
-               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 "
-                    "requests.",
-               deprecated_for_removal=True),
-    cfg.StrOpt('alt_password',
-               help="API key to use when authenticating as alternate user.",
-               secret=True,
-               deprecated_for_removal=True),
-    cfg.StrOpt('alt_domain_name',
-               help="Alternate domain name for authentication (Keystone V3)."
-                    "The same domain applies to user and project",
-               deprecated_for_removal=True),
     cfg.StrOpt('default_domain_id',
                default='default',
                help="ID of the default domain"),
+    cfg.BoolOpt('admin_domain_scope',
+                default=False,
+                help="Whether keystone identity v3 policy required "
+                     "a domain scoped token to use admin APIs"),
+    # Security Compliance (PCI-DSS)
+    cfg.IntOpt('user_lockout_failure_attempts',
+               default=2,
+               help="The number of unsuccessful login attempts the user is "
+                    "allowed before having the account locked."),
+    cfg.IntOpt('user_lockout_duration',
+               default=5,
+               help="The number of seconds a user account will remain "
+                    "locked."),
+    cfg.IntOpt('user_unique_last_password_count',
+               default=2,
+               help="The number of passwords for a user that must be unique "
+                    "before an old password can be reused."),
+]
+
+service_clients_group = cfg.OptGroup(name='service-clients',
+                                     title="Service Clients Options")
+
+ServiceClientsGroup = [
+    cfg.IntOpt('http_timeout',
+               default=60,
+               help='Timeout in seconds to wait for the http request to '
+                    'return'),
 ]
 
 identity_feature_group = cfg.OptGroup(name='identity-feature-enabled',
@@ -212,7 +215,19 @@
                 help="A list of enabled identity extensions with a special "
                      "entry all which indicates every extension is enabled. "
                      "Empty list indicates all extensions are disabled. "
-                     "To get the list of extensions run: 'keystone discover'")
+                     "To get the list of extensions run: 'keystone discover'"),
+    # TODO(rodrigods): Remove the reseller flag when Kilo and Liberty is end
+    # of life.
+    cfg.BoolOpt('reseller',
+                default=True,
+                help='Does the environment support reseller?',
+                deprecated_for_removal=True,
+                deprecated_reason="All supported version of OpenStack now "
+                                  "supports the 'reseller' feature"),
+    cfg.BoolOpt('security_compliance',
+                default=False,
+                help='Does the environment have the security compliance '
+                     'settings enabled?')
 ]
 
 compute_group = cfg.OptGroup(name='compute',
@@ -246,10 +261,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 "
+                    "a 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 +296,12 @@
                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('hypervisor_type',
+               default=None,
+               help="Hypervisor type of the test target on heterogeneous "
+                    "compute environment. The value can be 'QEMU', 'xen' or "
+                    "something."),
     cfg.StrOpt('min_microversion',
                default=None,
                help="Lower version of the test target microversion range. "
@@ -296,7 +310,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 +320,23 @@
                     "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 = [
+    # NOTE(mriedem): This is a feature toggle for bug 1175464 which is fixed in
+    # mitaka and newton. This option can be removed after liberty-eol.
+    cfg.BoolOpt('allow_port_security_disabled',
+                default=True,
+                help='Does the test environment support creating ports in a '
+                     'network where port security is disabled?',
+                deprecated_for_removal=True,
+                deprecated_reason='This config switch was added for Liberty '
+                                  'which is not supported anymore.'),
     cfg.BoolOpt('disk_config',
                 default=True,
                 help="If false, skip disk config tests"),
@@ -314,7 +345,13 @@
                 help='A list of enabled compute extensions with a special '
                      'entry all which indicates every extension is enabled. '
                      'Each extension should be specified with alias name. '
-                     'Empty list indicates all extensions are disabled'),
+                     'Empty list indicates all extensions are disabled',
+                     deprecated_for_removal=True,
+                     deprecated_reason='The Nova extensions API and mechanism '
+                                       'is deprecated. This option will be '
+                                       'removed when all releases supported '
+                                       'by tempest no longer contain the Nova '
+                                       'extensions API and mechanism.'),
     cfg.BoolOpt('change_password',
                 default=False,
                 help="Does the test environment support changing the admin "
@@ -335,10 +372,12 @@
     cfg.BoolOpt('suspend',
                 default=True,
                 help="Does the test environment support suspend/resume?"),
+    cfg.BoolOpt('cold_migration',
+                default=True,
+                help="Does the test environment support cold migration?"),
     cfg.BoolOpt('live_migration',
                 default=True,
-                help="Does the test environment support live migration "
-                     "available?"),
+                help="Does the test environment support live migration?"),
     cfg.BoolOpt('metadata_service',
                 default=True,
                 help="Does the test environment support metadata service? "
@@ -372,7 +411,8 @@
                 default=True,
                 help='Enables returning of the instance password by the '
                      'relevant server API calls such as create, rebuild '
-                     'or rescue.'),
+                     'or rescue. This configuration value should be same as '
+                     'nova.conf: DEFAULT.enable_instance_password'),
     cfg.BoolOpt('interface_attach',
                 default=True,
                 help='Does the test environment support dynamic network '
@@ -382,10 +422,14 @@
                 help='Does the test environment support creating snapshot '
                      'images of running instances?'),
     cfg.BoolOpt('nova_cert',
-                default=True,
-                help='Does the test environment have the nova cert running?'),
+                default=False,
+                help='Does the test environment have the nova cert running?',
+                deprecated_for_removal=True,
+                deprecated_reason="On Nova side, the nova-cert service is "
+                                  "deprecated and the service will be removed "
+                                  "as early as Ocata."),
     cfg.BoolOpt('personality',
-                default=True,
+                default=False,
                 help='Does the test environment support server personality'),
     cfg.BoolOpt('attach_encrypted_volume',
                 default=True,
@@ -393,17 +437,27 @@
                      'encrypted volume to a running server instance? This may '
                      'depend on the combination of compute_driver in nova and '
                      'the volume_driver(s) in cinder.'),
-    # TODO(mriedem): Remove allow_duplicate_networks once kilo-eol happens
-    # since the option was removed from nova in Liberty and is the default
-    # behavior starting in Liberty.
-    cfg.BoolOpt('allow_duplicate_networks',
-                default=False,
-                help='Does the test environment support creating instances '
-                     'with multiple ports on the same network? This is only '
-                     'valid when using Neutron.'),
     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 that are included "
+                     "with nova are enabled. Empty list indicates all filters "
+                     "are disabled. The full list of available filters is in "
+                     "nova.conf: DEFAULT.scheduler_available_filters. If the "
+                     "default value is overridden in nova.conf by the test "
+                     "environment (which means that a different set of "
+                     "filters is enabled than what is included in Nova by "
+                     "default) then, this option must be configured to "
+                     "contain the same filters that Nova uses in the test "
+                     "environment."),
+    cfg.BoolOpt('swap_volume',
+                default=False,
+                help='Does the test environment support in-place swapping of '
+                     'volumes attached to a server instance?'),
 ]
 
 
@@ -482,23 +536,28 @@
                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."),
+                     "'fixed' connect_method is selected."),
     cfg.StrOpt('public_network_id',
                default="",
                help="Id of the public network that provides external "
@@ -534,6 +593,19 @@
                 default=["1.0.0.0/16", "2.0.0.0/16"],
                 help="List of ip pools"
                      " for subnetpools creation"),
+    cfg.BoolOpt('shared_physical_network',
+                default=False,
+                help="The environment does not support network separation "
+                     "between tenants."),
+    # TODO(ylobankov): Delete this option once the Liberty release is EOL.
+    cfg.BoolOpt('dvr_extra_resources',
+                default=True,
+                help="Whether or not to create internal network, subnet, "
+                     "port and add network interface to distributed router "
+                     "in L3 agent scheduler test. Extra resources need to be "
+                     "provisioned in order to bind router to L3 agent in the "
+                     "Liberty release or older, and are not required since "
+                     "the Mitaka release.")
 ]
 
 network_feature_group = cfg.OptGroup(name='network-feature-enabled',
@@ -559,6 +631,9 @@
                 default=True,
                 help="Does the test environment support changing"
                      " port admin state"),
+    cfg.BoolOpt('port_security',
+                default=False,
+                help="Does the test environment support port security?"),
 ]
 
 validation_group = cfg.OptGroup(name='validation',
@@ -649,7 +724,7 @@
     cfg.StrOpt('network_for_ssh',
                default='public',
                help="Network used for SSH connections. Ignored if "
-                    "use_floatingip_for_ssh=true or run_validation=false.",
+                    "connect_method=floating.",
                deprecated_opts=[cfg.DeprecatedOpt('network_for_ssh',
                                                   group='compute')]),
 ]
@@ -679,22 +754,10 @@
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
                help="The endpoint type to use for the volume service."),
-    cfg.StrOpt('backend1_name',
-               default='',
-               help='Name of the backend1 (must be declared in cinder.conf)',
-               deprecated_for_removal=True),
-    cfg.StrOpt('backend2_name',
-               default='',
-               help='Name of the backend2 (must be declared in cinder.conf)',
-               deprecated_for_removal=True),
     cfg.ListOpt('backend_names',
                 default=['BACKEND_1', 'BACKEND_2'],
                 help='A list of backend names separated by comma. '
-                     'The backend name must be declared in cinder.conf',
-                deprecated_opts=[cfg.DeprecatedOpt('BACKEND_1',
-                                                   group='volume'),
-                                 cfg.DeprecatedOpt('BACKEND_2',
-                                                   group='volume')]),
+                     'The backend name must be declared in cinder.conf'),
     cfg.StrOpt('storage_protocol',
                default='iSCSI',
                help='Backend protocol to target when creating volume types'),
@@ -707,6 +770,24 @@
     cfg.IntOpt('volume_size',
                default=1,
                help='Default size in GB for volumes created by volumes tests'),
+    cfg.StrOpt('min_microversion',
+               default=None,
+               help="Lower version of the test target microversion range. "
+                    "The format is 'X.Y', where 'X' and 'Y' are int values. "
+                    "Tempest selects tests based on the range between "
+                    "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'",),
+    cfg.StrOpt('max_microversion',
+               default=None,
+               help="Upper version of the test target microversion range. "
+                    "The format is 'X.Y', where 'X' and 'Y' are int values. "
+                    "Tempest selects tests based on the range between "
+                    "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'",),
 ]
 
 volume_feature_group = cfg.OptGroup(name='volume-feature-enabled',
@@ -725,6 +806,9 @@
     cfg.BoolOpt('clone',
                 default=True,
                 help='Runs Cinder volume clone test'),
+    cfg.BoolOpt('manage_snapshot',
+                default=False,
+                help='Runs Cinder manage snapshot tests'),
     cfg.ListOpt('api_extensions',
                 default=['all'],
                 help='A list of enabled volume extensions with a special '
@@ -736,11 +820,16 @@
     cfg.BoolOpt('api_v2',
                 default=True,
                 help="Is the v2 volume API enabled"),
-    cfg.BoolOpt('bootable',
+    cfg.BoolOpt('api_v3',
+                default=False,
+                help="Is the v3 volume API enabled"),
+    # TODO(ynesenenko): Remove volume_services once liberty-eol happens.
+    cfg.BoolOpt('volume_services',
                 default=True,
-                help='Update bootable status of a volume '
-                     'Not implemented on icehouse ',
-                deprecated_for_removal=True)
+                help='Extract correct host info from host@backend',
+                deprecated_for_removal=True,
+                deprecated_reason='This config switch was added for Liberty '
+                                  'which is not supported anymore.')
 ]
 
 
@@ -811,21 +900,6 @@
                 help="Execute discoverability tests"),
 ]
 
-database_group = cfg.OptGroup(name='database',
-                              title='Database Service Options')
-
-DatabaseGroup = [
-    cfg.StrOpt('catalog_type',
-               default='database',
-               help="Catalog type of the Database service."),
-    cfg.StrOpt('db_flavor_ref',
-               default="1",
-               help="Valid primary flavor to use in database tests."),
-    cfg.StrOpt('db_current_version',
-               default="v1.0",
-               help="Current database version to use in database tests."),
-]
-
 orchestration_group = cfg.OptGroup(name='orchestration',
                                    title='Orchestration Service Options')
 
@@ -867,131 +941,6 @@
 ]
 
 
-telemetry_group = cfg.OptGroup(name='telemetry',
-                               title='Telemetry Service Options')
-
-TelemetryGroup = [
-    cfg.StrOpt('catalog_type',
-               default='metering',
-               help="Catalog type of the Telemetry service."),
-    cfg.StrOpt('endpoint_type',
-               default='publicURL',
-               choices=['public', 'admin', 'internal',
-                        'publicURL', 'adminURL', 'internalURL'],
-               help="The endpoint type to use for the telemetry service."),
-    cfg.BoolOpt('too_slow_to_test',
-                default=True,
-                deprecated_for_removal=True,
-                help="This variable is used as flag to enable "
-                     "notification tests")
-]
-
-alarming_group = cfg.OptGroup(name='alarming',
-                              title='Alarming Service Options')
-
-AlarmingGroup = [
-    cfg.StrOpt('catalog_type',
-               default='alarming',
-               help="Catalog type of the Alarming service."),
-    cfg.StrOpt('endpoint_type',
-               default='publicURL',
-               choices=['public', 'admin', 'internal',
-                        'publicURL', 'adminURL', 'internalURL'],
-               help="The endpoint type to use for the alarming service."),
-]
-
-
-telemetry_feature_group = cfg.OptGroup(name='telemetry-feature-enabled',
-                                       title='Enabled Ceilometer Features')
-
-TelemetryFeaturesGroup = [
-    cfg.BoolOpt('events',
-                default=False,
-                help="Runs Ceilometer event-related tests"),
-]
-
-
-dashboard_group = cfg.OptGroup(name="dashboard",
-                               title="Dashboard options")
-
-DashboardGroup = [
-    cfg.StrOpt('dashboard_url',
-               default='http://localhost/',
-               help="Where the dashboard can be found"),
-    cfg.StrOpt('login_url',
-               default='http://localhost/auth/login/',
-               help="Login page for the dashboard",
-               deprecated_for_removal=True),
-]
-
-
-data_processing_group = cfg.OptGroup(name="data-processing",
-                                     title="Data Processing options")
-
-DataProcessingGroup = [
-    cfg.StrOpt('catalog_type',
-               default='data-processing',
-               deprecated_group="data_processing",
-               help="Catalog type of the data processing service."),
-    cfg.StrOpt('endpoint_type',
-               default='publicURL',
-               choices=['public', 'admin', 'internal',
-                        'publicURL', 'adminURL', 'internalURL'],
-               deprecated_group="data_processing",
-               help="The endpoint type to use for the data processing "
-                    "service."),
-]
-
-
-data_processing_feature_group = cfg.OptGroup(
-    name="data-processing-feature-enabled",
-    title="Enabled Data Processing features")
-
-DataProcessingFeaturesGroup = [
-    cfg.ListOpt('plugins',
-                default=["vanilla", "hdp"],
-                deprecated_group="data_processing-feature-enabled",
-                help="List of enabled data processing plugins")
-]
-
-stress_group = cfg.OptGroup(name='stress', title='Stress Test Options')
-
-StressGroup = [
-    cfg.StrOpt('nova_logdir',
-               help='Directory containing log files on the compute nodes'),
-    cfg.IntOpt('max_instances',
-               default=16,
-               help='Maximum number of instances to create during test.'),
-    cfg.StrOpt('controller',
-               help='Controller host.'),
-    # new stress options
-    cfg.StrOpt('target_controller',
-               help='Controller host.'),
-    cfg.StrOpt('target_ssh_user',
-               help='ssh user.'),
-    cfg.StrOpt('target_private_key_path',
-               help='Path to private key.'),
-    cfg.StrOpt('target_logfiles',
-               help='regexp for list of log files.'),
-    cfg.IntOpt('log_check_interval',
-               default=60,
-               help='time (in seconds) between log file error checks.'),
-    cfg.IntOpt('default_thread_number_per_action',
-               default=4,
-               help='The number of threads created while stress test.'),
-    cfg.BoolOpt('leave_dirty_stack',
-                default=False,
-                help='Prevent the cleaning (tearDownClass()) between'
-                     ' each stress test run if an exception occurs'
-                     ' during this run.'),
-    cfg.BoolOpt('full_clean_stack',
-                default=False,
-                help='Allows a full cleaning process after a stress test.'
-                     ' Caution : this cleanup will remove every objects of'
-                     ' every tenant.')
-]
-
-
 scenario_group = cfg.OptGroup(name='scenario', title='Scenario Test Options')
 
 ScenarioGroup = [
@@ -1023,11 +972,6 @@
                default='cirros-0.3.1-x86_64-vmlinuz',
                help='AKI image file name',
                deprecated_for_removal=True),
-    cfg.IntOpt(
-        'large_ops_number',
-        default=0,
-        help="specifies how many resources to request at once. Used "
-        "for large operations testing."),
     # TODO(yfried): add support for dhcpcd
     cfg.StrOpt('dhcp_client',
                default='udhcpc',
@@ -1060,24 +1004,9 @@
     cfg.BoolOpt('heat',
                 default=False,
                 help="Whether or not Heat is expected to be available"),
-    cfg.BoolOpt('ceilometer',
-                default=True,
-                help="Whether or not Ceilometer is expected to be available"),
-    cfg.BoolOpt('aodh',
-                default=False,
-                help="Whether or not Aodh is expected to be available"),
-    cfg.BoolOpt('horizon',
-                default=True,
-                help="Whether or not Horizon is expected to be available"),
     cfg.BoolOpt('sahara',
                 default=False,
                 help="Whether or not Sahara is expected to be available"),
-    cfg.BoolOpt('ironic',
-                default=False,
-                help="Whether or not Ironic is expected to be available"),
-    cfg.BoolOpt('trove',
-                default=False,
-                help="Whether or not Trove is expected to be available"),
 ]
 
 debug_group = cfg.OptGroup(name="debug",
@@ -1108,81 +1037,28 @@
 
 input_scenario_group = cfg.OptGroup(name="input-scenario",
                                     title="Filters and values for"
-                                          " input scenarios")
+                                          " input scenarios[DEPRECATED]")
+
 
 InputScenarioGroup = [
     cfg.StrOpt('image_regex',
                default='^cirros-0.3.1-x86_64-uec$',
-               help="Matching images become parameters for scenario tests"),
+               help="Matching images become parameters for scenario tests",
+               deprecated_for_removal=True),
     cfg.StrOpt('flavor_regex',
                default='^m1.nano$',
-               help="Matching flavors become parameters for scenario tests"),
+               help="Matching flavors become parameters for scenario tests",
+               deprecated_for_removal=True),
     cfg.StrOpt('non_ssh_image_regex',
                default='^.*[Ww]in.*$',
                help="SSH verification in tests is skipped"
-                    "for matching images"),
+                    "for matching images",
+               deprecated_for_removal=True),
     cfg.StrOpt('ssh_user_regex',
                default="[[\"^.*[Cc]irros.*$\", \"cirros\"]]",
                help="List of user mapped to regex "
-                    "to matching image names."),
-]
-
-
-baremetal_group = cfg.OptGroup(name='baremetal',
-                               title='Baremetal provisioning service options',
-                               help='When enabling baremetal tests, Nova '
-                                    'must be configured to use the Ironic '
-                                    'driver. The following parameters for the '
-                                    '[compute] section must be disabled: '
-                                    'console_output, interface_attach, '
-                                    'live_migration, pause, rescue, resize '
-                                    'shelve, snapshot, and suspend')
-
-
-# NOTE(deva): Ironic tests have been ported to tempest-lib. New config options
-#             should be added to ironic/ironic_tempest_plugin/config.py.
-#             However, these options need to remain here for testing stable
-#             branches until Liberty release reaches EOL.
-BaremetalGroup = [
-    cfg.StrOpt('catalog_type',
-               default='baremetal',
-               help="Catalog type of the baremetal provisioning service"),
-    cfg.BoolOpt('driver_enabled',
-                default=False,
-                help="Whether the Ironic nova-compute driver is enabled"),
-    cfg.StrOpt('driver',
-               default='fake',
-               help="Driver name which Ironic uses"),
-    cfg.StrOpt('endpoint_type',
-               default='publicURL',
-               choices=['public', 'admin', 'internal',
-                        'publicURL', 'adminURL', 'internalURL'],
-               help="The endpoint type to use for the baremetal provisioning "
-                    "service"),
-    cfg.IntOpt('active_timeout',
-               default=300,
-               help="Timeout for Ironic node to completely provision"),
-    cfg.IntOpt('association_timeout',
-               default=30,
-               help="Timeout for association of Nova instance and Ironic "
-                    "node"),
-    cfg.IntOpt('power_timeout',
-               default=60,
-               help="Timeout for Ironic power transitions."),
-    cfg.IntOpt('unprovision_timeout',
-               default=300,
-               help="Timeout for unprovisioning an Ironic node. "
-                    "Takes longer since Kilo as Ironic performs an extra "
-                    "step in Node cleaning.")
-]
-
-negative_group = cfg.OptGroup(name='negative', title="Negative Test Options")
-
-NegativeGroup = [
-    cfg.StrOpt('test_generator',
-               default='tempest.common.' +
-               'generator.negative_generator.NegativeTestGenerator',
-               help="Test generator class for all negative tests"),
+                    "to matching image names.",
+               deprecated_for_removal=True),
 ]
 
 DefaultGroup = [
@@ -1199,6 +1075,7 @@
     (compute_group, ComputeGroup),
     (compute_features_group, ComputeFeaturesGroup),
     (identity_group, IdentityGroup),
+    (service_clients_group, ServiceClientsGroup),
     (identity_feature_group, IdentityFeatureGroup),
     (image_group, ImageGroup),
     (image_feature_group, ImageFeaturesGroup),
@@ -1209,21 +1086,11 @@
     (volume_feature_group, VolumeFeaturesGroup),
     (object_storage_group, ObjectStoreGroup),
     (object_storage_feature_group, ObjectStoreFeaturesGroup),
-    (database_group, DatabaseGroup),
     (orchestration_group, OrchestrationGroup),
-    (telemetry_group, TelemetryGroup),
-    (telemetry_feature_group, TelemetryFeaturesGroup),
-    (alarming_group, AlarmingGroup),
-    (dashboard_group, DashboardGroup),
-    (data_processing_group, DataProcessingGroup),
-    (data_processing_feature_group, DataProcessingFeaturesGroup),
-    (stress_group, StressGroup),
     (scenario_group, ScenarioGroup),
     (service_available_group, ServiceAvailableGroup),
     (debug_group, DebugGroup),
-    (baremetal_group, BaremetalGroup),
     (input_scenario_group, InputScenarioGroup),
-    (negative_group, NegativeGroup),
     (None, DefaultGroup)
 ]
 
@@ -1244,7 +1111,10 @@
     generator to discover the options exposed to users.
     """
     ext_plugins = plugins.TempestTestPluginManager()
-    opt_list = [(getattr(g, 'name', None), o) for g, o in _opts]
+    # Make a shallow copy of the options list that can be
+    # extended by plugins. Send back the group object
+    # to allow group help text to be generated.
+    opt_list = [(g, o) for g, o in _opts]
     opt_list.extend(ext_plugins.get_plugin_options_list())
     return opt_list
 
@@ -1266,6 +1136,7 @@
         self.compute = _CONF.compute
         self.compute_feature_enabled = _CONF['compute-feature-enabled']
         self.identity = _CONF.identity
+        self.service_clients = _CONF['service-clients']
         self.identity_feature_enabled = _CONF['identity-feature-enabled']
         self.image = _CONF.image
         self.image_feature_enabled = _CONF['image-feature-enabled']
@@ -1277,27 +1148,11 @@
         self.object_storage = _CONF['object-storage']
         self.object_storage_feature_enabled = _CONF[
             'object-storage-feature-enabled']
-        self.database = _CONF.database
         self.orchestration = _CONF.orchestration
-        self.telemetry = _CONF.telemetry
-        self.telemetry_feature_enabled = _CONF['telemetry-feature-enabled']
-        self.dashboard = _CONF.dashboard
-        self.data_processing = _CONF['data-processing']
-        self.data_processing_feature_enabled = _CONF[
-            'data-processing-feature-enabled']
-        self.stress = _CONF.stress
         self.scenario = _CONF.scenario
         self.service_available = _CONF.service_available
         self.debug = _CONF.debug
-        self.baremetal = _CONF.baremetal
         self.input_scenario = _CONF['input-scenario']
-        self.negative = _CONF.negative
-        _CONF.set_default('domain_name',
-                          self.auth.default_credentials_domain_name,
-                          group='identity')
-        _CONF.set_default('alt_domain_name',
-                          self.auth.default_credentials_domain_name,
-                          group='identity')
         logging.tempest_set_log_file('tempest.log')
 
     def __init__(self, parse_conf=True, config_path=None):
@@ -1339,11 +1194,11 @@
 
         logging.setup(_CONF, 'tempest')
         LOG = logging.getLogger('tempest')
-        LOG.info("Using tempest config file %s" % path)
+        LOG.info("Using tempest config file %s", path)
         register_opts()
         self._set_attrs()
         if parse_conf:
-            _CONF.log_opt_values(LOG, std_logging.DEBUG)
+            _CONF.log_opt_values(LOG, logging.DEBUG)
 
 
 class TempestConfigProxy(object):
@@ -1351,14 +1206,14 @@
     _path = None
 
     _extra_log_defaults = [
-        ('paramiko.transport', std_logging.INFO),
-        ('requests.packages.urllib3.connectionpool', std_logging.WARN),
+        ('paramiko.transport', logging.INFO),
+        ('requests.packages.urllib3.connectionpool', logging.WARN),
     ]
 
     def _fix_log_levels(self):
         """Tweak the oslo log defaults."""
         for name, level in self._extra_log_defaults:
-            std_logging.getLogger(name).setLevel(level)
+            logging.getLogger(name).logger.setLevel(level)
 
     def __getattr__(self, attr):
         if not self._config:
@@ -1367,6 +1222,15 @@
             lockutils.set_defaults(lock_dir)
             self._config = TempestConfigPrivate(config_path=self._path)
 
+            # Pushing tempest internal service client configuration to the
+            # service clients register. Doing this in the config module ensures
+            # that the configuration is available by the time we register the
+            # service clients.
+            # NOTE(andreaf) This has to be done at the time the first
+            # attribute is accessed, to ensure all plugins have been already
+            # loaded, options registered, and _config is set.
+            _register_tempest_service_clients()
+
         return getattr(self._config, attr)
 
     def set_config_path(self, path):
@@ -1374,3 +1238,181 @@
 
 
 CONF = TempestConfigProxy()
+
+
+def skip_unless_config(*args):
+    """Decorator to raise a skip if a config opt doesn't exist or is False
+
+    :param str group: The first arg, the option group to check
+    :param str name: The second arg, the option name to check
+    :param str msg: Optional third arg, the skip msg to use if a skip is raised
+    :raises testtools.TestCaseskipException: If the specified config option
+        doesn't exist or it exists and evaluates to False
+    """
+    def decorator(f):
+        group = args[0]
+        name = args[1]
+
+        @functools.wraps(f)
+        def wrapper(self, *func_args, **func_kwargs):
+            if not hasattr(CONF, group):
+                msg = "Config group %s doesn't exist" % group
+                raise testtools.TestCase.skipException(msg)
+
+            conf_group = getattr(CONF, group)
+            if not hasattr(conf_group, name):
+                msg = "Config option %s.%s doesn't exist" % (group,
+                                                             name)
+                raise testtools.TestCase.skipException(msg)
+
+            value = getattr(conf_group, name)
+            if not value:
+                if len(args) == 3:
+                    msg = args[2]
+                else:
+                    msg = "Config option %s.%s is false" % (group,
+                                                            name)
+                raise testtools.TestCase.skipException(msg)
+            return f(self, *func_args, **func_kwargs)
+        return wrapper
+    return decorator
+
+
+def skip_if_config(*args):
+    """Raise a skipException if a config exists and is True
+
+    :param str group: The first arg, the option group to check
+    :param str name: The second arg, the option name to check
+    :param str msg: Optional third arg, the skip msg to use if a skip is raised
+    :raises testtools.TestCase.skipException: If the specified config option
+        exists and evaluates to True
+    """
+    def decorator(f):
+        group = args[0]
+        name = args[1]
+
+        @functools.wraps(f)
+        def wrapper(self, *func_args, **func_kwargs):
+            if hasattr(CONF, group):
+                conf_group = getattr(CONF, group)
+                if hasattr(conf_group, name):
+                    value = getattr(conf_group, name)
+                    if value:
+                        if len(args) == 3:
+                            msg = args[2]
+                        else:
+                            msg = "Config option %s.%s is false" % (group,
+                                                                    name)
+                        raise testtools.TestCase.skipException(msg)
+            return f(self, *func_args, **func_kwargs)
+        return wrapper
+    return decorator
+
+
+def service_client_config(service_client_name=None):
+    """Return a dict with the parameters to init service clients
+
+    Extracts from CONF the settings specific to the service_client_name and
+    api_version, and formats them as dict ready to be passed to the service
+    clients __init__:
+
+        * `region` (default to identity)
+        * `catalog_type`
+        * `endpoint_type`
+        * `build_timeout` (object-storage and identity default to compute)
+        * `build_interval` (object-storage and identity default to compute)
+
+    The following common settings are always returned, even if
+    `service_client_name` is None:
+
+        * `disable_ssl_certificate_validation`
+        * `ca_certs`
+        * `trace_requests`
+        * `http_timeout`
+
+    The dict returned by this does not fit a few service clients:
+
+        * The endpoint type is not returned for identity client, since it takes
+          three different values for v2 admin, v2 public and v3
+        * The `ServersClient` from compute accepts an optional
+          `enable_instance_password` parameter, which is not returned.
+        * The `VolumesClient` for both v1 and v2 volume accept an optional
+          `default_volume_size` parameter, which is not returned.
+        * The `TokenClient` and `V3TokenClient` have a very different
+          interface, only auth_url is needed for them.
+
+    :param service_client_name: str Name of the service. Supported values are
+        'compute', 'identity', 'image', 'network', 'object-storage', 'volume'
+    :return: dictionary of __init__ parameters for the service clients
+    :rtype: dict
+    """
+    _parameters = {
+        'disable_ssl_certificate_validation':
+            CONF.identity.disable_ssl_certificate_validation,
+        'ca_certs': CONF.identity.ca_certificates_file,
+        'trace_requests': CONF.debug.trace_requests,
+        'http_timeout': CONF.service_clients.http_timeout
+    }
+
+    if service_client_name is None:
+        return _parameters
+
+    # Get the group of options first, by normalising the service_group_name
+    # Services with a '-' in the name have an '_' in the option group name
+    config_group = service_client_name.replace('-', '_')
+    # NOTE(andreaf) Check if the config group exists. This allows for this
+    # helper to be used for settings from registered plugins as well
+    try:
+        options = getattr(CONF, config_group)
+    except cfg.NoSuchOptError:
+        # Option group not defined
+        raise exceptions.UnknownServiceClient(services=service_client_name)
+    # Set endpoint_type
+    # Identity uses different settings depending on API version, so do not
+    # return the endpoint at all.
+    if service_client_name != 'identity':
+        _parameters['endpoint_type'] = getattr(options, 'endpoint_type')
+    # Set build_*
+    # Object storage and identity groups do not have conf settings for
+    # build_* parameters, and we default to compute in any case
+    for setting in ['build_timeout', 'build_interval']:
+        if not hasattr(options, setting) or not getattr(options, setting):
+            _parameters[setting] = getattr(CONF.compute, setting)
+        else:
+            _parameters[setting] = getattr(options, setting)
+    # Set region
+    # If a service client does not define region or region is not set
+    # default to the identity region
+    if not hasattr(options, 'region') or not getattr(options, 'region'):
+        _parameters['region'] = CONF.identity.region
+    else:
+        _parameters['region'] = getattr(options, 'region')
+    # Set service
+    _parameters['service'] = getattr(options, 'catalog_type')
+    return _parameters
+
+
+def _register_tempest_service_clients():
+    # Register tempest own service clients using the same mechanism used
+    # for external plugins.
+    # The configuration data is pushed to the registry so that automatic
+    # configuration of tempest own service clients is possible both for
+    # tempest as well as for the plugins.
+    service_clients = clients.tempest_modules()
+    registry = clients.ClientsRegistry()
+    all_clients = []
+    for service_client in service_clients:
+        module = service_clients[service_client]
+        configs = service_client.split('.')[0]
+        service_client_data = dict(
+            name=service_client.replace('.', '_'),
+            service_version=service_client,
+            module_path=module.__name__,
+            client_names=module.__all__,
+            **service_client_config(configs)
+        )
+        all_clients.append(service_client_data)
+    # NOTE(andreaf) Internal service clients do not actually belong
+    # to a plugin, so using '__tempest__' to indicate a virtual plugin
+    # which holds internal service clients.
+    registry.register_service_client('__tempest__', all_clients)
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 86e8460..43f919a 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -13,195 +13,54 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
+
+from tempest.lib import exceptions
 
 
-class TempestException(Exception):
-    """Base Tempest Exception
-
-    To correctly use this class, inherit from it and define
-    a 'message' property. That message will get printf'd
-    with the keyword arguments provided to the constructor.
-    """
-    message = "An unknown exception occurred"
-
-    def __init__(self, *args, **kwargs):
-        super(TempestException, self).__init__()
-        try:
-            self._error_string = self.message % kwargs
-        except Exception:
-            # at least get the core message out if something happened
-            self._error_string = self.message
-        if len(args) > 0:
-            # If there is a non-kwarg parameter, assume it's the error
-            # message or reason description and tack it on to the end
-            # of the exception message
-            # Convert all arguments into their string representations...
-            args = ["%s" % arg for arg in args]
-            self._error_string = (self._error_string +
-                                  "\nDetails: %s" % '\n'.join(args))
-
-    def __str__(self):
-        return self._error_string
-
-
-class RestClientException(TempestException,
-                          testtools.TestCase.failureException):
-    pass
-
-
-class InvalidConfiguration(TempestException):
-    message = "Invalid Configuration"
-
-
-class InvalidCredentials(TempestException):
-    message = "Invalid Credentials"
-
-
-class InvalidServiceTag(TempestException):
+class InvalidServiceTag(exceptions.TempestException):
     message = "Invalid service tag"
 
 
-class InvalidIdentityVersion(TempestException):
-    message = "Invalid version %(identity_version)s of the identity service"
-
-
-class TimeoutException(TempestException):
-    message = "Request timed out"
-
-
-class BuildErrorException(TempestException):
+class BuildErrorException(exceptions.TempestException):
     message = "Server %(server_id)s failed to build and is in ERROR status"
 
 
-class ImageKilledException(TempestException):
+class ImageKilledException(exceptions.TempestException):
     message = "Image %(image_id)s 'killed' while waiting for '%(status)s'"
 
 
-class AddImageException(TempestException):
+class AddImageException(exceptions.TempestException):
     message = "Image %(image_id)s failed to become ACTIVE in the allotted time"
 
 
-class EC2RegisterImageException(TempestException):
-    message = ("Image %(image_id)s failed to become 'available' "
-               "in the allotted time")
-
-
-class VolumeBuildErrorException(TempestException):
+class VolumeBuildErrorException(exceptions.TempestException):
     message = "Volume %(volume_id)s failed to build and is in ERROR status"
 
 
-class VolumeRestoreErrorException(TempestException):
+class VolumeRestoreErrorException(exceptions.TempestException):
     message = "Volume %(volume_id)s failed to restore and is in ERROR status"
 
 
-class SnapshotBuildErrorException(TempestException):
+class SnapshotBuildErrorException(exceptions.TempestException):
     message = "Snapshot %(snapshot_id)s failed to build and is in ERROR status"
 
 
-class VolumeBackupException(TempestException):
-    message = "Volume backup %(backup_id)s failed and is in ERROR status"
-
-
-class StackBuildErrorException(TempestException):
+class StackBuildErrorException(exceptions.TempestException):
     message = ("Stack %(stack_identifier)s is in %(stack_status)s status "
                "due to '%(stack_status_reason)s'")
 
 
-class StackResourceBuildErrorException(TempestException):
-    message = ("Resource %(resource_name)s in stack %(stack_identifier)s is "
-               "in %(resource_status)s status due to "
-               "'%(resource_status_reason)s'")
-
-
-class AuthenticationFailure(TempestException):
-    message = ("Authentication with user %(user)s and password "
-               "%(password)s failed auth using tenant %(tenant)s.")
-
-
-class EndpointNotFound(TempestException):
-    message = "Endpoint not found"
-
-
-class ImageFault(TempestException):
-    message = "Got image fault"
-
-
-class IdentityError(TempestException):
-    message = "Got identity error"
-
-
-class ServerUnreachable(TempestException):
-    message = "The server is not reachable via the configured network"
-
-
-class TearDownException(TempestException):
-    message = "%(num)d cleanUp operation failed"
+class ServerUnreachable(exceptions.TempestException):
+    message = ("Server %(server_id)s is not reachable via "
+               "the configured network")
 
 
 # NOTE(andreaf) This exception is added here to facilitate the migration
-# of get_network_from_name and preprov_creds to tempest-lib, and it should
+# of get_network_from_name and preprov_creds to tempest.lib, and it should
 # be migrated along with them
-class InvalidTestResource(TempestException):
-    message = "%(name) is not a valid %(type), or the name is ambiguous"
+class InvalidTestResource(exceptions.TempestException):
+    message = "%(name)s is not a valid %(type)s, or the name is ambiguous"
 
 
-class RFCViolation(RestClientException):
+class RFCViolation(exceptions.RestClientException):
     message = "RFC Violation"
-
-
-class InvalidHttpSuccessCode(RestClientException):
-    message = "The success code is different than the expected one"
-
-
-class BadRequest(RestClientException):
-    message = "Bad request"
-
-
-class ResponseWithNonEmptyBody(RFCViolation):
-    message = ("RFC Violation! Response with %(status)d HTTP Status Code "
-               "MUST NOT have a body")
-
-
-class ResponseWithEntity(RFCViolation):
-    message = ("RFC Violation! Response with 205 HTTP Status Code "
-               "MUST NOT have an entity")
-
-
-class InvalidHTTPResponseHeader(RestClientException):
-    message = "HTTP response header is invalid"
-
-
-class InvalidStructure(TempestException):
-    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__()
-        self.returncode = returncode
-        self.cmd = cmd
-        self.stdout = output
-        self.stderr = stderr
-
-    def __str__(self):
-        return ("Command '%s' returned non-zero exit status %d.\n"
-                "stdout:\n%s\n"
-                "stderr:\n%s" % (self.cmd,
-                                 self.returncode,
-                                 self.stdout,
-                                 self.stderr))
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index cd4f50d..4123ae5 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -19,8 +19,7 @@
 
 
 PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron',
-                  'trove', 'ironic', 'savanna', 'heat', 'ceilometer',
-                  'sahara']
+                  'ironic', 'heat', 'sahara']
 
 PYTHON_CLIENT_RE = re.compile('import (%s)client' % '|'.join(PYTHON_CLIENTS))
 TEST_DEFINITION = re.compile(r'^\s*def test.*')
@@ -69,10 +68,12 @@
     if pep8.noqa(physical_line):
         return
 
-    if 'tempest/test.py' not in filename:
-        if SETUP_TEARDOWN_CLASS_DEFINITION.match(physical_line):
-            return (physical_line.find('def'),
-                    "T105: (setUp|tearDown)Class can not be used in tests")
+    if 'tempest/test.py' in filename or 'tempest/lib/' in filename:
+        return
+
+    if SETUP_TEARDOWN_CLASS_DEFINITION.match(physical_line):
+        return (physical_line.find('def'),
+                "T105: (setUp|tearDown)Class can not be used in tests")
 
 
 def no_vi_headers(physical_line, line_number, lines):
@@ -117,11 +118,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
@@ -144,12 +140,12 @@
     """
     if TESTTOOLS_SKIP_DECORATOR.match(logical_line):
         yield (0, "T109: Cannot use testtools.skip decorator; instead use "
-               "decorators.skip_because from tempest-lib")
+               "decorators.skip_because from tempest.lib")
 
 
 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:
@@ -223,6 +219,60 @@
         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 use_rand_uuid_instead_of_uuid4(logical_line, filename):
+    """Check that tests use data_utils.rand_uuid() instead of uuid.uuid4()
+
+    T113
+    """
+    if 'tempest/lib/' in filename:
+        return
+
+    if 'uuid.uuid4()' not in logical_line:
+        return
+
+    msg = ("T113: Tests should use data_utils.rand_uuid()/rand_uuid_hex() "
+           "instead of uuid.uuid4()/uuid.uuid4().hex")
+    yield (0, msg)
+
+
+def dont_use_config_in_tempest_lib(logical_line, filename):
+    """Check that tempest.lib doesn't use tempest config
+
+    T114
+    """
+
+    if 'tempest/lib/' not in filename:
+        return
+
+    if ('tempest.config' in logical_line
+        or 'from tempest import config' in logical_line
+        or 'oslo_config' in logical_line):
+        msg = ('T114: tempest.lib can not have any dependency on tempest '
+               'config.')
+        yield(0, msg)
+
+
 def factory(register):
     register(import_no_clients_in_api_and_scenario_tests)
     register(scenario_tests_need_service_tags)
@@ -234,3 +284,6 @@
     register(no_testtools_skip_decorator)
     register(get_resources_on_service_clients)
     register(delete_resources_on_service_clients)
+    register(dont_import_local_tempest_into_lib)
+    register(dont_use_config_in_tempest_lib)
+    register(use_rand_uuid_instead_of_uuid4)
diff --git a/tempest/hacking/ignored_list_T110.txt b/tempest/hacking/ignored_list_T110.txt
index 50a5477..be875ee 100644
--- a/tempest/hacking/ignored_list_T110.txt
+++ b/tempest/hacking/ignored_list_T110.txt
@@ -1,7 +1,4 @@
 ./tempest/services/object_storage/object_client.py
-./tempest/services/telemetry/json/alarming_client.py
-./tempest/services/telemetry/json/telemetry_client.py
 ./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
diff --git a/tempest/stress/__init__.py b/tempest/lib/__init__.py
similarity index 100%
rename from tempest/stress/__init__.py
rename to tempest/lib/__init__.py
diff --git a/tempest/api_schema/__init__.py b/tempest/lib/api_schema/__init__.py
similarity index 100%
rename from tempest/api_schema/__init__.py
rename to tempest/lib/api_schema/__init__.py
diff --git a/tempest/api_schema/response/__init__.py b/tempest/lib/api_schema/response/__init__.py
similarity index 100%
rename from tempest/api_schema/response/__init__.py
rename to tempest/lib/api_schema/response/__init__.py
diff --git a/tempest/api_schema/response/compute/__init__.py b/tempest/lib/api_schema/response/compute/__init__.py
similarity index 100%
rename from tempest/api_schema/response/compute/__init__.py
rename to tempest/lib/api_schema/response/compute/__init__.py
diff --git a/tempest/api_schema/response/compute/v2_1/__init__.py b/tempest/lib/api_schema/response/compute/v2_1/__init__.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2_1/__init__.py
rename to tempest/lib/api_schema/response/compute/v2_1/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_1/agents.py b/tempest/lib/api_schema/response/compute/v2_1/agents.py
new file mode 100644
index 0000000..6f712b4
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/agents.py
@@ -0,0 +1,82 @@
+# 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.
+
+common_agent_info = {
+    'type': 'object',
+    'properties': {
+        'agent_id': {'type': ['integer', 'string']},
+        'hypervisor': {'type': 'string'},
+        'os': {'type': 'string'},
+        'architecture': {'type': 'string'},
+        'version': {'type': 'string'},
+        'url': {'type': 'string', 'format': 'uri'},
+        'md5hash': {'type': 'string'}
+    },
+    'additionalProperties': False,
+    'required': ['agent_id', 'hypervisor', 'os', 'architecture',
+                 'version', 'url', 'md5hash']
+}
+
+list_agents = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'agents': {
+                'type': 'array',
+                'items': common_agent_info
+            }
+        },
+        'additionalProperties': False,
+        'required': ['agents']
+    }
+}
+
+create_agent = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'agent': common_agent_info
+        },
+        'additionalProperties': False,
+        'required': ['agent']
+    }
+}
+
+update_agent = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'agent': {
+                'type': 'object',
+                'properties': {
+                    'agent_id': {'type': ['integer', 'string']},
+                    'version': {'type': 'string'},
+                    'url': {'type': 'string', 'format': 'uri'},
+                    'md5hash': {'type': 'string'}
+                },
+                'additionalProperties': False,
+                'required': ['agent_id', 'version', 'url', 'md5hash']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['agent']
+    }
+}
+
+delete_agent = {
+    'status_code': [200]
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/aggregates.py b/tempest/lib/api_schema/response/compute/v2_1/aggregates.py
new file mode 100644
index 0000000..1a9fe41
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/aggregates.py
@@ -0,0 +1,92 @@
+# 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.
+
+import copy
+
+# create-aggregate api doesn't have 'hosts' and 'metadata' attributes.
+aggregate_for_create = {
+    'type': 'object',
+    'properties': {
+        'availability_zone': {'type': ['string', 'null']},
+        'created_at': {'type': 'string'},
+        'deleted': {'type': 'boolean'},
+        'deleted_at': {'type': ['string', 'null']},
+        'id': {'type': 'integer'},
+        'name': {'type': 'string'},
+        'updated_at': {'type': ['string', 'null']}
+    },
+    'additionalProperties': False,
+    'required': ['availability_zone', 'created_at', 'deleted',
+                 'deleted_at', 'id', 'name', 'updated_at'],
+}
+
+common_aggregate_info = copy.deepcopy(aggregate_for_create)
+common_aggregate_info['properties'].update({
+    'hosts': {'type': 'array'},
+    'metadata': {'type': 'object'}
+})
+common_aggregate_info['required'].extend(['hosts', 'metadata'])
+
+list_aggregates = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'aggregates': {
+                'type': 'array',
+                'items': common_aggregate_info
+            }
+        },
+        'additionalProperties': False,
+        'required': ['aggregates'],
+    }
+}
+
+get_aggregate = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'aggregate': common_aggregate_info
+        },
+        'additionalProperties': False,
+        'required': ['aggregate'],
+    }
+}
+
+aggregate_set_metadata = get_aggregate
+# The 'updated_at' attribute of 'update_aggregate' can't be null.
+update_aggregate = copy.deepcopy(get_aggregate)
+update_aggregate['response_body']['properties']['aggregate']['properties'][
+    'updated_at'] = {
+        'type': 'string'
+    }
+
+delete_aggregate = {
+    'status_code': [200]
+}
+
+create_aggregate = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'aggregate': aggregate_for_create
+        },
+        'additionalProperties': False,
+        'required': ['aggregate'],
+    }
+}
+
+aggregate_add_remove_host = get_aggregate
diff --git a/tempest/lib/api_schema/response/compute/v2_1/availability_zone.py b/tempest/lib/api_schema/response/compute/v2_1/availability_zone.py
new file mode 100644
index 0000000..d9aebce
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/availability_zone.py
@@ -0,0 +1,78 @@
+# 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.
+
+import copy
+
+
+base = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'availabilityZoneInfo': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'zoneName': {'type': 'string'},
+                        'zoneState': {
+                            'type': 'object',
+                            'properties': {
+                                'available': {'type': 'boolean'}
+                            },
+                            'additionalProperties': False,
+                            'required': ['available']
+                        },
+                        # NOTE: Here is the difference between detail and
+                        # non-detail.
+                        'hosts': {'type': 'null'}
+                    },
+                    'additionalProperties': False,
+                    'required': ['zoneName', 'zoneState', 'hosts']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['availabilityZoneInfo']
+    }
+}
+
+detail = {
+    'type': 'object',
+    'patternProperties': {
+        # NOTE: Here is for a hostname
+        '^[a-zA-Z0-9-_.]+$': {
+            'type': 'object',
+            'patternProperties': {
+                # NOTE: Here is for a service name
+                '^.*$': {
+                    'type': 'object',
+                    'properties': {
+                        'available': {'type': 'boolean'},
+                        'active': {'type': 'boolean'},
+                        'updated_at': {'type': ['string', 'null']}
+                    },
+                    'additionalProperties': False,
+                    'required': ['available', 'active', 'updated_at']
+                }
+            }
+        }
+    }
+}
+
+list_availability_zone_list = copy.deepcopy(base)
+
+list_availability_zone_list_detail = copy.deepcopy(base)
+list_availability_zone_list_detail['response_body']['properties'][
+    'availabilityZoneInfo']['items']['properties']['hosts'] = detail
diff --git a/tempest/lib/api_schema/response/compute/v2_1/baremetal_nodes.py b/tempest/lib/api_schema/response/compute/v2_1/baremetal_nodes.py
new file mode 100644
index 0000000..d1ee877
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/baremetal_nodes.py
@@ -0,0 +1,63 @@
+# 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
+
+node = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'interfaces': {'type': 'array'},
+        'host': {'type': 'string'},
+        'task_state': {'type': ['string', 'null']},
+        'cpus': {'type': ['integer', 'string']},
+        'memory_mb': {'type': ['integer', 'string']},
+        'disk_gb': {'type': ['integer', 'string']},
+    },
+    'additionalProperties': False,
+    'required': ['id', 'interfaces', 'host', 'task_state', 'cpus', 'memory_mb',
+                 'disk_gb']
+}
+
+list_baremetal_nodes = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'nodes': {
+                'type': 'array',
+                'items': node
+            }
+        },
+        'additionalProperties': False,
+        'required': ['nodes']
+    }
+}
+
+baremetal_node = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'node': node
+        },
+        'additionalProperties': False,
+        'required': ['node']
+    }
+}
+get_baremetal_node = copy.deepcopy(baremetal_node)
+get_baremetal_node['response_body']['properties']['node'][
+    'properties'].update({'instance_uuid': {'type': ['string', 'null']}})
+get_baremetal_node['response_body']['properties']['node'][
+    'required'].append('instance_uuid')
diff --git a/tempest/lib/api_schema/response/compute/v2_1/certificates.py b/tempest/lib/api_schema/response/compute/v2_1/certificates.py
new file mode 100644
index 0000000..4e7cbe4
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/certificates.py
@@ -0,0 +1,41 @@
+# 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.
+
+import copy
+
+_common_schema = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'certificate': {
+                'type': 'object',
+                'properties': {
+                    'data': {'type': 'string'},
+                    'private_key': {'type': 'string'},
+                },
+                'additionalProperties': False,
+                'required': ['data', 'private_key']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['certificate']
+    }
+}
+
+get_certificate = copy.deepcopy(_common_schema)
+get_certificate['response_body']['properties']['certificate'][
+    'properties']['private_key'].update({'type': 'null'})
+
+create_certificate = copy.deepcopy(_common_schema)
diff --git a/tempest/lib/api_schema/response/compute/v2_1/extensions.py b/tempest/lib/api_schema/response/compute/v2_1/extensions.py
new file mode 100644
index 0000000..a6a455c
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/extensions.py
@@ -0,0 +1,47 @@
+# 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.
+
+list_extensions = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'extensions': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'updated': {
+                            'type': 'string',
+                            'format': 'data-time'
+                        },
+                        'name': {'type': 'string'},
+                        'links': {'type': 'array'},
+                        'namespace': {
+                            'type': 'string',
+                            'format': 'uri'
+                        },
+                        'alias': {'type': 'string'},
+                        'description': {'type': 'string'}
+                    },
+                    'additionalProperties': False,
+                    'required': ['updated', 'name', 'links', 'namespace',
+                                 'alias', 'description']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['extensions']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/fixed_ips.py b/tempest/lib/api_schema/response/compute/v2_1/fixed_ips.py
new file mode 100644
index 0000000..a653213
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/fixed_ips.py
@@ -0,0 +1,41 @@
+# 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.
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+get_fixed_ip = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'fixed_ip': {
+                'type': 'object',
+                'properties': {
+                    'address': parameter_types.ip_address,
+                    'cidr': {'type': 'string'},
+                    'host': {'type': 'string'},
+                    'hostname': {'type': 'string'}
+                },
+                'additionalProperties': False,
+                'required': ['address', 'cidr', 'host', 'hostname']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['fixed_ip']
+    }
+}
+
+reserve_unreserve_fixed_ip = {
+    'status_code': [202]
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/flavors.py b/tempest/lib/api_schema/response/compute/v2_1/flavors.py
new file mode 100644
index 0000000..547d94d
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/flavors.py
@@ -0,0 +1,103 @@
+# 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.
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+list_flavors = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'flavors': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'name': {'type': 'string'},
+                        'links': parameter_types.links,
+                        'id': {'type': 'string'}
+                    },
+                    'additionalProperties': False,
+                    'required': ['name', 'links', 'id']
+                }
+            },
+            'flavors_links': parameter_types.links
+        },
+        'additionalProperties': False,
+        # NOTE(gmann): flavors_links attribute is not necessary
+        # to be present always So it is not 'required'.
+        'required': ['flavors']
+    }
+}
+
+common_flavor_info = {
+    'type': 'object',
+    'properties': {
+        'name': {'type': 'string'},
+        'links': parameter_types.links,
+        'ram': {'type': 'integer'},
+        'vcpus': {'type': 'integer'},
+        # 'swap' attributes comes as integer value but if it is empty
+        # it comes as "". So defining type of as string and integer.
+        'swap': {'type': ['integer', 'string']},
+        'disk': {'type': 'integer'},
+        'id': {'type': 'string'},
+        'OS-FLV-DISABLED:disabled': {'type': 'boolean'},
+        'os-flavor-access:is_public': {'type': 'boolean'},
+        'rxtx_factor': {'type': 'number'},
+        'OS-FLV-EXT-DATA:ephemeral': {'type': 'integer'}
+    },
+    'additionalProperties': False,
+    # 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and
+    # 'OS-FLV-EXT-DATA' are API extensions. So they are not 'required'.
+    'required': ['name', 'links', 'ram', 'vcpus', 'swap', 'disk', 'id']
+}
+
+list_flavors_details = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'flavors': {
+                'type': 'array',
+                'items': common_flavor_info
+            },
+            # NOTE(gmann): flavors_links attribute is not necessary
+            # to be present always So it is not 'required'.
+            'flavors_links': parameter_types.links
+        },
+        'additionalProperties': False,
+        'required': ['flavors']
+    }
+}
+
+unset_flavor_extra_specs = {
+    'status_code': [200]
+}
+
+create_get_flavor_details = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'flavor': common_flavor_info
+        },
+        'additionalProperties': False,
+        'required': ['flavor']
+    }
+}
+
+delete_flavor = {
+    'status_code': [202]
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/flavors_access.py b/tempest/lib/api_schema/response/compute/v2_1/flavors_access.py
new file mode 100644
index 0000000..a4d6af0
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/flavors_access.py
@@ -0,0 +1,36 @@
+# 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.
+
+add_remove_list_flavor_access = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'flavor_access': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'flavor_id': {'type': 'string'},
+                        'tenant_id': {'type': 'string'},
+                    },
+                    'additionalProperties': False,
+                    'required': ['flavor_id', 'tenant_id'],
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['flavor_access']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/flavors_extra_specs.py b/tempest/lib/api_schema/response/compute/v2_1/flavors_extra_specs.py
new file mode 100644
index 0000000..a438d48
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/flavors_extra_specs.py
@@ -0,0 +1,40 @@
+# 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.
+
+set_get_flavor_extra_specs = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'extra_specs': {
+                'type': 'object',
+                'patternProperties': {
+                    '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['extra_specs']
+    }
+}
+
+set_get_flavor_extra_specs_key = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'patternProperties': {
+            '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
+        }
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/floating_ips.py b/tempest/lib/api_schema/response/compute/v2_1/floating_ips.py
new file mode 100644
index 0000000..0c66590
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/floating_ips.py
@@ -0,0 +1,148 @@
+# 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.
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+common_floating_ip_info = {
+    'type': 'object',
+    'properties': {
+        # NOTE: Now the type of 'id' is integer, but
+        # here allows 'string' also because we will be
+        # able to change it to 'uuid' in the future.
+        'id': {'type': ['integer', 'string']},
+        'pool': {'type': ['string', 'null']},
+        'instance_id': {'type': ['string', 'null']},
+        'ip': parameter_types.ip_address,
+        'fixed_ip': parameter_types.ip_address
+    },
+    'additionalProperties': False,
+    'required': ['id', 'pool', 'instance_id',
+                 'ip', 'fixed_ip'],
+
+}
+list_floating_ips = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'floating_ips': {
+                'type': 'array',
+                'items': common_floating_ip_info
+            },
+        },
+        'additionalProperties': False,
+        'required': ['floating_ips'],
+    }
+}
+
+create_get_floating_ip = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'floating_ip': common_floating_ip_info
+        },
+        'additionalProperties': False,
+        'required': ['floating_ip'],
+    }
+}
+
+list_floating_ip_pools = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'floating_ip_pools': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'name': {'type': 'string'}
+                    },
+                    'additionalProperties': False,
+                    'required': ['name'],
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['floating_ip_pools'],
+    }
+}
+
+add_remove_floating_ip = {
+    'status_code': [202]
+}
+
+create_floating_ips_bulk = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'floating_ips_bulk_create': {
+                'type': 'object',
+                'properties': {
+                    'interface': {'type': ['string', 'null']},
+                    'ip_range': {'type': 'string'},
+                    'pool': {'type': ['string', 'null']},
+                },
+                'additionalProperties': False,
+                'required': ['interface', 'ip_range', 'pool'],
+            }
+        },
+        'additionalProperties': False,
+        'required': ['floating_ips_bulk_create'],
+    }
+}
+
+delete_floating_ips_bulk = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'floating_ips_bulk_delete': {'type': 'string'}
+        },
+        'additionalProperties': False,
+        'required': ['floating_ips_bulk_delete'],
+    }
+}
+
+list_floating_ips_bulk = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'floating_ip_info': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'address': parameter_types.ip_address,
+                        'instance_uuid': {'type': ['string', 'null']},
+                        'interface': {'type': ['string', 'null']},
+                        'pool': {'type': ['string', 'null']},
+                        'project_id': {'type': ['string', 'null']},
+                        'fixed_ip': parameter_types.ip_address
+                    },
+                    'additionalProperties': False,
+                    # NOTE: fixed_ip is introduced after JUNO release,
+                    # So it is not defined as 'required'.
+                    'required': ['address', 'instance_uuid', 'interface',
+                                 'pool', 'project_id'],
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['floating_ip_info'],
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/hosts.py b/tempest/lib/api_schema/response/compute/v2_1/hosts.py
new file mode 100644
index 0000000..ae70ff1
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/hosts.py
@@ -0,0 +1,116 @@
+# 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.
+
+import copy
+
+
+list_hosts = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'hosts': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'host_name': {'type': 'string'},
+                        'service': {'type': 'string'},
+                        'zone': {'type': 'string'}
+                    },
+                    'additionalProperties': False,
+                    'required': ['host_name', 'service', 'zone']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['hosts']
+    }
+}
+
+get_host_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'host': {
+                'type': 'array',
+                'item': {
+                    'type': 'object',
+                    'properties': {
+                        'resource': {
+                            'type': 'object',
+                            'properties': {
+                                'cpu': {'type': 'integer'},
+                                'disk_gb': {'type': 'integer'},
+                                'host': {'type': 'string'},
+                                'memory_mb': {'type': 'integer'},
+                                'project': {'type': 'string'}
+                            },
+                            'additionalProperties': False,
+                            'required': ['cpu', 'disk_gb', 'host',
+                                         'memory_mb', 'project']
+                        }
+                    },
+                    'additionalProperties': False,
+                    'required': ['resource']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['host']
+    }
+}
+
+startup_host = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'host': {'type': 'string'},
+            'power_action': {'enum': ['startup']}
+        },
+        'additionalProperties': False,
+        'required': ['host', 'power_action']
+    }
+}
+
+# The 'power_action' attribute of 'shutdown_host' API is 'shutdown'
+shutdown_host = copy.deepcopy(startup_host)
+
+shutdown_host['response_body']['properties']['power_action'] = {
+    'enum': ['shutdown']
+}
+
+# The 'power_action' attribute of 'reboot_host' API is 'reboot'
+reboot_host = copy.deepcopy(startup_host)
+
+reboot_host['response_body']['properties']['power_action'] = {
+    'enum': ['reboot']
+}
+
+update_host = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'host': {'type': 'string'},
+            'maintenance_mode': {'enum': ['on_maintenance',
+                                          'off_maintenance']},
+            'status': {'enum': ['enabled', 'disabled']}
+        },
+        'additionalProperties': False,
+        'required': ['host', 'maintenance_mode', 'status']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/hypervisors.py b/tempest/lib/api_schema/response/compute/v2_1/hypervisors.py
new file mode 100644
index 0000000..d15b4f6
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/hypervisors.py
@@ -0,0 +1,195 @@
+# 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.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+get_hypervisor_statistics = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'hypervisor_statistics': {
+                'type': 'object',
+                'properties': {
+                    'count': {'type': 'integer'},
+                    'current_workload': {'type': 'integer'},
+                    'disk_available_least': {'type': ['integer', 'null']},
+                    'free_disk_gb': {'type': 'integer'},
+                    'free_ram_mb': {'type': 'integer'},
+                    'local_gb': {'type': 'integer'},
+                    'local_gb_used': {'type': 'integer'},
+                    'memory_mb': {'type': 'integer'},
+                    'memory_mb_used': {'type': 'integer'},
+                    'running_vms': {'type': 'integer'},
+                    'vcpus': {'type': 'integer'},
+                    'vcpus_used': {'type': 'integer'}
+                },
+                'additionalProperties': False,
+                'required': ['count', 'current_workload',
+                             'disk_available_least', 'free_disk_gb',
+                             'free_ram_mb', 'local_gb', 'local_gb_used',
+                             'memory_mb', 'memory_mb_used', 'running_vms',
+                             'vcpus', 'vcpus_used']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['hypervisor_statistics']
+    }
+}
+
+
+hypervisor_detail = {
+    'type': 'object',
+    'properties': {
+        'status': {'type': 'string'},
+        'state': {'type': 'string'},
+        'cpu_info': {'type': 'string'},
+        'current_workload': {'type': 'integer'},
+        'disk_available_least': {'type': ['integer', 'null']},
+        'host_ip': parameter_types.ip_address,
+        'free_disk_gb': {'type': 'integer'},
+        'free_ram_mb': {'type': 'integer'},
+        'hypervisor_hostname': {'type': 'string'},
+        'hypervisor_type': {'type': 'string'},
+        'hypervisor_version': {'type': 'integer'},
+        'id': {'type': ['integer', 'string']},
+        'local_gb': {'type': 'integer'},
+        'local_gb_used': {'type': 'integer'},
+        'memory_mb': {'type': 'integer'},
+        'memory_mb_used': {'type': 'integer'},
+        'running_vms': {'type': 'integer'},
+        'service': {
+            'type': 'object',
+            'properties': {
+                'host': {'type': 'string'},
+                'id': {'type': ['integer', 'string']},
+                'disabled_reason': {'type': ['string', 'null']}
+            },
+            'additionalProperties': False,
+            'required': ['host', 'id']
+        },
+        'vcpus': {'type': 'integer'},
+        'vcpus_used': {'type': 'integer'}
+    },
+    'additionalProperties': False,
+    # NOTE: When loading os-hypervisor-status extension,
+    # a response contains status and state. So these params
+    # should not be required.
+    'required': ['cpu_info', 'current_workload',
+                 'disk_available_least', 'host_ip',
+                 'free_disk_gb', 'free_ram_mb',
+                 'hypervisor_hostname', 'hypervisor_type',
+                 'hypervisor_version', 'id', 'local_gb',
+                 'local_gb_used', 'memory_mb', 'memory_mb_used',
+                 'running_vms', 'service', 'vcpus', 'vcpus_used']
+}
+
+list_hypervisors_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'hypervisors': {
+                'type': 'array',
+                'items': hypervisor_detail
+            }
+        },
+        'additionalProperties': False,
+        'required': ['hypervisors']
+    }
+}
+
+get_hypervisor = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'hypervisor': hypervisor_detail
+        },
+        'additionalProperties': False,
+        'required': ['hypervisor']
+    }
+}
+
+list_search_hypervisors = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'hypervisors': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'status': {'type': 'string'},
+                        'state': {'type': 'string'},
+                        'id': {'type': ['integer', 'string']},
+                        'hypervisor_hostname': {'type': 'string'}
+                    },
+                    'additionalProperties': False,
+                    # NOTE: When loading os-hypervisor-status extension,
+                    # a response contains status and state. So these params
+                    # should not be required.
+                    'required': ['id', 'hypervisor_hostname']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['hypervisors']
+    }
+}
+
+get_hypervisor_uptime = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'hypervisor': {
+                'type': 'object',
+                'properties': {
+                    'status': {'type': 'string'},
+                    'state': {'type': 'string'},
+                    'id': {'type': ['integer', 'string']},
+                    'hypervisor_hostname': {'type': 'string'},
+                    'uptime': {'type': 'string'}
+                },
+                'additionalProperties': False,
+                # NOTE: When loading os-hypervisor-status extension,
+                # a response contains status and state. So these params
+                # should not be required.
+                'required': ['id', 'hypervisor_hostname', 'uptime']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['hypervisor']
+    }
+}
+
+get_hypervisors_servers = copy.deepcopy(list_search_hypervisors)
+get_hypervisors_servers['response_body']['properties']['hypervisors']['items'][
+    'properties']['servers'] = {
+        'type': 'array',
+        'items': {
+            'type': 'object',
+            'properties': {
+                'uuid': {'type': 'string'},
+                'name': {'type': 'string'}
+            },
+            'additionalProperties': False,
+        }
+    }
+# In V2 API, if there is no servers (VM) on the Hypervisor host then 'servers'
+# attribute will not be present in response body So it is not 'required'.
diff --git a/tempest/lib/api_schema/response/compute/v2_1/images.py b/tempest/lib/api_schema/response/compute/v2_1/images.py
new file mode 100644
index 0000000..f65b9d8
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/images.py
@@ -0,0 +1,156 @@
+# 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.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+image_links = copy.deepcopy(parameter_types.links)
+image_links['items']['properties'].update({'type': {'type': 'string'}})
+
+image_status_enums = ['ACTIVE', 'SAVING', 'DELETED', 'ERROR', 'UNKNOWN']
+
+common_image_schema = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'status': {'enum': image_status_enums},
+        'updated': {'type': 'string'},
+        'links': image_links,
+        'name': {'type': ['string', 'null']},
+        'created': {'type': 'string'},
+        'minDisk': {'type': 'integer'},
+        'minRam': {'type': 'integer'},
+        'progress': {'type': 'integer'},
+        'metadata': {'type': 'object'},
+        'server': {
+            'type': 'object',
+            'properties': {
+                'id': {'type': 'string'},
+                'links': parameter_types.links
+            },
+            'additionalProperties': False,
+            'required': ['id', 'links']
+        },
+        'OS-EXT-IMG-SIZE:size': {'type': ['integer', 'null']},
+        'OS-DCF:diskConfig': {'type': 'string'}
+    },
+    'additionalProperties': False,
+    # 'server' attributes only comes in response body if image is
+    # associated with any server. 'OS-EXT-IMG-SIZE:size' & 'OS-DCF:diskConfig'
+    # are API extension,  So those are not defined as 'required'.
+    'required': ['id', 'status', 'updated', 'links', 'name',
+                 'created', 'minDisk', 'minRam', 'progress',
+                 'metadata']
+}
+
+get_image = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'image': common_image_schema
+        },
+        'additionalProperties': False,
+        'required': ['image']
+    }
+}
+
+list_images = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'images': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'id': {'type': 'string'},
+                        'links': image_links,
+                        'name': {'type': ['string', 'null']}
+                    },
+                    'additionalProperties': False,
+                    'required': ['id', 'links', 'name']
+                }
+            },
+            'images_links': parameter_types.links
+        },
+        'additionalProperties': False,
+        # NOTE(gmann): images_links attribute is not necessary to be
+        # present always So it is not 'required'.
+        'required': ['images']
+    }
+}
+
+create_image = {
+    'status_code': [202],
+    'response_header': {
+        'type': 'object',
+        'properties': parameter_types.response_header
+    }
+}
+create_image['response_header']['properties'].update(
+    {'location': {
+        'type': 'string',
+        'format': 'uri'}
+     }
+)
+create_image['response_header']['required'] = ['location']
+
+delete = {
+    'status_code': [204]
+}
+
+image_metadata = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'metadata': {'type': 'object'}
+        },
+        'additionalProperties': False,
+        'required': ['metadata']
+    }
+}
+
+image_meta_item = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'meta': {'type': 'object'}
+        },
+        'additionalProperties': False,
+        'required': ['meta']
+    }
+}
+
+list_images_details = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'images': {
+                'type': 'array',
+                'items': common_image_schema
+            },
+            'images_links': parameter_types.links
+        },
+        'additionalProperties': False,
+        # NOTE(gmann): images_links attribute is not necessary to be
+        # present always So it is not 'required'.
+        'required': ['images']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/instance_usage_audit_logs.py b/tempest/lib/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
new file mode 100644
index 0000000..15224c5
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
@@ -0,0 +1,78 @@
+# 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.
+
+common_instance_usage_audit_log = {
+    'type': 'object',
+    'properties': {
+        'hosts_not_run': {
+            'type': 'array',
+            'items': {'type': 'string'}
+        },
+        'log': {
+            'type': 'object',
+            'patternProperties': {
+                # NOTE: Here is a host name.
+                '^.+$': {
+                    'type': 'object',
+                    'properties': {
+                        'state': {'type': 'string'},
+                        'instances': {'type': 'integer'},
+                        'errors': {'type': 'integer'},
+                        'message': {'type': 'string'}
+                    },
+                    'additionalProperties': False,
+                    'required': ['state', 'instances', 'errors', 'message']
+                }
+            }
+        },
+        'num_hosts': {'type': 'integer'},
+        'num_hosts_done': {'type': 'integer'},
+        'num_hosts_not_run': {'type': 'integer'},
+        'num_hosts_running': {'type': 'integer'},
+        'overall_status': {'type': 'string'},
+        'period_beginning': {'type': 'string'},
+        'period_ending': {'type': 'string'},
+        'total_errors': {'type': 'integer'},
+        'total_instances': {'type': 'integer'}
+    },
+    'additionalProperties': False,
+    'required': ['hosts_not_run', 'log', 'num_hosts', 'num_hosts_done',
+                 'num_hosts_not_run', 'num_hosts_running', 'overall_status',
+                 'period_beginning', 'period_ending', 'total_errors',
+                 'total_instances']
+}
+
+get_instance_usage_audit_log = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'instance_usage_audit_log': common_instance_usage_audit_log
+        },
+        'additionalProperties': False,
+        'required': ['instance_usage_audit_log']
+    }
+}
+
+list_instance_usage_audit_log = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'instance_usage_audit_logs': common_instance_usage_audit_log
+        },
+        'additionalProperties': False,
+        'required': ['instance_usage_audit_logs']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/interfaces.py b/tempest/lib/api_schema/response/compute/v2_1/interfaces.py
new file mode 100644
index 0000000..9984750
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/interfaces.py
@@ -0,0 +1,73 @@
+# 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.
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+interface_common_info = {
+    'type': 'object',
+    'properties': {
+        'port_state': {'type': 'string'},
+        'fixed_ips': {
+            'type': 'array',
+            'items': {
+                'type': 'object',
+                'properties': {
+                    'subnet_id': {
+                        'type': 'string',
+                        'format': 'uuid'
+                    },
+                    'ip_address': parameter_types.ip_address
+                },
+                'additionalProperties': False,
+                'required': ['subnet_id', 'ip_address']
+            }
+        },
+        'port_id': {'type': 'string', 'format': 'uuid'},
+        'net_id': {'type': 'string', 'format': 'uuid'},
+        'mac_addr': parameter_types.mac_address
+    },
+    'additionalProperties': False,
+    'required': ['port_state', 'fixed_ips', 'port_id', 'net_id', 'mac_addr']
+}
+
+get_create_interfaces = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'interfaceAttachment': interface_common_info
+        },
+        'additionalProperties': False,
+        'required': ['interfaceAttachment']
+    }
+}
+
+list_interfaces = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'interfaceAttachments': {
+                'type': 'array',
+                'items': interface_common_info
+            }
+        },
+        'additionalProperties': False,
+        'required': ['interfaceAttachments']
+    }
+}
+
+delete_interface = {
+    'status_code': [202]
+}
diff --git a/tempest/api_schema/response/compute/v2_1/keypairs.py b/tempest/lib/api_schema/response/compute/v2_1/keypairs.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2_1/keypairs.py
rename to tempest/lib/api_schema/response/compute/v2_1/keypairs.py
diff --git a/tempest/lib/api_schema/response/compute/v2_1/limits.py b/tempest/lib/api_schema/response/compute/v2_1/limits.py
new file mode 100644
index 0000000..81f175f
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/limits.py
@@ -0,0 +1,106 @@
+# 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_limit = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'limits': {
+                'type': 'object',
+                'properties': {
+                    'absolute': {
+                        'type': 'object',
+                        'properties': {
+                            'maxTotalRAMSize': {'type': 'integer'},
+                            'totalCoresUsed': {'type': 'integer'},
+                            'maxTotalInstances': {'type': 'integer'},
+                            'maxTotalFloatingIps': {'type': 'integer'},
+                            'totalSecurityGroupsUsed': {'type': 'integer'},
+                            'maxTotalCores': {'type': 'integer'},
+                            'totalFloatingIpsUsed': {'type': 'integer'},
+                            'maxSecurityGroups': {'type': 'integer'},
+                            'maxServerMeta': {'type': 'integer'},
+                            'maxPersonality': {'type': 'integer'},
+                            'maxImageMeta': {'type': 'integer'},
+                            'maxPersonalitySize': {'type': 'integer'},
+                            'maxSecurityGroupRules': {'type': 'integer'},
+                            'maxTotalKeypairs': {'type': 'integer'},
+                            'totalRAMUsed': {'type': 'integer'},
+                            'totalInstancesUsed': {'type': 'integer'},
+                            'maxServerGroupMembers': {'type': 'integer'},
+                            'maxServerGroups': {'type': 'integer'},
+                            'totalServerGroupsUsed': {'type': 'integer'}
+                        },
+                        'additionalProperties': False,
+                        # NOTE(gmann): maxServerGroupMembers,  maxServerGroups
+                        # and totalServerGroupsUsed are API extension,
+                        # and some environments return a response without these
+                        # attributes.So they are not 'required'.
+                        'required': ['maxImageMeta',
+                                     'maxPersonality',
+                                     'maxPersonalitySize',
+                                     'maxSecurityGroupRules',
+                                     'maxSecurityGroups',
+                                     'maxServerMeta',
+                                     'maxTotalCores',
+                                     'maxTotalFloatingIps',
+                                     'maxTotalInstances',
+                                     'maxTotalKeypairs',
+                                     'maxTotalRAMSize',
+                                     'totalCoresUsed',
+                                     'totalFloatingIpsUsed',
+                                     'totalInstancesUsed',
+                                     'totalRAMUsed',
+                                     'totalSecurityGroupsUsed']
+                    },
+                    'rate': {
+                        'type': 'array',
+                        'items': {
+                            'type': 'object',
+                            'properties': {
+                                'limit': {
+                                    'type': 'array',
+                                    'items': {
+                                        'type': 'object',
+                                        'properties': {
+                                            'next-available':
+                                                {'type': 'string'},
+                                            'remaining':
+                                                {'type': 'integer'},
+                                            'unit':
+                                                {'type': 'string'},
+                                            'value':
+                                                {'type': 'integer'},
+                                            'verb':
+                                                {'type': 'string'}
+                                        },
+                                        'additionalProperties': False,
+                                    }
+                                },
+                                'regex': {'type': 'string'},
+                                'uri': {'type': 'string'}
+                            },
+                            'additionalProperties': False,
+                        }
+                    }
+                },
+                'additionalProperties': False,
+                'required': ['absolute', 'rate']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['limits']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/migrations.py b/tempest/lib/api_schema/response/compute/v2_1/migrations.py
new file mode 100644
index 0000000..b7d66ea
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/migrations.py
@@ -0,0 +1,51 @@
+# 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.
+
+list_migrations = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'migrations': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'id': {'type': 'integer'},
+                        'status': {'type': ['string', 'null']},
+                        'instance_uuid': {'type': ['string', 'null']},
+                        'source_node': {'type': ['string', 'null']},
+                        'source_compute': {'type': ['string', 'null']},
+                        'dest_node': {'type': ['string', 'null']},
+                        'dest_compute': {'type': ['string', 'null']},
+                        'dest_host': {'type': ['string', 'null']},
+                        'old_instance_type_id': {'type': ['integer', 'null']},
+                        'new_instance_type_id': {'type': ['integer', 'null']},
+                        'created_at': {'type': 'string'},
+                        'updated_at': {'type': ['string', 'null']}
+                    },
+                    'additionalProperties': False,
+                    'required': [
+                        'id', 'status', 'instance_uuid', 'source_node',
+                        'source_compute', 'dest_node', 'dest_compute',
+                        'dest_host', 'old_instance_type_id',
+                        'new_instance_type_id', 'created_at', 'updated_at'
+                    ]
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['migrations']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py b/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
new file mode 100644
index 0000000..3cc5ca4
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
@@ -0,0 +1,107 @@
+# 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.
+
+links = {
+    'type': 'array',
+    'items': {
+        'type': 'object',
+        'properties': {
+            'href': {
+                'type': 'string',
+                'format': 'uri'
+            },
+            'rel': {'type': 'string'}
+        },
+        'additionalProperties': False,
+        'required': ['href', 'rel']
+    }
+}
+
+mac_address = {
+    'type': 'string',
+    'pattern': '(?:[a-f0-9]{2}:){5}[a-f0-9]{2}'
+}
+
+ip_address = {
+    'oneOf': [
+        {
+            'type': 'string',
+            'oneOf': [
+                {'format': 'ipv4'},
+                {'format': 'ipv6'}
+            ]
+        },
+        {'type': 'null'}
+    ]
+}
+
+access_ip_v4 = {
+    'type': 'string',
+    'oneOf': [{'format': 'ipv4'}, {'enum': ['']}]
+}
+
+access_ip_v6 = {
+    'type': 'string',
+    'oneOf': [{'format': 'ipv6'}, {'enum': ['']}]
+}
+
+addresses = {
+    'type': 'object',
+    'patternProperties': {
+        # NOTE: Here is for 'private' or something.
+        '^[a-zA-Z0-9-_.]+$': {
+            'type': 'array',
+            'items': {
+                'type': 'object',
+                'properties': {
+                    'version': {'type': 'integer'},
+                    'addr': {
+                        'type': 'string',
+                        'oneOf': [
+                            {'format': 'ipv4'},
+                            {'format': 'ipv6'}
+                        ]
+                    }
+                },
+                'additionalProperties': False,
+                'required': ['version', 'addr']
+            }
+        }
+    }
+}
+
+response_header = {
+    'connection': {'type': 'string'},
+    'content-length': {'type': 'string'},
+    'content-type': {'type': 'string'},
+    'status': {'type': 'string'},
+    'x-compute-request-id': {'type': 'string'},
+    'vary': {'type': 'string'},
+    'x-openstack-nova-api-version': {'type': 'string'},
+    'date': {
+        'type': 'string',
+        'format': 'data-time'
+    }
+}
+
+power_state = {
+    'type': 'integer',
+    # 0: NOSTATE
+    # 1: RUNNING
+    # 3: PAUSED
+    # 4: SHUTDOWN
+    # 6: CRASHED
+    # 7: SUSPENDED
+    'enum': [0, 1, 3, 4, 6, 7]
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/quota_classes.py b/tempest/lib/api_schema/response/compute/v2_1/quota_classes.py
new file mode 100644
index 0000000..03d7f12
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/quota_classes.py
@@ -0,0 +1,31 @@
+# Copyright 2014 IBM 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.lib.api_schema.response.compute.v2_1 import quotas
+
+# NOTE(mriedem): os-quota-class-sets responses are the same as os-quota-sets
+# except for the key in the response body is quota_class_set instead of
+# quota_set, so update this copy of the schema from os-quota-sets.
+get_quota_class_set = copy.deepcopy(quotas.get_quota_set)
+get_quota_class_set['response_body']['properties']['quota_class_set'] = (
+    get_quota_class_set['response_body']['properties'].pop('quota_set'))
+get_quota_class_set['response_body']['required'] = ['quota_class_set']
+
+update_quota_class_set = copy.deepcopy(quotas.update_quota_set)
+update_quota_class_set['response_body']['properties']['quota_class_set'] = (
+    update_quota_class_set['response_body']['properties'].pop('quota_set'))
+update_quota_class_set['response_body']['required'] = ['quota_class_set']
diff --git a/tempest/lib/api_schema/response/compute/v2_1/quotas.py b/tempest/lib/api_schema/response/compute/v2_1/quotas.py
new file mode 100644
index 0000000..7953983
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/quotas.py
@@ -0,0 +1,65 @@
+# 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.
+
+import copy
+
+update_quota_set = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'quota_set': {
+                'type': 'object',
+                'properties': {
+                    'instances': {'type': 'integer'},
+                    'cores': {'type': 'integer'},
+                    'ram': {'type': 'integer'},
+                    'floating_ips': {'type': 'integer'},
+                    'fixed_ips': {'type': 'integer'},
+                    'metadata_items': {'type': 'integer'},
+                    'key_pairs': {'type': 'integer'},
+                    'security_groups': {'type': 'integer'},
+                    'security_group_rules': {'type': 'integer'},
+                    'server_group_members': {'type': 'integer'},
+                    'server_groups': {'type': 'integer'},
+                    'injected_files': {'type': 'integer'},
+                    'injected_file_content_bytes': {'type': 'integer'},
+                    'injected_file_path_bytes': {'type': 'integer'}
+                },
+                'additionalProperties': False,
+                # NOTE: server_group_members and server_groups are represented
+                # when enabling quota_server_group extension. So they should
+                # not be required.
+                'required': ['instances', 'cores', 'ram',
+                             'floating_ips', 'fixed_ips',
+                             'metadata_items', 'key_pairs',
+                             'security_groups', 'security_group_rules',
+                             'injected_files', 'injected_file_content_bytes',
+                             'injected_file_path_bytes']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['quota_set']
+    }
+}
+
+get_quota_set = copy.deepcopy(update_quota_set)
+get_quota_set['response_body']['properties']['quota_set']['properties'][
+    'id'] = {'type': 'string'}
+get_quota_set['response_body']['properties']['quota_set']['required'].extend([
+    'id'])
+
+delete_quota = {
+    'status_code': [202]
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/security_group_default_rule.py b/tempest/lib/api_schema/response/compute/v2_1/security_group_default_rule.py
new file mode 100644
index 0000000..2ec2826
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/security_group_default_rule.py
@@ -0,0 +1,65 @@
+# 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.
+
+common_security_group_default_rule_info = {
+    'type': 'object',
+    'properties': {
+        'from_port': {'type': 'integer'},
+        'id': {'type': 'integer'},
+        'ip_protocol': {'type': 'string'},
+        'ip_range': {
+            'type': 'object',
+            'properties': {
+                'cidr': {'type': 'string'}
+            },
+            'additionalProperties': False,
+            'required': ['cidr'],
+        },
+        'to_port': {'type': 'integer'},
+    },
+    'additionalProperties': False,
+    'required': ['from_port', 'id', 'ip_protocol', 'ip_range', 'to_port'],
+}
+
+create_get_security_group_default_rule = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'security_group_default_rule':
+                common_security_group_default_rule_info
+        },
+        'additionalProperties': False,
+        'required': ['security_group_default_rule']
+    }
+}
+
+delete_security_group_default_rule = {
+    'status_code': [204]
+}
+
+list_security_group_default_rules = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'security_group_default_rules': {
+                'type': 'array',
+                'items': common_security_group_default_rule_info
+            }
+        },
+        'additionalProperties': False,
+        'required': ['security_group_default_rules']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/security_groups.py b/tempest/lib/api_schema/response/compute/v2_1/security_groups.py
new file mode 100644
index 0000000..5ed5a5c
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/security_groups.py
@@ -0,0 +1,113 @@
+# 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.
+
+common_security_group_rule = {
+    'from_port': {'type': ['integer', 'null']},
+    'to_port': {'type': ['integer', 'null']},
+    'group': {
+        'type': 'object',
+        'properties': {
+            'tenant_id': {'type': 'string'},
+            'name': {'type': 'string'}
+        },
+        'additionalProperties': False,
+    },
+    'ip_protocol': {'type': ['string', 'null']},
+    # 'parent_group_id' can be UUID so defining it as 'string' also.
+    'parent_group_id': {'type': ['string', 'integer', 'null']},
+    'ip_range': {
+        'type': 'object',
+        'properties': {
+            'cidr': {'type': 'string'}
+        },
+        'additionalProperties': False,
+        # When optional argument is provided in request body
+        # like 'group_id' then, attribute 'cidr' does not
+        # comes in response body. So it is not 'required'.
+    },
+    'id': {'type': ['string', 'integer']}
+}
+
+common_security_group = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': ['integer', 'string']},
+        'name': {'type': 'string'},
+        'tenant_id': {'type': 'string'},
+        'rules': {
+            'type': 'array',
+            'items': {
+                'type': ['object', 'null'],
+                'properties': common_security_group_rule,
+                'additionalProperties': False,
+            }
+        },
+        'description': {'type': 'string'},
+    },
+    'additionalProperties': False,
+    'required': ['id', 'name', 'tenant_id', 'rules', 'description'],
+}
+
+list_security_groups = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'security_groups': {
+                'type': 'array',
+                'items': common_security_group
+            }
+        },
+        'additionalProperties': False,
+        'required': ['security_groups']
+    }
+}
+
+get_security_group = create_security_group = update_security_group = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'security_group': common_security_group
+        },
+        'additionalProperties': False,
+        'required': ['security_group']
+    }
+}
+
+delete_security_group = {
+    'status_code': [202]
+}
+
+create_security_group_rule = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'security_group_rule': {
+                'type': 'object',
+                'properties': common_security_group_rule,
+                'additionalProperties': False,
+                'required': ['from_port', 'to_port', 'group', 'ip_protocol',
+                             'parent_group_id', 'id', 'ip_range']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['security_group_rule']
+    }
+}
+
+delete_security_group_rule = {
+    'status_code': [202]
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/servers.py b/tempest/lib/api_schema/response/compute/v2_1/servers.py
new file mode 100644
index 0000000..1264416
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -0,0 +1,566 @@
+# 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.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+create_server = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'server': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string'},
+                    'security_groups': {'type': 'array'},
+                    'links': parameter_types.links,
+                    'OS-DCF:diskConfig': {'type': 'string'}
+                },
+                'additionalProperties': False,
+                # NOTE: OS-DCF:diskConfig & security_groups are API extension,
+                # and some environments return a response without these
+                # attributes.So they are not 'required'.
+                'required': ['id', 'links']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['server']
+    }
+}
+
+create_server_with_admin_pass = copy.deepcopy(create_server)
+create_server_with_admin_pass['response_body']['properties']['server'][
+    'properties'].update({'adminPass': {'type': 'string'}})
+create_server_with_admin_pass['response_body']['properties']['server'][
+    'required'].append('adminPass')
+
+list_servers = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'servers': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'id': {'type': 'string'},
+                        'links': parameter_types.links,
+                        'name': {'type': 'string'}
+                    },
+                    'additionalProperties': False,
+                    'required': ['id', 'links', 'name']
+                }
+            },
+            'servers_links': parameter_types.links
+        },
+        'additionalProperties': False,
+        # NOTE(gmann): servers_links attribute is not necessary to be
+        # present always So it is not 'required'.
+        'required': ['servers']
+    }
+}
+
+delete_server = {
+    'status_code': [204],
+}
+
+common_show_server = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'name': {'type': 'string'},
+        'status': {'type': 'string'},
+        'image': {'oneOf': [
+            {'type': 'object',
+                'properties': {
+                    'id': {'type': 'string'},
+                    'links': parameter_types.links
+                },
+                'additionalProperties': False,
+                'required': ['id', 'links']},
+            {'type': ['string', 'null']}
+        ]},
+        'flavor': {
+            'type': 'object',
+            'properties': {
+                'id': {'type': 'string'},
+                'links': parameter_types.links
+            },
+            'additionalProperties': False,
+            'required': ['id', 'links']
+        },
+        'fault': {
+            'type': 'object',
+            'properties': {
+                'code': {'type': 'integer'},
+                'created': {'type': 'string'},
+                'message': {'type': 'string'},
+                'details': {'type': 'string'},
+            },
+            'additionalProperties': False,
+            # NOTE(gmann): 'details' is not necessary to be present
+            #  in the 'fault'. So it is not defined as 'required'.
+            'required': ['code', 'created', 'message']
+        },
+        'user_id': {'type': 'string'},
+        'tenant_id': {'type': 'string'},
+        'created': {'type': 'string'},
+        'updated': {'type': 'string'},
+        'progress': {'type': 'integer'},
+        'metadata': {'type': 'object'},
+        'links': parameter_types.links,
+        'addresses': parameter_types.addresses,
+        'hostId': {'type': 'string'},
+        'OS-DCF:diskConfig': {'type': 'string'},
+        'accessIPv4': parameter_types.access_ip_v4,
+        'accessIPv6': parameter_types.access_ip_v6
+    },
+    'additionalProperties': False,
+    # NOTE(GMann): 'progress' attribute is present in the response
+    # only when server's status is one of the progress statuses
+    # ("ACTIVE","BUILD", "REBUILD", "RESIZE","VERIFY_RESIZE")
+    # 'fault' attribute is present in the response
+    # only when server's status is one of the  "ERROR", "DELETED".
+    # OS-DCF:diskConfig and accessIPv4/v6 are API
+    # extensions, and some environments return a response
+    # without these attributes.So these are not defined as 'required'.
+    'required': ['id', 'name', 'status', 'image', 'flavor',
+                 'user_id', 'tenant_id', 'created', 'updated',
+                 'metadata', 'links', 'addresses', 'hostId']
+}
+
+update_server = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'server': common_show_server
+        },
+        'additionalProperties': False,
+        'required': ['server']
+    }
+}
+
+server_detail = copy.deepcopy(common_show_server)
+server_detail['properties'].update({
+    'key_name': {'type': ['string', 'null']},
+    'security_groups': {'type': 'array'},
+
+    # NOTE: Non-admin users also can see "OS-SRV-USG" and "OS-EXT-AZ"
+    # attributes.
+    'OS-SRV-USG:launched_at': {'type': ['string', 'null']},
+    'OS-SRV-USG:terminated_at': {'type': ['string', 'null']},
+    'OS-EXT-AZ:availability_zone': {'type': 'string'},
+
+    # NOTE: Admin users only can see "OS-EXT-STS" and "OS-EXT-SRV-ATTR"
+    # attributes.
+    'OS-EXT-STS:task_state': {'type': ['string', 'null']},
+    'OS-EXT-STS:vm_state': {'type': 'string'},
+    'OS-EXT-STS:power_state': parameter_types.power_state,
+    'OS-EXT-SRV-ATTR:host': {'type': ['string', 'null']},
+    'OS-EXT-SRV-ATTR:instance_name': {'type': 'string'},
+    'OS-EXT-SRV-ATTR:hypervisor_hostname': {'type': ['string', 'null']},
+    'os-extended-volumes:volumes_attached': {
+        'type': 'array',
+        'items': {
+            'type': 'object',
+            'properties': {
+                'id': {'type': 'string'}
+            },
+            'additionalProperties': False,
+        },
+    },
+    'config_drive': {'type': 'string'}
+})
+server_detail['properties']['addresses']['patternProperties'][
+    '^[a-zA-Z0-9-_.]+$']['items']['properties'].update({
+        'OS-EXT-IPS:type': {'type': 'string'},
+        'OS-EXT-IPS-MAC:mac_addr': parameter_types.mac_address})
+# NOTE(gmann): Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr
+# attributes in server address. Those are API extension,
+# and some environments return a response without
+# these attributes. So they are not 'required'.
+
+get_server = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'server': server_detail
+        },
+        'additionalProperties': False,
+        'required': ['server']
+    }
+}
+
+list_servers_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'servers': {
+                'type': 'array',
+                'items': server_detail
+            },
+            'servers_links': parameter_types.links
+        },
+        'additionalProperties': False,
+        # NOTE(gmann): servers_links attribute is not necessary to be
+        # present always So it is not 'required'.
+        'required': ['servers']
+    }
+}
+
+rebuild_server = copy.deepcopy(update_server)
+rebuild_server['status_code'] = [202]
+
+rebuild_server_with_admin_pass = copy.deepcopy(rebuild_server)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'properties'].update({'adminPass': {'type': 'string'}})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'required'].append('adminPass')
+
+rescue_server = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'additionalProperties': False,
+    }
+}
+
+rescue_server_with_admin_pass = copy.deepcopy(rescue_server)
+rescue_server_with_admin_pass['response_body'].update(
+    {'properties': {'adminPass': {'type': 'string'}}})
+rescue_server_with_admin_pass['response_body'].update(
+    {'required': ['adminPass']})
+
+
+list_virtual_interfaces = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'virtual_interfaces': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'id': {'type': 'string'},
+                        'mac_address': parameter_types.mac_address,
+                        'OS-EXT-VIF-NET:net_id': {'type': 'string'}
+                    },
+                    'additionalProperties': False,
+                    # 'OS-EXT-VIF-NET:net_id' is API extension So it is
+                    # not defined as 'required'
+                    'required': ['id', 'mac_address']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['virtual_interfaces']
+    }
+}
+
+common_attach_volume_info = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'device': {'type': ['string', 'null']},
+        'volumeId': {'type': 'string'},
+        'serverId': {'type': ['integer', 'string']}
+    },
+    'additionalProperties': False,
+    # 'device' is optional in response.
+    'required': ['id', 'volumeId', 'serverId']
+}
+
+attach_volume = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'volumeAttachment': common_attach_volume_info
+        },
+        'additionalProperties': False,
+        'required': ['volumeAttachment']
+    }
+}
+
+detach_volume = {
+    'status_code': [202]
+}
+
+show_volume_attachment = copy.deepcopy(attach_volume)
+show_volume_attachment['response_body']['properties'][
+    'volumeAttachment']['properties'].update({'serverId': {'type': 'string'}})
+
+list_volume_attachments = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'volumeAttachments': {
+                'type': 'array',
+                'items': common_attach_volume_info
+            }
+        },
+        'additionalProperties': False,
+        'required': ['volumeAttachments']
+    }
+}
+list_volume_attachments['response_body']['properties'][
+    'volumeAttachments']['items']['properties'].update(
+    {'serverId': {'type': 'string'}})
+
+list_addresses_by_network = {
+    'status_code': [200],
+    'response_body': parameter_types.addresses
+}
+
+list_addresses = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'addresses': parameter_types.addresses
+        },
+        'additionalProperties': False,
+        'required': ['addresses']
+    }
+}
+
+common_server_group = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'name': {'type': 'string'},
+        'policies': {
+            'type': 'array',
+            'items': {'type': 'string'}
+        },
+        # 'members' attribute contains the array of instance's UUID of
+        # instances present in server group
+        'members': {
+            'type': 'array',
+            'items': {'type': 'string'}
+        },
+        'metadata': {'type': 'object'}
+    },
+    'additionalProperties': False,
+    'required': ['id', 'name', 'policies', 'members', 'metadata']
+}
+
+create_show_server_group = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'server_group': common_server_group
+        },
+        'additionalProperties': False,
+        'required': ['server_group']
+    }
+}
+
+delete_server_group = {
+    'status_code': [204]
+}
+
+list_server_groups = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'server_groups': {
+                'type': 'array',
+                'items': common_server_group
+            }
+        },
+        'additionalProperties': False,
+        'required': ['server_groups']
+    }
+}
+
+instance_actions = {
+    'type': 'object',
+    'properties': {
+        'action': {'type': 'string'},
+        'request_id': {'type': 'string'},
+        'user_id': {'type': 'string'},
+        'project_id': {'type': 'string'},
+        'start_time': {'type': 'string'},
+        'message': {'type': ['string', 'null']},
+        'instance_uuid': {'type': 'string'}
+    },
+    'additionalProperties': False,
+    'required': ['action', 'request_id', 'user_id', 'project_id',
+                 'start_time', 'message', 'instance_uuid']
+}
+
+instance_action_events = {
+    'type': 'array',
+    'items': {
+        'type': 'object',
+        'properties': {
+            'event': {'type': 'string'},
+            'start_time': {'type': 'string'},
+            'finish_time': {'type': 'string'},
+            'result': {'type': 'string'},
+            'traceback': {'type': ['string', 'null']}
+        },
+        'additionalProperties': False,
+        'required': ['event', 'start_time', 'finish_time', 'result',
+                     'traceback']
+    }
+}
+
+list_instance_actions = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'instanceActions': {
+                'type': 'array',
+                'items': instance_actions
+            }
+        },
+        'additionalProperties': False,
+        'required': ['instanceActions']
+    }
+}
+
+instance_actions_with_events = copy.deepcopy(instance_actions)
+instance_actions_with_events['properties'].update({
+    'events': instance_action_events})
+# 'events' does not come in response body always so it is not
+# defined as 'required'
+
+show_instance_action = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'instanceAction': instance_actions_with_events
+        },
+        'additionalProperties': False,
+        'required': ['instanceAction']
+    }
+}
+
+show_password = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'password': {'type': 'string'}
+        },
+        'additionalProperties': False,
+        'required': ['password']
+    }
+}
+
+get_vnc_console = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'console': {
+                'type': 'object',
+                'properties': {
+                    'type': {'type': 'string'},
+                    'url': {
+                        'type': 'string',
+                        'format': 'uri'
+                    }
+                },
+                'additionalProperties': False,
+                'required': ['type', 'url']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['console']
+    }
+}
+
+get_console_output = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'output': {'type': 'string'}
+        },
+        'additionalProperties': False,
+        'required': ['output']
+    }
+}
+
+set_server_metadata = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'metadata': {
+                'type': 'object',
+                'patternProperties': {
+                    '^.+$': {'type': 'string'}
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['metadata']
+    }
+}
+
+list_server_metadata = copy.deepcopy(set_server_metadata)
+
+update_server_metadata = copy.deepcopy(set_server_metadata)
+
+delete_server_metadata_item = {
+    'status_code': [204]
+}
+
+set_show_server_metadata_item = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'meta': {
+                'type': 'object',
+                'patternProperties': {
+                    '^.+$': {'type': 'string'}
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['meta']
+    }
+}
+
+server_actions_common_schema = {
+    'status_code': [202]
+}
+
+server_actions_delete_password = {
+    'status_code': [204]
+}
+
+server_actions_confirm_resize = copy.deepcopy(
+    server_actions_delete_password)
+
+update_attached_volume = {
+    'status_code': [202]
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/services.py b/tempest/lib/api_schema/response/compute/v2_1/services.py
new file mode 100644
index 0000000..ddef7b2
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/services.py
@@ -0,0 +1,65 @@
+# 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.
+
+list_services = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'services': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'id': {'type': ['integer', 'string'],
+                               'pattern': '^[a-zA-Z!]*@[0-9]+$'},
+                        'zone': {'type': 'string'},
+                        'host': {'type': 'string'},
+                        'state': {'type': 'string'},
+                        'binary': {'type': 'string'},
+                        'status': {'type': 'string'},
+                        'updated_at': {'type': ['string', 'null']},
+                        'disabled_reason': {'type': ['string', 'null']}
+                    },
+                    'additionalProperties': False,
+                    'required': ['id', 'zone', 'host', 'state', 'binary',
+                                 'status', 'updated_at', 'disabled_reason']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['services']
+    }
+}
+
+enable_disable_service = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'service': {
+                'type': 'object',
+                'properties': {
+                    'status': {'type': 'string'},
+                    'binary': {'type': 'string'},
+                    'host': {'type': 'string'}
+                },
+                'additionalProperties': False,
+                'required': ['status', 'binary', 'host']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['service']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/snapshots.py b/tempest/lib/api_schema/response/compute/v2_1/snapshots.py
new file mode 100644
index 0000000..01a524b
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/snapshots.py
@@ -0,0 +1,61 @@
+# Copyright 2015 Fujitsu(fnst) 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.
+
+common_snapshot_info = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'volumeId': {'type': 'string'},
+        'status': {'type': 'string'},
+        'size': {'type': 'integer'},
+        'createdAt': {'type': 'string'},
+        'displayName': {'type': ['string', 'null']},
+        'displayDescription': {'type': ['string', 'null']}
+    },
+    'additionalProperties': False,
+    'required': ['id', 'volumeId', 'status', 'size',
+                 'createdAt', 'displayName', 'displayDescription']
+}
+
+create_get_snapshot = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'snapshot': common_snapshot_info
+        },
+        'additionalProperties': False,
+        'required': ['snapshot']
+    }
+}
+
+list_snapshots = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'snapshots': {
+                'type': 'array',
+                'items': common_snapshot_info
+            }
+        },
+        'additionalProperties': False,
+        'required': ['snapshots']
+    }
+}
+
+delete_snapshot = {
+    'status_code': [202]
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/tenant_networks.py b/tempest/lib/api_schema/response/compute/v2_1/tenant_networks.py
new file mode 100644
index 0000000..ddfab96
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/tenant_networks.py
@@ -0,0 +1,53 @@
+# 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.
+
+param_network = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'cidr': {'type': ['string', 'null']},
+        'label': {'type': 'string'}
+    },
+    'additionalProperties': False,
+    'required': ['id', 'cidr', 'label']
+}
+
+
+list_tenant_networks = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'networks': {
+                'type': 'array',
+                'items': param_network
+            }
+        },
+        'additionalProperties': False,
+        'required': ['networks']
+    }
+}
+
+
+get_tenant_network = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'network': param_network
+        },
+        'additionalProperties': False,
+        'required': ['network']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/tenant_usages.py b/tempest/lib/api_schema/response/compute/v2_1/tenant_usages.py
new file mode 100644
index 0000000..d51ef12
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/tenant_usages.py
@@ -0,0 +1,92 @@
+# 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.
+
+import copy
+
+_server_usages = {
+    'type': 'array',
+    'items': {
+        'type': 'object',
+        'properties': {
+            'ended_at': {
+                'oneOf': [
+                    {'type': 'string'},
+                    {'type': 'null'}
+                ]
+            },
+            'flavor': {'type': 'string'},
+            'hours': {'type': 'number'},
+            'instance_id': {'type': 'string'},
+            'local_gb': {'type': 'integer'},
+            'memory_mb': {'type': 'integer'},
+            'name': {'type': 'string'},
+            'started_at': {'type': 'string'},
+            'state': {'type': 'string'},
+            'tenant_id': {'type': 'string'},
+            'uptime': {'type': 'integer'},
+            'vcpus': {'type': 'integer'},
+        },
+        'required': ['ended_at', 'flavor', 'hours', 'instance_id', 'local_gb',
+                     'memory_mb', 'name', 'started_at', 'state', 'tenant_id',
+                     'uptime', 'vcpus']
+    }
+}
+
+_tenant_usage_list = {
+    'type': 'object',
+    'properties': {
+        'server_usages': _server_usages,
+        'start': {'type': 'string'},
+        'stop': {'type': 'string'},
+        'tenant_id': {'type': 'string'},
+        'total_hours': {'type': 'number'},
+        'total_local_gb_usage': {'type': 'number'},
+        'total_memory_mb_usage': {'type': 'number'},
+        'total_vcpus_usage': {'type': 'number'},
+    },
+    'required': ['start', 'stop', 'tenant_id',
+                 'total_hours', 'total_local_gb_usage',
+                 'total_memory_mb_usage', 'total_vcpus_usage']
+}
+
+# 'required' of get_tenant is different from list_tenant's.
+_tenant_usage_get = copy.deepcopy(_tenant_usage_list)
+_tenant_usage_get['required'] = ['server_usages', 'start', 'stop', 'tenant_id',
+                                 'total_hours', 'total_local_gb_usage',
+                                 'total_memory_mb_usage', 'total_vcpus_usage']
+
+list_tenant_usage = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'tenant_usages': {
+                'type': 'array',
+                'items': _tenant_usage_list
+            }
+        },
+        'required': ['tenant_usages']
+    }
+}
+
+get_tenant_usage = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'tenant_usage': _tenant_usage_get
+        },
+        'required': ['tenant_usage']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/versions.py b/tempest/lib/api_schema/response/compute/v2_1/versions.py
new file mode 100644
index 0000000..08a9fab
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/versions.py
@@ -0,0 +1,110 @@
+# 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
+
+
+_version = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'links': {
+            'type': 'array',
+            'items': {
+                'type': 'object',
+                'properties': {
+                    'href': {'type': 'string', 'format': 'uri'},
+                    'rel': {'type': 'string'},
+                    'type': {'type': 'string'},
+                },
+                'required': ['href', 'rel'],
+                'additionalProperties': False
+            }
+        },
+        'status': {'type': 'string'},
+        'updated': {'type': 'string', 'format': 'date-time'},
+        'version': {'type': 'string'},
+        'min_version': {'type': 'string'},
+        'media-types': {
+            'type': 'array',
+            'properties': {
+                'base': {'type': 'string'},
+                'type': {'type': 'string'},
+            }
+        },
+    },
+    # NOTE: version and min_version have been added since Kilo,
+    # so they should not be required.
+    # NOTE(sdague): media-types only shows up in single version requests.
+    'required': ['id', 'links', 'status', 'updated'],
+    'additionalProperties': False
+}
+
+list_versions = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'versions': {
+                'type': 'array',
+                'items': _version
+            }
+        },
+        'required': ['versions'],
+        'additionalProperties': False
+    }
+}
+
+
+_detail_get_version = copy.deepcopy(_version)
+_detail_get_version['properties'].pop('min_version')
+_detail_get_version['properties'].pop('version')
+_detail_get_version['properties'].pop('updated')
+_detail_get_version['properties']['media-types'] = {
+    'type': 'array',
+    'items': {
+        'type': 'object',
+        'properties': {
+            'base': {'type': 'string'},
+            'type': {'type': 'string'}
+        }
+    }
+}
+_detail_get_version['required'] = ['id', 'links', 'status', 'media-types']
+
+get_version = {
+    'status_code': [300],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'choices': {
+                'type': 'array',
+                'items': _detail_get_version
+            }
+        },
+        'required': ['choices'],
+        'additionalProperties': False
+    }
+}
+
+get_one_version = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'version': _version
+        },
+        'additionalProperties': False
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/volumes.py b/tempest/lib/api_schema/response/compute/v2_1/volumes.py
new file mode 100644
index 0000000..bb34acb
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/volumes.py
@@ -0,0 +1,120 @@
+# 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.
+
+create_get_volume = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'volume': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string'},
+                    'status': {'type': 'string'},
+                    'displayName': {'type': ['string', 'null']},
+                    'availabilityZone': {'type': 'string'},
+                    'createdAt': {'type': 'string'},
+                    'displayDescription': {'type': ['string', 'null']},
+                    'volumeType': {'type': ['string', 'null']},
+                    'snapshotId': {'type': ['string', 'null']},
+                    'metadata': {'type': 'object'},
+                    'size': {'type': 'integer'},
+                    'attachments': {
+                        'type': 'array',
+                        'items': {
+                            'type': 'object',
+                            'properties': {
+                                'id': {'type': 'string'},
+                                'device': {'type': 'string'},
+                                'volumeId': {'type': 'string'},
+                                'serverId': {'type': 'string'}
+                            },
+                            'additionalProperties': False,
+                            # NOTE- If volume is not attached to any server
+                            # then, 'attachments' attributes comes as array
+                            # with empty objects "[{}]" due to that elements
+                            # of 'attachments' cannot defined as 'required'.
+                            # If it would come as empty array "[]" then,
+                            # those elements can be defined as 'required'.
+                        }
+                    }
+                },
+                'additionalProperties': False,
+                'required': ['id', 'status', 'displayName', 'availabilityZone',
+                             'createdAt', 'displayDescription', 'volumeType',
+                             'snapshotId', 'metadata', 'size', 'attachments']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['volume']
+    }
+}
+
+list_volumes = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'volumes': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'id': {'type': 'string'},
+                        'status': {'type': 'string'},
+                        'displayName': {'type': ['string', 'null']},
+                        'availabilityZone': {'type': 'string'},
+                        'createdAt': {'type': 'string'},
+                        'displayDescription': {'type': ['string', 'null']},
+                        'volumeType': {'type': ['string', 'null']},
+                        'snapshotId': {'type': ['string', 'null']},
+                        'metadata': {'type': 'object'},
+                        'size': {'type': 'integer'},
+                        'attachments': {
+                            'type': 'array',
+                            'items': {
+                                'type': 'object',
+                                'properties': {
+                                    'id': {'type': 'string'},
+                                    'device': {'type': 'string'},
+                                    'volumeId': {'type': 'string'},
+                                    'serverId': {'type': 'string'}
+                                },
+                                'additionalProperties': False,
+                                # NOTE- If volume is not attached to any server
+                                # then, 'attachments' attributes comes as array
+                                # with empty object "[{}]" due to that elements
+                                # of 'attachments' cannot defined as 'required'
+                                # If it would come as empty array "[]" then,
+                                # those elements can be defined as 'required'.
+                            }
+                        }
+                    },
+                    'additionalProperties': False,
+                    'required': ['id', 'status', 'displayName',
+                                 'availabilityZone', 'createdAt',
+                                 'displayDescription', 'volumeType',
+                                 'snapshotId', 'metadata', 'size',
+                                 'attachments']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['volumes']
+    }
+}
+
+delete_volume = {
+    'status_code': [202]
+}
diff --git a/tempest/api/baremetal/__init__.py b/tempest/lib/api_schema/response/compute/v2_16/__init__.py
similarity index 100%
copy from tempest/api/baremetal/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_16/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_16/servers.py b/tempest/lib/api_schema/response/compute/v2_16/servers.py
new file mode 100644
index 0000000..3eb658f
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_16/servers.py
@@ -0,0 +1,160 @@
+# 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.
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.compute.v2_9 import servers
+
+# Compute microversion 2.16:
+# 1. New attributes in 'server' dict.
+#      'host_status'
+
+server_detail = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'name': {'type': 'string'},
+        'status': {'type': 'string'},
+        'image': {'oneOf': [
+            {'type': 'object',
+                'properties': {
+                    'id': {'type': 'string'},
+                    'links': parameter_types.links
+                },
+                'additionalProperties': False,
+                'required': ['id', 'links']},
+            {'type': ['string', 'null']}
+        ]},
+        'flavor': {
+            'type': 'object',
+            'properties': {
+                'id': {'type': 'string'},
+                'links': parameter_types.links
+            },
+            'additionalProperties': False,
+            'required': ['id', 'links']
+        },
+        'fault': {
+            'type': 'object',
+            'properties': {
+                'code': {'type': 'integer'},
+                'created': {'type': 'string'},
+                'message': {'type': 'string'},
+                'details': {'type': 'string'},
+            },
+            'additionalProperties': False,
+            # NOTE(gmann): 'details' is not necessary to be present
+            #  in the 'fault'. So it is not defined as 'required'.
+            'required': ['code', 'created', 'message']
+        },
+        'user_id': {'type': 'string'},
+        'tenant_id': {'type': 'string'},
+        'created': {'type': 'string'},
+        'updated': {'type': 'string'},
+        'progress': {'type': 'integer'},
+        'metadata': {'type': 'object'},
+        'links': parameter_types.links,
+        'addresses': parameter_types.addresses,
+        'hostId': {'type': 'string'},
+        'OS-DCF:diskConfig': {'type': 'string'},
+        'accessIPv4': parameter_types.access_ip_v4,
+        'accessIPv6': parameter_types.access_ip_v6,
+        'key_name': {'type': ['string', 'null']},
+        'security_groups': {'type': 'array'},
+        'OS-SRV-USG:launched_at': {'type': ['string', 'null']},
+        'OS-SRV-USG:terminated_at': {'type': ['string', 'null']},
+        'OS-EXT-AZ:availability_zone': {'type': 'string'},
+        'OS-EXT-STS:task_state': {'type': ['string', 'null']},
+        'OS-EXT-STS:vm_state': {'type': 'string'},
+        'OS-EXT-STS:power_state': parameter_types.power_state,
+        'OS-EXT-SRV-ATTR:host': {'type': ['string', 'null']},
+        'OS-EXT-SRV-ATTR:instance_name': {'type': 'string'},
+        'OS-EXT-SRV-ATTR:hypervisor_hostname': {'type': ['string', 'null']},
+        'config_drive': {'type': 'string'},
+        'os-extended-volumes:volumes_attached': {
+            'type': 'array',
+            'items': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string'},
+                    'delete_on_termination': {'type': 'boolean'}
+                },
+                'additionalProperties': False,
+            },
+        },
+        'OS-EXT-SRV-ATTR:reservation_id': {'type': ['string', 'null']},
+        'OS-EXT-SRV-ATTR:launch_index': {'type': 'integer'},
+        'OS-EXT-SRV-ATTR:kernel_id': {'type': ['string', 'null']},
+        'OS-EXT-SRV-ATTR:ramdisk_id': {'type': ['string', 'null']},
+        'OS-EXT-SRV-ATTR:hostname': {'type': 'string'},
+        'OS-EXT-SRV-ATTR:root_device_name': {'type': ['string', 'null']},
+        'OS-EXT-SRV-ATTR:user_data': {'type': ['string', 'null']},
+        'locked': {'type': 'boolean'},
+        # NOTE(gmann): new attributes in version 2.16
+        'host_status': {'type': 'string'}
+    },
+    'additionalProperties': False,
+    # NOTE(gmann): 'progress' attribute is present in the response
+    # only when server's status is one of the progress statuses
+    # ("ACTIVE","BUILD", "REBUILD", "RESIZE","VERIFY_RESIZE")
+    # 'fault' attribute is present in the response
+    # only when server's status is one of the  "ERROR", "DELETED".
+    # OS-DCF:diskConfig and accessIPv4/v6 are API
+    # extensions, and some environments return a response
+    # without these attributes.So these are not defined as 'required'.
+    'required': ['id', 'name', 'status', 'image', 'flavor',
+                 'user_id', 'tenant_id', 'created', 'updated',
+                 'metadata', 'links', 'addresses', 'hostId']
+}
+
+server_detail['properties']['addresses']['patternProperties'][
+    '^[a-zA-Z0-9-_.]+$']['items']['properties'].update({
+        'OS-EXT-IPS:type': {'type': 'string'},
+        'OS-EXT-IPS-MAC:mac_addr': parameter_types.mac_address})
+# NOTE(gmann)dd: Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr
+# attributes in server address. Those are API extension,
+# and some environments return a response without
+# these attributes. So they are not 'required'.
+
+get_server = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'server': server_detail
+        },
+        'additionalProperties': False,
+        'required': ['server']
+    }
+}
+
+list_servers_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'servers': {
+                'type': 'array',
+                'items': server_detail
+            },
+            'servers_links': parameter_types.links
+        },
+        'additionalProperties': False,
+        # NOTE(gmann): servers_links attribute is not necessary to be
+        # present always So it is not 'required'.
+        'required': ['servers']
+    }
+}
+
+list_servers = copy.deepcopy(servers.list_servers)
diff --git a/tempest/api/baremetal/__init__.py b/tempest/lib/api_schema/response/compute/v2_19/__init__.py
similarity index 100%
copy from tempest/api/baremetal/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_19/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_19/servers.py b/tempest/lib/api_schema/response/compute/v2_19/servers.py
new file mode 100644
index 0000000..05cc32c
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py
@@ -0,0 +1,52 @@
+# 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import servers as serversv21
+from tempest.lib.api_schema.response.compute.v2_16 import servers \
+    as serversv216
+
+list_servers = copy.deepcopy(serversv216.list_servers)
+
+get_server = copy.deepcopy(serversv216.get_server)
+get_server['response_body']['properties']['server'][
+    'properties'].update({'description': {'type': ['string', 'null']}})
+get_server['response_body']['properties']['server'][
+    'required'].append('description')
+
+list_servers_detail = copy.deepcopy(serversv216.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+    'properties'].update({'description': {'type': ['string', 'null']}})
+list_servers_detail['response_body']['properties']['servers']['items'][
+    'required'].append('description')
+
+update_server = copy.deepcopy(serversv21.update_server)
+update_server['response_body']['properties']['server'][
+    'properties'].update({'description': {'type': ['string', 'null']}})
+update_server['response_body']['properties']['server'][
+    'required'].append('description')
+
+rebuild_server = copy.deepcopy(serversv21.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+    'properties'].update({'description': {'type': ['string', 'null']}})
+rebuild_server['response_body']['properties']['server'][
+    'required'].append('description')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+    serversv21.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'properties'].update({'description': {'type': ['string', 'null']}})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'required'].append('description')
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/lib/api_schema/response/compute/v2_2/keypairs.py b/tempest/lib/api_schema/response/compute/v2_2/keypairs.py
new file mode 100644
index 0000000..0bb7771
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_2/keypairs.py
@@ -0,0 +1,41 @@
+# 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import keypairs
+
+get_keypair = copy.deepcopy(keypairs.get_keypair)
+get_keypair['response_body']['properties']['keypair'][
+    'properties'].update({'type': {'type': 'string'}})
+get_keypair['response_body']['properties']['keypair'][
+    'required'].append('type')
+
+create_keypair = copy.deepcopy(keypairs.create_keypair)
+create_keypair['status_code'] = [201]
+create_keypair['response_body']['properties']['keypair'][
+    'properties'].update({'type': {'type': 'string'}})
+create_keypair['response_body']['properties']['keypair'][
+    'required'].append('type')
+
+delete_keypair = {
+    'status_code': [204],
+}
+
+list_keypairs = copy.deepcopy(keypairs.list_keypairs)
+list_keypairs['response_body']['properties']['keypairs'][
+    'items']['properties']['keypair'][
+    'properties'].update({'type': {'type': 'string'}})
+list_keypairs['response_body']['properties']['keypairs'][
+    'items']['properties']['keypair']['required'].append('type')
diff --git a/tempest/services/identity/v3/__init__.py b/tempest/lib/api_schema/response/compute/v2_23/__init__.py
similarity index 100%
copy from tempest/services/identity/v3/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_23/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_23/migrations.py b/tempest/lib/api_schema/response/compute/v2_23/migrations.py
new file mode 100644
index 0000000..3cd0f6e
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_23/migrations.py
@@ -0,0 +1,62 @@
+# 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.
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+# Compute microversion 2.23:
+# New attributes in 'migrations' list.
+#    'migration_type'
+#    'links'
+
+list_migrations = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'migrations': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'id': {'type': 'integer'},
+                        'status': {'type': ['string', 'null']},
+                        'instance_uuid': {'type': ['string', 'null']},
+                        'source_node': {'type': ['string', 'null']},
+                        'source_compute': {'type': ['string', 'null']},
+                        'dest_node': {'type': ['string', 'null']},
+                        'dest_compute': {'type': ['string', 'null']},
+                        'dest_host': {'type': ['string', 'null']},
+                        'old_instance_type_id': {'type': ['integer', 'null']},
+                        'new_instance_type_id': {'type': ['integer', 'null']},
+                        'created_at': {'type': 'string'},
+                        'updated_at': {'type': ['string', 'null']},
+                        # New attributes in version 2.23
+                        'migration_type': {'type': ['string', 'null']},
+                        'links': parameter_types.links
+                    },
+                    'additionalProperties': False,
+                    'required': [
+                        'id', 'status', 'instance_uuid', 'source_node',
+                        'source_compute', 'dest_node', 'dest_compute',
+                        'dest_host', 'old_instance_type_id',
+                        'new_instance_type_id', 'created_at', 'updated_at',
+                        'migration_type'
+                    ]
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['migrations']
+    }
+}
diff --git a/tempest/api/baremetal/__init__.py b/tempest/lib/api_schema/response/compute/v2_26/__init__.py
similarity index 100%
copy from tempest/api/baremetal/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_26/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_26/servers.py b/tempest/lib/api_schema/response/compute/v2_26/servers.py
new file mode 100644
index 0000000..bc5d18e
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_26/servers.py
@@ -0,0 +1,47 @@
+# 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import servers as servers21
+from tempest.lib.api_schema.response.compute.v2_19 import servers as servers219
+
+# The 2.26 microversion changes the server GET and (detailed) LIST responses to
+# include the server 'tags' which is just a list of strings.
+
+tag_items = {
+    'type': 'array',
+    'maxItems': 50,
+    'items': {
+        'type': 'string',
+        'pattern': '^[^,/]*$',
+        'maxLength': 60
+    }
+}
+
+get_server = copy.deepcopy(servers219.get_server)
+get_server['response_body']['properties']['server'][
+    'properties'].update({'tags': tag_items})
+get_server['response_body']['properties']['server'][
+    'required'].append('tags')
+
+list_servers_detail = copy.deepcopy(servers219.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+    'properties'].update({'tags': tag_items})
+list_servers_detail['response_body']['properties']['servers']['items'][
+    'required'].append('tags')
+
+# list response schema wasn't changed for v2.26 so use v2.1
+
+list_servers = copy.deepcopy(servers21.list_servers)
diff --git a/tempest/services/identity/v3/__init__.py b/tempest/lib/api_schema/response/compute/v2_3/__init__.py
similarity index 100%
copy from tempest/services/identity/v3/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_3/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_3/servers.py b/tempest/lib/api_schema/response/compute/v2_3/servers.py
new file mode 100644
index 0000000..f24103e
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_3/servers.py
@@ -0,0 +1,166 @@
+# 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.
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.compute.v2_1 import servers
+
+# Compute microversion 2.3:
+# 1. New attributes in 'os-extended-volumes:volumes_attached' dict.
+#      'delete_on_termination'
+# 2. New attributes in 'server' dict.
+#      'OS-EXT-SRV-ATTR:reservation_id'
+#      'OS-EXT-SRV-ATTR:launch_index'
+#      'OS-EXT-SRV-ATTR:kernel_id'
+#      'OS-EXT-SRV-ATTR:ramdisk_id'
+#      'OS-EXT-SRV-ATTR:hostname'
+#      'OS-EXT-SRV-ATTR:root_device_name'
+#      'OS-EXT-SRV-ATTR:user_data'
+
+server_detail = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'name': {'type': 'string'},
+        'status': {'type': 'string'},
+        'image': {'oneOf': [
+            {'type': 'object',
+                'properties': {
+                    'id': {'type': 'string'},
+                    'links': parameter_types.links
+                },
+                'additionalProperties': False,
+                'required': ['id', 'links']},
+            {'type': ['string', 'null']}
+        ]},
+        'flavor': {
+            'type': 'object',
+            'properties': {
+                'id': {'type': 'string'},
+                'links': parameter_types.links
+            },
+            'additionalProperties': False,
+            'required': ['id', 'links']
+        },
+        'fault': {
+            'type': 'object',
+            'properties': {
+                'code': {'type': 'integer'},
+                'created': {'type': 'string'},
+                'message': {'type': 'string'},
+                'details': {'type': 'string'},
+            },
+            'additionalProperties': False,
+            # NOTE(gmann): 'details' is not necessary to be present
+            #  in the 'fault'. So it is not defined as 'required'.
+            'required': ['code', 'created', 'message']
+        },
+        'user_id': {'type': 'string'},
+        'tenant_id': {'type': 'string'},
+        'created': {'type': 'string'},
+        'updated': {'type': 'string'},
+        'progress': {'type': 'integer'},
+        'metadata': {'type': 'object'},
+        'links': parameter_types.links,
+        'addresses': parameter_types.addresses,
+        'hostId': {'type': 'string'},
+        'OS-DCF:diskConfig': {'type': 'string'},
+        'accessIPv4': parameter_types.access_ip_v4,
+        'accessIPv6': parameter_types.access_ip_v6,
+        'key_name': {'type': ['string', 'null']},
+        'security_groups': {'type': 'array'},
+        'OS-SRV-USG:launched_at': {'type': ['string', 'null']},
+        'OS-SRV-USG:terminated_at': {'type': ['string', 'null']},
+        'OS-EXT-AZ:availability_zone': {'type': 'string'},
+        'OS-EXT-STS:task_state': {'type': ['string', 'null']},
+        'OS-EXT-STS:vm_state': {'type': 'string'},
+        'OS-EXT-STS:power_state': parameter_types.power_state,
+        'OS-EXT-SRV-ATTR:host': {'type': ['string', 'null']},
+        'OS-EXT-SRV-ATTR:instance_name': {'type': 'string'},
+        'OS-EXT-SRV-ATTR:hypervisor_hostname': {'type': ['string', 'null']},
+        'config_drive': {'type': 'string'},
+        # NOTE(gmann): new attributes in version 2.3
+        'os-extended-volumes:volumes_attached': {
+            'type': 'array',
+            'items': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string'},
+                    'delete_on_termination': {'type': 'boolean'}
+                },
+                'additionalProperties': False,
+            },
+        },
+        'OS-EXT-SRV-ATTR:reservation_id': {'type': ['string', 'null']},
+        'OS-EXT-SRV-ATTR:launch_index': {'type': 'integer'},
+        'OS-EXT-SRV-ATTR:kernel_id': {'type': ['string', 'null']},
+        'OS-EXT-SRV-ATTR:ramdisk_id': {'type': ['string', 'null']},
+        'OS-EXT-SRV-ATTR:hostname': {'type': 'string'},
+        'OS-EXT-SRV-ATTR:root_device_name': {'type': ['string', 'null']},
+        'OS-EXT-SRV-ATTR:user_data': {'type': ['string', 'null']},
+    },
+    'additionalProperties': False,
+    # NOTE(gmann): 'progress' attribute is present in the response
+    # only when server's status is one of the progress statuses
+    # ("ACTIVE","BUILD", "REBUILD", "RESIZE","VERIFY_RESIZE")
+    # 'fault' attribute is present in the response
+    # only when server's status is one of the  "ERROR", "DELETED".
+    # OS-DCF:diskConfig and accessIPv4/v6 are API
+    # extensions, and some environments return a response
+    # without these attributes.So these are not defined as 'required'.
+    'required': ['id', 'name', 'status', 'image', 'flavor',
+                 'user_id', 'tenant_id', 'created', 'updated',
+                 'metadata', 'links', 'addresses', 'hostId']
+}
+
+server_detail['properties']['addresses']['patternProperties'][
+    '^[a-zA-Z0-9-_.]+$']['items']['properties'].update({
+        'OS-EXT-IPS:type': {'type': 'string'},
+        'OS-EXT-IPS-MAC:mac_addr': parameter_types.mac_address})
+# NOTE(gmann)dd: Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr
+# attributes in server address. Those are API extension,
+# and some environments return a response without
+# these attributes. So they are not 'required'.
+
+get_server = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'server': server_detail
+        },
+        'additionalProperties': False,
+        'required': ['server']
+    }
+}
+
+list_servers_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'servers': {
+                'type': 'array',
+                'items': server_detail
+            },
+            'servers_links': parameter_types.links
+        },
+        'additionalProperties': False,
+        # NOTE(gmann): servers_links attribute is not necessary to be
+        # present always So it is not 'required'.
+        'required': ['servers']
+    }
+}
+
+list_servers = copy.deepcopy(servers.list_servers)
diff --git a/tempest/api/baremetal/__init__.py b/tempest/lib/api_schema/response/compute/v2_9/__init__.py
similarity index 100%
copy from tempest/api/baremetal/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_9/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_9/servers.py b/tempest/lib/api_schema/response/compute/v2_9/servers.py
new file mode 100644
index 0000000..470190c
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py
@@ -0,0 +1,31 @@
+# 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_3 import servers
+
+list_servers = copy.deepcopy(servers.list_servers)
+
+get_server = copy.deepcopy(servers.get_server)
+get_server['response_body']['properties']['server'][
+    'properties'].update({'locked': {'type': 'boolean'}})
+get_server['response_body']['properties']['server'][
+    'required'].append('locked')
+
+list_servers_detail = copy.deepcopy(servers.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+    'properties'].update({'locked': {'type': 'boolean'}})
+list_servers_detail['response_body']['properties']['servers']['items'][
+    'required'].append('locked')
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
new file mode 100644
index 0000000..83aa405
--- /dev/null
+++ b/tempest/lib/auth.py
@@ -0,0 +1,851 @@
+# Copyright 2014 Hewlett-Packard Development Company, L.P.
+# Copyright 2016 Rackspace Inc.
+# 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 abc
+import copy
+import datetime
+import re
+
+from oslo_log import log as logging
+import six
+from six.moves.urllib import parse as urlparse
+
+from tempest.lib import exceptions
+from tempest.lib.services.identity.v2 import token_client as json_v2id
+from tempest.lib.services.identity.v3 import token_client as json_v3id
+
+ISO8601_FLOAT_SECONDS = '%Y-%m-%dT%H:%M:%S.%fZ'
+ISO8601_INT_SECONDS = '%Y-%m-%dT%H:%M:%SZ'
+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"""
+
+    SCOPES = set(['project'])
+
+    def __init__(self, credentials, scope='project'):
+        """Auth provider __init__
+
+        :param credentials: credentials for authentication
+        :param scope: the default scope to be used by the credential providers
+                      when requesting a token. Valid values depend on the
+                      AuthProvider class implementation, and are defined in
+                      the set SCOPES. Default value is 'project'.
+        """
+        if self.check_credentials(credentials):
+            self.credentials = credentials
+        else:
+            if isinstance(credentials, Credentials):
+                password = credentials.get('password')
+                message = "Credentials are: " + str(credentials)
+                if password is None:
+                    message += " Password is not defined."
+                else:
+                    message += " Password is defined."
+                raise exceptions.InvalidCredentials(message)
+            else:
+                raise TypeError("credentials object is of type %s, which is"
+                                " not a valid Credentials object type." %
+                                credentials.__class__.__name__)
+        self._scope = None
+        self.scope = scope
+        self.cache = None
+        self.alt_auth_data = None
+        self.alt_part = None
+
+    def __str__(self):
+        return "Creds :{creds}, cached auth data: {cache}".format(
+            creds=self.credentials, cache=self.cache)
+
+    @abc.abstractmethod
+    def _decorate_request(self, filters, method, url, headers=None, body=None,
+                          auth_data=None):
+        """Decorate request with authentication data"""
+        return
+
+    @abc.abstractmethod
+    def _get_auth(self):
+        return
+
+    @abc.abstractmethod
+    def _fill_credentials(self, auth_data_body):
+        return
+
+    def fill_credentials(self):
+        """Fill credentials object with data from auth"""
+        auth_data = self.get_auth()
+        self._fill_credentials(auth_data[1])
+        return self.credentials
+
+    @classmethod
+    def check_credentials(cls, credentials):
+        """Verify credentials are valid."""
+        return isinstance(credentials, Credentials) and credentials.is_valid()
+
+    @property
+    def auth_data(self):
+        """Auth data for set scope"""
+        return self.get_auth()
+
+    @property
+    def scope(self):
+        """Scope used in auth requests"""
+        return self._scope
+
+    @auth_data.deleter
+    def auth_data(self):
+        self.clear_auth()
+
+    def get_auth(self):
+        """Returns auth from cache if available, else auth first"""
+        if self.cache is None or self.is_expired(self.cache):
+            self.set_auth()
+        return self.cache
+
+    def set_auth(self):
+        """Forces setting auth.
+
+        Forces setting auth, ignores cache if it exists.
+        Refills credentials.
+        """
+        self.cache = self._get_auth()
+        self._fill_credentials(self.cache[1])
+
+    def clear_auth(self):
+        """Clear access cache
+
+        Can be called to clear the access cache so that next request
+        will fetch a new token and base_url.
+        """
+        self.cache = None
+        self.credentials.reset()
+
+    @abc.abstractmethod
+    def is_expired(self, auth_data):
+        return
+
+    def auth_request(self, method, url, headers=None, body=None, filters=None):
+        """Obtains auth data and decorates a request with that.
+
+        :param method: HTTP method of the request
+        :param url: relative URL of the request (path)
+        :param headers: HTTP headers of the request
+        :param body: HTTP body in case of POST / PUT
+        :param filters: select a base URL out of the catalog
+        :return: a Tuple (url, headers, body)
+        """
+        orig_req = dict(url=url, headers=headers, body=body)
+
+        auth_url, auth_headers, auth_body = self._decorate_request(
+            filters, method, url, headers, body)
+        auth_req = dict(url=auth_url, headers=auth_headers, body=auth_body)
+
+        # Overwrite part if the request if it has been requested
+        if self.alt_part is not None:
+            if self.alt_auth_data is not None:
+                alt_url, alt_headers, alt_body = self._decorate_request(
+                    filters, method, url, headers, body,
+                    auth_data=self.alt_auth_data)
+                alt_auth_req = dict(url=alt_url, headers=alt_headers,
+                                    body=alt_body)
+                if auth_req[self.alt_part] == alt_auth_req[self.alt_part]:
+                    raise exceptions.BadAltAuth(part=self.alt_part)
+                auth_req[self.alt_part] = alt_auth_req[self.alt_part]
+
+            else:
+                # If the requested part is not affected by auth, we are
+                # not altering auth as expected, raise an exception
+                if auth_req[self.alt_part] == orig_req[self.alt_part]:
+                    raise exceptions.BadAltAuth(part=self.alt_part)
+                # If alt auth data is None, skip auth in the requested part
+                auth_req[self.alt_part] = orig_req[self.alt_part]
+
+            # Next auth request will be normal, unless otherwise requested
+            self.reset_alt_auth_data()
+
+        return auth_req['url'], auth_req['headers'], auth_req['body']
+
+    def reset_alt_auth_data(self):
+        """Configure auth provider to provide valid authentication data"""
+        self.alt_part = None
+        self.alt_auth_data = None
+
+    def set_alt_auth_data(self, request_part, auth_data):
+        """Alternate auth data on next request
+
+        Configure auth provider to provide alt authentication data
+        on a part of the *next* auth_request. If credentials are None,
+        set invalid data.
+
+        :param request_part: request part to contain invalid auth: url,
+                             headers, body
+        :param auth_data: alternative auth_data from which to get the
+                          invalid data to be injected
+        """
+        self.alt_part = request_part
+        self.alt_auth_data = auth_data
+
+    @abc.abstractmethod
+    def base_url(self, filters, auth_data=None):
+        """Extracts the base_url based on provided filters"""
+        return
+
+    @scope.setter
+    def scope(self, value):
+        """Set the scope to be used in token requests
+
+        :param scope: scope to be used. If the scope is different, clear caches
+        """
+        if value not in self.SCOPES:
+            raise exceptions.InvalidScope(
+                scope=value, auth_provider=self.__class__.__name__)
+        if value != self.scope:
+            self.clear_auth()
+            self._scope = value
+
+
+class KeystoneAuthProvider(AuthProvider):
+
+    EXPIRY_DATE_FORMATS = (ISO8601_FLOAT_SECONDS, ISO8601_INT_SECONDS)
+
+    token_expiry_threshold = datetime.timedelta(seconds=60)
+
+    def __init__(self, credentials, auth_url,
+                 disable_ssl_certificate_validation=None,
+                 ca_certs=None, trace_requests=None, scope='project',
+                 http_timeout=None):
+        super(KeystoneAuthProvider, self).__init__(credentials, scope)
+        self.dscv = disable_ssl_certificate_validation
+        self.ca_certs = ca_certs
+        self.trace_requests = trace_requests
+        self.http_timeout = http_timeout
+        self.auth_url = auth_url
+        self.auth_client = self._auth_client(auth_url)
+
+    def _decorate_request(self, filters, method, url, headers=None, body=None,
+                          auth_data=None):
+        if auth_data is None:
+            auth_data = self.get_auth()
+        token, _ = auth_data
+        base_url = self.base_url(filters=filters, auth_data=auth_data)
+        # build authenticated request
+        # returns new request, it does not touch the original values
+        _headers = copy.deepcopy(headers) if headers is not None else {}
+        _headers['X-Auth-Token'] = str(token)
+        if url is None or url == "":
+            _url = base_url
+        else:
+            # Join base URL and url, and remove multiple contiguous slashes
+            _url = "/".join([base_url, url])
+            parts = [x for x in urlparse.urlparse(_url)]
+            parts[2] = re.sub("/{2,}", "/", parts[2])
+            _url = urlparse.urlunparse(parts)
+        # no change to method or body
+        return str(_url), _headers, body
+
+    @abc.abstractmethod
+    def _auth_client(self):
+        return
+
+    @abc.abstractmethod
+    def _auth_params(self):
+        """Auth parameters to be passed to the token request
+
+        By default all fields available in Credentials are passed to the
+        token request. Scope may affect this.
+        """
+        return
+
+    def _get_auth(self):
+        # Bypasses the cache
+        auth_func = getattr(self.auth_client, 'get_token')
+        auth_params = self._auth_params()
+
+        # returns token, auth_data
+        token, auth_data = auth_func(**auth_params)
+        return token, auth_data
+
+    def _parse_expiry_time(self, expiry_string):
+        expiry = None
+        for date_format in self.EXPIRY_DATE_FORMATS:
+            try:
+                expiry = datetime.datetime.strptime(
+                    expiry_string, date_format)
+            except ValueError:
+                pass
+        if expiry is None:
+            raise ValueError(
+                "time data '{data}' does not match any of the"
+                "expected formats: {formats}".format(
+                    data=expiry_string, formats=self.EXPIRY_DATE_FORMATS))
+        return expiry
+
+    def get_token(self):
+        return self.get_auth()[0]
+
+
+class KeystoneV2AuthProvider(KeystoneAuthProvider):
+    """Provides authentication based on the Identity V2 API
+
+    The Keystone Identity V2 API defines both unscoped and project scoped
+    tokens. This auth provider only implements 'project'.
+    """
+
+    SCOPES = set(['project'])
+
+    def _auth_client(self, auth_url):
+        return json_v2id.TokenClient(
+            auth_url, disable_ssl_certificate_validation=self.dscv,
+            ca_certs=self.ca_certs, trace_requests=self.trace_requests,
+            http_timeout=self.http_timeout)
+
+    def _auth_params(self):
+        """Auth parameters to be passed to the token request
+
+        All fields available in Credentials are passed to the token request.
+        """
+        return dict(
+            user=self.credentials.username,
+            password=self.credentials.password,
+            tenant=self.credentials.tenant_name,
+            auth_data=True)
+
+    def _fill_credentials(self, auth_data_body):
+        tenant = auth_data_body['token']['tenant']
+        user = auth_data_body['user']
+        if self.credentials.tenant_name is None:
+            self.credentials.tenant_name = tenant['name']
+        if self.credentials.tenant_id is None:
+            self.credentials.tenant_id = tenant['id']
+        if self.credentials.username is None:
+            self.credentials.username = user['name']
+        if self.credentials.user_id is None:
+            self.credentials.user_id = user['id']
+
+    def base_url(self, filters, auth_data=None):
+        """Base URL from catalog
+
+        :param filters: Used to filter results
+
+        Filters can be:
+
+        - service: service type name such as compute, image, etc.
+        - region: service region name
+        - name: service name, only if service exists
+        - endpoint_type: type of endpoint such as
+            adminURL, publicURL, internalURL
+        - api_version: the version of api used to replace catalog version
+        - skip_path: skips the suffix path of the url and uses base URL
+
+        :rtype: string
+        :return: url with filters applied
+        """
+        if auth_data is None:
+            auth_data = self.get_auth()
+        token, _auth_data = auth_data
+        service = filters.get('service')
+        region = filters.get('region')
+        name = filters.get('name')
+        endpoint_type = filters.get('endpoint_type', 'publicURL')
+
+        if service is None:
+            raise exceptions.EndpointNotFound("No service provided")
+
+        _base_url = None
+        for ep in _auth_data['serviceCatalog']:
+            if ep["type"] == service:
+                if name is not None and ep["name"] != name:
+                    continue
+                for _ep in ep['endpoints']:
+                    if region is not None and _ep['region'] == region:
+                        _base_url = _ep.get(endpoint_type)
+                if not _base_url:
+                    # No region or name matching, use the first
+                    _base_url = ep['endpoints'][0].get(endpoint_type)
+                break
+        if _base_url is None:
+            raise exceptions.EndpointNotFound(
+                "service: %s, region: %s, endpoint_type: %s, name: %s" %
+                (service, region, endpoint_type, name))
+        return apply_url_filters(_base_url, filters)
+
+    def is_expired(self, auth_data):
+        _, access = auth_data
+        expiry = self._parse_expiry_time(access['token']['expires'])
+        return (expiry - self.token_expiry_threshold <=
+                datetime.datetime.utcnow())
+
+
+class KeystoneV3AuthProvider(KeystoneAuthProvider):
+    """Provides authentication based on the Identity V3 API"""
+
+    SCOPES = set(['project', 'domain', 'unscoped', None])
+
+    def _auth_client(self, auth_url):
+        return json_v3id.V3TokenClient(
+            auth_url, disable_ssl_certificate_validation=self.dscv,
+            ca_certs=self.ca_certs, trace_requests=self.trace_requests,
+            http_timeout=self.http_timeout)
+
+    def _auth_params(self):
+        """Auth parameters to be passed to the token request
+
+        Fields available in Credentials are passed to the token request,
+        depending on the value of scope. Valid values for scope are: "project",
+        "domain". Any other string (e.g. "unscoped") or None will lead to an
+        unscoped token request.
+        """
+
+        auth_params = dict(
+            user_id=self.credentials.user_id,
+            username=self.credentials.username,
+            user_domain_id=self.credentials.user_domain_id,
+            user_domain_name=self.credentials.user_domain_name,
+            password=self.credentials.password,
+            auth_data=True)
+
+        if self.scope == 'project':
+            auth_params.update(
+                project_domain_id=self.credentials.project_domain_id,
+                project_domain_name=self.credentials.project_domain_name,
+                project_id=self.credentials.project_id,
+                project_name=self.credentials.project_name)
+
+        if self.scope == 'domain':
+            auth_params.update(
+                domain_id=self.credentials.domain_id,
+                domain_name=self.credentials.domain_name)
+
+        return auth_params
+
+    def _fill_credentials(self, auth_data_body):
+        # project or domain, depending on the scope
+        project = auth_data_body.get('project', None)
+        domain = auth_data_body.get('domain', None)
+        # user is always there
+        user = auth_data_body['user']
+        # Set project fields
+        if project is not None:
+            if self.credentials.project_name is None:
+                self.credentials.project_name = project['name']
+            if self.credentials.project_id is None:
+                self.credentials.project_id = project['id']
+            if self.credentials.project_domain_id is None:
+                self.credentials.project_domain_id = project['domain']['id']
+            if self.credentials.project_domain_name is None:
+                self.credentials.project_domain_name = (
+                    project['domain']['name'])
+        # Set domain fields
+        if domain is not None:
+            if self.credentials.domain_id is None:
+                self.credentials.domain_id = domain['id']
+            if self.credentials.domain_name is None:
+                self.credentials.domain_name = domain['name']
+        # Set user fields
+        if self.credentials.username is None:
+            self.credentials.username = user['name']
+        if self.credentials.user_id is None:
+            self.credentials.user_id = user['id']
+        if self.credentials.user_domain_id is None:
+            self.credentials.user_domain_id = user['domain']['id']
+        if self.credentials.user_domain_name is None:
+            self.credentials.user_domain_name = user['domain']['name']
+
+    def base_url(self, filters, auth_data=None):
+        """Base URL from catalog
+
+        If scope is not 'project', it may be that there is not catalog in
+        the auth_data. In such case, as long as the requested service is
+        'identity', we can use the original auth URL to build the base_url.
+
+        :param filters: Used to filter results
+
+        Filters can be:
+
+        - service: service type name such as compute, image, etc.
+        - region: service region name
+        - name: service name, only if service exists
+        - endpoint_type: type of endpoint such as
+            adminURL, publicURL, internalURL
+        - api_version: the version of api used to replace catalog version
+        - skip_path: skips the suffix path of the url and uses base URL
+
+        :rtype: string
+        :return: url with filters applied
+        """
+        if auth_data is None:
+            auth_data = self.get_auth()
+        token, _auth_data = auth_data
+        service = filters.get('service')
+        region = filters.get('region')
+        name = filters.get('name')
+        endpoint_type = filters.get('endpoint_type', 'public')
+
+        if service is None:
+            raise exceptions.EndpointNotFound("No service provided")
+
+        if 'URL' in endpoint_type:
+            endpoint_type = endpoint_type.replace('URL', '')
+        _base_url = None
+        catalog = _auth_data.get('catalog', [])
+
+        # Select entries with matching service type
+        service_catalog = [ep for ep in catalog if ep['type'] == service]
+        if len(service_catalog) > 0:
+            if name is not None:
+                service_catalog = (
+                    [ep for ep in service_catalog if ep['name'] == name])
+                if len(service_catalog) > 0:
+                    service_catalog = service_catalog[0]['endpoints']
+                else:
+                    raise exceptions.EndpointNotFound(name)
+            else:
+                service_catalog = service_catalog[0]['endpoints']
+        else:
+            if len(catalog) == 0 and service == 'identity':
+                # NOTE(andreaf) If there's no catalog at all and the service
+                # is identity, it's a valid use case. Having a non-empty
+                # catalog with no identity in it is not valid instead.
+                msg = ('Got an empty catalog. Scope: %s. '
+                       'Falling back to configured URL for %s: %s')
+                LOG.debug(msg, self.scope, service, self.auth_url)
+                return apply_url_filters(self.auth_url, filters)
+            else:
+                # No matching service
+                msg = ('No matching service found in the catalog.\n'
+                       'Scope: %s, Credentials: %s\n'
+                       'Auth data: %s\n'
+                       'Service: %s, Region: %s, endpoint_type: %s\n'
+                       'Catalog: %s')
+                raise exceptions.EndpointNotFound(msg % (
+                    self.scope, self.credentials, _auth_data, service, region,
+                    endpoint_type, catalog))
+        # Filter by endpoint type (interface)
+        filtered_catalog = [ep for ep in service_catalog if
+                            ep['interface'] == endpoint_type]
+        if len(filtered_catalog) == 0:
+            # No matching type, keep all and try matching by region at least
+            filtered_catalog = service_catalog
+        # Filter by region
+        filtered_catalog = [ep for ep in filtered_catalog if
+                            ep['region'] == region]
+        if len(filtered_catalog) == 0:
+            # No matching region (or name), take the first endpoint
+            filtered_catalog = [service_catalog[0]]
+        # There should be only one match. If not take the first.
+        _base_url = filtered_catalog[0].get('url', None)
+        if _base_url is None:
+            raise exceptions.EndpointNotFound(service)
+        return apply_url_filters(_base_url, filters)
+
+    def is_expired(self, auth_data):
+        _, access = auth_data
+        expiry = self._parse_expiry_time(access['expires_at'])
+        return (expiry - self.token_expiry_threshold <=
+                datetime.datetime.utcnow())
+
+
+def is_identity_version_supported(identity_version):
+    return identity_version in IDENTITY_VERSION
+
+
+def get_credentials(auth_url, fill_in=True, identity_version='v2',
+                    disable_ssl_certificate_validation=None, ca_certs=None,
+                    trace_requests=None, http_timeout=None, **kwargs):
+    """Builds a credentials object based on the configured auth_version
+
+    :param auth_url (string): Full URI of the OpenStack Identity API(Keystone)
+           which is used to fetch the token from Identity service.
+    :param fill_in (boolean): obtain a token and fill in all credential
+           details provided by the identity service. When fill_in is not
+           specified, credentials are not validated. Validation can be invoked
+           by invoking ``is_valid()``
+    :param identity_version (string): identity API version is used to
+           select the matching auth provider and credentials class
+    :param disable_ssl_certificate_validation: whether to enforce SSL
+           certificate validation in SSL API requests to the auth system
+    :param ca_certs: CA certificate bundle for validation of certificates
+           in SSL API requests to the auth system
+    :param trace_requests: trace in log API requests to the auth system
+    :param http_timeout: timeout in seconds to wait for the http request to
+           return
+    :param kwargs (dict): Dict of credential key/value pairs
+
+    Examples:
+
+        Returns credentials from the provided parameters:
+        >>> get_credentials(username='foo', password='bar')
+
+        Returns credentials including IDs:
+        >>> get_credentials(username='foo', password='bar', fill_in=True)
+    """
+    if not is_identity_version_supported(identity_version):
+        raise exceptions.InvalidIdentityVersion(
+            identity_version=identity_version)
+
+    credential_class, auth_provider_class = IDENTITY_VERSION.get(
+        identity_version)
+
+    creds = credential_class(**kwargs)
+    # Fill in the credentials fields that were not specified
+    if fill_in:
+        dscv = disable_ssl_certificate_validation
+        auth_provider = auth_provider_class(
+            creds, auth_url, disable_ssl_certificate_validation=dscv,
+            ca_certs=ca_certs, trace_requests=trace_requests,
+            http_timeout=http_timeout)
+        creds = auth_provider.fill_credentials()
+    return creds
+
+
+class Credentials(object):
+    """Set of credentials for accessing OpenStack services
+
+    ATTRIBUTES: list of valid class attributes representing credentials.
+    """
+
+    ATTRIBUTES = []
+    COLLISIONS = []
+
+    def __init__(self, **kwargs):
+        """Enforce the available attributes at init time (only).
+
+        Additional attributes can still be set afterwards if tests need
+        to do so.
+        """
+        self._initial = kwargs
+        self._apply_credentials(kwargs)
+
+    def _apply_credentials(self, attr):
+        for (key1, key2) in self.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)
+        for key in attr:
+            if key in self.ATTRIBUTES:
+                setattr(self, key, attr[key])
+            else:
+                msg = '%s is not a valid attr for %s' % (key, self.__class__)
+                raise exceptions.InvalidCredentials(msg)
+
+    def __str__(self):
+        """Represent only attributes included in self.ATTRIBUTES"""
+        attrs = [attr for attr in self.ATTRIBUTES if attr is not 'password']
+        _repr = dict((k, getattr(self, k)) for k in attrs)
+        return str(_repr)
+
+    def __eq__(self, other):
+        """Credentials are equal if attributes in self.ATTRIBUTES are equal"""
+        return str(self) == str(other)
+
+    def __ne__(self, other):
+        """Contrary to the __eq__"""
+        return not self.__eq__(other)
+
+    def __getattr__(self, key):
+        # If an attribute is set, __getattr__ is not invoked
+        # If an attribute is not set, and it is a known one, return None
+        if key in self.ATTRIBUTES:
+            return None
+        else:
+            raise AttributeError
+
+    def __delitem__(self, key):
+        # For backwards compatibility, support dict behaviour
+        if key in self.ATTRIBUTES:
+            delattr(self, key)
+        else:
+            raise AttributeError
+
+    def get(self, item, default=None):
+        # In this patch act as dict for backward compatibility
+        try:
+            return getattr(self, item)
+        except AttributeError:
+            return default
+
+    def get_init_attributes(self):
+        return self._initial.keys()
+
+    def is_valid(self):
+        raise NotImplementedError
+
+    def reset(self):
+        # First delete all known attributes
+        for key in self.ATTRIBUTES:
+            if getattr(self, key) is not None:
+                delattr(self, key)
+        # Then re-apply initial setup
+        self._apply_credentials(self._initial)
+
+
+class KeystoneV2Credentials(Credentials):
+
+    ATTRIBUTES = ['username', 'password', 'tenant_name', 'user_id',
+                  'tenant_id', 'project_id', 'project_name']
+    COLLISIONS = [('project_name', 'tenant_name'), ('project_id', 'tenant_id')]
+
+    def __str__(self):
+        """Represent only attributes included in self.ATTRIBUTES"""
+        attrs = [attr for attr in self.ATTRIBUTES if attr is not 'password']
+        _repr = dict((k, getattr(self, k)) for k in attrs)
+        return str(_repr)
+
+    def __setattr__(self, key, value):
+        # NOTE(andreaf) In order to ease the migration towards 'project' we
+        # support v2 credentials configured with 'project' and translate it
+        # to tenant on the fly. The original kwargs are stored for clients
+        # that may rely on them. We also set project when tenant is defined
+        # so clients can rely on project being part of credentials.
+        parent = super(KeystoneV2Credentials, self)
+        # for project_* set tenant only
+        if key == 'project_id':
+            parent.__setattr__('tenant_id', value)
+        elif key == 'project_name':
+            parent.__setattr__('tenant_name', value)
+        if key == 'tenant_id':
+            parent.__setattr__('project_id', value)
+        elif key == 'tenant_name':
+            parent.__setattr__('project_name', value)
+        # trigger default behaviour for all attributes
+        parent.__setattr__(key, value)
+
+    def is_valid(self):
+        """Check of credentials (no API call)
+
+        Minimum set of valid credentials, are username and password.
+        Tenant is optional.
+        """
+        return None not in (self.username, self.password)
+
+
+class KeystoneV3Credentials(Credentials):
+    """Credentials suitable for the Keystone Identity V3 API"""
+
+    ATTRIBUTES = ['domain_id', 'domain_name', 'password', 'username',
+                  'project_domain_id', 'project_domain_name', 'project_id',
+                  'project_name', 'tenant_id', 'tenant_name', 'user_domain_id',
+                  'user_domain_name', 'user_id']
+    COLLISIONS = [('project_name', 'tenant_name'), ('project_id', 'tenant_id')]
+
+    def __setattr__(self, key, value):
+        parent = super(KeystoneV3Credentials, self)
+        # for tenant_* set both project and tenant
+        if key == 'tenant_id':
+            parent.__setattr__('project_id', value)
+        elif key == 'tenant_name':
+            parent.__setattr__('project_name', value)
+        # for project_* set both project and tenant
+        if key == 'project_id':
+            parent.__setattr__('tenant_id', value)
+        elif key == 'project_name':
+            parent.__setattr__('tenant_name', value)
+        # for *_domain_* set both user and project if not set yet
+        if key == 'user_domain_id':
+            if self.project_domain_id is None:
+                parent.__setattr__('project_domain_id', value)
+        if key == 'project_domain_id':
+            if self.user_domain_id is None:
+                parent.__setattr__('user_domain_id', value)
+        if key == 'user_domain_name':
+            if self.project_domain_name is None:
+                parent.__setattr__('project_domain_name', value)
+        if key == 'project_domain_name':
+            if self.user_domain_name is None:
+                parent.__setattr__('user_domain_name', value)
+        # support domain_name coming from config
+        if key == 'domain_name':
+            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)
+
+    def is_valid(self):
+        """Check of credentials (no API call)
+
+        Valid combinations of v3 credentials (excluding token)
+        - User id, password (optional domain)
+        - User name, password and its domain id/name
+        For the scope, valid combinations are:
+        - None
+        - Project id (optional domain)
+        - Project name and its domain id/name
+        - Domain id
+        - Domain name
+        """
+        valid_user_domain = any(
+            [self.user_domain_id is not None,
+             self.user_domain_name is not None])
+        valid_project_domain = any(
+            [self.project_domain_id is not None,
+             self.project_domain_name is not None])
+        valid_user = any(
+            [self.user_id is not None,
+             self.username is not None and valid_user_domain])
+        valid_project_scope = any(
+            [self.project_name is None and self.project_id is None,
+             self.project_id is not None,
+             self.project_name is not None and valid_project_domain])
+        valid_domain_scope = any(
+            [self.domain_id is None and self.domain_name is None,
+             self.domain_id or self.domain_name])
+        return all([self.password is not None,
+                    valid_user,
+                    valid_project_scope and valid_domain_scope])
+
+
+IDENTITY_VERSION = {'v2': (KeystoneV2Credentials, KeystoneV2AuthProvider),
+                    'v3': (KeystoneV3Credentials, KeystoneV3AuthProvider)}
diff --git a/tempest/lib/base.py b/tempest/lib/base.py
new file mode 100644
index 0000000..33a32ee
--- /dev/null
+++ b/tempest/lib/base.py
@@ -0,0 +1,68 @@
+# 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 os
+
+import fixtures
+import testtools
+
+
+class BaseTestCase(testtools.testcase.WithAttributes, testtools.TestCase):
+    setUpClassCalled = False
+
+    # NOTE(sdague): log_format is defined inline here instead of using the oslo
+    # default because going through the config path recouples config to the
+    # stress tests too early, and depending on testr order will fail unit tests
+    log_format = ('%(asctime)s %(process)d %(levelname)-8s '
+                  '[%(name)s] %(message)s')
+
+    @classmethod
+    def setUpClass(cls):
+        if hasattr(super(BaseTestCase, cls), 'setUpClass'):
+            super(BaseTestCase, cls).setUpClass()
+        cls.setUpClassCalled = True
+
+    @classmethod
+    def tearDownClass(cls):
+        if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
+            super(BaseTestCase, cls).tearDownClass()
+
+    def setUp(self):
+        super(BaseTestCase, self).setUp()
+        if not self.setUpClassCalled:
+            raise RuntimeError("setUpClass does not calls the super's "
+                               "setUpClass in the "
+                               + self.__class__.__name__)
+        test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
+        try:
+            test_timeout = int(test_timeout)
+        except ValueError:
+            test_timeout = 0
+        if test_timeout > 0:
+            self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
+
+        if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
+                os.environ.get('OS_STDOUT_CAPTURE') == '1'):
+            stdout = self.useFixture(fixtures.StringStream('stdout')).stream
+            self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
+        if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
+                os.environ.get('OS_STDERR_CAPTURE') == '1'):
+            stderr = self.useFixture(fixtures.StringStream('stderr')).stream
+            self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
+        if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
+            os.environ.get('OS_LOG_CAPTURE') != '0'):
+            self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
+                                                   format=self.log_format,
+                                                   level=None))
diff --git a/tempest/stress/__init__.py b/tempest/lib/cli/__init__.py
similarity index 100%
copy from tempest/stress/__init__.py
copy to tempest/lib/cli/__init__.py
diff --git a/tempest/lib/cli/base.py b/tempest/lib/cli/base.py
new file mode 100644
index 0000000..5468a7b
--- /dev/null
+++ b/tempest/lib/cli/base.py
@@ -0,0 +1,416 @@
+# Copyright 2013 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 os
+import shlex
+import subprocess
+
+from oslo_log import log as logging
+import six
+
+from tempest.lib import base
+import tempest.lib.cli.output_parser
+from tempest.lib import exceptions
+
+
+LOG = logging.getLogger(__name__)
+
+
+def execute(cmd, action, flags='', params='', fail_ok=False,
+            merge_stderr=False, cli_dir='/usr/bin', prefix=''):
+    """Executes specified command for the given action.
+
+    :param cmd: command to be executed
+    :type cmd: string
+    :param action: string of the cli command to run
+    :type action: string
+    :param flags: any optional cli flags to use
+    :type flags: string
+    :param params: string of any optional positional args to use
+    :type params: string
+    :param fail_ok: boolean if True an exception is not raised when the
+                    cli return code is non-zero
+    :type fail_ok: boolean
+    :param merge_stderr: boolean if True the stderr buffer is merged into
+                         stdout
+    :type merge_stderr: boolean
+    :param cli_dir: The path where the cmd can be executed
+    :type cli_dir: string
+    :param prefix: prefix to insert before command
+    :type prefix: string
+    """
+    cmd = ' '.join([prefix, os.path.join(cli_dir, cmd),
+                    flags, action, params])
+    cmd = cmd.strip()
+    LOG.info("running: '%s'", cmd)
+    if six.PY2:
+        cmd = cmd.encode('utf-8')
+    cmd = shlex.split(cmd)
+    result = ''
+    result_err = ''
+    stdout = subprocess.PIPE
+    stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
+    proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
+    result, result_err = proc.communicate()
+    if not fail_ok and proc.returncode != 0:
+        raise exceptions.CommandFailed(proc.returncode,
+                                       cmd,
+                                       result,
+                                       result_err)
+    if six.PY2:
+        return result
+    else:
+        return os.fsdecode(result)
+
+
+class CLIClient(object):
+    """Class to use OpenStack official python client CLI's with auth
+
+    :param username: The username to authenticate with
+    :type username: string
+    :param password: The password to authenticate with
+    :type password: string
+    :param tenant_name: The name of the tenant to use with the client calls
+    :type tenant_name: string
+    :param uri: The auth uri for the OpenStack Deployment
+    :type uri: string
+    :param cli_dir: The path where the python client binaries are installed.
+                    defaults to /usr/bin
+    :type cli_dir: string
+    :param insecure: if True, --insecure is passed to python client binaries.
+    :type insecure: boolean
+    :param prefix: prefix to insert before commands
+    :type prefix: string
+    """
+
+    def __init__(self, username='', password='', tenant_name='', uri='',
+                 cli_dir='', insecure=False, prefix='', *args, **kwargs):
+        """Initialize a new CLIClient object."""
+        super(CLIClient, self).__init__()
+        self.cli_dir = cli_dir if cli_dir else '/usr/bin'
+        self.username = username
+        self.tenant_name = tenant_name
+        self.password = password
+        self.uri = uri
+        self.insecure = insecure
+        self.prefix = prefix
+
+    def nova(self, action, flags='', params='', fail_ok=False,
+             endpoint_type='publicURL', merge_stderr=False):
+        """Executes nova command for the given action.
+
+        :param action: the cli command to run using nova
+        :type action: string
+        :param flags: any optional cli flags to use
+        :type flags: string
+        :param params: any optional positional args to use
+        :type params: string
+        :param fail_ok: if True an exception is not raised when the
+                        cli return code is non-zero
+        :type fail_ok: boolean
+        :param endpoint_type: the type of endpoint for the service
+        :type endpoint_type: string
+        :param merge_stderr: if True the stderr buffer is merged into stdout
+        :type merge_stderr: boolean
+        """
+        flags += ' --os-endpoint-type %s' % endpoint_type
+        return self.cmd_with_auth(
+            'nova', action, flags, params, fail_ok, merge_stderr)
+
+    def nova_manage(self, action, flags='', params='', fail_ok=False,
+                    merge_stderr=False):
+        """Executes nova-manage command for the given action.
+
+        :param action: the cli command to run using nova-manage
+        :type action: string
+        :param flags: any optional cli flags to use
+        :type flags: string
+        :param params: any optional positional args to use
+        :type params: string
+        :param fail_ok: if True an exception is not raised when the
+                        cli return code is non-zero
+        :type fail_ok: boolean
+        :param merge_stderr: if True the stderr buffer is merged into stdout
+        :type merge_stderr: boolean
+        """
+        return execute(
+            'nova-manage', action, flags, params, fail_ok, merge_stderr,
+            self.cli_dir)
+
+    def keystone(self, action, flags='', params='', fail_ok=False,
+                 merge_stderr=False):
+        """Executes keystone command for the given action.
+
+        :param action: the cli command to run using keystone
+        :type action: string
+        :param flags: any optional cli flags to use
+        :type flags: string
+        :param params: any optional positional args to use
+        :type params: string
+        :param fail_ok: if True an exception is not raised when the
+                        cli return code is non-zero
+        :type fail_ok: boolean
+        :param merge_stderr: if True the stderr buffer is merged into stdout
+        :type merge_stderr: boolean
+        """
+        return self.cmd_with_auth(
+            'keystone', action, flags, params, fail_ok, merge_stderr)
+
+    def glance(self, action, flags='', params='', fail_ok=False,
+               endpoint_type='publicURL', merge_stderr=False):
+        """Executes glance command for the given action.
+
+        :param action: the cli command to run using glance
+        :type action: string
+        :param flags: any optional cli flags to use
+        :type flags: string
+        :param params: any optional positional args to use
+        :type params: string
+        :param fail_ok: if True an exception is not raised when the
+                        cli return code is non-zero
+        :type fail_ok: boolean
+        :param endpoint_type: the type of endpoint for the service
+        :type endpoint_type: string
+        :param merge_stderr: if True the stderr buffer is merged into stdout
+        :type merge_stderr: boolean
+        """
+        flags += ' --os-endpoint-type %s' % endpoint_type
+        return self.cmd_with_auth(
+            'glance', action, flags, params, fail_ok, merge_stderr)
+
+    def ceilometer(self, action, flags='', params='',
+                   fail_ok=False, endpoint_type='publicURL',
+                   merge_stderr=False):
+        """Executes ceilometer command for the given action.
+
+        :param action: the cli command to run using ceilometer
+        :type action: string
+        :param flags: any optional cli flags to use
+        :type flags: string
+        :param params: any optional positional args to use
+        :type params: string
+        :param fail_ok: if True an exception is not raised when the
+                        cli return code is non-zero
+        :type fail_ok: boolean
+        :param endpoint_type: the type of endpoint for the service
+        :type endpoint_type: string
+        :param merge_stderr: if True the stderr buffer is merged into stdout
+        :type merge_stderr: boolean
+        """
+        flags += ' --os-endpoint-type %s' % endpoint_type
+        return self.cmd_with_auth(
+            'ceilometer', action, flags, params, fail_ok, merge_stderr)
+
+    def heat(self, action, flags='', params='',
+             fail_ok=False, endpoint_type='publicURL', merge_stderr=False):
+        """Executes heat command for the given action.
+
+        :param action: the cli command to run using heat
+        :type action: string
+        :param flags: any optional cli flags to use
+        :type flags: string
+        :param params: any optional positional args to use
+        :type params: string
+        :param fail_ok: if True an exception is not raised when the
+                        cli return code is non-zero
+        :type fail_ok: boolean
+        :param endpoint_type: the type of endpoint for the service
+        :type endpoint_type: string
+        :param merge_stderr: if True the stderr buffer is merged into stdout
+        :type merge_stderr: boolean
+        """
+        flags += ' --os-endpoint-type %s' % endpoint_type
+        return self.cmd_with_auth(
+            'heat', action, flags, params, fail_ok, merge_stderr)
+
+    def cinder(self, action, flags='', params='', fail_ok=False,
+               endpoint_type='publicURL', merge_stderr=False):
+        """Executes cinder command for the given action.
+
+        :param action: the cli command to run using cinder
+        :type action: string
+        :param flags: any optional cli flags to use
+        :type flags: string
+        :param params: any optional positional args to use
+        :type params: string
+        :param fail_ok: if True an exception is not raised when the
+                        cli return code is non-zero
+        :type fail_ok: boolean
+        :param endpoint_type: the type of endpoint for the service
+        :type endpoint_type: string
+        :param merge_stderr: if True the stderr buffer is merged into stdout
+        :type merge_stderr: boolean
+        """
+        flags += ' --endpoint-type %s' % endpoint_type
+        return self.cmd_with_auth(
+            'cinder', action, flags, params, fail_ok, merge_stderr)
+
+    def swift(self, action, flags='', params='', fail_ok=False,
+              endpoint_type='publicURL', merge_stderr=False):
+        """Executes swift command for the given action.
+
+        :param action: the cli command to run using swift
+        :type action: string
+        :param flags: any optional cli flags to use
+        :type flags: string
+        :param params: any optional positional args to use
+        :type params: string
+        :param fail_ok: if True an exception is not raised when the
+                        cli return code is non-zero
+        :type fail_ok: boolean
+        :param endpoint_type: the type of endpoint for the service
+        :type endpoint_type: string
+        :param merge_stderr: if True the stderr buffer is merged into stdout
+        :type merge_stderr: boolean
+        """
+        flags += ' --os-endpoint-type %s' % endpoint_type
+        return self.cmd_with_auth(
+            'swift', action, flags, params, fail_ok, merge_stderr)
+
+    def neutron(self, action, flags='', params='', fail_ok=False,
+                endpoint_type='publicURL', merge_stderr=False):
+        """Executes neutron command for the given action.
+
+        :param action: the cli command to run using neutron
+        :type action: string
+        :param flags: any optional cli flags to use
+        :type flags: string
+        :param params: any optional positional args to use
+        :type params: string
+        :param fail_ok: if True an exception is not raised when the
+                        cli return code is non-zero
+        :type fail_ok: boolean
+        :param endpoint_type: the type of endpoint for the service
+        :type endpoint_type: string
+        :param merge_stderr: if True the stderr buffer is merged into stdout
+        :type merge_stderr: boolean
+        """
+        flags += ' --endpoint-type %s' % endpoint_type
+        return self.cmd_with_auth(
+            'neutron', action, flags, params, fail_ok, merge_stderr)
+
+    def sahara(self, action, flags='', params='',
+               fail_ok=False, endpoint_type='publicURL', merge_stderr=True):
+        """Executes sahara command for the given action.
+
+        :param action: the cli command to run using sahara
+        :type action: string
+        :param flags: any optional cli flags to use
+        :type flags: string
+        :param params: any optional positional args to use
+        :type params: string
+        :param fail_ok: if True an exception is not raised when the
+                        cli return code is non-zero
+        :type fail_ok: boolean
+        :param endpoint_type: the type of endpoint for the service
+        :type endpoint_type: string
+        :param merge_stderr: if True the stderr buffer is merged into stdout
+        :type merge_stderr: boolean
+        """
+        flags += ' --endpoint-type %s' % endpoint_type
+        return self.cmd_with_auth(
+            'sahara', action, flags, params, fail_ok, merge_stderr)
+
+    def openstack(self, action, flags='', params='', fail_ok=False,
+                  merge_stderr=False):
+        """Executes openstack command for the given action.
+
+        :param action: the cli command to run using openstack
+        :type action: string
+        :param flags: any optional cli flags to use
+        :type flags: string
+        :param params: any optional positional args to use
+        :type params: string
+        :param fail_ok: if True an exception is not raised when the
+                        cli return code is non-zero
+        :type fail_ok: boolean
+        :param merge_stderr: if True the stderr buffer is merged into stdout
+        :type merge_stderr: boolean
+        """
+        return self.cmd_with_auth(
+            'openstack', action, flags, params, fail_ok, merge_stderr)
+
+    def cmd_with_auth(self, cmd, action, flags='', params='',
+                      fail_ok=False, merge_stderr=False):
+        """Executes given command with auth attributes appended.
+
+        :param cmd: command to be executed
+        :type cmd: string
+        :param action: command on cli to run
+        :type action: string
+        :param flags: optional cli flags to use
+        :type flags: string
+        :param params: optional positional args to use
+        :type params: string
+        :param fail_ok: if True an exception is not raised when the cli return
+                        code is non-zero
+        :type fail_ok: boolean
+        :param merge_stderr:  if True the stderr buffer is merged into stdout
+        :type merge_stderr: boolean
+        """
+        creds = ('--os-username %s --os-tenant-name %s --os-password %s '
+                 '--os-auth-url %s' %
+                 (self.username,
+                  self.tenant_name,
+                  self.password,
+                  self.uri))
+        if self.insecure:
+            flags = creds + ' --insecure ' + flags
+        else:
+            flags = creds + ' ' + flags
+        return execute(cmd, action, flags, params, fail_ok, merge_stderr,
+                       self.cli_dir, prefix=self.prefix)
+
+
+class ClientTestBase(base.BaseTestCase):
+    """Base test class for testing the OpenStack client CLI interfaces."""
+
+    def setUp(self):
+        super(ClientTestBase, self).setUp()
+        self.clients = self._get_clients()
+        self.parser = tempest.lib.cli.output_parser
+
+    def _get_clients(self):
+        """Abstract method to initialize CLIClient object.
+
+        This method must be overloaded in child test classes. It should be
+        used to initialize the CLIClient object with the appropriate
+        credentials during the setUp() phase of tests.
+        """
+        raise NotImplementedError
+
+    def assertTableStruct(self, items, field_names):
+        """Verify that all items has keys listed in field_names.
+
+        :param items: items to assert are field names in the output table
+        :type items: list
+        :param field_names: field names from the output table of the cmd
+        :type field_names: list
+        """
+        for item in items:
+            for field in field_names:
+                self.assertIn(field, item)
+
+    def assertFirstLineStartsWith(self, lines, beginning):
+        """Verify that the first line starts with a string
+
+        :param lines: strings for each line of output
+        :type lines: list
+        :param beginning: verify this is at the beginning of the first line
+        :type beginning: string
+        """
+        self.assertTrue(lines[0].startswith(beginning),
+                        msg=('Beginning of first line has invalid content: %s'
+                             % lines[:3]))
diff --git a/tempest/lib/cli/output_parser.py b/tempest/lib/cli/output_parser.py
new file mode 100644
index 0000000..716f374
--- /dev/null
+++ b/tempest/lib/cli/output_parser.py
@@ -0,0 +1,171 @@
+# Copyright 2013 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.
+
+"""Collection of utilities for parsing CLI clients output."""
+
+import re
+
+from oslo_log import log as logging
+
+from tempest.lib import exceptions
+
+
+LOG = logging.getLogger(__name__)
+
+
+delimiter_line = re.compile('^\+\-[\+\-]+\-\+$')
+
+
+def details_multiple(output_lines, with_label=False):
+    """Return list of dicts with item details from cli output tables.
+
+    If with_label is True, key '__label' is added to each items dict.
+    For more about 'label' see OutputParser.tables().
+    """
+    items = []
+    tables_ = tables(output_lines)
+    for table_ in tables_:
+        if ('Property' not in table_['headers']
+            or 'Value' not in table_['headers']):
+            raise exceptions.InvalidStructure()
+        item = {}
+        for value in table_['values']:
+            item[value[0]] = value[1]
+        if with_label:
+            item['__label'] = table_['label']
+        items.append(item)
+    return items
+
+
+def details(output_lines, with_label=False):
+    """Return dict with details of first item (table) found in output."""
+    items = details_multiple(output_lines, with_label)
+    return items[0]
+
+
+def listing(output_lines):
+    """Return list of dicts with basic item info parsed from cli output."""
+
+    items = []
+    table_ = table(output_lines)
+    for row in table_['values']:
+        item = {}
+        for col_idx, col_key in enumerate(table_['headers']):
+            item[col_key] = row[col_idx]
+        items.append(item)
+    return items
+
+
+def tables(output_lines):
+    """Find all ascii-tables in output and parse them.
+
+    Return list of tables parsed from cli output as dicts.
+    (see OutputParser.table())
+
+    And, if found, label key (separated line preceding the table)
+    is added to each tables dict.
+    """
+    tables_ = []
+
+    table_ = []
+    label = None
+
+    start = False
+    header = False
+
+    if not isinstance(output_lines, list):
+        output_lines = output_lines.split('\n')
+
+    for line in output_lines:
+        if delimiter_line.match(line):
+            if not start:
+                start = True
+            elif not header:
+                # we are after head area
+                header = True
+            else:
+                # table ends here
+                start = header = None
+                table_.append(line)
+
+                parsed = table(table_)
+                parsed['label'] = label
+                tables_.append(parsed)
+
+                table_ = []
+                label = None
+                continue
+        if start:
+            table_.append(line)
+        else:
+            if label is None:
+                label = line
+            else:
+                LOG.warning('Invalid line between tables: %s', line)
+    if len(table_) > 0:
+        LOG.warning('Missing end of table')
+
+    return tables_
+
+
+def table(output_lines):
+    """Parse single table from cli output.
+
+    Return dict with list of column names in 'headers' key and
+    rows in 'values' key.
+    """
+    table_ = {'headers': [], 'values': []}
+    columns = None
+
+    if not isinstance(output_lines, list):
+        output_lines = output_lines.split('\n')
+
+    if not output_lines[-1]:
+        # skip last line if empty (just newline at the end)
+        output_lines = output_lines[:-1]
+
+    for line in output_lines:
+        if delimiter_line.match(line):
+            columns = _table_columns(line)
+            continue
+        if '|' not in line:
+            LOG.warning('skipping invalid table line: %s', line)
+            continue
+        row = []
+        for col in columns:
+            row.append(line[col[0]:col[1]].strip())
+        if table_['headers']:
+            table_['values'].append(row)
+        else:
+            table_['headers'] = row
+
+    return table_
+
+
+def _table_columns(first_table_row):
+    """Find column ranges in output line.
+
+    Return list of tuples (start,end) for each column
+    detected by plus (+) characters in delimiter line.
+    """
+    positions = []
+    start = 1  # there is '+' at 0
+    while start < len(first_table_row):
+        end = first_table_row.find('+', start)
+        if end == -1:
+            break
+        positions.append((start, end))
+        start = end + 1
+    return positions
diff --git a/tempest/stress/__init__.py b/tempest/lib/cmd/__init__.py
similarity index 100%
copy from tempest/stress/__init__.py
copy to tempest/lib/cmd/__init__.py
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
new file mode 100755
index 0000000..88ce775
--- /dev/null
+++ b/tempest/lib/cmd/check_uuid.py
@@ -0,0 +1,360 @@
+#!/usr/bin/env python
+
+# Copyright 2014 Mirantis, Inc.
+#
+# 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 argparse
+import ast
+import importlib
+import inspect
+import os
+import sys
+import unittest
+import uuid
+
+from oslo_utils import uuidutils
+import six.moves.urllib.parse as urlparse
+
+DECORATOR_MODULE = 'test'
+DECORATOR_NAME = 'idempotent_id'
+DECORATOR_IMPORT = 'tempest.%s' % DECORATOR_MODULE
+IMPORT_LINE = 'from tempest import %s' % DECORATOR_MODULE
+DECORATOR_TEMPLATE = "@%s.%s('%%s')" % (DECORATOR_MODULE,
+                                        DECORATOR_NAME)
+UNIT_TESTS_EXCLUDE = 'tempest.tests'
+
+
+class SourcePatcher(object):
+
+    """"Lazy patcher for python source files"""
+
+    def __init__(self):
+        self.source_files = None
+        self.patches = None
+        self.clear()
+
+    def clear(self):
+        """Clear inner state"""
+        self.source_files = {}
+        self.patches = {}
+
+    @staticmethod
+    def _quote(s):
+        return urlparse.quote(s)
+
+    @staticmethod
+    def _unquote(s):
+        return urlparse.unquote(s)
+
+    def add_patch(self, filename, patch, line_no):
+        """Add lazy patch"""
+        if filename not in self.source_files:
+            with open(filename) as f:
+                self.source_files[filename] = self._quote(f.read())
+        patch_id = uuidutils.generate_uuid()
+        if not patch.endswith('\n'):
+            patch += '\n'
+        self.patches[patch_id] = self._quote(patch)
+        lines = self.source_files[filename].split(self._quote('\n'))
+        lines[line_no - 1] = ''.join(('{%s:s}' % patch_id, lines[line_no - 1]))
+        self.source_files[filename] = self._quote('\n').join(lines)
+
+    @staticmethod
+    def _save_changes(filename, source):
+        print('%s fixed' % filename)
+        with open(filename, 'w') as f:
+            f.write(source)
+
+    def apply_patches(self):
+        """Apply all patches"""
+        for filename in self.source_files:
+            patched_source = self._unquote(
+                self.source_files[filename].format(**self.patches)
+            )
+            self._save_changes(filename, patched_source)
+        self.clear()
+
+
+class TestChecker(object):
+
+    def __init__(self, package):
+        self.package = package
+        self.base_path = os.path.abspath(os.path.dirname(package.__file__))
+
+    def _path_to_package(self, path):
+        relative_path = path[len(self.base_path) + 1:]
+        if relative_path:
+            return '.'.join((self.package.__name__,) +
+                            tuple(relative_path.split('/')))
+        else:
+            return self.package.__name__
+
+    def _modules_search(self):
+        """Recursive search for python modules in base package"""
+        modules = []
+        for root, dirs, files in os.walk(self.base_path):
+            if not os.path.exists(os.path.join(root, '__init__.py')):
+                continue
+            root_package = self._path_to_package(root)
+            for item in files:
+                if item.endswith('.py'):
+                    module_name = '.'.join((root_package,
+                                           os.path.splitext(item)[0]))
+                    if not module_name.startswith(UNIT_TESTS_EXCLUDE):
+                        modules.append(module_name)
+        return modules
+
+    @staticmethod
+    def _get_idempotent_id(test_node):
+        """Return key-value dict with all metadata from @test.idempotent_id"""
+        idempotent_id = None
+        for decorator in test_node.decorator_list:
+            if (hasattr(decorator, 'func') and
+                hasattr(decorator.func, 'attr') and
+                decorator.func.attr == DECORATOR_NAME and
+                hasattr(decorator.func, 'value') and
+                decorator.func.value.id == DECORATOR_MODULE):
+                for arg in decorator.args:
+                    idempotent_id = ast.literal_eval(arg)
+        return idempotent_id
+
+    @staticmethod
+    def _is_decorator(line):
+        return line.strip().startswith('@')
+
+    @staticmethod
+    def _is_def(line):
+        return line.strip().startswith('def ')
+
+    def _add_uuid_to_test(self, patcher, test_node, source_path):
+        with open(source_path) as src:
+            src_lines = src.read().split('\n')
+        lineno = test_node.lineno
+        insert_position = lineno
+        while True:
+            if (self._is_def(src_lines[lineno - 1]) or
+                    (self._is_decorator(src_lines[lineno - 1]) and
+                        (DECORATOR_TEMPLATE.split('(')[0] <=
+                            src_lines[lineno - 1].strip().split('(')[0]))):
+                insert_position = lineno
+                break
+            lineno += 1
+        patcher.add_patch(
+            source_path,
+            ' ' * test_node.col_offset + DECORATOR_TEMPLATE % uuid.uuid4(),
+            insert_position
+        )
+
+    @staticmethod
+    def _is_test_case(module, node):
+        if (node.__class__ is ast.ClassDef and
+                hasattr(module, node.name) and
+                inspect.isclass(getattr(module, node.name))):
+            return issubclass(getattr(module, node.name), unittest.TestCase)
+
+    @staticmethod
+    def _is_test_method(node):
+        return (node.__class__ is ast.FunctionDef
+                and node.name.startswith('test_'))
+
+    @staticmethod
+    def _next_node(body, node):
+        if body.index(node) < len(body):
+            return body[body.index(node) + 1]
+
+    @staticmethod
+    def _import_name(node):
+        if isinstance(node, ast.Import):
+            return node.names[0].name
+        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):
+        with open(source_path) as f:
+            src_lines = f.read().split('\n')
+        line_no = 0
+        tempest_imports = [node for node in src_parsed.body
+                           if self._import_name(node) and
+                           'tempest.' in self._import_name(node)]
+        if not tempest_imports:
+            import_snippet = '\n'.join(('', IMPORT_LINE, ''))
+        else:
+            for node in tempest_imports:
+                if self._import_name(node) < DECORATOR_IMPORT:
+                    continue
+                else:
+                    line_no = node.lineno
+                    import_snippet = IMPORT_LINE
+                    break
+            else:
+                line_no = tempest_imports[-1].lineno
+                while True:
+                    if (not src_lines[line_no - 1] or
+                            getattr(self._next_node(src_parsed.body,
+                                                    tempest_imports[-1]),
+                                    'lineno') == line_no or
+                            line_no == len(src_lines)):
+                        break
+                    line_no += 1
+                import_snippet = '\n'.join((IMPORT_LINE, ''))
+        patcher.add_patch(source_path, import_snippet, line_no)
+
+    def get_tests(self):
+        """Get test methods with sources from base package with metadata"""
+        tests = {}
+        for module_name in self._modules_search():
+            tests[module_name] = {}
+            module = importlib.import_module(module_name)
+            source_path = '.'.join(
+                (os.path.splitext(module.__file__)[0], 'py')
+            )
+            with open(source_path, 'r') as f:
+                source = f.read()
+            tests[module_name]['source_path'] = source_path
+            tests[module_name]['tests'] = {}
+            source_parsed = ast.parse(source)
+            tests[module_name]['ast'] = source_parsed
+            tests[module_name]['import_valid'] = (
+                hasattr(module, DECORATOR_MODULE) and
+                inspect.ismodule(getattr(module, DECORATOR_MODULE))
+            )
+            test_cases = (node for node in source_parsed.body
+                          if self._is_test_case(module, node))
+            for node in test_cases:
+                for subnode in filter(self._is_test_method, node.body):
+                        test_name = '%s.%s' % (node.name, subnode.name)
+                        tests[module_name]['tests'][test_name] = subnode
+        return tests
+
+    @staticmethod
+    def _filter_tests(function, tests):
+        """Filter tests with condition 'function(test_node) == True'"""
+        result = {}
+        for module_name in tests:
+            for test_name in tests[module_name]['tests']:
+                if function(module_name, test_name, tests):
+                    if module_name not in result:
+                        result[module_name] = {
+                            'ast': tests[module_name]['ast'],
+                            'source_path': tests[module_name]['source_path'],
+                            'import_valid': tests[module_name]['import_valid'],
+                            'tests': {}
+                        }
+                    result[module_name]['tests'][test_name] = \
+                        tests[module_name]['tests'][test_name]
+        return result
+
+    def find_untagged(self, tests):
+        """Filter all tests without uuid in metadata"""
+        def check_uuid_in_meta(module_name, test_name, tests):
+            idempotent_id = self._get_idempotent_id(
+                tests[module_name]['tests'][test_name])
+            return not idempotent_id
+        return self._filter_tests(check_uuid_in_meta, tests)
+
+    def report_collisions(self, tests):
+        """Reports collisions if there are any
+
+        Returns true if collisions exist.
+        """
+        uuids = {}
+
+        def report(module_name, test_name, tests):
+            test_uuid = self._get_idempotent_id(
+                tests[module_name]['tests'][test_name])
+            if not test_uuid:
+                return
+            if test_uuid in uuids:
+                error_str = "%s:%s\n uuid %s collision: %s<->%s\n%s:%s" % (
+                    tests[module_name]['source_path'],
+                    tests[module_name]['tests'][test_name].lineno,
+                    test_uuid,
+                    test_name,
+                    uuids[test_uuid]['test_name'],
+                    uuids[test_uuid]['source_path'],
+                    uuids[test_uuid]['test_node'].lineno,
+                )
+                print(error_str)
+                print("cannot automatically resolve the collision, please "
+                      "manually remove the duplicate value on the new test.")
+                return True
+            else:
+                uuids[test_uuid] = {
+                    'module': module_name,
+                    'test_name': test_name,
+                    'test_node': tests[module_name]['tests'][test_name],
+                    'source_path': tests[module_name]['source_path']
+                }
+        return bool(self._filter_tests(report, tests))
+
+    def report_untagged(self, tests):
+        """Reports untagged tests if there are any
+
+        Returns true if untagged tests exist.
+        """
+        def report(module_name, test_name, tests):
+            error_str = "%s:%s\nmissing @test.idempotent_id('...')\n%s\n" % (
+                tests[module_name]['source_path'],
+                tests[module_name]['tests'][test_name].lineno,
+                test_name
+            )
+            print(error_str)
+            return True
+        return bool(self._filter_tests(report, tests))
+
+    def fix_tests(self, tests):
+        """Add uuids to all specified in tests and fix it in source files"""
+        patcher = SourcePatcher()
+        for module_name in tests:
+            add_import_once = True
+            for test_name in tests[module_name]['tests']:
+                if not tests[module_name]['import_valid'] and add_import_once:
+                    self._add_import_for_test_uuid(
+                        patcher,
+                        tests[module_name]['ast'],
+                        tests[module_name]['source_path']
+                    )
+                    add_import_once = False
+                self._add_uuid_to_test(
+                    patcher, tests[module_name]['tests'][test_name],
+                    tests[module_name]['source_path'])
+        patcher.apply_patches()
+
+
+def run():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--package', action='store', dest='package',
+                        default='tempest', type=str,
+                        help='Package with tests')
+    parser.add_argument('--fix', action='store_true', dest='fix_tests',
+                        help='Attempt to fix tests without UUIDs')
+    args = parser.parse_args()
+    sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+    pkg = importlib.import_module(args.package)
+    checker = TestChecker(pkg)
+    errors = False
+    tests = checker.get_tests()
+    untagged = checker.find_untagged(tests)
+    errors = checker.report_collisions(tests) or errors
+    if args.fix_tests and untagged:
+        checker.fix_tests(untagged)
+    else:
+        errors = checker.report_untagged(untagged) or errors
+    if errors:
+        sys.exit("@test.idempotent_id existence and uniqueness checks failed\n"
+                 "Run 'tox -v -euuidgen' to automatically fix tests with\n"
+                 "missing @test.idempotent_id decorators.")
+
+if __name__ == '__main__':
+    run()
diff --git a/tempest/lib/cmd/skip_tracker.py b/tempest/lib/cmd/skip_tracker.py
new file mode 100755
index 0000000..07b811d
--- /dev/null
+++ b/tempest/lib/cmd/skip_tracker.py
@@ -0,0 +1,164 @@
+#!/usr/bin/env python2
+
+# 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.
+
+"""
+Track test skips via launchpadlib API and raise alerts if a bug
+is fixed but a skip is still in the Tempest test code
+"""
+
+import argparse
+import os
+import re
+
+from oslo_log import log as logging
+
+try:
+    from launchpadlib import launchpad
+except ImportError:
+    launchpad = None
+
+LPCACHEDIR = os.path.expanduser('~/.launchpadlib/cache')
+LOG = logging.getLogger(__name__)
+
+
+def parse_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('test_path', help='Path of test dir')
+    return parser.parse_args()
+
+
+def info(msg, *args, **kwargs):
+    LOG.info(msg, *args, **kwargs)
+
+
+def debug(msg, *args, **kwargs):
+    LOG.debug(msg, *args, **kwargs)
+
+
+def find_skips(start):
+    """Find the entire list of skipped tests.
+
+    Returns a list of tuples (method, bug) that represent
+    test methods that have been decorated to skip because of
+    a particular bug.
+    """
+    results = {}
+    debug("Searching in %s", start)
+    for root, _dirs, files in os.walk(start):
+        for name in files:
+            if name.startswith('test_') and name.endswith('py'):
+                path = os.path.join(root, name)
+                debug("Searching in %s", path)
+                temp_result = find_skips_in_file(path)
+                for method_name, bug_no in temp_result:
+                    if results.get(bug_no):
+                        result_dict = results.get(bug_no)
+                        if result_dict.get(name):
+                            result_dict[name].append(method_name)
+                        else:
+                            result_dict[name] = [method_name]
+                        results[bug_no] = result_dict
+                    else:
+                        results[bug_no] = {name: [method_name]}
+    return results
+
+
+def find_skips_in_file(path):
+    """Return the skip tuples in a test file."""
+    BUG_RE = re.compile(r'\s*@.*skip_because\(bug=[\'"](\d+)[\'"]')
+    DEF_RE = re.compile(r'\s*def (\w+)\(')
+    bug_found = False
+    results = []
+    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
+
+
+def get_results(result_dict):
+    results = []
+    for bug_no in result_dict:
+        for method in result_dict[bug_no]:
+            results.append((method, bug_no))
+    return results
+
+
+def main():
+    parser = parse_args()
+    results = find_skips(parser.test_path)
+    unique_bugs = sorted(set([bug for (method, bug) in get_results(results)]))
+    unskips = []
+    duplicates = []
+    info("Total bug skips found: %d", len(results))
+    info("Total unique bugs causing skips: %d", len(unique_bugs))
+    if launchpad is not None:
+        lp = launchpad.Launchpad.login_anonymously('grabbing bugs',
+                                                   'production',
+                                                   LPCACHEDIR)
+    else:
+        print("To check the bug status launchpadlib should be installed")
+        exit(1)
+
+    for bug_no in unique_bugs:
+        bug = lp.bugs[bug_no]
+        duplicate = bug.duplicate_of_link
+        if duplicate is not None:
+            dup_id = duplicate.split('/')[-1]
+            duplicates.append((bug_no, dup_id))
+        for task in bug.bug_tasks:
+            info("Bug #%7s (%12s - %12s)", bug_no,
+                 task.importance, task.status)
+            if task.status in ('Fix Released', 'Fix Committed'):
+                unskips.append(bug_no)
+
+    for bug_id, dup_id in duplicates:
+        if bug_id not in unskips:
+            dup_bug = lp.bugs[dup_id]
+            for task in dup_bug.bug_tasks:
+                info("Bug #%7s is a duplicate of Bug#%7s (%12s - %12s)",
+                     bug_id, dup_id, task.importance, task.status)
+                if task.status in ('Fix Released', 'Fix Committed'):
+                    unskips.append(bug_id)
+
+    unskips = sorted(set(unskips))
+    if unskips:
+        print("The following bugs have been fixed and the corresponding skips")
+        print("should be removed from the test cases:")
+        print()
+        for bug in unskips:
+            message = "  %7s in " % bug
+            locations = ["%s" % x for x in results[bug].keys()]
+            message += " and ".join(locations)
+            print(message)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/tempest/api_schema/__init__.py b/tempest/lib/common/__init__.py
similarity index 100%
copy from tempest/api_schema/__init__.py
copy to tempest/lib/common/__init__.py
diff --git a/tempest/lib/common/api_version_request.py b/tempest/lib/common/api_version_request.py
new file mode 100644
index 0000000..b2b68a6
--- /dev/null
+++ b/tempest/lib/common/api_version_request.py
@@ -0,0 +1,159 @@
+# Copyright 2014 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 re
+
+from tempest.lib import exceptions
+
+
+# Define the minimum and maximum version of the API across all of the
+# REST API. The format of the version is:
+# X.Y where:
+#
+# - X will only be changed if a significant backwards incompatible API
+# change is made which affects the API as whole. That is, something
+# that is only very very rarely incremented.
+#
+# - Y when you make any change to the API. Note that this includes
+# semantic changes which may not affect the input or output formats or
+# even originate in the API code layer. We are not distinguishing
+# between backwards compatible and backwards incompatible changes in
+# the versioning system. It must be made clear in the documentation as
+# to what is a backwards compatible change and what is a backwards
+# incompatible one.
+
+class APIVersionRequest(object):
+    """This class represents an API Version Request.
+
+    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
+    # projects(Nova, etc.) never achieve this number.
+    latest_ver_major = 99999
+    latest_ver_minor = 99999
+
+    def __init__(self, version_string=None):
+        """Create an API version request object."""
+        # NOTE(gmann): 'version_string' as String "None" will be considered as
+        # invalid version string.
+        self.ver_major = 0
+        self.ver_minor = 0
+
+        if version_string is not None:
+            match = re.match(r"^([1-9]\d*)\.([1-9]\d*|0)$",
+                             version_string)
+            if match:
+                self.ver_major = int(match.group(1))
+                self.ver_minor = int(match.group(2))
+            elif version_string == 'latest':
+                self.ver_major = self.latest_ver_major
+                self.ver_minor = self.latest_ver_minor
+            else:
+                raise exceptions.InvalidAPIVersionString(
+                    version=version_string)
+
+    def __str__(self):
+        """Debug/Logging representation of object."""
+        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):
+        return TypeError("'%(other)s' should be an instance of '%(cls)s'" %
+                         {"other": other, "cls": self.__class__})
+
+    def __lt__(self, other):
+        if not isinstance(other, APIVersionRequest):
+            raise self._format_type_error(other)
+
+        return ((self.ver_major, self.ver_minor) <
+                (other.ver_major, other.ver_minor))
+
+    def __eq__(self, other):
+        if not isinstance(other, APIVersionRequest):
+            raise self._format_type_error(other)
+
+        return ((self.ver_major, self.ver_minor) ==
+                (other.ver_major, other.ver_minor))
+
+    def __gt__(self, other):
+        if not isinstance(other, APIVersionRequest):
+            raise self._format_type_error(other)
+
+        return ((self.ver_major, self.ver_minor) >
+                (other.ver_major, other.ver_minor))
+
+    def __le__(self, other):
+        return self < other or self == other
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __ge__(self, other):
+        return self > other or self == other
+
+    def matches(self, min_version, max_version):
+        """Matches the version object.
+
+        Returns whether the version object represents a version
+        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
+
+        If min_version is null then there is no minimum limit.
+        If max_version is null then there is no maximum limit.
+        If self is null then raise ValueError
+        """
+
+        if self.is_null():
+            raise ValueError
+        if max_version.is_null() and min_version.is_null():
+            return True
+        elif max_version.is_null():
+            return min_version <= self
+        elif min_version.is_null():
+            return self <= max_version
+        else:
+            return min_version <= self <= max_version
+
+    def get_string(self):
+        """Version string representation.
+
+        Converts object to string representation which if used to create
+        an APIVersionRequest object results in the same version request.
+        """
+        if self.is_null():
+            return None
+        if (self.ver_major == self.latest_ver_major and
+            self.ver_minor == self.latest_ver_minor):
+            return 'latest'
+        return "%s.%s" % (self.ver_major, self.ver_minor)
diff --git a/tempest/lib/common/api_version_utils.py b/tempest/lib/common/api_version_utils.py
new file mode 100644
index 0000000..1371b3c
--- /dev/null
+++ b/tempest/lib/common/api_version_utils.py
@@ -0,0 +1,122 @@
+# 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 testtools
+
+from tempest.lib.common import api_version_request
+from tempest.lib import exceptions
+
+
+LATEST_MICROVERSION = 'latest'
+
+
+class BaseMicroversionTest(object):
+    """Mixin class for API microversion test class."""
+
+    # NOTE: Basically, each microversion is small API change and we
+    # can use the same tests for most microversions in most cases.
+    # So it is nice to define the test class as possible to run
+    # for all microversions. We need to define microversion range
+    # (min_microversion, max_microversion) on each test class if necessary.
+    min_microversion = None
+    max_microversion = LATEST_MICROVERSION
+
+
+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)
+    config_max_version = api_version_request.APIVersionRequest(cfg_max_version)
+    if ((min_version > max_version) or
+       (config_min_version > config_max_version)):
+        msg = ("Test Class versions [%s - %s]. "
+               "Configuration versions [%s - %s]."
+               % (min_version.get_string(),
+                  max_version.get_string(),
+                  config_min_version.get_string(),
+                  config_max_version.get_string()))
+        raise exceptions.InvalidAPIVersionRange(msg)
+
+    # NOTE: Select tests which are in range of configuration like
+    #               config min           config max
+    # ----------------+--------------------------+----------------
+    # ...don't-select|
+    #            ...select...  ...select...  ...select...
+    #                                             |don't-select...
+    # ......................select............................
+    if (max_version < config_min_version or
+        config_max_version < min_version):
+        msg = ("The microversion range[%s - %s] of this test is out of the "
+               "configuration range[%s - %s]."
+               % (min_version.get_string(),
+                  max_version.get_string(),
+                  config_min_version.get_string(),
+                  config_max_version.get_string()))
+        raise testtools.TestCase.skipException(msg)
+
+
+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
+    return max_version.get_string()
+
+
+def assert_version_header_matches_request(api_microversion_header_name,
+                                          api_microversion,
+                                          response_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
+            Example- "X-OpenStack-Nova-API-Version"
+    :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()
+    if (api_microversion_header_name not in response_header or
+        api_microversion != response_header[api_microversion_header_name]):
+        msg = ("Microversion header '%s' with value '%s' does not match in "
+               "response - %s. " % (api_microversion_header_name,
+                                    api_microversion,
+                                    response_header))
+        raise exceptions.InvalidHTTPResponseHeader(msg)
diff --git a/tempest/lib/common/cred_client.py b/tempest/lib/common/cred_client.py
new file mode 100644
index 0000000..ea06011
--- /dev/null
+++ b/tempest/lib/common/cred_client.py
@@ -0,0 +1,211 @@
+# 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 abc
+
+from oslo_log import log as logging
+import six
+
+from tempest.lib import auth
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.identity.v2 import identity_client as v2_identity
+
+LOG = logging.getLogger(__name__)
+
+
+@six.add_metaclass(abc.ABCMeta)
+class CredsClient(object):
+    """This class is a wrapper around the identity clients
+
+     to provide a single interface for managing credentials in both v2 and v3
+     cases. It's not bound to created credentials, only to a specific set of
+     admin credentials used for generating credentials.
+    """
+
+    def __init__(self, identity_client, projects_client, users_client,
+                 roles_client):
+        # The client implies version and credentials
+        self.identity_client = identity_client
+        self.users_client = users_client
+        self.projects_client = projects_client
+        self.roles_client = roles_client
+
+    def create_user(self, username, password, project, email):
+        params = {'name': username,
+                  'password': password,
+                  self.project_id_param: project['id'],
+                  'email': email}
+        user = self.users_client.create_user(**params)
+        if 'user' in user:
+            user = user['user']
+        return user
+
+    def delete_user(self, user_id):
+        self.users_client.delete_user(user_id)
+
+    @abc.abstractmethod
+    def create_project(self, name, description):
+        pass
+
+    def _check_role_exists(self, role_name):
+        try:
+            roles = self._list_roles()
+            role = next(r for r in roles if r['name'] == role_name)
+        except StopIteration:
+            return None
+        return role
+
+    def create_user_role(self, role_name):
+        if not self._check_role_exists(role_name):
+            self.roles_client.create_role(name=role_name)
+
+    def assign_user_role(self, user, project, role_name):
+        role = self._check_role_exists(role_name)
+        if not role:
+            msg = 'No "%s" role found' % role_name
+            raise lib_exc.NotFound(msg)
+        try:
+            self.roles_client.create_user_role_on_project(project['id'],
+                                                          user['id'],
+                                                          role['id'])
+        except lib_exc.Conflict:
+            LOG.debug("Role %s already assigned on project %s for user %s",
+                      role['id'], project['id'], user['id'])
+
+    @abc.abstractmethod
+    def get_credentials(self, user, project, password):
+        """Produces a Credentials object from the details provided
+
+        :param user: a user dict
+        :param project: a project dict
+        :param password: the password as a string
+        :return: a Credentials object with all the available credential details
+        """
+        pass
+
+    def _list_roles(self):
+        roles = self.roles_client.list_roles()['roles']
+        return roles
+
+
+class V2CredsClient(CredsClient):
+    project_id_param = 'tenantId'
+
+    def __init__(self, identity_client, projects_client, users_client,
+                 roles_client):
+        super(V2CredsClient, self).__init__(identity_client,
+                                            projects_client,
+                                            users_client,
+                                            roles_client)
+
+    def create_project(self, name, description):
+        tenant = self.projects_client.create_tenant(
+            name=name, description=description)['tenant']
+        return tenant
+
+    def delete_project(self, project_id):
+        self.projects_client.delete_tenant(project_id)
+
+    def get_credentials(self, user, project, password):
+        # User and project already include both ID and name here,
+        # so there's no need to use the fill_in mode
+        return auth.get_credentials(
+            auth_url=None,
+            fill_in=False,
+            identity_version='v2',
+            username=user['name'], user_id=user['id'],
+            tenant_name=project['name'], tenant_id=project['id'],
+            password=password)
+
+
+class V3CredsClient(CredsClient):
+    project_id_param = 'project_id'
+
+    def __init__(self, identity_client, projects_client, users_client,
+                 roles_client, domains_client, domain_name):
+        super(V3CredsClient, self).__init__(identity_client, projects_client,
+                                            users_client, roles_client)
+        self.domains_client = domains_client
+
+        try:
+            # Domain names must be unique, in any case a list is returned,
+            # selecting the first (and only) element
+            self.creds_domain = self.domains_client.list_domains(
+                name=domain_name)['domains'][0]
+        except lib_exc.NotFound:
+            # TODO(andrea) we could probably create the domain on the fly
+            msg = "Requested domain %s could not be found" % domain_name
+            raise lib_exc.InvalidCredentials(msg)
+
+    def create_project(self, name, description):
+        project = self.projects_client.create_project(
+            name=name, description=description,
+            domain_id=self.creds_domain['id'])['project']
+        return project
+
+    def delete_project(self, project_id):
+        self.projects_client.delete_project(project_id)
+
+    def get_credentials(self, user, project, password):
+        # User, project and domain already include both ID and name here,
+        # so there's no need to use the fill_in mode.
+        # NOTE(andreaf) We need to set all fields in the returned credentials.
+        # Scope is then used to pick only those relevant for the type of
+        # token needed by each service client.
+        return auth.get_credentials(
+            auth_url=None,
+            fill_in=False,
+            identity_version='v3',
+            username=user['name'], user_id=user['id'],
+            project_name=project['name'], project_id=project['id'],
+            password=password,
+            project_domain_id=self.creds_domain['id'],
+            project_domain_name=self.creds_domain['name'],
+            domain_id=self.creds_domain['id'],
+            domain_name=self.creds_domain['name'])
+
+    def assign_user_role_on_domain(self, user, role_name, domain=None):
+        """Assign the specified role on a domain
+
+        :param user: a user dict
+        :param role_name: name of the role to be assigned
+        :param domain: (optional) The domain to assign the role on. If not
+                                  specified the default domain of cred_client
+        """
+        # NOTE(andreaf) This method is very specific to the v3 case, and
+        # because of that it's not defined in the parent class.
+        if domain is None:
+            domain = self.creds_domain
+        role = self._check_role_exists(role_name)
+        if not role:
+            msg = 'No "%s" role found' % role_name
+            raise lib_exc.NotFound(msg)
+        try:
+            self.roles_client.create_user_role_on_domain(
+                domain['id'], user['id'], role['id'])
+        except lib_exc.Conflict:
+            LOG.debug("Role %s already assigned on domain %s for user %s",
+                      role['id'], domain['id'], user['id'])
+
+
+def get_creds_client(identity_client,
+                     projects_client,
+                     users_client,
+                     roles_client,
+                     domains_client=None,
+                     project_domain_name=None):
+    if isinstance(identity_client, v2_identity.IdentityClient):
+        return V2CredsClient(identity_client, projects_client, users_client,
+                             roles_client)
+    else:
+        return V3CredsClient(identity_client, projects_client, users_client,
+                             roles_client, domains_client, project_domain_name)
diff --git a/tempest/lib/common/cred_provider.py b/tempest/lib/common/cred_provider.py
new file mode 100644
index 0000000..1b450ab
--- /dev/null
+++ b/tempest/lib/common/cred_provider.py
@@ -0,0 +1,103 @@
+# Copyright (c) 2014 Deutsche Telekom AG
+# Copyright (c) 2014 Hewlett-Packard 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.
+
+import abc
+
+import six
+
+from tempest.lib import auth
+from tempest.lib import exceptions
+
+
+@six.add_metaclass(abc.ABCMeta)
+class CredentialProvider(object):
+    def __init__(self, identity_version, name=None, network_resources=None,
+                 credentials_domain=None, admin_role=None):
+        """A CredentialProvider supplies credentials to test classes.
+
+        :param identity_version: Identity version of the credentials provided
+        :param name: Name of the calling test. Included in provisioned
+                     credentials when credentials are provisioned on the fly
+        :param network_resources: Network resources required for the
+                                  credentials
+        :param credentials_domain: Domain credentials belong to
+        :param admin_role: Name of the role of the admin account
+        """
+        self.identity_version = identity_version
+        self.name = name or "test_creds"
+        self.network_resources = network_resources
+        self.credentials_domain = credentials_domain or 'Default'
+        self.admin_role = admin_role
+        if not auth.is_identity_version_supported(self.identity_version):
+            raise exceptions.InvalidIdentityVersion(
+                identity_version=self.identity_version)
+
+    @abc.abstractmethod
+    def get_primary_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_admin_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_alt_creds(self):
+        return
+
+    @abc.abstractmethod
+    def clear_creds(self):
+        return
+
+    @abc.abstractmethod
+    def is_multi_user(self):
+        return
+
+    @abc.abstractmethod
+    def is_multi_tenant(self):
+        return
+
+    @abc.abstractmethod
+    def get_creds_by_roles(self, roles, force_new=False):
+        return
+
+    @abc.abstractmethod
+    def is_role_available(self, role):
+        return
+
+
+class TestResources(object):
+    """Readonly Credentials, with network resources added."""
+
+    def __init__(self, credentials):
+        self._credentials = credentials
+        self.network = None
+        self.subnet = None
+        self.router = None
+
+    def __getattr__(self, item):
+        return getattr(self._credentials, item)
+
+    def __str__(self):
+        _format = "Credentials: %s, Network: %s, Subnet: %s, Router: %s"
+        return _format % (self._credentials, self.network, self.subnet,
+                          self.router)
+
+    def set_resources(self, **kwargs):
+        for key in kwargs:
+            if hasattr(self, key):
+                setattr(self, key, kwargs[key])
+
+    @property
+    def credentials(self):
+        return self._credentials
diff --git a/tempest/lib/common/http.py b/tempest/lib/common/http.py
new file mode 100644
index 0000000..8a47d44
--- /dev/null
+++ b/tempest/lib/common/http.py
@@ -0,0 +1,61 @@
+# Copyright 2016 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
+import urllib3
+
+
+class ClosingHttp(urllib3.poolmanager.PoolManager):
+    def __init__(self, disable_ssl_certificate_validation=False,
+                 ca_certs=None, timeout=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
+
+        if timeout:
+            kwargs['timeout'] = timeout
+
+        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():
+                    # We assume HTTP header name to be string, not random
+                    # bytes, thus ensure we have string keys.
+                    self[six.u(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)
+
+        # 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
new file mode 100644
index 0000000..2c36f55
--- /dev/null
+++ b/tempest/lib/common/rest_client.py
@@ -0,0 +1,956 @@
+# Copyright 2012 OpenStack Foundation
+# Copyright 2013 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 collections
+import email.utils
+import re
+import time
+
+import jsonschema
+from oslo_log import log as logging
+from oslo_serialization import jsonutils as json
+import six
+
+from tempest.lib.common import http
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
+
+# redrive rate limited calls at most twice
+MAX_RECURSION_DEPTH = 2
+
+# All the successful HTTP status codes from RFC 7231 & 4918
+HTTP_SUCCESS = (200, 201, 202, 203, 204, 205, 206, 207)
+
+# All the redirection HTTP status codes from RFC 7231 & 4918
+HTTP_REDIRECTION = (300, 301, 302, 303, 304, 305, 306, 307)
+
+# JSON Schema validator and format checker used for JSON Schema validation
+JSONSCHEMA_VALIDATOR = jsonschema.Draft4Validator
+FORMAT_CHECKER = jsonschema.draft4_format_checker
+
+
+class RestClient(object):
+    """Unified OpenStack RestClient class
+
+    This class is used for building openstack api clients on top of. It is
+    intended to provide a base layer for wrapping outgoing http requests in
+    keystone auth as well as providing response code checking and error
+    handling.
+
+    :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 str name: The endpoint name to use for the catalog lookup; this
+                     returns only if the service exists
+    :param str endpoint_type: The endpoint type to use for the catalog lookup
+    :param int build_interval: Time in seconds between to status checks in
+                               wait loops
+    :param int build_timeout: Timeout in seconds to wait for a wait operation.
+    :param bool disable_ssl_certificate_validation: Set to true to disable ssl
+                                                    certificate validation
+    :param str ca_certs: File containing the CA Bundle to use in verifying a
+                         TLS server cert
+    :param str trace_requests: Regex to use for specifying logging the entirety
+                              of the request and response payload
+    :param str http_timeout: Timeout in seconds to wait for the http request to
+                             return
+    """
+    TYPE = "json"
+
+    # The version of the API this client implements
+    api_version = None
+
+    LOG = logging.getLogger(__name__)
+
+    def __init__(self, auth_provider, service, region,
+                 endpoint_type='publicURL',
+                 build_interval=1, build_timeout=60,
+                 disable_ssl_certificate_validation=False, ca_certs=None,
+                 trace_requests='', name=None, http_timeout=None):
+        self.auth_provider = auth_provider
+        self.service = service
+        self.region = region
+        self.name = name
+        self.endpoint_type = endpoint_type
+        self.build_interval = build_interval
+        self.build_timeout = build_timeout
+        self.trace_requests = trace_requests
+
+        self._skip_path = False
+        self.general_header_lc = set(('cache-control', 'connection',
+                                      'date', 'pragma', 'trailer',
+                                      'transfer-encoding', 'via',
+                                      'warning'))
+        self.response_header_lc = set(('accept-ranges', 'age', 'etag',
+                                       'location', 'proxy-authenticate',
+                                       'retry-after', 'server',
+                                       'vary', 'www-authenticate'))
+        dscv = disable_ssl_certificate_validation
+        self.http_obj = http.ClosingHttp(
+            disable_ssl_certificate_validation=dscv, ca_certs=ca_certs,
+            timeout=http_timeout)
+
+    def _get_type(self):
+        if self.TYPE != "json":
+            self.LOG.warning("Tempest has dropped XML support and the TYPE "
+                             "became meaningless")
+        return self.TYPE
+
+    def get_headers(self, accept_type=None, send_type=None):
+        """Return the default headers which will be used with outgoing requests
+
+        :param str accept_type: The media type to use for the Accept header, if
+                                one isn't provided the object var TYPE will be
+                                used
+        :param str send_type: The media-type to use for the Content-Type
+                              header, if one isn't provided the object var
+                              TYPE will be used
+        :rtype: dict
+        :return: The dictionary of headers which can be used in the headers
+                 dict for outgoing request
+        """
+        if accept_type is None:
+            accept_type = self._get_type()
+        if send_type is None:
+            send_type = self._get_type()
+        return {'Content-Type': 'application/%s' % send_type,
+                'Accept': 'application/%s' % accept_type}
+
+    def __str__(self):
+        STRING_LIMIT = 80
+        str_format = ("service:%s, base_url:%s, "
+                      "filters: %s, build_interval:%s, build_timeout:%s"
+                      "\ntoken:%s..., \nheaders:%s...")
+        return str_format % (self.service, self.base_url,
+                             self.filters, self.build_interval,
+                             self.build_timeout,
+                             str(self.token)[0:STRING_LIMIT],
+                             str(self.get_headers())[0:STRING_LIMIT])
+
+    @property
+    def user(self):
+        """The username used for requests
+
+        :rtype: string
+        :return: The username being used for requests
+        """
+
+        return self.auth_provider.credentials.username
+
+    @property
+    def user_id(self):
+        """The user_id used for requests
+
+        :rtype: string
+        :return: The user id being used for requests
+        """
+        return self.auth_provider.credentials.user_id
+
+    @property
+    def tenant_name(self):
+        """The tenant/project being used for requests
+
+        :rtype: string
+        :return: The tenant/project name being used for requests
+        """
+        return self.auth_provider.credentials.tenant_name
+
+    @property
+    def tenant_id(self):
+        """The tenant/project id being used for requests
+
+        :rtype: string
+        :return: The tenant/project id being used for requests
+        """
+        return self.auth_provider.credentials.tenant_id
+
+    @property
+    def password(self):
+        """The password being used for requests
+
+        :rtype: string
+        :return: The password being used for requests
+        """
+        return self.auth_provider.credentials.password
+
+    @property
+    def base_url(self):
+        return self.auth_provider.base_url(filters=self.filters)
+
+    @property
+    def token(self):
+        return self.auth_provider.get_token()
+
+    @property
+    def filters(self):
+        _filters = dict(
+            service=self.service,
+            endpoint_type=self.endpoint_type,
+            region=self.region,
+            name=self.name
+        )
+        if self.api_version is not None:
+            _filters['api_version'] = self.api_version
+        if self._skip_path:
+            _filters['skip_path'] = self._skip_path
+        return _filters
+
+    def skip_path(self):
+        """When set, ignore the path part of the base URL from the catalog"""
+        self._skip_path = True
+
+    def reset_path(self):
+        """When reset, use the base URL from the catalog as-is"""
+        self._skip_path = False
+
+    @classmethod
+    def expected_success(cls, expected_code, read_code):
+        """Check expected success response code against the http response
+
+        :param int expected_code: The response code that is expected.
+                                  Optionally a list of integers can be used
+                                  to specify multiple valid success codes
+        :param int read_code: The response code which was returned in the
+                              response
+        :raises AssertionError: if the expected_code isn't a valid http success
+                                response code
+        :raises exceptions.InvalidHttpSuccessCode: if the read code isn't an
+                                                   expected http success code
+        """
+        if not isinstance(read_code, int):
+            raise TypeError("'read_code' must be an int instead of (%s)"
+                            % type(read_code))
+
+        assert_msg = ("This function only allowed to use for HTTP status "
+                      "codes which explicitly defined in the RFC 7231 & 4918. "
+                      "{0} is not a defined Success Code!"
+                      ).format(expected_code)
+        if isinstance(expected_code, list):
+            for code in expected_code:
+                assert code in HTTP_SUCCESS + HTTP_REDIRECTION, assert_msg
+        else:
+            assert expected_code in HTTP_SUCCESS + HTTP_REDIRECTION, assert_msg
+
+        # NOTE(afazekas): the http status code above 400 is processed by
+        # the _error_checker method
+        if read_code < 400:
+            pattern = ("Unexpected http success status code {0}, "
+                       "The expected status code is {1}")
+            if ((not isinstance(expected_code, list) and
+                 (read_code != expected_code)) or
+                (isinstance(expected_code, list) and
+                 (read_code not in expected_code))):
+                details = pattern.format(read_code, expected_code)
+                raise exceptions.InvalidHttpSuccessCode(details)
+
+    def post(self, url, body, headers=None, extra_headers=False,
+             chunked=False):
+        """Send a HTTP POST request using keystone auth
+
+        :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 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 bool chunked: sends the body with chunked encoding
+        :return: a tuple with the first entry containing the response headers
+                 and the second the response body
+        :rtype: tuple
+        """
+        return self.request('POST', url, extra_headers, headers, body, chunked)
+
+    def get(self, url, headers=None, extra_headers=False):
+        """Send a HTTP GET request using keystone service catalog and auth
+
+        :param str url: the relative url to send the post request to
+        :param dict headers: The headers to use for the request
+        :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
+        """
+        return self.request('GET', url, extra_headers, headers)
+
+    def delete(self, url, headers=None, body=None, extra_headers=False):
+        """Send a HTTP DELETE request using keystone service catalog and auth
+
+        :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 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
+        """
+        return self.request('DELETE', url, extra_headers, headers, body)
+
+    def patch(self, url, body, headers=None, extra_headers=False):
+        """Send a HTTP PATCH request using keystone service catalog and auth
+
+        :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 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
+        """
+        return self.request('PATCH', url, extra_headers, headers, body)
+
+    def put(self, url, body, headers=None, extra_headers=False, chunked=False):
+        """Send a HTTP PUT request using keystone service catalog and auth
+
+        :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 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 bool chunked: sends the body with chunked encoding
+        :return: a tuple with the first entry containing the response headers
+                 and the second the response body
+        :rtype: tuple
+        """
+        return self.request('PUT', url, extra_headers, headers, body, chunked)
+
+    def head(self, url, headers=None, extra_headers=False):
+        """Send a HTTP HEAD request using keystone service catalog and auth
+
+        :param str url: the relative url to send the post request to
+        :param dict headers: The headers to use for the request
+        :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
+        """
+        return self.request('HEAD', url, extra_headers, headers)
+
+    def copy(self, url, headers=None, extra_headers=False):
+        """Send a HTTP COPY request using keystone service catalog and auth
+
+        :param str url: the relative url to send the post request to
+        :param dict headers: The headers to use for the request
+        :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
+        """
+        return self.request('COPY', url, extra_headers, headers)
+
+    def get_versions(self):
+        """Get the versions on a endpoint from the keystone catalog
+
+        This method will make a GET request on the baseurl from the keystone
+        catalog to return a list of API versions. It is expected that a GET
+        on the endpoint in the catalog will return a list of supported API
+        versions.
+
+        :return tuple with response headers and list of version numbers
+        :rtype: tuple
+        """
+        resp, body = self.get('')
+        body = self._parse_resp(body)
+        versions = map(lambda x: x['id'], body)
+        return resp, versions
+
+    def _get_request_id(self, resp):
+        for i in ('x-openstack-request-id', 'x-compute-request-id'):
+            if i in resp:
+                return resp[i]
+        return ""
+
+    def _safe_body(self, body, maxlen=4096):
+        # convert a structure into a string safely
+        try:
+            text = six.text_type(body)
+        except UnicodeDecodeError:
+            # if this isn't actually text, return marker that
+            return "<BinaryData: removed>"
+        if len(text) > maxlen:
+            return text[:maxlen]
+        else:
+            return text
+
+    def _log_request_start(self, method, req_url):
+        caller_name = test_utils.find_test_caller()
+        if self.trace_requests and re.search(self.trace_requests, caller_name):
+            self.LOG.debug('Starting Request (%s): %s %s', caller_name,
+                           method, req_url)
+
+    def _log_request_full(self, resp, req_headers=None, req_body=None,
+                          resp_body=None, extra=None):
+        if 'X-Auth-Token' in req_headers:
+            req_headers['X-Auth-Token'] = '<omitted>'
+        # A shallow copy is sufficient
+        resp_log = resp.copy()
+        if 'x-subject-token' in resp_log:
+            resp_log['x-subject-token'] = '<omitted>'
+        log_fmt = """Request - Headers: %s
+        Body: %s
+    Response - Headers: %s
+        Body: %s"""
+
+        self.LOG.debug(
+            log_fmt,
+            str(req_headers),
+            self._safe_body(req_body),
+            str(resp_log),
+            self._safe_body(resp_body),
+            extra=extra)
+
+    def _log_request(self, method, req_url, resp,
+                     secs="", req_headers=None,
+                     req_body=None, resp_body=None):
+        if req_headers is None:
+            req_headers = {}
+        # if we have the request id, put it in the right part of the log
+        extra = dict(request_id=self._get_request_id(resp))
+        # NOTE(sdague): while we still have 6 callers to this function
+        # we're going to just provide work around on who is actually
+        # providing timings by gracefully adding no content if they don't.
+        # Once we're down to 1 caller, clean this up.
+        caller_name = test_utils.find_test_caller()
+        if secs:
+            secs = " %.3fs" % secs
+        self.LOG.info(
+            'Request (%s): %s %s %s%s',
+            caller_name,
+            resp['status'],
+            method,
+            req_url,
+            secs,
+            extra=extra)
+
+        # Also look everything at DEBUG if you want to filter this
+        # out, don't run at debug.
+        if self.LOG.isEnabledFor(logging.DEBUG):
+            self._log_request_full(resp, req_headers, req_body,
+                                   resp_body, extra)
+
+    def _parse_resp(self, body):
+        try:
+            body = json.loads(body)
+        except ValueError:
+            return body
+
+        # We assume, that if the first value of the deserialized body's
+        # item set is a dict or a list, that we just return the first value
+        # of deserialized body.
+        # Essentially "cutting out" the first placeholder element in a body
+        # that looks like this:
+        #
+        #  {
+        #    "users": [
+        #      ...
+        #    ]
+        #  }
+        try:
+            # Ensure there are not more than one top-level keys
+            # NOTE(freerunner): Ensure, that JSON is not nullable to
+            # to prevent StopIteration Exception
+            if len(body.keys()) != 1:
+                return body
+            # Just return the "wrapped" element
+            first_key, first_item = six.next(six.iteritems(body))
+            if isinstance(first_item, (dict, list)):
+                return first_item
+        except (ValueError, IndexError):
+            pass
+        return body
+
+    def response_checker(self, method, resp, resp_body):
+        """A sanity check on the response from a HTTP request
+
+        This method does a sanity check on whether the response from an HTTP
+        request conforms the HTTP RFC.
+
+        :param str method: The HTTP verb of the request associated with the
+                           response being passed in.
+        :param resp: The response headers
+        :param resp_body: The body of the response
+        :raises ResponseWithNonEmptyBody: If the response with the status code
+                                          is not supposed to have a body
+        :raises ResponseWithEntity: If the response code is 205 but has an
+                                    entity
+        """
+        if (resp.status in set((204, 205, 304)) or resp.status < 200 or
+                method.upper() == 'HEAD') and resp_body:
+            raise exceptions.ResponseWithNonEmptyBody(status=resp.status)
+        # NOTE(afazekas):
+        # If the HTTP Status Code is 205
+        #   'The response MUST NOT include an entity.'
+        # A HTTP entity has an entity-body and an 'entity-header'.
+        # In the HTTP response specification (Section 6) the 'entity-header'
+        # 'generic-header' and 'response-header' are in OR relation.
+        # All headers not in the above two group are considered as entity
+        # header in every interpretation.
+
+        if (resp.status == 205 and
+            0 != len(set(resp.keys()) - set(('status',)) -
+                     self.response_header_lc - self.general_header_lc)):
+                        raise exceptions.ResponseWithEntity()
+        # NOTE(afazekas)
+        # Now the swift sometimes (delete not empty container)
+        # returns with non json error response, we can create new rest class
+        # for swift.
+        # Usually RFC2616 says error responses SHOULD contain an explanation.
+        # The warning is normal for SHOULD/SHOULD NOT case
+
+        # Likely it will cause an error
+        if method != 'HEAD' and not resp_body and resp.status >= 400:
+            self.LOG.warning("status >= 400 response with empty body")
+
+    def _request(self, method, url, headers=None, body=None, chunked=False):
+        """A simple HTTP request interface."""
+        # Authenticate the request with the auth provider
+        req_url, req_headers, req_body = self.auth_provider.auth_request(
+            method, url, headers, body, self.filters)
+
+        # Do the actual request, and time it
+        start = time.time()
+        self._log_request_start(method, req_url)
+        resp, resp_body = self.raw_request(
+            req_url, method, headers=req_headers, body=req_body,
+            chunked=chunked
+        )
+        end = time.time()
+        self._log_request(method, req_url, resp, secs=(end - start),
+                          req_headers=req_headers, req_body=req_body,
+                          resp_body=resp_body)
+
+        # Verify HTTP response codes
+        self.response_checker(method, resp, resp_body)
+
+        return resp, resp_body
+
+    def raw_request(self, url, method, headers=None, body=None, chunked=False):
+        """Send a raw HTTP request without the keystone catalog or auth
+
+        This method sends a HTTP request in the same manner as the request()
+        method, however it does so without using keystone auth or the catalog
+        to determine the base url. Additionally no response handling is done
+        the results from the request are just returned.
+
+        :param str url: Full url to send the request
+        :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 send with the request
+        :param bool chunked: sends the body with chunked encoding
+        :rtype: tuple
+        :return: a tuple with the first entry containing the response headers
+                 and the second the response body
+        """
+        if headers is None:
+            headers = self.get_headers()
+        return self.http_obj.request(url, method, headers=headers,
+                                     body=body, chunked=chunked)
+
+    def request(self, method, url, extra_headers=False, headers=None,
+                body=None, chunked=False):
+        """Send a HTTP request with keystone auth and using the catalog
+
+        This method will send an HTTP request using keystone auth in the
+        headers and the catalog to determine the endpoint to use for the
+        baseurl to send the request to. Additionally
+
+        When a response is received it will check it to see if an error
+        response was received. If it was an exception will be raised to enable
+        it to be handled quickly.
+
+        This method will also handle rate-limiting, if a 413 response code is
+        received it will retry the request after waiting the 'retry-after'
+        duration from the header.
+
+        :param str method: The HTTP verb to use for the request
+        :param str url: Relative url to send the request to
+        :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 send with the request
+        :param bool chunked: sends the body with chunked encoding
+        :rtype: tuple
+        :return: a tuple with the first entry containing the response headers
+                 and the second the response body
+        :raises UnexpectedContentType: If the content-type of the response
+                                       isn't an expect type
+        :raises Unauthorized: If a 401 response code is received
+        :raises Forbidden: If a 403 response code is received
+        :raises NotFound: If a 404 response code is received
+        :raises BadRequest: If a 400 response code is received
+        :raises Gone: If a 410 response code is received
+        :raises Conflict: If a 409 response code is received
+        :raises OverLimit: If a 413 response code is received and over_limit is
+                          not in the response body
+        :raises RateLimitExceeded: If a 413 response code is received and
+                                   over_limit is in the response body
+        :raises InvalidContentType: If a 415 response code is received
+        :raises UnprocessableEntity: If a 422 response code is received
+        :raises InvalidHTTPResponseBody: The response body wasn't valid JSON
+                                         and couldn't be parsed
+        :raises NotImplemented: If a 501 response code is received
+        :raises ServerFault: If a 500 response code is received
+        :raises UnexpectedResponseCode: If a response code above 400 is
+                                        received and it doesn't fall into any
+                                        of the handled checks
+        """
+        # if extra_headers is True
+        # default headers would be added to headers
+        retry = 0
+
+        if headers is None:
+            # NOTE(vponomaryov): if some client do not need headers,
+            # it should explicitly pass empty dict
+            headers = self.get_headers()
+        elif extra_headers:
+            try:
+                headers.update(self.get_headers())
+            except (ValueError, TypeError):
+                headers = self.get_headers()
+
+        resp, resp_body = self._request(method, url, headers=headers,
+                                        body=body, chunked=chunked)
+
+        while (resp.status == 413 and
+               'retry-after' in resp and
+                not self.is_absolute_limit(
+                    resp, self._parse_resp(resp_body)) and
+                retry < MAX_RECURSION_DEPTH):
+            retry += 1
+            delay = self._get_retry_after_delay(resp)
+            self.LOG.debug(
+                "Sleeping %s seconds based on retry-after header", delay
+            )
+            time.sleep(delay)
+            resp, resp_body = self._request(method, url,
+                                            headers=headers, body=body)
+        self._error_checker(resp, resp_body)
+        return resp, resp_body
+
+    def _get_retry_after_delay(self, resp):
+        """Extract the delay from the retry-after header.
+
+        This supports both integer and HTTP date formatted retry-after headers
+        per RFC 2616.
+
+        :param resp: The response containing the retry-after headers
+        :rtype: int
+        :return: The delay in seconds, clamped to be at least 1 second
+        :raises ValueError: On failing to parse the delay
+        """
+        delay = None
+        try:
+            delay = int(resp['retry-after'])
+        except (ValueError, KeyError):
+            pass
+
+        try:
+            retry_timestamp = self._parse_http_date(resp['retry-after'])
+            date_timestamp = self._parse_http_date(resp['date'])
+            delay = int(retry_timestamp - date_timestamp)
+        except (ValueError, OverflowError, KeyError):
+            pass
+
+        if delay is None:
+            raise ValueError(
+                "Failed to parse retry-after header %r as either int or "
+                "HTTP-date." % resp.get('retry-after')
+            )
+
+        # Retry-after headers do not have sub-second precision. Clients may
+        # receive a delay of 0. After sleeping 0 seconds, we would (likely) hit
+        # another 413. To avoid this, always sleep at least 1 second.
+        return max(1, delay)
+
+    def _parse_http_date(self, val):
+        """Parse an HTTP date, like 'Fri, 31 Dec 1999 23:59:59 GMT'.
+
+        Return an epoch timestamp (float), as returned by time.mktime().
+        """
+        parts = email.utils.parsedate(val)
+        if not parts:
+            raise ValueError("Failed to parse date %s" % val)
+        return time.mktime(parts)
+
+    def _error_checker(self, resp, resp_body):
+
+        # NOTE(mtreinish): Check for httplib response from glance_http. The
+        # object can't be used here because importing httplib breaks httplib2.
+        # If another object from a class not imported were passed here as
+        # resp this could possibly fail
+        if str(type(resp)) == "<type 'instance'>":
+            ctype = resp.getheader('content-type')
+        else:
+            try:
+                ctype = resp['content-type']
+            # NOTE(mtreinish): Keystone delete user responses doesn't have a
+            # content-type header. (They don't have a body) So just pretend it
+            # is set.
+            except KeyError:
+                ctype = 'application/json'
+
+        # It is not an error response
+        if resp.status < 400:
+            return
+
+        JSON_ENC = ['application/json', 'application/json; charset=utf-8']
+        # NOTE(mtreinish): This is for compatibility with Glance and swift
+        # APIs. These are the return content types that Glance api v1
+        # (and occasionally swift) are using.
+        TXT_ENC = ['text/plain', 'text/html', 'text/html; charset=utf-8',
+                   'text/plain; charset=utf-8']
+
+        if ctype.lower() in JSON_ENC:
+            parse_resp = True
+        elif ctype.lower() in TXT_ENC:
+            parse_resp = False
+        else:
+            raise exceptions.UnexpectedContentType(str(resp.status),
+                                                   resp=resp)
+
+        if resp.status == 401:
+            if parse_resp:
+                resp_body = self._parse_resp(resp_body)
+            raise exceptions.Unauthorized(resp_body, resp=resp)
+
+        if resp.status == 403:
+            if parse_resp:
+                resp_body = self._parse_resp(resp_body)
+            raise exceptions.Forbidden(resp_body, resp=resp)
+
+        if resp.status == 404:
+            if parse_resp:
+                resp_body = self._parse_resp(resp_body)
+            raise exceptions.NotFound(resp_body, resp=resp)
+
+        if resp.status == 400:
+            if parse_resp:
+                resp_body = self._parse_resp(resp_body)
+            raise exceptions.BadRequest(resp_body, resp=resp)
+
+        if resp.status == 410:
+            if parse_resp:
+                resp_body = self._parse_resp(resp_body)
+            raise exceptions.Gone(resp_body, resp=resp)
+
+        if resp.status == 409:
+            if parse_resp:
+                resp_body = self._parse_resp(resp_body)
+            raise exceptions.Conflict(resp_body, resp=resp)
+
+        if resp.status == 413:
+            if parse_resp:
+                resp_body = self._parse_resp(resp_body)
+            if self.is_absolute_limit(resp, resp_body):
+                raise exceptions.OverLimit(resp_body, resp=resp)
+            else:
+                raise exceptions.RateLimitExceeded(resp_body, resp=resp)
+
+        if resp.status == 415:
+            if parse_resp:
+                resp_body = self._parse_resp(resp_body)
+            raise exceptions.InvalidContentType(resp_body, resp=resp)
+
+        if resp.status == 422:
+            if parse_resp:
+                resp_body = self._parse_resp(resp_body)
+            raise exceptions.UnprocessableEntity(resp_body, resp=resp)
+
+        if resp.status in (500, 501):
+            message = resp_body
+            if parse_resp:
+                try:
+                    resp_body = self._parse_resp(resp_body)
+                except ValueError:
+                    # If response body is a non-json string message.
+                    # Use resp_body as is and raise InvalidResponseBody
+                    # exception.
+                    raise exceptions.InvalidHTTPResponseBody(message)
+                else:
+                    if isinstance(resp_body, dict):
+                        # I'm seeing both computeFault
+                        # and cloudServersFault come back.
+                        # Will file a bug to fix, but leave as is for now.
+                        if 'cloudServersFault' in resp_body:
+                            message = resp_body['cloudServersFault']['message']
+                        elif 'computeFault' in resp_body:
+                            message = resp_body['computeFault']['message']
+                        elif 'error' in resp_body:
+                            message = resp_body['error']['message']
+                        elif 'message' in resp_body:
+                            message = resp_body['message']
+                    else:
+                        message = resp_body
+
+            if resp.status == 501:
+                raise exceptions.NotImplemented(resp_body, resp=resp,
+                                                message=message)
+            else:
+                raise exceptions.ServerFault(resp_body, resp=resp,
+                                             message=message)
+
+        if resp.status >= 400:
+            raise exceptions.UnexpectedResponseCode(str(resp.status),
+                                                    resp=resp)
+
+    def is_absolute_limit(self, resp, resp_body):
+        if (not isinstance(resp_body, collections.Mapping) or
+                'retry-after' not in resp):
+            return True
+        return 'exceed' in resp_body.get('message', 'blabla')
+
+    def wait_for_resource_deletion(self, id):
+        """Waits for a resource to be deleted
+
+        This method will loop over is_resource_deleted until either
+        is_resource_deleted returns True or the build timeout is reached. This
+        depends on is_resource_deleted being implemented
+
+        :param str id: The id of the resource to check
+        :raises TimeoutException: If the build_timeout has elapsed and the
+                                  resource still hasn't been deleted
+        """
+        start_time = int(time.time())
+        while True:
+            if self.is_resource_deleted(id):
+                return
+            if int(time.time()) - start_time >= self.build_timeout:
+                message = ('Failed to delete %(resource_type)s %(id)s within '
+                           'the required time (%(timeout)s s).' %
+                           {'resource_type': self.resource_type, 'id': id,
+                            'timeout': self.build_timeout})
+                caller = test_utils.find_test_caller()
+                if caller:
+                    message = '(%s) %s' % (caller, message)
+                raise exceptions.TimeoutException(message)
+            time.sleep(self.build_interval)
+
+    def is_resource_deleted(self, id):
+        """Subclasses override with specific deletion detection."""
+        message = ('"%s" does not implement is_resource_deleted'
+                   % self.__class__.__name__)
+        raise NotImplementedError(message)
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'resource'
+
+    @classmethod
+    def validate_response(cls, schema, resp, body):
+        # Only check the response if the status code is a success code
+        # TODO(cyeoh): Eventually we should be able to verify that a failure
+        # code if it exists is something that we expect. This is explicitly
+        # declared in the V3 API and so we should be able to export this in
+        # the response schema. For now we'll ignore it.
+        if resp.status in HTTP_SUCCESS + HTTP_REDIRECTION:
+            cls.expected_success(schema['status_code'], resp.status)
+
+            # Check the body of a response
+            body_schema = schema.get('response_body')
+            if body_schema:
+                try:
+                    jsonschema.validate(body, body_schema,
+                                        cls=JSONSCHEMA_VALIDATOR,
+                                        format_checker=FORMAT_CHECKER)
+                except jsonschema.ValidationError as ex:
+                    msg = ("HTTP response body is invalid (%s)" % ex)
+                    raise exceptions.InvalidHTTPResponseBody(msg)
+            else:
+                if body:
+                    msg = ("HTTP response body should not exist (%s)" % body)
+                    raise exceptions.InvalidHTTPResponseBody(msg)
+
+            # Check the header of a response
+            header_schema = schema.get('response_header')
+            if header_schema:
+                try:
+                    jsonschema.validate(resp, header_schema,
+                                        cls=JSONSCHEMA_VALIDATOR,
+                                        format_checker=FORMAT_CHECKER)
+                except jsonschema.ValidationError as ex:
+                    msg = ("HTTP response header is invalid (%s)" % ex)
+                    raise exceptions.InvalidHTTPResponseHeader(msg)
+
+
+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/lib/common/ssh.py b/tempest/lib/common/ssh.py
new file mode 100644
index 0000000..4226cd6
--- /dev/null
+++ b/tempest/lib/common/ssh.py
@@ -0,0 +1,177 @@
+# 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 select
+import socket
+import time
+import warnings
+
+from oslo_log import log as logging
+import six
+
+from tempest.lib import exceptions
+
+
+with warnings.catch_warnings():
+    warnings.simplefilter("ignore")
+    import paramiko
+
+
+LOG = logging.getLogger(__name__)
+
+
+class Client(object):
+
+    def __init__(self, host, username, password=None, timeout=300, pkey=None,
+                 channel_timeout=10, look_for_keys=False, key_filename=None,
+                 port=22):
+        self.host = host
+        self.username = username
+        self.port = port
+        self.password = password
+        if isinstance(pkey, six.string_types):
+            pkey = paramiko.RSAKey.from_private_key(
+                six.StringIO(str(pkey)))
+        self.pkey = pkey
+        self.look_for_keys = look_for_keys
+        self.key_filename = key_filename
+        self.timeout = int(timeout)
+        self.channel_timeout = float(channel_timeout)
+        self.buf_size = 1024
+
+    def _get_ssh_connection(self, sleep=1.5, backoff=1):
+        """Returns an ssh connection to the specified host."""
+        bsleep = sleep
+        ssh = paramiko.SSHClient()
+        ssh.set_missing_host_key_policy(
+            paramiko.AutoAddPolicy())
+        _start_time = time.time()
+        if self.pkey is not None:
+            LOG.info("Creating ssh connection to '%s:%d' as '%s'"
+                     " with public key authentication",
+                     self.host, self.port, self.username)
+        else:
+            LOG.info("Creating ssh connection to '%s:%d' as '%s'"
+                     " with password %s",
+                     self.host, self.port, self.username, str(self.password))
+        attempts = 0
+        while True:
+            try:
+                ssh.connect(self.host, port=self.port, username=self.username,
+                            password=self.password,
+                            look_for_keys=self.look_for_keys,
+                            key_filename=self.key_filename,
+                            timeout=self.channel_timeout, pkey=self.pkey)
+                LOG.info("ssh connection to %s@%s successfully created",
+                         self.username, self.host)
+                return ssh
+            except (EOFError,
+                    socket.error, socket.timeout,
+                    paramiko.SSHException) as e:
+                if self._is_timed_out(_start_time):
+                    LOG.exception("Failed to establish authenticated ssh"
+                                  " connection to %s@%s after %d attempts",
+                                  self.username, self.host, attempts)
+                    raise exceptions.SSHTimeout(host=self.host,
+                                                user=self.username,
+                                                password=self.password)
+                bsleep += backoff
+                attempts += 1
+                LOG.warning("Failed to establish authenticated ssh"
+                            " connection to %s@%s (%s). Number attempts: %s."
+                            " Retry after %d seconds.",
+                            self.username, self.host, e, attempts, bsleep)
+                time.sleep(bsleep)
+
+    def _is_timed_out(self, start_time):
+        return (time.time() - self.timeout) > start_time
+
+    @staticmethod
+    def _can_system_poll():
+        return hasattr(select, 'poll')
+
+    def exec_command(self, cmd, encoding="utf-8"):
+        """Execute the specified command on the server
+
+        Note that this method is reading whole command outputs to memory, thus
+        shouldn't be used for large outputs.
+
+        :param str cmd: Command to run at remote server.
+        :param str encoding: Encoding for result from paramiko.
+                             Result will not be decoded if None.
+        :returns: data read from standard output of the command.
+        :raises: SSHExecCommandFailed if command returns nonzero
+                 status. The exception contains command status stderr content.
+        :raises: TimeoutException if cmd doesn't end when timeout expires.
+        """
+        ssh = self._get_ssh_connection()
+        transport = ssh.get_transport()
+        with transport.open_session() as channel:
+            channel.fileno()  # Register event pipe
+            channel.exec_command(cmd)
+            channel.shutdown_write()
+
+            # If the executing host is linux-based, poll the channel
+            if self._can_system_poll():
+                out_data_chunks = []
+                err_data_chunks = []
+                poll = select.poll()
+                poll.register(channel, select.POLLIN)
+                start_time = time.time()
+
+                while True:
+                    ready = poll.poll(self.channel_timeout)
+                    if not any(ready):
+                        if not self._is_timed_out(start_time):
+                            continue
+                        raise exceptions.TimeoutException(
+                            "Command: '{0}' executed on host '{1}'.".format(
+                                cmd, self.host))
+                    if not ready[0]:  # If there is nothing to read.
+                        continue
+                    out_chunk = err_chunk = None
+                    if channel.recv_ready():
+                        out_chunk = channel.recv(self.buf_size)
+                        out_data_chunks += out_chunk,
+                    if channel.recv_stderr_ready():
+                        err_chunk = channel.recv_stderr(self.buf_size)
+                        err_data_chunks += err_chunk,
+                    if not err_chunk and not out_chunk:
+                        break
+                out_data = b''.join(out_data_chunks)
+                err_data = b''.join(err_data_chunks)
+            # Just read from the channels
+            else:
+                out_file = channel.makefile('rb', self.buf_size)
+                err_file = channel.makefile_stderr('rb', self.buf_size)
+                out_data = out_file.read()
+                err_data = err_file.read()
+            if encoding:
+                out_data = out_data.decode(encoding)
+                err_data = err_data.decode(encoding)
+
+            exit_status = channel.recv_exit_status()
+
+            if 0 != exit_status:
+                raise exceptions.SSHExecCommandFailed(
+                    command=cmd, exit_status=exit_status,
+                    stderr=err_data, stdout=out_data)
+            return out_data
+
+    def test_connection_auth(self):
+        """Raises an exception when we can not connect to server via ssh."""
+        connection = self._get_ssh_connection()
+        connection.close()
diff --git a/tempest/stress/__init__.py b/tempest/lib/common/utils/__init__.py
similarity index 100%
copy from tempest/stress/__init__.py
copy to tempest/lib/common/utils/__init__.py
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
new file mode 100644
index 0000000..75c2e51
--- /dev/null
+++ b/tempest/lib/common/utils/data_utils.py
@@ -0,0 +1,214 @@
+# 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 itertools
+import netaddr
+import random
+import string
+import uuid
+
+from debtcollector import removals
+from oslo_utils import netutils
+from oslo_utils import uuidutils
+import six.moves
+
+
+def rand_uuid():
+    """Generate a random UUID string
+
+    :return: a random UUID (e.g. '1dc12c7d-60eb-4b61-a7a2-17cf210155b6')
+    :rtype: string
+    """
+    return uuidutils.generate_uuid()
+
+
+def rand_uuid_hex():
+    """Generate a random UUID hex string
+
+    :return: a random UUID (e.g. '0b98cf96d90447bda4b46f31aeb1508c')
+    :rtype: string
+    """
+    return uuid.uuid4().hex
+
+
+def rand_name(name='', prefix=None):
+    """Generate a random name that includes a random number
+
+    :param str name: The name that you want to include
+    :param str prefix: The prefix that you want to include
+    :return: a random name. The format is
+             '<prefix>-<name>-<random number>'.
+             (e.g. 'prefixfoo-namebar-154876201')
+    :rtype: string
+    """
+    randbits = str(random.randint(1, 0x7fffffff))
+    rand_name = randbits
+    if name:
+        rand_name = name + '-' + rand_name
+    if prefix:
+        rand_name = prefix + '-' + rand_name
+    return rand_name
+
+
+def rand_password(length=15):
+    """Generate a random password
+
+    :param int length: The length of password that you expect to set
+                       (If it's smaller than 3, it's same as 3.)
+    :return: a random password. The format is
+             '<random upper letter>-<random number>-<random special character>
+              -<random ascii letters or digit characters or special symbols>'
+             (e.g. 'G2*ac8&lKFFgh%2')
+    :rtype: string
+    """
+    upper = random.choice(string.ascii_uppercase)
+    ascii_char = string.ascii_letters
+    digits = string.digits
+    digit = random.choice(string.digits)
+    puncs = '~!@#%^&*_=+'
+    punc = random.choice(puncs)
+    seed = ascii_char + digits + puncs
+    pre = upper + digit + punc
+    password = pre + ''.join(random.choice(seed) for x in range(length - 3))
+    return password
+
+
+def rand_url():
+    """Generate a random url that includes a random number
+
+    :return: a random url. The format is 'https://url-<random number>.com'.
+             (e.g. 'https://url-154876201.com')
+    :rtype: string
+    """
+    randbits = str(random.randint(1, 0x7fffffff))
+    return 'https://url-' + randbits + '.com'
+
+
+def rand_int_id(start=0, end=0x7fffffff):
+    """Generate a random integer value
+
+    :param int start: The value that you expect to start here
+    :param int end: The value that you expect to end here
+    :return: a random integer value
+    :rtype: int
+    """
+    return random.randint(start, end)
+
+
+def rand_mac_address():
+    """Generate an Ethernet MAC address
+
+    :return: an random Ethernet MAC address
+    :rtype: string
+    """
+    # NOTE(vish): We would prefer to use 0xfe here to ensure that linux
+    #             bridge mac addresses don't change, but it appears to
+    #             conflict with libvirt, so we use the next highest octet
+    #             that has the unicast and locally administered bits set
+    #             properly: 0xfa.
+    #             Discussion: https://bugs.launchpad.net/nova/+bug/921838
+    mac = [0xfa, 0x16, 0x3e,
+           random.randint(0x00, 0xff),
+           random.randint(0x00, 0xff),
+           random.randint(0x00, 0xff)]
+    return ':'.join(["%02x" % x for x in mac])
+
+
+def rand_infiniband_guid_address():
+    """Generate an Infiniband GUID address
+
+    :return: an random Infiniband GUID address
+    :rtype: string
+    """
+    guid = []
+    for i in range(8):
+        guid.append("%02x" % random.randint(0x00, 0xff))
+    return ':'.join(guid)
+
+
+def parse_image_id(image_ref):
+    """Return the image id from a given image ref
+
+    This function just returns the last word of the given image ref string
+    splitting with '/'.
+    :param str image_ref: a string that includes the image id
+    :return: the image id string
+    :rtype: string
+    """
+    return image_ref.rsplit('/')[-1]
+
+
+def arbitrary_string(size=4, base_text=None):
+    """Return size characters from base_text
+
+    This generates a string with an arbitrary number of characters, generated
+    by looping the base_text string. If the size is smaller than the size of
+    base_text, returning string is shrunk to the size.
+    :param int size: a returning characters size
+    :param str base_text: a string you want to repeat
+    :return: size string
+    :rtype: string
+    """
+    if not base_text:
+        base_text = 'test'
+    return ''.join(itertools.islice(itertools.cycle(base_text), size))
+
+
+def random_bytes(size=1024):
+    """Return size randomly selected bytes as a string
+
+    :param int size: a returning bytes size
+    :return: size randomly bytes
+    :rtype: string
+    """
+    return b''.join([six.int2byte(random.randint(0, 255))
+                    for i in range(size)])
+
+
+@removals.remove(
+    message="use get_ipv6_addr_by_EUI64 from oslo_utils.netutils",
+    version="Newton",
+    removal_version="Ocata")
+def get_ipv6_addr_by_EUI64(cidr, mac):
+    """Generate a IPv6 addr by EUI-64 with CIDR and MAC
+
+    :param str cidr: a IPv6 CIDR
+    :param str mac: a MAC address
+    :return: an IPv6 Address
+    :rtype: netaddr.IPAddress
+    """
+    # Check if the prefix is IPv4 address
+    is_ipv4 = netutils.is_valid_ipv4(cidr)
+    if is_ipv4:
+        msg = "Unable to generate IP address by EUI64 for IPv4 prefix"
+        raise TypeError(msg)
+    try:
+        eui64 = int(netaddr.EUI(mac).eui64())
+        prefix = netaddr.IPNetwork(cidr)
+        return netaddr.IPAddress(prefix.first + eui64 ^ (1 << 57))
+    except (ValueError, netaddr.AddrFormatError):
+        raise TypeError('Bad prefix or mac format for generating IPv6 '
+                        'address by EUI-64: %(prefix)s, %(mac)s:'
+                        % {'prefix': cidr, 'mac': mac})
+    except TypeError:
+        raise TypeError('Bad prefix type for generate IPv6 address by '
+                        'EUI-64: %s' % cidr)
+
+
+# Courtesy of http://stackoverflow.com/a/312464
+def chunkify(sequence, chunksize):
+    """Yield successive chunks from `sequence`."""
+    for i in range(0, len(sequence), chunksize):
+        yield sequence[i:i + chunksize]
diff --git a/tempest/lib/common/utils/misc.py b/tempest/lib/common/utils/misc.py
new file mode 100644
index 0000000..f13b4c8
--- /dev/null
+++ b/tempest/lib/common/utils/misc.py
@@ -0,0 +1,37 @@
+# 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_log import log as logging
+
+from tempest.lib.common.utils import test_utils
+
+LOG = logging.getLogger(__name__)
+
+
+def singleton(cls):
+    """Simple wrapper for classes that should only have a single instance."""
+    instances = {}
+
+    def getinstance():
+        if cls not in instances:
+            instances[cls] = cls()
+        return instances[cls]
+    return getinstance
+
+
+def find_test_caller(*args, **kwargs):
+    LOG.warning("tempest.lib.common.utils.misc.find_test_caller is deprecated "
+                "in favor of tempest.lib.common.utils.test_utils."
+                "find_test_caller")
+    test_utils.find_test_caller(*args, **kwargs)
diff --git a/tempest/lib/common/utils/test_utils.py b/tempest/lib/common/utils/test_utils.py
new file mode 100644
index 0000000..bd0db7c
--- /dev/null
+++ b/tempest/lib/common/utils/test_utils.py
@@ -0,0 +1,107 @@
+# Copyright 2016 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 inspect
+import re
+import time
+
+from oslo_log import log as logging
+
+from tempest.lib import exceptions
+
+LOG = logging.getLogger(__name__)
+
+
+def find_test_caller():
+    """Find the caller class and test name.
+
+    Because we know that the interesting things that call us are
+    test_* methods, and various kinds of setUp / tearDown, we
+    can look through the call stack to find appropriate methods,
+    and the class we were in when those were called.
+    """
+    caller_name = None
+    names = []
+    frame = inspect.currentframe()
+    is_cleanup = False
+    # Start climbing the ladder until we hit a good method
+    while True:
+        try:
+            frame = frame.f_back
+            name = frame.f_code.co_name
+            names.append(name)
+            if re.search("^(test_|setUp|tearDown)", name):
+                cname = ""
+                if 'self' in frame.f_locals:
+                    cname = frame.f_locals['self'].__class__.__name__
+                if 'cls' in frame.f_locals:
+                    cname = frame.f_locals['cls'].__name__
+                caller_name = cname + ":" + name
+                break
+            elif re.search("^_run_cleanup", name):
+                is_cleanup = True
+            elif name == 'main':
+                caller_name = 'main'
+                break
+            else:
+                cname = ""
+                if 'self' in frame.f_locals:
+                    cname = frame.f_locals['self'].__class__.__name__
+                if 'cls' in frame.f_locals:
+                    cname = frame.f_locals['cls'].__name__
+
+                # the fact that we are running cleanups is indicated pretty
+                # deep in the stack, so if we see that we want to just
+                # start looking for a real class name, and declare victory
+                # once we do.
+                if is_cleanup and cname:
+                    if not re.search("^RunTest", cname):
+                        caller_name = cname + ":_run_cleanups"
+                        break
+        except Exception:
+            break
+    # prevents frame leaks
+    del frame
+    if caller_name is None:
+        LOG.debug("Sane call name not found in %s", names)
+    return caller_name
+
+
+def call_and_ignore_notfound_exc(func, *args, **kwargs):
+    """Call the given function and pass if a `NotFound` exception is raised."""
+    try:
+        return func(*args, **kwargs)
+    except exceptions.NotFound:
+        pass
+
+
+def call_until_true(func, duration, sleep_for):
+    """Call the given function until it returns True (and return True)
+
+    or until the specified duration (in seconds) elapses (and return False).
+
+    :param func: A zero argument callable that returns True on success.
+    :param duration: The number of seconds for which to attempt a
+        successful call of the function.
+    :param sleep_for: The number of seconds to sleep after an unsuccessful
+                      invocation of the function.
+    """
+    now = time.time()
+    timeout = now + duration
+    while now < timeout:
+        if func():
+            return True
+        time.sleep(sleep_for)
+        now = time.time()
+    return False
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
new file mode 100644
index 0000000..6ed99b4
--- /dev/null
+++ b/tempest/lib/decorators.py
@@ -0,0 +1,79 @@
+# Copyright 2015 Hewlett-Packard 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.
+
+import functools
+import uuid
+
+import six
+import testtools
+
+
+def skip_because(*args, **kwargs):
+    """A decorator useful to skip tests hitting known bugs
+
+    @param bug: bug number causing the test to skip
+    @param condition: optional condition to be True for the skip to have place
+    """
+    def decorator(f):
+        @functools.wraps(f)
+        def wrapper(self, *func_args, **func_kwargs):
+            skip = False
+            if "condition" in kwargs:
+                if kwargs["condition"] is True:
+                    skip = True
+            else:
+                skip = True
+            if "bug" in kwargs and skip is True:
+                if not kwargs['bug'].isdigit():
+                    raise ValueError('bug must be a valid bug number')
+                msg = "Skipped until Bug: %s is resolved." % kwargs["bug"]
+                raise testtools.TestCase.skipException(msg)
+            return f(self, *func_args, **func_kwargs)
+        return wrapper
+    return decorator
+
+
+def idempotent_id(id):
+    """Stub for metadata decorator"""
+    if not isinstance(id, six.string_types):
+        raise TypeError('Test idempotent_id must be string not %s'
+                        '' % type(id).__name__)
+    uuid.UUID(id)
+
+    def decorator(f):
+        f = testtools.testcase.attr('id-%s' % id)(f)
+        if f.__doc__:
+            f.__doc__ = 'Test idempotent id: %s\n%s' % (id, f.__doc__)
+        else:
+            f.__doc__ = 'Test idempotent id: %s' % id
+        return f
+    return decorator
+
+
+class skip_unless_attr(object):
+    """Decorator to skip tests if a specified attr does not exists or False"""
+    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):
+        @functools.wraps(func)
+        def _skipper(*args, **kw):
+            """Wrapped skipper function."""
+            testobj = args[0]
+            if not getattr(testobj, self.attr, False):
+                raise testtools.TestCase.skipException(self.message)
+            func(*args, **kw)
+        return _skipper
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
new file mode 100644
index 0000000..108ba70
--- /dev/null
+++ b/tempest/lib/exceptions.py
@@ -0,0 +1,261 @@
+# 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 testtools
+
+
+class TempestException(Exception):
+    """Base Tempest Exception
+
+    To correctly use this class, inherit from it and define
+    a 'message' property. That message will get printf'd
+    with the keyword arguments provided to the constructor.
+    """
+    message = "An unknown exception occurred"
+
+    def __init__(self, *args, **kwargs):
+        super(TempestException, self).__init__()
+        try:
+            self._error_string = self.message % kwargs
+        except Exception:
+            # at least get the core message out if something happened
+            self._error_string = self.message
+        if len(args) > 0:
+            # If there is a non-kwarg parameter, assume it's the error
+            # message or reason description and tack it on to the end
+            # of the exception message
+            # Convert all arguments into their string representations...
+            args = ["%s" % arg for arg in args]
+            self._error_string = (self._error_string +
+                                  "\nDetails: %s" % '\n'.join(args))
+
+    def __str__(self):
+        return self._error_string
+
+
+class RestClientException(TempestException,
+                          testtools.TestCase.failureException):
+    def __init__(self, resp_body=None, *args, **kwargs):
+        if 'resp' in kwargs:
+            self.resp = kwargs.get('resp')
+        self.resp_body = resp_body
+        message = kwargs.get("message", resp_body)
+        super(RestClientException, self).__init__(message, *args, **kwargs)
+
+
+class OtherRestClientException(RestClientException):
+    pass
+
+
+class ServerRestClientException(RestClientException):
+    pass
+
+
+class ClientRestClientException(RestClientException):
+    pass
+
+
+class InvalidHttpSuccessCode(OtherRestClientException):
+    message = "The success code is different than the expected one"
+
+
+class BadRequest(ClientRestClientException):
+    status_code = 400
+    message = "Bad request"
+
+
+class Unauthorized(ClientRestClientException):
+    status_code = 401
+    message = 'Unauthorized'
+
+
+class Forbidden(ClientRestClientException):
+    status_code = 403
+    message = "Forbidden"
+
+
+class NotFound(ClientRestClientException):
+    status_code = 404
+    message = "Object not found"
+
+
+class Conflict(ClientRestClientException):
+    status_code = 409
+    message = "An object with that identifier already exists"
+
+
+class Gone(ClientRestClientException):
+    status_code = 410
+    message = "The requested resource is no longer available"
+
+
+class RateLimitExceeded(ClientRestClientException):
+    status_code = 413
+    message = "Rate limit exceeded"
+
+
+class OverLimit(ClientRestClientException):
+    status_code = 413
+    message = "Request entity is too large"
+
+
+class InvalidContentType(ClientRestClientException):
+    status_code = 415
+    message = "Invalid content type provided"
+
+
+class UnprocessableEntity(ClientRestClientException):
+    status_code = 422
+    message = "Unprocessable entity"
+
+
+class ServerFault(ServerRestClientException):
+    status_code = 500
+    message = "Got server fault"
+
+
+class NotImplemented(ServerRestClientException):
+    status_code = 501
+    message = "Got NotImplemented error"
+
+
+class TimeoutException(OtherRestClientException):
+    message = "Request timed out"
+
+
+class ResponseWithNonEmptyBody(OtherRestClientException):
+    message = ("RFC Violation! Response with %(status)d HTTP Status Code "
+               "MUST NOT have a body")
+
+
+class ResponseWithEntity(OtherRestClientException):
+    message = ("RFC Violation! Response with 205 HTTP Status Code "
+               "MUST NOT have an entity")
+
+
+class InvalidHTTPResponseBody(OtherRestClientException):
+    message = "HTTP response body is invalid json or xml"
+
+
+class InvalidHTTPResponseHeader(OtherRestClientException):
+    message = "HTTP response header is invalid"
+
+
+class UnexpectedContentType(OtherRestClientException):
+    message = "Unexpected content type provided"
+
+
+class UnexpectedResponseCode(OtherRestClientException):
+    message = "Unexpected response code received"
+
+
+class InvalidConfiguration(TempestException):
+    message = "Invalid Configuration"
+
+
+class InvalidIdentityVersion(TempestException):
+    message = "Invalid version %(identity_version)s of the identity service"
+
+
+class InvalidStructure(TempestException):
+    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.
+
+    If alt creds end up the same as primary creds, use this
+    exception. This is often going to be the case when you assume
+    project_id is in the url, but it's not.
+
+    """
+    message = "The alt auth looks the same as primary auth for %(part)s"
+
+
+class CommandFailed(Exception):
+    def __init__(self, returncode, cmd, output, stderr):
+        super(CommandFailed, self).__init__()
+        self.returncode = returncode
+        self.cmd = cmd
+        self.stdout = output
+        self.stderr = stderr
+
+    def __str__(self):
+        return ("Command '%s' returned non-zero exit status %d.\n"
+                "stdout:\n%s\n"
+                "stderr:\n%s" % (self.cmd,
+                                 self.returncode,
+                                 self.stdout,
+                                 self.stderr))
+
+
+class IdentityError(TempestException):
+    message = "Got identity error"
+
+
+class EndpointNotFound(TempestException):
+    message = "Endpoint not found"
+
+
+class InvalidCredentials(TempestException):
+    message = "Invalid Credentials"
+
+
+class InvalidScope(TempestException):
+    message = "Invalid Scope %(scope)s for %(auth_provider)s"
+
+
+class SSHTimeout(TempestException):
+    message = ("Connection to the %(host)s via SSH timed out.\n"
+               "User: %(user)s, Password: %(password)s")
+
+
+class SSHExecCommandFailed(TempestException):
+    """Raised when remotely executed command returns nonzero status."""
+    message = ("Command '%(command)s', exit status: %(exit_status)d, "
+               "stderr:\n%(stderr)s\n"
+               "stdout:\n%(stdout)s")
+
+
+class UnknownServiceClient(TempestException):
+    message = "Service clients named %(services)s are not known"
+
+
+class ServiceClientRegistrationException(TempestException):
+    message = ("Error registering module %(name)s in path %(module_path)s, "
+               "with service %(service_version)s and clients "
+               "%(client_names)s: %(detailed_error)s")
+
+
+class PluginRegistrationException(TempestException):
+    message = "Error registering plugin %(name)s: %(detailed_error)s"
+
+
+class VolumeBackupException(TempestException):
+    message = "Volume backup %(backup_id)s failed and is in ERROR status"
diff --git a/tempest/stress/__init__.py b/tempest/lib/services/__init__.py
similarity index 100%
copy from tempest/stress/__init__.py
copy to tempest/lib/services/__init__.py
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
new file mode 100644
index 0000000..445e8bd
--- /dev/null
+++ b/tempest/lib/services/clients.py
@@ -0,0 +1,454 @@
+# Copyright 2012 OpenStack Foundation
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+# 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
+import importlib
+import inspect
+
+from oslo_log import log as logging
+
+from tempest.lib import auth
+from tempest.lib.common.utils import misc
+from tempest.lib import exceptions
+from tempest.lib.services import compute
+from tempest.lib.services import identity
+from tempest.lib.services import image
+from tempest.lib.services import network
+from tempest.lib.services import volume
+
+
+LOG = logging.getLogger(__name__)
+
+
+def tempest_modules():
+    """Dict of service client modules available in Tempest.
+
+    Provides a dict of stable service modules available in Tempest, with
+    ``service_version`` as key, and the module object as value.
+    """
+    return {
+        'compute': compute,
+        'identity.v2': identity.v2,
+        'identity.v3': identity.v3,
+        'image.v1': image.v1,
+        'image.v2': image.v2,
+        'network': network,
+        'volume.v1': volume.v1,
+        'volume.v2': volume.v2,
+        'volume.v3': volume.v3
+    }
+
+
+def _tempest_internal_modules():
+    # Set of unstable service clients available in Tempest
+    # NOTE(andreaf) This list will exists only as long the remain clients
+    # are migrated to tempest.lib, and it will then be deleted without
+    # deprecation or advance notice
+    return set(['object-storage'])
+
+
+def available_modules():
+    """Set of service client modules available in Tempest and plugins
+
+    Set of stable service clients from Tempest and service clients exposed
+    by plugins. This set of available modules can be used for automatic
+    configuration.
+
+    :raise PluginRegistrationException: if a plugin exposes a service_version
+        already defined by Tempest or another plugin.
+
+    Examples:
+
+        >>> from tempest import config
+        >>> params = {}
+        >>> for service_version in available_modules():
+        >>>     service = service_version.split('.')[0]
+        >>>     params[service] = config.service_client_config(service)
+        >>> service_clients = ServiceClients(creds, identity_uri,
+        >>>                                  client_parameters=params)
+    """
+    extra_service_versions = set([])
+    _tempest_modules = set(tempest_modules())
+    plugin_services = ClientsRegistry().get_service_clients()
+    for plugin_name in plugin_services:
+        plug_service_versions = set([x['service_version'] for x in
+                                     plugin_services[plugin_name]])
+        # If a plugin exposes a duplicate service_version raise an exception
+        if plug_service_versions:
+            if not plug_service_versions.isdisjoint(extra_service_versions):
+                detailed_error = (
+                    'Plugin %s is trying to register a service %s already '
+                    'claimed by another one' % (plugin_name,
+                                                extra_service_versions &
+                                                plug_service_versions))
+                raise exceptions.PluginRegistrationException(
+                    name=plugin_name, detailed_error=detailed_error)
+            # NOTE(andreaf) Once all tempest clients are stable, the following
+            # if will have to be removed.
+            if not plug_service_versions.isdisjoint(
+                    _tempest_internal_modules()):
+                detailed_error = (
+                    'Plugin %s is trying to register a service %s already '
+                    'claimed by a Tempest one' % (plugin_name,
+                                                  _tempest_internal_modules() &
+                                                  plug_service_versions))
+                raise exceptions.PluginRegistrationException(
+                    name=plugin_name, detailed_error=detailed_error)
+        extra_service_versions |= plug_service_versions
+    return _tempest_modules | extra_service_versions
+
+
+@misc.singleton
+class ClientsRegistry(object):
+    """Registry of all service clients available from plugins"""
+
+    def __init__(self):
+        self._service_clients = {}
+
+    def register_service_client(self, plugin_name, service_client_data):
+        if plugin_name in self._service_clients:
+            detailed_error = 'Clients for plugin %s already registered'
+            raise exceptions.PluginRegistrationException(
+                name=plugin_name,
+                detailed_error=detailed_error % plugin_name)
+        self._service_clients[plugin_name] = service_client_data
+
+    def get_service_clients(self):
+        return self._service_clients
+
+
+class ClientsFactory(object):
+    """Builds service clients for a service client module
+
+    This class implements the logic of feeding service client parameters
+    to service clients from a specific module. It allows setting the
+    parameters once and obtaining new instances of the clients without the
+    need of passing any parameter.
+
+    ClientsFactory can be used directly, or consumed via the `ServiceClients`
+    class, which manages the authorization part.
+    """
+
+    def __init__(self, module_path, client_names, auth_provider, **kwargs):
+        """Initialises the client factory
+
+        :param module_path: Path to module that includes all service clients.
+            All service client classes must be exposed by a single module.
+            If they are separated in different modules, defining __all__
+            in the root module can help, similar to what is done by service
+            clients in tempest.
+        :param client_names: List or set of names of the service client
+            classes.
+        :param auth_provider: The auth provider used to initialise client.
+        :param kwargs: Parameters to be passed to all clients. Parameters
+            values can be overwritten when clients are initialised, but
+            parameters cannot be deleted.
+        :raise ImportError if the specified module_path cannot be imported
+
+        Example:
+
+            >>> # Get credentials and an auth_provider
+            >>> clients = ClientsFactory(
+            >>>     module_path='my_service.my_service_clients',
+            >>>     client_names=['ServiceClient1', 'ServiceClient2'],
+            >>>     auth_provider=auth_provider,
+            >>>     service='my_service',
+            >>>     region='region1')
+            >>> my_api_client = clients.MyApiClient()
+            >>> my_api_client_region2 = clients.MyApiClient(region='region2')
+
+        """
+        # Import the module. If it's not importable, the raised exception
+        # provides good enough information about what happened
+        _module = importlib.import_module(module_path)
+        # If any of the classes is not in the module we fail
+        for class_name in client_names:
+            # TODO(andreaf) This always passes all parameters to all clients.
+            # In future to allow clients to specify the list of parameters
+            # that they accept based out of a list of standard ones.
+
+            # Obtain the class
+            klass = self._get_class(_module, class_name)
+            final_kwargs = copy.copy(kwargs)
+
+            # Set the function as an attribute of the factory
+            setattr(self, class_name, self._get_partial_class(
+                klass, auth_provider, final_kwargs))
+
+    def _get_partial_class(self, klass, auth_provider, kwargs):
+
+        # Define a function that returns a new class instance by
+        # combining default kwargs with extra ones
+        def partial_class(alias=None, **later_kwargs):
+            """Returns a callable the initialises a service client
+
+            Builds a callable that accepts kwargs, which are passed through
+            to the __init__ of the service client, along with a set of defaults
+            set in factory at factory __init__ time.
+            Original args in the service client can only be passed as kwargs.
+
+            It accepts one extra parameter 'alias' compared to the original
+            service client. When alias is provided, the returned callable will
+            also set an attribute called with a name defined in 'alias', which
+            contains the instance of the service client.
+
+            :param alias: str Name of the attribute set on the factory once
+                the callable is invoked which contains the initialised
+                service client. If None, no attribute is set.
+            :param later_kwargs: kwargs passed through to the service client
+                __init__ on top of defaults set at factory level.
+            """
+            kwargs.update(later_kwargs)
+            _client = klass(auth_provider=auth_provider, **kwargs)
+            if alias:
+                setattr(self, alias, _client)
+            return _client
+
+        return partial_class
+
+    @classmethod
+    def _get_class(cls, module, class_name):
+        klass = getattr(module, class_name, None)
+        if not klass:
+            msg = 'Invalid class name, %s is not found in %s'
+            raise AttributeError(msg % (class_name, module))
+        if not inspect.isclass(klass):
+            msg = 'Expected a class, got %s of type %s instead'
+            raise TypeError(msg % (klass, type(klass)))
+        return klass
+
+
+class ServiceClients(object):
+    """Service client provider class
+
+    The ServiceClients object provides a useful means for tests to access
+    service clients configured for a specified set of credentials.
+    It hides some of the complexity from the authorization and configuration
+    layers.
+
+    Examples:
+
+        >>> from tempest.lib.services import clients
+        >>> johndoe = cred_provider.get_creds_by_role(['johndoe'])
+        >>> johndoe_clients = clients.ServiceClients(johndoe,
+        >>>                                                  identity_uri)
+        >>> johndoe_servers = johndoe_clients.servers_client.list_servers()
+
+    """
+    # NOTE(andreaf) This class does not depend on tempest configuration
+    # and its meant for direct consumption by external clients such as tempest
+    # plugins. Tempest provides a wrapper class, `clients.Manager`, that
+    # initialises this class using values from tempest CONF object. The wrapper
+    # class should only be used by tests hosted in Tempest.
+
+    def __init__(self, credentials, identity_uri, region=None, scope='project',
+                 disable_ssl_certificate_validation=True, ca_certs=None,
+                 trace_requests='', client_parameters=None):
+        """Service Clients provider
+
+        Instantiate a `ServiceClients` object, from a set of credentials and an
+        identity URI. The identity version is inferred from the credentials
+        object. Optionally auth scope can be provided.
+
+        A few parameters can be given a value which is applied as default
+        for all service clients: region, dscv, ca_certs, trace_requests.
+
+        Parameters dscv, ca_certs and trace_requests all apply to the auth
+        provider as well as any service clients provided by this manager.
+
+        Any other client parameter must be set via client_parameters.
+        The list of available parameters is defined in the service clients
+        interfaces. For reference, most clients will accept 'region',
+        'service', 'endpoint_type', 'build_timeout' and 'build_interval', which
+        are all inherited from RestClient.
+
+        The `config` module in Tempest exposes an helper function
+        `service_client_config` that can be used to extract from configuration
+        a dictionary ready to be injected in kwargs.
+
+        Exceptions are:
+        - Token clients for 'identity' must be given an 'auth_url' parameter
+        - Volume client for 'volume' accepts 'default_volume_size'
+        - Servers client from 'compute' accepts 'enable_instance_password'
+
+        Examples:
+
+            >>> identity_params = config.service_client_config('identity')
+            >>> params = {
+            >>>     'identity': identity_params,
+            >>>     'compute': {'region': 'region2'}}
+            >>> manager = lib_manager.Manager(
+            >>>     my_creds, identity_uri, client_parameters=params)
+
+        :param credentials: An instance of `auth.Credentials`
+        :param identity_uri: URI of the identity API. This should be a
+                             mandatory parameter, and it will so soon.
+        :param region: Default value of region for service clients.
+        :param scope: default scope for tokens produced by the auth provider
+        :param disable_ssl_certificate_validation: Applies to auth and to all
+                                                  service clients.
+        :param ca_certs: Applies to auth and to all service clients.
+        :param trace_requests: Applies to auth and to all service clients.
+        :param client_parameters: Dictionary with parameters for service
+            clients. Keys of the dictionary are the service client service
+            name, as declared in `service_clients.available_modules()` except
+            for the version. Values are dictionaries of parameters that are
+            going to be passed to all clients in the service client module.
+
+        Examples:
+
+            >>> params_service_x = {'param_name': 'param_value'}
+            >>> client_parameters = { 'service_x': params_service_x }
+
+            >>> params_service_y = config.service_client_config('service_y')
+            >>> client_parameters['service_y'] = params_service_y
+
+        """
+        self._registered_services = set([])
+        self.credentials = credentials
+        self.identity_uri = identity_uri
+        if not identity_uri:
+            raise exceptions.InvalidCredentials(
+                'ServiceClients requires a non-empty identity_uri.')
+        self.region = region
+        # Check if passed or default credentials are valid
+        if not self.credentials.is_valid():
+            raise exceptions.InvalidCredentials()
+        # Get the identity classes matching the provided credentials
+        # TODO(andreaf) Define a new interface in Credentials to get
+        # the API version from an instance
+        identity = [(k, auth.IDENTITY_VERSION[k][1]) for k in
+                    auth.IDENTITY_VERSION.keys() if
+                    isinstance(self.credentials, auth.IDENTITY_VERSION[k][0])]
+        # Zero matches or more than one are both not valid.
+        if len(identity) != 1:
+            raise exceptions.InvalidCredentials()
+        self.auth_version, auth_provider_class = identity[0]
+        self.dscv = disable_ssl_certificate_validation
+        self.ca_certs = ca_certs
+        self.trace_requests = trace_requests
+        # Creates an auth provider for the credentials
+        self.auth_provider = auth_provider_class(
+            self.credentials, self.identity_uri, scope=scope,
+            disable_ssl_certificate_validation=self.dscv,
+            ca_certs=self.ca_certs, trace_requests=self.trace_requests)
+        # Setup some defaults for client parameters of registered services
+        client_parameters = client_parameters or {}
+        self.parameters = {}
+        # Parameters are provided for unversioned services
+        all_modules = available_modules() | _tempest_internal_modules()
+        unversioned_services = set(
+            [x.split('.')[0] for x in all_modules])
+        for service in unversioned_services:
+            self.parameters[service] = self._setup_parameters(
+                client_parameters.pop(service, {}))
+        # Check that no client parameters was supplied for unregistered clients
+        if client_parameters:
+            raise exceptions.UnknownServiceClient(
+                services=list(client_parameters.keys()))
+
+        # Register service clients from the registry (__tempest__ and plugins)
+        clients_registry = ClientsRegistry()
+        plugin_service_clients = clients_registry.get_service_clients()
+        for plugin in plugin_service_clients:
+            service_clients = plugin_service_clients[plugin]
+            # Each plugin returns a list of service client parameters
+            for service_client in service_clients:
+                # NOTE(andreaf) If a plugin cannot register, stop the
+                # registration process, log some details to help
+                # troubleshooting, and re-raise
+                try:
+                    self.register_service_client_module(**service_client)
+                except Exception:
+                    LOG.exception(
+                        'Failed to register service client from plugin %s '
+                        'with parameters %s', plugin, service_client)
+                    raise
+
+    def register_service_client_module(self, name, service_version,
+                                       module_path, client_names, **kwargs):
+        """Register a service client module
+
+        Initiates a client factory for the specified module, using this
+        class auth_provider, and accessible via a `name` attribute in the
+        service client.
+
+        :param name: Name used to access the client
+        :param service_version: Name of the service complete with version.
+            Used to track registered services. When a plugin implements it,
+            it can be used by other plugins to obtain their configuration.
+        :param module_path: Path to module that includes all service clients.
+            All service client classes must be exposed by a single module.
+            If they are separated in different modules, defining __all__
+            in the root module can help, similar to what is done by service
+            clients in tempest.
+        :param client_names: List or set of names of service client classes.
+        :param kwargs: Extra optional parameters to be passed to all clients.
+            ServiceClient provides defaults for region, dscv, ca_certs and
+            trace_requests.
+        :raise ServiceClientRegistrationException: if the provided name is
+            already in use or if service_version is already registered.
+        :raise ImportError: if module_path cannot be imported.
+        """
+        if hasattr(self, name):
+            using_name = getattr(self, name)
+            detailed_error = 'Module name already in use: %s' % using_name
+            raise exceptions.ServiceClientRegistrationException(
+                name=name, service_version=service_version,
+                module_path=module_path, client_names=client_names,
+                detailed_error=detailed_error)
+        if service_version in self.registered_services:
+            detailed_error = 'Service %s already registered.' % service_version
+            raise exceptions.ServiceClientRegistrationException(
+                name=name, service_version=service_version,
+                module_path=module_path, client_names=client_names,
+                detailed_error=detailed_error)
+        params = dict(region=self.region,
+                      disable_ssl_certificate_validation=self.dscv,
+                      ca_certs=self.ca_certs,
+                      trace_requests=self.trace_requests)
+        params.update(kwargs)
+        # Instantiate the client factory
+        _factory = ClientsFactory(module_path=module_path,
+                                  client_names=client_names,
+                                  auth_provider=self.auth_provider,
+                                  **params)
+        # Adds the client factory to the service_client
+        setattr(self, name, _factory)
+        # Add the name of the new service in self.SERVICES for discovery
+        self._registered_services.add(service_version)
+
+    @property
+    def registered_services(self):
+        # NOTE(andreaf) Once all tempest modules are stable this needs to
+        # be updated to remove _tempest_internal_modules
+        return self._registered_services | _tempest_internal_modules()
+
+    def _setup_parameters(self, parameters):
+        """Setup default values for client parameters
+
+        Region by default is the region passed as an __init__ parameter.
+        Checks that no parameter for an unknown service is provided.
+        """
+        _parameters = {}
+        # Use region from __init__
+        if self.region:
+            _parameters['region'] = self.region
+        # Update defaults with specified parameters
+        _parameters.update(parameters)
+        # If any parameter is left, parameters for an unknown service were
+        # provided as input. Fail rather than ignore silently.
+        return _parameters
diff --git a/tempest/lib/services/compute/__init__.py b/tempest/lib/services/compute/__init__.py
new file mode 100644
index 0000000..91e896a
--- /dev/null
+++ b/tempest/lib/services/compute/__init__.py
@@ -0,0 +1,77 @@
+# Copyright (c) 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.
+
+from tempest.lib.services.compute.agents_client import AgentsClient
+from tempest.lib.services.compute.aggregates_client import AggregatesClient
+from tempest.lib.services.compute.availability_zone_client import \
+    AvailabilityZoneClient
+from tempest.lib.services.compute.baremetal_nodes_client import \
+    BaremetalNodesClient
+from tempest.lib.services.compute.certificates_client import \
+    CertificatesClient
+from tempest.lib.services.compute.extensions_client import \
+    ExtensionsClient
+from tempest.lib.services.compute.fixed_ips_client import FixedIPsClient
+from tempest.lib.services.compute.flavors_client import FlavorsClient
+from tempest.lib.services.compute.floating_ip_pools_client import \
+    FloatingIPPoolsClient
+from tempest.lib.services.compute.floating_ips_bulk_client import \
+    FloatingIPsBulkClient
+from tempest.lib.services.compute.floating_ips_client import \
+    FloatingIPsClient
+from tempest.lib.services.compute.hosts_client import HostsClient
+from tempest.lib.services.compute.hypervisor_client import \
+    HypervisorClient
+from tempest.lib.services.compute.images_client import ImagesClient
+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
+from tempest.lib.services.compute.quota_classes_client import \
+    QuotaClassesClient
+from tempest.lib.services.compute.quotas_client import QuotasClient
+from tempest.lib.services.compute.security_group_default_rules_client import \
+    SecurityGroupDefaultRulesClient
+from tempest.lib.services.compute.security_group_rules_client import \
+    SecurityGroupRulesClient
+from tempest.lib.services.compute.security_groups_client import \
+    SecurityGroupsClient
+from tempest.lib.services.compute.server_groups_client import \
+    ServerGroupsClient
+from tempest.lib.services.compute.servers_client import ServersClient
+from tempest.lib.services.compute.services_client import ServicesClient
+from tempest.lib.services.compute.snapshots_client import SnapshotsClient
+from tempest.lib.services.compute.tenant_networks_client import \
+    TenantNetworksClient
+from tempest.lib.services.compute.tenant_usages_client import \
+    TenantUsagesClient
+from tempest.lib.services.compute.versions_client import VersionsClient
+from tempest.lib.services.compute.volumes_client import \
+    VolumesClient
+
+__all__ = ['AgentsClient', 'AggregatesClient', 'AvailabilityZoneClient',
+           'BaremetalNodesClient', 'CertificatesClient', 'ExtensionsClient',
+           'FixedIPsClient', 'FlavorsClient', 'FloatingIPPoolsClient',
+           'FloatingIPsBulkClient', 'FloatingIPsClient', 'HostsClient',
+           'HypervisorClient', 'ImagesClient', 'InstanceUsagesAuditLogClient',
+           'InterfacesClient', 'KeyPairsClient', 'LimitsClient',
+           'MigrationsClient', 'NetworksClient', 'QuotaClassesClient',
+           'QuotasClient', 'SecurityGroupDefaultRulesClient',
+           'SecurityGroupRulesClient', 'SecurityGroupsClient',
+           'ServerGroupsClient', 'ServersClient', 'ServicesClient',
+           'SnapshotsClient', 'TenantNetworksClient', 'TenantUsagesClient',
+           'VersionsClient', 'VolumesClient']
diff --git a/tempest/lib/services/compute/agents_client.py b/tempest/lib/services/compute/agents_client.py
new file mode 100644
index 0000000..169d978
--- /dev/null
+++ b/tempest/lib/services/compute/agents_client.py
@@ -0,0 +1,76 @@
+# 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.
+
+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 agents as schema
+from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
+
+
+class AgentsClient(base_compute_client.BaseComputeClient):
+    """Tests Agents API"""
+
+    def list_agents(self, **params):
+        """List all agent builds.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listbuilds
+        """
+        url = 'os-agents'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_agents, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_agent(self, **kwargs):
+        """Create an agent build.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#agentbuild
+        """
+        post_body = json.dumps({'agent': kwargs})
+        resp, body = self.post('os-agents', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_agent, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_agent(self, agent_id):
+        """Delete an existing agent build.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteBuild
+        """
+        resp, body = self.delete("os-agents/%s" % agent_id)
+        self.validate_response(schema.delete_agent, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_agent(self, agent_id, **kwargs):
+        """Update an agent build.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#updatebuild
+        """
+        put_body = json.dumps({'para': kwargs})
+        resp, body = self.put('os-agents/%s' % agent_id, put_body)
+        body = json.loads(body)
+        self.validate_response(schema.update_agent, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/aggregates_client.py b/tempest/lib/services/compute/aggregates_client.py
new file mode 100644
index 0000000..c1a6c8c
--- /dev/null
+++ b/tempest/lib/services/compute/aggregates_client.py
@@ -0,0 +1,126 @@
+# Copyright 2013 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 oslo_serialization import jsonutils as json
+
+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(base_compute_client.BaseComputeClient):
+
+    def list_aggregates(self):
+        """Get aggregate list."""
+        resp, body = self.get("os-aggregates")
+        body = json.loads(body)
+        self.validate_response(schema.list_aggregates, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_aggregate(self, aggregate_id):
+        """Get details of the given aggregate."""
+        resp, body = self.get("os-aggregates/%s" % aggregate_id)
+        body = json.loads(body)
+        self.validate_response(schema.get_aggregate, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_aggregate(self, **kwargs):
+        """Create a new aggregate.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createAggregate
+        """
+        post_body = json.dumps({'aggregate': kwargs})
+        resp, body = self.post('os-aggregates', post_body)
+
+        body = json.loads(body)
+        self.validate_response(schema.create_aggregate, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_aggregate(self, aggregate_id, **kwargs):
+        """Update an aggregate.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#updateAggregate
+        """
+        put_body = json.dumps({'aggregate': kwargs})
+        resp, body = self.put('os-aggregates/%s' % aggregate_id, put_body)
+
+        body = json.loads(body)
+        self.validate_response(schema.update_aggregate, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_aggregate(self, aggregate_id):
+        """Delete the given aggregate."""
+        resp, body = self.delete("os-aggregates/%s" % aggregate_id)
+        self.validate_response(schema.delete_aggregate, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_aggregate(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Return the primary type of resource this client works with."""
+        return 'aggregate'
+
+    def add_host(self, aggregate_id, **kwargs):
+        """Add a host to the given aggregate.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#addHost
+        """
+        post_body = json.dumps({'add_host': kwargs})
+        resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
+                               post_body)
+        body = json.loads(body)
+        self.validate_response(schema.aggregate_add_remove_host, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def remove_host(self, aggregate_id, **kwargs):
+        """Remove a host from the given aggregate.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#removeAggregateHost
+        """
+        post_body = json.dumps({'remove_host': kwargs})
+        resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
+                               post_body)
+        body = json.loads(body)
+        self.validate_response(schema.aggregate_add_remove_host, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_metadata(self, aggregate_id, **kwargs):
+        """Replace the aggregate's existing metadata with new metadata.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#addAggregateMetadata
+        """
+        post_body = json.dumps({'set_metadata': kwargs})
+        resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
+                               post_body)
+        body = json.loads(body)
+        self.validate_response(schema.aggregate_set_metadata, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/availability_zone_client.py b/tempest/lib/services/compute/availability_zone_client.py
new file mode 100644
index 0000000..a911191
--- /dev/null
+++ b/tempest/lib/services/compute/availability_zone_client.py
@@ -0,0 +1,36 @@
+# Copyright 2013 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 oslo_serialization import jsonutils as json
+
+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(base_compute_client.BaseComputeClient):
+
+    def list_availability_zones(self, detail=False):
+        url = 'os-availability-zone'
+        schema_list = schema.list_availability_zone_list
+        if detail:
+            url += '/detail'
+            schema_list = schema.list_availability_zone_list_detail
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema_list, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/baremetal_nodes_client.py b/tempest/lib/services/compute/baremetal_nodes_client.py
new file mode 100644
index 0000000..06dc369
--- /dev/null
+++ b/tempest/lib/services/compute/baremetal_nodes_client.py
@@ -0,0 +1,43 @@
+# 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 oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+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(base_compute_client.BaseComputeClient):
+    """Tests Baremetal API"""
+
+    def list_baremetal_nodes(self, **params):
+        """List all baremetal nodes."""
+        url = 'os-baremetal-nodes'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_baremetal_nodes, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_baremetal_node(self, baremetal_node_id):
+        """Return the details of a single baremetal node."""
+        url = 'os-baremetal-nodes/%s' % baremetal_node_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_baremetal_node, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/base_compute_client.py b/tempest/lib/services/compute/base_compute_client.py
new file mode 100644
index 0000000..a706a79
--- /dev/null
+++ b/tempest/lib/services/compute/base_compute_client.py
@@ -0,0 +1,91 @@
+# 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 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 get_headers(self):
+        headers = super(BaseComputeClient, self).get_headers()
+        if COMPUTE_MICROVERSION:
+            headers[self.api_microversion_header_name] = COMPUTE_MICROVERSION
+        return headers
+
+    def request(self, method, url, extra_headers=False, headers=None,
+                body=None, chunked=False):
+        resp, resp_body = super(BaseComputeClient, self).request(
+            method, url, extra_headers, headers, body, chunked)
+        if (COMPUTE_MICROVERSION and
+            COMPUTE_MICROVERSION != api_version_utils.LATEST_MICROVERSION):
+            api_version_utils.assert_version_header_matches_request(
+                self.api_microversion_header_name,
+                COMPUTE_MICROVERSION,
+                resp)
+        return resp, resp_body
+
+    def get_schema(self, schema_versions_info):
+        """Get JSON schema
+
+        This method provides the matching schema for requested
+        microversion.
+
+        :param schema_versions_info: List of dict which provides schema
+                                     information with range of valid versions.
+
+        Example::
+
+         schema_versions_info = [
+             {'min': None, 'max': '2.1', 'schema': schemav21},
+             {'min': '2.2', 'max': '2.9', 'schema': schemav22},
+             {'min': '2.10', 'max': None, 'schema': schemav210}]
+        """
+        schema = None
+        version = api_version_request.APIVersionRequest(COMPUTE_MICROVERSION)
+        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 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 COMPUTE_MICROVERSION
+            elif version.matches(min_version, max_version):
+                schema = items['schema']
+                break
+        if schema is None:
+            raise exceptions.JSONSchemaNotFound(
+                version=version.get_string(),
+                schema_versions_info=schema_versions_info)
+        return schema
diff --git a/tempest/lib/services/compute/certificates_client.py b/tempest/lib/services/compute/certificates_client.py
new file mode 100644
index 0000000..822756c
--- /dev/null
+++ b/tempest/lib/services/compute/certificates_client.py
@@ -0,0 +1,38 @@
+# Copyright 2013 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.
+
+from oslo_serialization import jsonutils as json
+
+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(base_compute_client.BaseComputeClient):
+
+    def show_certificate(self, certificate_id):
+        url = "os-certificates/%s" % certificate_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_certificate, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_certificate(self):
+        """Create a certificate."""
+        url = "os-certificates"
+        resp, body = self.post(url, None)
+        body = json.loads(body)
+        self.validate_response(schema.create_certificate, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/extensions_client.py b/tempest/lib/services/compute/extensions_client.py
new file mode 100644
index 0000000..afaf282
--- /dev/null
+++ b/tempest/lib/services/compute/extensions_client.py
@@ -0,0 +1,35 @@
+# 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.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(base_compute_client.BaseComputeClient):
+
+    def list_extensions(self):
+        url = 'extensions'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_extensions, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_extension(self, extension_alias):
+        resp, body = self.get('extensions/%s' % extension_alias)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/fixed_ips_client.py b/tempest/lib/services/compute/fixed_ips_client.py
new file mode 100644
index 0000000..682ee86
--- /dev/null
+++ b/tempest/lib/services/compute/fixed_ips_client.py
@@ -0,0 +1,42 @@
+# Copyright 2013 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.
+
+from oslo_serialization import jsonutils as json
+
+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(base_compute_client.BaseComputeClient):
+
+    def show_fixed_ip(self, fixed_ip):
+        url = "os-fixed-ips/%s" % fixed_ip
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_fixed_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def reserve_fixed_ip(self, fixed_ip, **kwargs):
+        """Reserve/Unreserve a fixed IP.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#reserveIP
+        """
+        url = "os-fixed-ips/%s/action" % fixed_ip
+        resp, body = self.post(url, json.dumps(kwargs))
+        self.validate_response(schema.reserve_unreserve_fixed_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
new file mode 100644
index 0000000..a83c68b
--- /dev/null
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -0,0 +1,229 @@
+# 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 six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import flavors as schema
+from tempest.lib.api_schema.response.compute.v2_1 import flavors_access \
+    as schema_access
+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(base_compute_client.BaseComputeClient):
+
+    def list_flavors(self, detail=False, **params):
+        """Lists flavors.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listFlavors
+        """
+        url = 'flavors'
+        _schema = schema.list_flavors
+
+        if detail:
+            url += '/detail'
+            _schema = schema.list_flavors_details
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(_schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_flavor(self, flavor_id):
+        """Shows details for a flavor.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#showFlavor
+        """
+        resp, body = self.get("flavors/%s" % flavor_id)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_flavor_details, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_flavor(self, **kwargs):
+        """Create a new flavor or instance type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createFlavor
+        """
+        if 'ephemeral' in kwargs:
+            kwargs['OS-FLV-EXT-DATA:ephemeral'] = kwargs.pop('ephemeral')
+        if 'is_public' in kwargs:
+            kwargs['os-flavor-access:is_public'] = kwargs.pop('is_public')
+
+        post_body = json.dumps({'flavor': kwargs})
+        resp, body = self.post('flavors', post_body)
+
+        body = json.loads(body)
+        self.validate_response(schema.create_get_flavor_details, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_flavor(self, flavor_id):
+        """Delete the given flavor.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteFlavor
+        """
+        resp, body = self.delete("flavors/{0}".format(flavor_id))
+        self.validate_response(schema.delete_flavor, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        # Did not use show_flavor(id) for verification as it gives
+        # 200 ok even for deleted id. LP #981263
+        # we can remove the loop here and use get by ID when bug gets sortedout
+        flavors = self.list_flavors(detail=True)['flavors']
+        for flavor in flavors:
+            if flavor['id'] == id:
+                return False
+        return True
+
+    @property
+    def resource_type(self):
+        """Return the primary type of resource this client works with."""
+        return 'flavor'
+
+    def set_flavor_extra_spec(self, flavor_id, **kwargs):
+        """Set extra Specs to the mentioned flavor.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createFlavorExtraSpec
+        """
+        post_body = json.dumps({'extra_specs': kwargs})
+        resp, body = self.post('flavors/%s/os-extra_specs' % flavor_id,
+                               post_body)
+        body = json.loads(body)
+        self.validate_response(schema_extra_specs.set_get_flavor_extra_specs,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_flavor_extra_specs(self, flavor_id):
+        """Get extra Specs details of the mentioned flavor.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listFlavorExtraSpecs
+        """
+        resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id)
+        body = json.loads(body)
+        self.validate_response(schema_extra_specs.set_get_flavor_extra_specs,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_flavor_extra_spec(self, flavor_id, key):
+        """Get extra Specs key-value of the mentioned flavor and key.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#showFlavorExtraSpec
+        """
+        resp, body = self.get('flavors/%s/os-extra_specs/%s' % (flavor_id,
+                              key))
+        body = json.loads(body)
+        self.validate_response(
+            schema_extra_specs.set_get_flavor_extra_specs_key,
+            resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_flavor_extra_spec(self, flavor_id, key, **kwargs):
+        """Update specified extra Specs of the mentioned flavor and key.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#updateFlavorExtraSpec
+        """
+        resp, body = self.put('flavors/%s/os-extra_specs/%s' %
+                              (flavor_id, key), json.dumps(kwargs))
+        body = json.loads(body)
+        self.validate_response(
+            schema_extra_specs.set_get_flavor_extra_specs_key,
+            resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    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.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteFlavorExtraSpec
+        """
+        resp, body = self.delete('flavors/%s/os-extra_specs/%s' %
+                                 (flavor_id, key))
+        self.validate_response(schema.unset_flavor_extra_specs, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_flavor_access(self, flavor_id):
+        """Get flavor access information given the flavor id.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listFlavorAccess
+        """
+        resp, body = self.get('flavors/%s/os-flavor-access' % flavor_id)
+        body = json.loads(body)
+        self.validate_response(schema_access.add_remove_list_flavor_access,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def add_flavor_access(self, flavor_id, tenant_id):
+        """Add flavor access for the specified tenant.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#addFlavorAccess
+        """
+        post_body = {
+            'addTenantAccess': {
+                'tenant': tenant_id
+            }
+        }
+        post_body = json.dumps(post_body)
+        resp, body = self.post('flavors/%s/action' % flavor_id, post_body)
+        body = json.loads(body)
+        self.validate_response(schema_access.add_remove_list_flavor_access,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def remove_flavor_access(self, flavor_id, tenant_id):
+        """Remove flavor access from the specified tenant.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#removeFlavorAccess
+        """
+        post_body = {
+            'removeTenantAccess': {
+                'tenant': tenant_id
+            }
+        }
+        post_body = json.dumps(post_body)
+        resp, body = self.post('flavors/%s/action' % flavor_id, post_body)
+        body = json.loads(body)
+        self.validate_response(schema_access.add_remove_list_flavor_access,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/floating_ip_pools_client.py b/tempest/lib/services/compute/floating_ip_pools_client.py
new file mode 100644
index 0000000..d3af050
--- /dev/null
+++ b/tempest/lib/services/compute/floating_ip_pools_client.py
@@ -0,0 +1,35 @@
+# 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 six.moves.urllib import parse as urllib
+
+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(base_compute_client.BaseComputeClient):
+
+    def list_floating_ip_pools(self, params=None):
+        """Gets all floating IP Pools list."""
+        url = 'os-floating-ip-pools'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_floating_ip_pools, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/floating_ips_bulk_client.py b/tempest/lib/services/compute/floating_ips_bulk_client.py
new file mode 100644
index 0000000..5f06009
--- /dev/null
+++ b/tempest/lib/services/compute/floating_ips_bulk_client.py
@@ -0,0 +1,51 @@
+# 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.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(base_compute_client.BaseComputeClient):
+
+    def create_floating_ips_bulk(self, ip_range, pool, interface):
+        """Allocate floating IPs in bulk."""
+        post_body = {
+            'ip_range': ip_range,
+            'pool': pool,
+            'interface': interface
+        }
+        post_body = json.dumps({'floating_ips_bulk_create': post_body})
+        resp, body = self.post('os-floating-ips-bulk', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_floating_ips_bulk, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_floating_ips_bulk(self):
+        """Gets all floating IPs in bulk."""
+        resp, body = self.get('os-floating-ips-bulk')
+        body = json.loads(body)
+        self.validate_response(schema.list_floating_ips_bulk, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_floating_ips_bulk(self, ip_range):
+        """Deletes the provided floating IPs in bulk."""
+        post_body = json.dumps({'ip_range': ip_range})
+        resp, body = self.put('os-floating-ips-bulk/delete', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.delete_floating_ips_bulk, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/floating_ips_client.py b/tempest/lib/services/compute/floating_ips_client.py
new file mode 100644
index 0000000..744e14c
--- /dev/null
+++ b/tempest/lib/services/compute/floating_ips_client.py
@@ -0,0 +1,120 @@
+# 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 six.moves.urllib import parse as urllib
+
+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(base_compute_client.BaseComputeClient):
+
+    def list_floating_ips(self, **params):
+        """Returns a list of all floating IPs filtered by any parameters.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listfloatingipsObject
+        """
+        url = 'os-floating-ips'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_floating_ips, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_floating_ip(self, floating_ip_id):
+        """Get the details of a floating IP.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#showFloatingIP
+        """
+        url = "os-floating-ips/%s" % floating_ip_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_floating_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_floating_ip(self, **kwargs):
+        """Allocate a floating IP to the project.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createFloatingIP
+        """
+        url = 'os-floating-ips'
+        post_body = json.dumps(kwargs)
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_floating_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_floating_ip(self, floating_ip_id):
+        """Deletes the provided floating IP from the project.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteFloatingIP
+        """
+        url = "os-floating-ips/%s" % floating_ip_id
+        resp, body = self.delete(url)
+        self.validate_response(schema.add_remove_floating_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def associate_floating_ip_to_server(self, floating_ip, server_id):
+        """Associate the provided floating IP to a specific server."""
+        url = "servers/%s/action" % server_id
+        post_body = {
+            'addFloatingIp': {
+                'address': floating_ip,
+            }
+        }
+
+        post_body = json.dumps(post_body)
+        resp, body = self.post(url, post_body)
+        self.validate_response(schema.add_remove_floating_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def disassociate_floating_ip_from_server(self, floating_ip, server_id):
+        """Disassociate the provided floating IP from a specific server."""
+        url = "servers/%s/action" % server_id
+        post_body = {
+            'removeFloatingIp': {
+                'address': floating_ip,
+            }
+        }
+
+        post_body = json.dumps(post_body)
+        resp, body = self.post(url, post_body)
+        self.validate_response(schema.add_remove_floating_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_floating_ip(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'floating_ip'
diff --git a/tempest/lib/services/compute/hosts_client.py b/tempest/lib/services/compute/hosts_client.py
new file mode 100644
index 0000000..1b93b00
--- /dev/null
+++ b/tempest/lib/services/compute/hosts_client.py
@@ -0,0 +1,99 @@
+# 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.
+
+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 hosts as schema
+from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
+
+
+class HostsClient(base_compute_client.BaseComputeClient):
+
+    def list_hosts(self, **params):
+        """List all hosts."""
+
+        url = 'os-hosts'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_hosts, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_host(self, hostname):
+        """Show detail information for the host."""
+
+        resp, body = self.get("os-hosts/%s" % hostname)
+        body = json.loads(body)
+        self.validate_response(schema.get_host_detail, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_host(self, hostname, **kwargs):
+        """Update a host.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#enablehost
+        """
+
+        request_body = {
+            'status': None,
+            'maintenance_mode': None,
+        }
+        request_body.update(**kwargs)
+        request_body = json.dumps(request_body)
+
+        resp, body = self.put("os-hosts/%s" % hostname, request_body)
+        body = json.loads(body)
+        self.validate_response(schema.update_host, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    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)
+        body = json.loads(body)
+        self.validate_response(schema.startup_host, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    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)
+        body = json.loads(body)
+        self.validate_response(schema.shutdown_host, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    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)
+        body = json.loads(body)
+        self.validate_response(schema.reboot_host, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/hypervisor_client.py b/tempest/lib/services/compute/hypervisor_client.py
new file mode 100644
index 0000000..23c304e
--- /dev/null
+++ b/tempest/lib/services/compute/hypervisor_client.py
@@ -0,0 +1,73 @@
+# Copyright 2013 IBM 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 oslo_serialization import jsonutils as json
+
+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(base_compute_client.BaseComputeClient):
+
+    def list_hypervisors(self, detail=False):
+        """List hypervisors information."""
+        url = 'os-hypervisors'
+        _schema = schema.list_search_hypervisors
+        if detail:
+            url += '/detail'
+            _schema = schema.list_hypervisors_detail
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(_schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_hypervisor(self, hypervisor_id):
+        """Display the details of the specified hypervisor."""
+        resp, body = self.get('os-hypervisors/%s' % hypervisor_id)
+        body = json.loads(body)
+        self.validate_response(schema.get_hypervisor, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_servers_on_hypervisor(self, hypervisor_name):
+        """List instances belonging to the specified hypervisor."""
+        resp, body = self.get('os-hypervisors/%s/servers' % hypervisor_name)
+        body = json.loads(body)
+        self.validate_response(schema.get_hypervisors_servers, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_hypervisor_statistics(self):
+        """Get hypervisor statistics over all compute nodes."""
+        resp, body = self.get('os-hypervisors/statistics')
+        body = json.loads(body)
+        self.validate_response(schema.get_hypervisor_statistics, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_hypervisor_uptime(self, hypervisor_id):
+        """Display the uptime of the specified hypervisor."""
+        resp, body = self.get('os-hypervisors/%s/uptime' % hypervisor_id)
+        body = json.loads(body)
+        self.validate_response(schema.get_hypervisor_uptime, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    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)
+        self.validate_response(schema.list_search_hypervisors, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/images_client.py b/tempest/lib/services/compute/images_client.py
new file mode 100644
index 0000000..e937c13
--- /dev/null
+++ b/tempest/lib/services/compute/images_client.py
@@ -0,0 +1,149 @@
+# 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 six.moves.urllib import parse as urllib
+
+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(base_compute_client.BaseComputeClient):
+
+    def create_image(self, server_id, **kwargs):
+        """Create an image of the original server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createImage
+        """
+
+        post_body = {'createImage': kwargs}
+        post_body = json.dumps(post_body)
+        resp, body = self.post('servers/%s/action' % server_id,
+                               post_body)
+        self.validate_response(schema.create_image, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_images(self, detail=False, **params):
+        """Return a list of all images filtered by any parameter.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listImages
+        """
+        url = 'images'
+        _schema = schema.list_images
+        if detail:
+            url += '/detail'
+            _schema = schema.list_images_details
+
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(_schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_image(self, image_id):
+        """Return the details of a single image."""
+        resp, body = self.get("images/%s" % image_id)
+        body = json.loads(body)
+        self.validate_response(schema.get_image, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_image(self, image_id):
+        """Delete the provided image."""
+        resp, body = self.delete("images/%s" % image_id)
+        self.validate_response(schema.delete, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_image_metadata(self, image_id):
+        """List all metadata items for an image."""
+        resp, body = self.get("images/%s/metadata" % image_id)
+        body = json.loads(body)
+        self.validate_response(schema.image_metadata, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_image_metadata(self, image_id, meta):
+        """Set the metadata for an image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createImageMetadata
+        """
+        post_body = json.dumps({'metadata': meta})
+        resp, body = self.put('images/%s/metadata' % image_id, post_body)
+        body = json.loads(body)
+        self.validate_response(schema.image_metadata, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_image_metadata(self, image_id, meta):
+        """Update the metadata for an image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#updateImageMetadata
+        """
+        post_body = json.dumps({'metadata': meta})
+        resp, body = self.post('images/%s/metadata' % image_id, post_body)
+        body = json.loads(body)
+        self.validate_response(schema.image_metadata, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_image_metadata_item(self, image_id, key):
+        """Return the value for a specific image metadata key."""
+        resp, body = self.get("images/%s/metadata/%s" % (image_id, key))
+        body = json.loads(body)
+        self.validate_response(schema.image_meta_item, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_image_metadata_item(self, image_id, key, meta):
+        """Set the value for a specific image metadata key.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#setImageMetadataItem
+        """
+        post_body = json.dumps({'meta': meta})
+        resp, body = self.put('images/%s/metadata/%s' % (image_id, key),
+                              post_body)
+        body = json.loads(body)
+        self.validate_response(schema.image_meta_item, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_image_metadata_item(self, image_id, key):
+        """Delete a single image metadata key/value pair."""
+        resp, body = self.delete("images/%s/metadata/%s" %
+                                 (image_id, key))
+        self.validate_response(schema.delete, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        # Added status check for user with admin role
+        try:
+            if self.show_image(id)['image']['status'] == 'DELETED':
+                return True
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Return the primary type of resource this client works with."""
+        return 'image'
diff --git a/tempest/lib/services/compute/instance_usage_audit_log_client.py b/tempest/lib/services/compute/instance_usage_audit_log_client.py
new file mode 100644
index 0000000..1b94306
--- /dev/null
+++ b/tempest/lib/services/compute/instance_usage_audit_log_client.py
@@ -0,0 +1,39 @@
+# Copyright 2013 IBM 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 oslo_serialization import jsonutils as json
+
+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(base_compute_client.BaseComputeClient):
+
+    def list_instance_usage_audit_logs(self):
+        url = 'os-instance_usage_audit_log'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_instance_usage_audit_log,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_instance_usage_audit_log(self, time_before):
+        url = 'os-instance_usage_audit_log/%s' % time_before
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_instance_usage_audit_log, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/interfaces_client.py b/tempest/lib/services/compute/interfaces_client.py
new file mode 100644
index 0000000..37157a4
--- /dev/null
+++ b/tempest/lib/services/compute/interfaces_client.py
@@ -0,0 +1,57 @@
+# Copyright 2013 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.
+
+from oslo_serialization import jsonutils as json
+
+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(base_compute_client.BaseComputeClient):
+
+    def list_interfaces(self, server_id):
+        resp, body = self.get('servers/%s/os-interface' % server_id)
+        body = json.loads(body)
+        self.validate_response(schema.list_interfaces, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_interface(self, server_id, **kwargs):
+        """Create an interface.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createAttachInterface
+        """
+        post_body = {'interfaceAttachment': kwargs}
+        post_body = json.dumps(post_body)
+        resp, body = self.post('servers/%s/os-interface' % server_id,
+                               body=post_body)
+        body = json.loads(body)
+        self.validate_response(schema.get_create_interfaces, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_interface(self, server_id, port_id):
+        resp, body = self.get('servers/%s/os-interface/%s' % (server_id,
+                                                              port_id))
+        body = json.loads(body)
+        self.validate_response(schema.get_create_interfaces, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_interface(self, server_id, port_id):
+        resp, body = self.delete('servers/%s/os-interface/%s' % (server_id,
+                                                                 port_id))
+        self.validate_response(schema.delete_interface, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/keypairs_client.py b/tempest/lib/services/compute/keypairs_client.py
new file mode 100644
index 0000000..c3f1781
--- /dev/null
+++ b/tempest/lib/services/compute/keypairs_client.py
@@ -0,0 +1,89 @@
+# 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 six.moves.urllib import parse as urllib
+
+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(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, **params):
+        """Lists keypairs that are associated with the account.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listKeypairs
+        """
+        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, **params):
+        """Shows details for a keypair that is associated with the account.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#showKeypair
+        """
+        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)
+
+    def create_keypair(self, **kwargs):
+        """Create a keypair.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createKeypair
+        """
+        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, **params):
+        """Deletes a keypair.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteKeypair
+        """
+        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
new file mode 100644
index 0000000..efe9889
--- /dev/null
+++ b/tempest/lib/services/compute/limits_client.py
@@ -0,0 +1,29 @@
+# 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.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(base_compute_client.BaseComputeClient):
+
+    def show_limits(self):
+        resp, body = self.get("limits")
+        body = json.loads(body)
+        self.validate_response(schema.get_limit, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/migrations_client.py b/tempest/lib/services/compute/migrations_client.py
new file mode 100644
index 0000000..375cbda
--- /dev/null
+++ b/tempest/lib/services/compute/migrations_client.py
@@ -0,0 +1,46 @@
+# Copyright 2014 NEC Corporation.
+#
+#    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.lib.api_schema.response.compute.v2_1 import migrations as schema
+from tempest.lib.api_schema.response.compute.v2_23 import migrations \
+    as schemav223
+from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
+
+
+class MigrationsClient(base_compute_client.BaseComputeClient):
+    schema_versions_info = [
+        {'min': None, 'max': '2.22', 'schema': schema},
+        {'min': '2.23', 'max': None, 'schema': schemav223}]
+
+    def list_migrations(self, **params):
+        """List all migrations.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listMigrations
+        """
+
+        url = 'os-migrations'
+        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_migrations, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/networks_client.py b/tempest/lib/services/compute/networks_client.py
new file mode 100644
index 0000000..6c8c943
--- /dev/null
+++ b/tempest/lib/services/compute/networks_client.py
@@ -0,0 +1,34 @@
+# 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.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
+
+
+class NetworksClient(base_compute_client.BaseComputeClient):
+
+    def list_networks(self):
+        resp, body = self.get("os-networks")
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_network(self, network_id):
+        resp, body = self.get("os-networks/%s" % network_id)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/quota_classes_client.py b/tempest/lib/services/compute/quota_classes_client.py
new file mode 100644
index 0000000..523a306
--- /dev/null
+++ b/tempest/lib/services/compute/quota_classes_client.py
@@ -0,0 +1,50 @@
+# Copyright 2012 NTT Data
+# 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.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(base_compute_client.BaseComputeClient):
+
+    def show_quota_class_set(self, quota_class_id):
+        """List the quota class set for a quota class."""
+
+        url = 'os-quota-class-sets/%s' % quota_class_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(classes_schema.get_quota_class_set, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_quota_class_set(self, quota_class_id, **kwargs):
+        """Update the quota class's limits for one or more resources.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#updatequota
+        """
+        post_body = json.dumps({'quota_class_set': kwargs})
+
+        resp, body = self.put('os-quota-class-sets/%s' % quota_class_id,
+                              post_body)
+
+        body = json.loads(body)
+        self.validate_response(classes_schema.update_quota_class_set,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/quotas_client.py b/tempest/lib/services/compute/quotas_client.py
new file mode 100644
index 0000000..a2b0397
--- /dev/null
+++ b/tempest/lib/services/compute/quotas_client.py
@@ -0,0 +1,70 @@
+# Copyright 2012 NTT Data
+# 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.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(base_compute_client.BaseComputeClient):
+
+    def show_quota_set(self, tenant_id, user_id=None):
+        """List the quota set for a tenant."""
+
+        url = 'os-quota-sets/%s' % tenant_id
+        if user_id:
+            url += '?user_id=%s' % user_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_quota_set, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_default_quota_set(self, tenant_id):
+        """List the default quota set for a tenant."""
+
+        url = 'os-quota-sets/%s/defaults' % tenant_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_quota_set, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_quota_set(self, tenant_id, user_id=None, **kwargs):
+        """Updates the tenant's quota limits for one or more resources.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#updateQuota
+        """
+
+        post_body = json.dumps({'quota_set': kwargs})
+
+        if user_id:
+            resp, body = self.put('os-quota-sets/%s?user_id=%s' %
+                                  (tenant_id, user_id), post_body)
+        else:
+            resp, body = self.put('os-quota-sets/%s' % tenant_id,
+                                  post_body)
+
+        body = json.loads(body)
+        self.validate_response(schema.update_quota_set, 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.validate_response(schema.delete_quota, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/security_group_default_rules_client.py b/tempest/lib/services/compute/security_group_default_rules_client.py
new file mode 100644
index 0000000..e2d3c98
--- /dev/null
+++ b/tempest/lib/services/compute/security_group_default_rules_client.py
@@ -0,0 +1,65 @@
+# 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.
+
+from oslo_serialization import jsonutils as json
+
+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(base_compute_client.BaseComputeClient):
+
+    def create_security_default_group_rule(self, **kwargs):
+        """Create security group default rule.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createSecGroupDefaultRule
+        """
+        post_body = json.dumps({'security_group_default_rule': kwargs})
+        url = 'os-security-group-default-rules'
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_security_group_default_rule,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_security_group_default_rule(self,
+                                           security_group_default_rule_id):
+        """Delete the provided Security Group default rule."""
+        resp, body = self.delete('os-security-group-default-rules/%s' % (
+            security_group_default_rule_id))
+        self.validate_response(schema.delete_security_group_default_rule,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_security_group_default_rules(self):
+        """List all Security Group default rules."""
+        resp, body = self.get('os-security-group-default-rules')
+        body = json.loads(body)
+        self.validate_response(schema.list_security_group_default_rules,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_security_group_default_rule(self, security_group_default_rule_id):
+        """Return the details of provided Security Group default rule."""
+        resp, body = self.get('os-security-group-default-rules/%s' %
+                              security_group_default_rule_id)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_security_group_default_rule,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/security_group_rules_client.py b/tempest/lib/services/compute/security_group_rules_client.py
new file mode 100644
index 0000000..3121e24
--- /dev/null
+++ b/tempest/lib/services/compute/security_group_rules_client.py
@@ -0,0 +1,45 @@
+# 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.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(base_compute_client.BaseComputeClient):
+
+    def create_security_group_rule(self, **kwargs):
+        """Create a new security group rule.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createSecGroupRule
+        """
+        post_body = json.dumps({'security_group_rule': kwargs})
+        url = 'os-security-group-rules'
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_security_group_rule, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_security_group_rule(self, group_rule_id):
+        """Deletes the provided Security Group rule."""
+        resp, body = self.delete('os-security-group-rules/%s' %
+                                 group_rule_id)
+        self.validate_response(schema.delete_security_group_rule, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/security_groups_client.py b/tempest/lib/services/compute/security_groups_client.py
new file mode 100644
index 0000000..a247346
--- /dev/null
+++ b/tempest/lib/services/compute/security_groups_client.py
@@ -0,0 +1,107 @@
+# 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 six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import \
+    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(base_compute_client.BaseComputeClient):
+
+    def list_security_groups(self, **params):
+        """List all security groups for a user.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listSecGroups
+        """
+
+        url = 'os-security-groups'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_security_groups, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_security_group(self, security_group_id):
+        """Get the details of a Security Group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#showSecGroup
+        """
+        url = "os-security-groups/%s" % security_group_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_security_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_security_group(self, **kwargs):
+        """Create a new security group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createSecGroup
+        """
+        post_body = json.dumps({'security_group': kwargs})
+        resp, body = self.post('os-security-groups', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.get_security_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_security_group(self, security_group_id, **kwargs):
+        """Update a security group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#updateSecGroup
+        """
+        post_body = json.dumps({'security_group': kwargs})
+        resp, body = self.put('os-security-groups/%s' % security_group_id,
+                              post_body)
+        body = json.loads(body)
+        self.validate_response(schema.update_security_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_security_group(self, security_group_id):
+        """Delete the provided Security Group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteSecGroup
+        """
+        resp, body = self.delete(
+            'os-security-groups/%s' % security_group_id)
+        self.validate_response(schema.delete_security_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_security_group(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Return the primary type of resource this client works with."""
+        return 'security_group'
diff --git a/tempest/lib/services/compute/server_groups_client.py b/tempest/lib/services/compute/server_groups_client.py
new file mode 100644
index 0000000..9ba8d38
--- /dev/null
+++ b/tempest/lib/services/compute/server_groups_client.py
@@ -0,0 +1,58 @@
+# Copyright 2012 OpenStack Foundation
+# Copyright 2013 Hewlett-Packard Development Company, L.P.
+# 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.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(base_compute_client.BaseComputeClient):
+
+    def create_server_group(self, **kwargs):
+        """Create the server group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createServerGroup
+        """
+        post_body = json.dumps({'server_group': kwargs})
+        resp, body = self.post('os-server-groups', post_body)
+
+        body = json.loads(body)
+        self.validate_response(schema.create_show_server_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_server_group(self, server_group_id):
+        """Delete the given server-group."""
+        resp, body = self.delete("os-server-groups/%s" % server_group_id)
+        self.validate_response(schema.delete_server_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_server_groups(self):
+        """List the server-groups."""
+        resp, body = self.get("os-server-groups")
+        body = json.loads(body)
+        self.validate_response(schema.list_server_groups, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_server_group(self, server_group_id):
+        """Get the details of given server_group."""
+        resp, body = self.get("os-server-groups/%s" % server_group_id)
+        body = json.loads(body)
+        self.validate_response(schema.create_show_server_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
new file mode 100644
index 0000000..50ce32e
--- /dev/null
+++ b/tempest/lib/services/compute/servers_client.py
@@ -0,0 +1,717 @@
+# Copyright 2012 OpenStack Foundation
+# Copyright 2013 Hewlett-Packard Development Company, L.P.
+# 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 oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
+from tempest.lib.api_schema.response.compute.v2_16 import servers as schemav216
+from tempest.lib.api_schema.response.compute.v2_19 import servers as schemav219
+from tempest.lib.api_schema.response.compute.v2_26 import servers as schemav226
+from tempest.lib.api_schema.response.compute.v2_3 import servers as schemav23
+from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
+from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
+
+
+class ServersClient(base_compute_client.BaseComputeClient):
+    schema_versions_info = [
+        {'min': None, 'max': '2.2', 'schema': schema},
+        {'min': '2.3', 'max': '2.8', 'schema': schemav23},
+        {'min': '2.9', 'max': '2.15', 'schema': schemav29},
+        {'min': '2.16', 'max': '2.18', 'schema': schemav216},
+        {'min': '2.19', 'max': '2.25', 'schema': schemav219},
+        {'min': '2.26', 'max': None, 'schema': schemav226}]
+
+    def __init__(self, auth_provider, service, region,
+                 enable_instance_password=True, **kwargs):
+        super(ServersClient, self).__init__(
+            auth_provider, service, region, **kwargs)
+        self.enable_instance_password = enable_instance_password
+
+    def create_server(self, **kwargs):
+        """Create server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/compute/#create-server
+
+        :param name: Server name
+        :param imageRef: Image reference (UUID)
+        :param flavorRef: Flavor reference (UUID or full URL)
+
+        Most parameters except the following are passed to the API without
+        any changes.
+        :param disk_config: The name is changed to OS-DCF:diskConfig
+        :param scheduler_hints: The name is changed to os:scheduler_hints and
+        the parameter is set in the same level as the parameter 'server'.
+        """
+        body = copy.deepcopy(kwargs)
+        if body.get('disk_config'):
+            body['OS-DCF:diskConfig'] = body.pop('disk_config')
+
+        hints = None
+        if body.get('scheduler_hints'):
+            hints = {'os:scheduler_hints': body.pop('scheduler_hints')}
+
+        post_body = {'server': body}
+
+        if hints:
+            post_body.update(hints)
+
+        post_body = json.dumps(post_body)
+        resp, body = self.post('servers', post_body)
+
+        body = json.loads(body)
+        # NOTE(maurosr): this deals with the case of multiple server create
+        # with return reservation id set True
+        if 'reservation_id' in body:
+            return rest_client.ResponseBody(resp, body)
+        if self.enable_instance_password:
+            create_schema = schema.create_server_with_admin_pass
+        else:
+            create_schema = schema.create_server
+        self.validate_response(create_schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_server(self, server_id, **kwargs):
+        """Update server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#updateServer
+
+        Most parameters except the following are passed to the API without
+        any changes.
+        :param disk_config: The name is changed to OS-DCF:diskConfig
+        """
+        if 'disk_config' in kwargs:
+            kwargs['OS-DCF:diskConfig'] = kwargs.pop('disk_config')
+
+        post_body = json.dumps({'server': kwargs})
+        resp, body = self.put("servers/%s" % server_id, post_body)
+        body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.update_server, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_server(self, server_id):
+        """Get server details.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#showServer
+        """
+        resp, body = self.get("servers/%s" % server_id)
+        body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.get_server, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_server(self, server_id):
+        """Delete server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteServer
+        """
+        resp, body = self.delete("servers/%s" % server_id)
+        self.validate_response(schema.delete_server, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_servers(self, detail=False, **params):
+        """List servers.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listServers
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listDetailServers
+        """
+
+        url = 'servers'
+        schema = self.get_schema(self.schema_versions_info)
+        _schema = schema.list_servers
+
+        if detail:
+            url += '/detail'
+            _schema = schema.list_servers_detail
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(_schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_addresses(self, server_id):
+        """Lists all addresses for a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#list-ips
+        """
+        resp, body = self.get("servers/%s/ips" % server_id)
+        body = json.loads(body)
+        self.validate_response(schema.list_addresses, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_addresses_by_network(self, server_id, network_id):
+        """Lists all addresses of a specific network type for a server."""
+        resp, body = self.get("servers/%s/ips/%s" %
+                              (server_id, network_id))
+        body = json.loads(body)
+        self.validate_response(schema.list_addresses_by_network, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def action(self, server_id, action_name,
+               schema=schema.server_actions_common_schema,
+               **kwargs):
+        post_body = json.dumps({action_name: kwargs})
+        resp, body = self.post('servers/%s/action' % server_id,
+                               post_body)
+        if body:
+            body = json.loads(body)
+        self.validate_response(schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_backup(self, server_id, **kwargs):
+        """Backup a server instance.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createBackup
+        """
+        return self.action(server_id, "createBackup", **kwargs)
+
+    def change_password(self, server_id, **kwargs):
+        """Change the root password for the server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#changePassword
+        """
+        return self.action(server_id, 'changePassword', **kwargs)
+
+    def show_password(self, server_id):
+        resp, body = self.get("servers/%s/os-server-password" %
+                              server_id)
+        body = json.loads(body)
+        self.validate_response(schema.show_password, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_password(self, server_id):
+        """Removes the encrypted server password from the metadata server
+
+        Note that this does not actually change the instance server
+        password.
+        """
+        resp, body = self.delete("servers/%s/os-server-password" %
+                                 server_id)
+        self.validate_response(schema.server_actions_delete_password,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def reboot_server(self, server_id, **kwargs):
+        """Reboot a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#reboot
+        """
+        return self.action(server_id, 'reboot', **kwargs)
+
+    def rebuild_server(self, server_id, image_ref, **kwargs):
+        """Rebuild a server with a new image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#rebuild
+
+        Most parameters except the following are passed to the API without
+        any changes.
+        :param disk_config: The name is changed to OS-DCF:diskConfig
+        """
+        kwargs['imageRef'] = image_ref
+        if 'disk_config' in kwargs:
+            kwargs['OS-DCF:diskConfig'] = kwargs.pop('disk_config')
+        schema = self.get_schema(self.schema_versions_info)
+        if self.enable_instance_password:
+            rebuild_schema = schema.rebuild_server_with_admin_pass
+        else:
+            rebuild_schema = schema.rebuild_server
+        return self.action(server_id, 'rebuild',
+                           rebuild_schema, **kwargs)
+
+    def resize_server(self, server_id, flavor_ref, **kwargs):
+        """Change the flavor of a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#resize
+
+        Most parameters except the following are passed to the API without
+        any changes.
+        :param disk_config: The name is changed to OS-DCF:diskConfig
+        """
+        kwargs['flavorRef'] = flavor_ref
+        if 'disk_config' in kwargs:
+            kwargs['OS-DCF:diskConfig'] = kwargs.pop('disk_config')
+        return self.action(server_id, 'resize', **kwargs)
+
+    def confirm_resize_server(self, server_id, **kwargs):
+        """Confirm the flavor change for a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#confirmResize
+        """
+        return self.action(server_id, 'confirmResize',
+                           schema.server_actions_confirm_resize,
+                           **kwargs)
+
+    def revert_resize_server(self, server_id, **kwargs):
+        """Revert a server back to its original flavor.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#revertResize
+        """
+        return self.action(server_id, 'revertResize', **kwargs)
+
+    def list_server_metadata(self, server_id):
+        """Lists all metadata for a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listServerMetadata
+        """
+        resp, body = self.get("servers/%s/metadata" % server_id)
+        body = json.loads(body)
+        self.validate_response(schema.list_server_metadata, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_server_metadata(self, server_id, meta, no_metadata_field=False):
+        """Sets one or more metadata items for a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createServerMetadata
+        """
+        if no_metadata_field:
+            post_body = ""
+        else:
+            post_body = json.dumps({'metadata': meta})
+        resp, body = self.put('servers/%s/metadata' % server_id,
+                              post_body)
+        body = json.loads(body)
+        self.validate_response(schema.set_server_metadata, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_server_metadata(self, server_id, meta):
+        """Updates one or more metadata items for a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#updateServerMetadata
+        """
+        post_body = json.dumps({'metadata': meta})
+        resp, body = self.post('servers/%s/metadata' % server_id,
+                               post_body)
+        body = json.loads(body)
+        self.validate_response(schema.update_server_metadata,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_server_metadata_item(self, server_id, key):
+        """Shows details for a metadata item, by key, for a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#showServerMetadataItem
+        """
+        resp, body = self.get("servers/%s/metadata/%s" % (server_id, key))
+        body = json.loads(body)
+        self.validate_response(schema.set_show_server_metadata_item,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_server_metadata_item(self, server_id, key, meta):
+        """Sets a metadata item, by key, for a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#setServerMetadataItem
+        """
+        post_body = json.dumps({'meta': meta})
+        resp, body = self.put('servers/%s/metadata/%s' % (server_id, key),
+                              post_body)
+        body = json.loads(body)
+        self.validate_response(schema.set_show_server_metadata_item,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_server_metadata_item(self, server_id, key):
+        """Deletes a metadata item, by key, from a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteServerMetadataItem
+        """
+        resp, body = self.delete("servers/%s/metadata/%s" %
+                                 (server_id, key))
+        self.validate_response(schema.delete_server_metadata_item,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def stop_server(self, server_id, **kwargs):
+        """Stops a running server and changes its status to SHUTOFF.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#stop
+        """
+        return self.action(server_id, 'os-stop', **kwargs)
+
+    def start_server(self, server_id, **kwargs):
+        """Starts a stopped server and changes its status to ACTIVE.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#start
+        """
+        return self.action(server_id, 'os-start', **kwargs)
+
+    def attach_volume(self, server_id, **kwargs):
+        """Attaches a volume to a server instance.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#attachVolume
+        """
+        post_body = json.dumps({'volumeAttachment': kwargs})
+        resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
+                               post_body)
+        body = json.loads(body)
+        self.validate_response(schema.attach_volume, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_attached_volume(self, server_id, attachment_id, **kwargs):
+        """Swaps a volume attached to an instance for another volume"""
+        post_body = json.dumps({'volumeAttachment': kwargs})
+        resp, body = self.put('servers/%s/os-volume_attachments/%s' %
+                              (server_id, attachment_id),
+                              post_body)
+        self.validate_response(schema.update_attached_volume, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def detach_volume(self, server_id, volume_id):  # noqa
+        """Detaches a volume from a server instance.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteVolumeAttachment
+        """
+        resp, body = self.delete('servers/%s/os-volume_attachments/%s' %
+                                 (server_id, volume_id))
+        self.validate_response(schema.detach_volume, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_attachment(self, server_id, volume_id):
+        """Return details about the given volume attachment.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#getVolumeAttachmentDetails
+        """
+        resp, body = self.get('servers/%s/os-volume_attachments/%s' % (
+            server_id, volume_id))
+        body = json.loads(body)
+        self.validate_response(schema.show_volume_attachment, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_volume_attachments(self, server_id):
+        """Returns the list of volume attachments for a given instance.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listVolumeAttachments
+        """
+        resp, body = self.get('servers/%s/os-volume_attachments' % (
+            server_id))
+        body = json.loads(body)
+        self.validate_response(schema.list_volume_attachments, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def add_security_group(self, server_id, **kwargs):
+        """Add a security group to the server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#addSecurityGroup
+        """
+        # TODO(oomichi): The api-site doesn't contain this API description.
+        # So the above should be changed to the api-site link after
+        # adding the description on the api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524199
+        return self.action(server_id, 'addSecurityGroup', **kwargs)
+
+    def remove_security_group(self, server_id, **kwargs):
+        """Remove a security group from the server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#removeSecurityGroup
+        """
+        # TODO(oomichi): The api-site doesn't contain this API description.
+        # So the above should be changed to the api-site link after
+        # adding the description on the api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524199
+        return self.action(server_id, 'removeSecurityGroup', **kwargs)
+
+    def live_migrate_server(self, server_id, **kwargs):
+        """This should be called with administrator privileges.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#migrateLive
+        """
+        return self.action(server_id, 'os-migrateLive', **kwargs)
+
+    def migrate_server(self, server_id, **kwargs):
+        """Migrate a server to a new host.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#migrate
+        """
+        return self.action(server_id, 'migrate', **kwargs)
+
+    def lock_server(self, server_id, **kwargs):
+        """Lock the given server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#lock
+        """
+        return self.action(server_id, 'lock', **kwargs)
+
+    def unlock_server(self, server_id, **kwargs):
+        """UNlock the given server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#unlock
+        """
+        return self.action(server_id, 'unlock', **kwargs)
+
+    def suspend_server(self, server_id, **kwargs):
+        """Suspend the provided server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#suspend
+        """
+        return self.action(server_id, 'suspend', **kwargs)
+
+    def resume_server(self, server_id, **kwargs):
+        """Un-suspend the provided server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#resume
+        """
+        return self.action(server_id, 'resume', **kwargs)
+
+    def pause_server(self, server_id, **kwargs):
+        """Pause the provided server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#pause
+        """
+        return self.action(server_id, 'pause', **kwargs)
+
+    def unpause_server(self, server_id, **kwargs):
+        """Un-pause the provided server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#unpause
+        """
+        return self.action(server_id, 'unpause', **kwargs)
+
+    def reset_state(self, server_id, **kwargs):
+        """Reset the state of a server to active/error.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#resetState
+        """
+        return self.action(server_id, 'os-resetState', **kwargs)
+
+    def shelve_server(self, server_id, **kwargs):
+        """Shelve the provided server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#shelve
+        """
+        return self.action(server_id, 'shelve', **kwargs)
+
+    def unshelve_server(self, server_id, **kwargs):
+        """Un-shelve the provided server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#unshelve
+        """
+        return self.action(server_id, 'unshelve', **kwargs)
+
+    def shelve_offload_server(self, server_id, **kwargs):
+        """Shelve-offload the provided server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#shelveOffload
+        """
+        return self.action(server_id, 'shelveOffload', **kwargs)
+
+    def get_console_output(self, server_id, **kwargs):
+        """Get console output.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#getConsoleOutput
+        """
+        return self.action(server_id, 'os-getConsoleOutput',
+                           schema.get_console_output, **kwargs)
+
+    def list_virtual_interfaces(self, server_id):
+        """List the virtual interfaces used in an instance."""
+        resp, body = self.get('/'.join(['servers', server_id,
+                              'os-virtual-interfaces']))
+        body = json.loads(body)
+        self.validate_response(schema.list_virtual_interfaces, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def rescue_server(self, server_id, **kwargs):
+        """Rescue the provided server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#rescue
+        """
+        if self.enable_instance_password:
+            rescue_schema = schema.rescue_server_with_admin_pass
+        else:
+            rescue_schema = schema.rescue_server
+        return self.action(server_id, 'rescue', rescue_schema, **kwargs)
+
+    def unrescue_server(self, server_id):
+        """Unrescue the provided server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#unrescue
+        """
+        return self.action(server_id, 'unrescue')
+
+    def show_server_diagnostics(self, server_id):
+        """Get the usage data for a server."""
+        resp, body = self.get("servers/%s/diagnostics" % server_id)
+        return rest_client.ResponseBody(resp, json.loads(body))
+
+    def list_instance_actions(self, server_id):
+        """List the provided server action."""
+        resp, body = self.get("servers/%s/os-instance-actions" %
+                              server_id)
+        body = json.loads(body)
+        self.validate_response(schema.list_instance_actions, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_instance_action(self, server_id, request_id):
+        """Returns the action details of the provided server."""
+        resp, body = self.get("servers/%s/os-instance-actions/%s" %
+                              (server_id, request_id))
+        body = json.loads(body)
+        self.validate_response(schema.show_instance_action, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def force_delete_server(self, server_id, **kwargs):
+        """Force delete a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#forceDelete
+        """
+        return self.action(server_id, 'forceDelete', **kwargs)
+
+    def restore_soft_deleted_server(self, server_id, **kwargs):
+        """Restore a soft-deleted server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#restore
+        """
+        return self.action(server_id, 'restore', **kwargs)
+
+    def reset_network(self, server_id, **kwargs):
+        """Reset the Network of a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#resetNetwork
+        """
+        return self.action(server_id, 'resetNetwork', **kwargs)
+
+    def inject_network_info(self, server_id, **kwargs):
+        """Inject the Network Info into server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#injectNetworkInfo
+        """
+        return self.action(server_id, 'injectNetworkInfo', **kwargs)
+
+    def get_vnc_console(self, server_id, **kwargs):
+        """Get URL of VNC console.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#getVNCConsole
+        """
+        return self.action(server_id, "os-getVNCConsole",
+                           schema.get_vnc_console, **kwargs)
+
+    def add_fixed_ip(self, server_id, **kwargs):
+        """Add a fixed IP to server instance.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#addFixedIp
+        """
+        return self.action(server_id, 'addFixedIp', **kwargs)
+
+    def remove_fixed_ip(self, server_id, **kwargs):
+        """Remove input fixed IP from input server instance.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#removeFixedIp
+        """
+        return self.action(server_id, 'removeFixedIp', **kwargs)
diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py
new file mode 100644
index 0000000..0dbd1b2
--- /dev/null
+++ b/tempest/lib/services/compute/services_client.py
@@ -0,0 +1,67 @@
+# Copyright 2013 NEC Corporation
+# Copyright 2013 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.
+
+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 services as schema
+from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
+
+
+class ServicesClient(base_compute_client.BaseComputeClient):
+
+    def list_services(self, **params):
+        """Lists all running Compute services for a tenant.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listServices
+        """
+        url = 'os-services'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_services, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def enable_service(self, **kwargs):
+        """Enable service on a host.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#enableScheduling
+        """
+        post_body = json.dumps(kwargs)
+        resp, body = self.put('os-services/enable', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.enable_disable_service, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def disable_service(self, **kwargs):
+        """Disable service on a host.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#disableScheduling
+        """
+        post_body = json.dumps(kwargs)
+        resp, body = self.put('os-services/disable', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.enable_disable_service, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/snapshots_client.py b/tempest/lib/services/compute/snapshots_client.py
new file mode 100644
index 0000000..fde5288
--- /dev/null
+++ b/tempest/lib/services/compute/snapshots_client.py
@@ -0,0 +1,78 @@
+# Copyright 2015 Fujitsu(fnst) 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 oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+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(base_compute_client.BaseComputeClient):
+
+    def create_snapshot(self, volume_id, **kwargs):
+        """Create a snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createSnapshot
+        """
+        post_body = {
+            'volume_id': volume_id
+        }
+        post_body.update(kwargs)
+        post_body = json.dumps({'snapshot': post_body})
+        resp, body = self.post('os-snapshots', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_snapshot, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_snapshot(self, snapshot_id):
+        url = "os-snapshots/%s" % snapshot_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_snapshot, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_snapshots(self, detail=False, params=None):
+        url = 'os-snapshots'
+
+        if detail:
+            url += '/detail'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_snapshots, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_snapshot(self, snapshot_id):
+        resp, body = self.delete("os-snapshots/%s" % snapshot_id)
+        self.validate_response(schema.delete_snapshot, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_snapshot(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Return the primary type of resource this client works with."""
+        return 'snapshot'
diff --git a/tempest/lib/services/compute/tenant_networks_client.py b/tempest/lib/services/compute/tenant_networks_client.py
new file mode 100644
index 0000000..04d8bab
--- /dev/null
+++ b/tempest/lib/services/compute/tenant_networks_client.py
@@ -0,0 +1,35 @@
+# 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 oslo_serialization import jsonutils as json
+
+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(base_compute_client.BaseComputeClient):
+
+    def list_tenant_networks(self):
+        resp, body = self.get("os-tenant-networks")
+        body = json.loads(body)
+        self.validate_response(tenant_networks.list_tenant_networks, resp,
+                               body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_tenant_network(self, network_id):
+        resp, body = self.get("os-tenant-networks/%s" % network_id)
+        body = json.loads(body)
+        self.validate_response(tenant_networks.get_tenant_network, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/tenant_usages_client.py b/tempest/lib/services/compute/tenant_usages_client.py
new file mode 100644
index 0000000..d0eb1c9
--- /dev/null
+++ b/tempest/lib/services/compute/tenant_usages_client.py
@@ -0,0 +1,56 @@
+# Copyright 2013 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 oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+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(base_compute_client.BaseComputeClient):
+
+    def list_tenant_usages(self, **params):
+        """List Tenant Usage For All Tenants.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/compute/#list-tenant-usage-for-all-tenants
+        """
+        url = 'os-simple-tenant-usage'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(tenant_usages.list_tenant_usage, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_tenant_usage(self, tenant_id, **params):
+        """Show Usage Details For Tenant.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/compute/#show-usage-details-for-tenant
+        """
+        url = 'os-simple-tenant-usage/%s' % tenant_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(tenant_usages.get_tenant_usage, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/versions_client.py b/tempest/lib/services/compute/versions_client.py
new file mode 100644
index 0000000..b2052c3
--- /dev/null
+++ b/tempest/lib/services/compute/versions_client.py
@@ -0,0 +1,63 @@
+# Copyright (c) 2015 Hewlett-Packard 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.
+
+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(base_compute_client.BaseComputeClient):
+
+    def _get_base_version_url(self):
+        # 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.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()
+        resp, body = self.raw_request(version_url, 'GET')
+        self._error_checker(resp, body)
+        body = json.loads(body)
+        self.validate_response(schema.list_versions, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def get_version_by_url(self, version_url):
+        """Get the version document by url.
+
+        This gets the version document for a url, useful in testing
+        the contents of things like /v2/ or /v2.1/ in Nova. That
+        controller needs authenticated access, so we have to get
+        ourselves a token before making the request.
+
+        """
+        # we need a token for this request
+        resp, body = self.raw_request(version_url, 'GET',
+                                      {'X-Auth-Token': self.token})
+        self._error_checker(resp, body)
+        body = json.loads(body)
+        self.validate_response(schema.get_one_version, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/volumes_client.py b/tempest/lib/services/compute/volumes_client.py
new file mode 100644
index 0000000..b75f22e
--- /dev/null
+++ b/tempest/lib/services/compute/volumes_client.py
@@ -0,0 +1,93 @@
+# 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 six.moves.urllib import parse as urllib
+
+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(base_compute_client.BaseComputeClient):
+
+    def list_volumes(self, detail=False, **params):
+        """List all the volumes created.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#listVolumes
+        """
+        url = 'os-volumes'
+
+        if detail:
+            url += '/detail'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_volumes, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume(self, volume_id):
+        """Return the details of a single volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#showVolume
+        """
+        url = "os-volumes/%s" % volume_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_volume, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_volume(self, **kwargs):
+        """Create a new Volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#createVolume
+        """
+        post_body = json.dumps({'volume': kwargs})
+        resp, body = self.post('os-volumes', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_volume, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume(self, volume_id):
+        """Delete the Specified Volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteVolume
+        """
+        resp, body = self.delete("os-volumes/%s" % volume_id)
+        self.validate_response(schema.delete_volume, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_volume(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Return the primary type of resource this client works with."""
+        return 'volume'
diff --git a/tempest/lib/services/identity/__init__.py b/tempest/lib/services/identity/__init__.py
new file mode 100644
index 0000000..941a10e
--- /dev/null
+++ b/tempest/lib/services/identity/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (c) 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.
+
+from tempest.lib.services.identity import v2
+from tempest.lib.services.identity import v3
+
+__all__ = ['v2', 'v3']
diff --git a/tempest/lib/services/identity/v2/__init__.py b/tempest/lib/services/identity/v2/__init__.py
new file mode 100644
index 0000000..b7d3c74
--- /dev/null
+++ b/tempest/lib/services/identity/v2/__init__.py
@@ -0,0 +1,24 @@
+# Copyright (c) 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.
+
+from tempest.lib.services.identity.v2.endpoints_client import EndpointsClient
+from tempest.lib.services.identity.v2.identity_client import IdentityClient
+from tempest.lib.services.identity.v2.roles_client import RolesClient
+from tempest.lib.services.identity.v2.services_client import ServicesClient
+from tempest.lib.services.identity.v2.tenants_client import TenantsClient
+from tempest.lib.services.identity.v2.token_client import TokenClient
+from tempest.lib.services.identity.v2.users_client import UsersClient
+
+__all__ = ['EndpointsClient', 'IdentityClient', 'RolesClient',
+           'ServicesClient', 'TenantsClient', 'TokenClient', 'UsersClient']
diff --git a/tempest/lib/services/identity/v2/endpoints_client.py b/tempest/lib/services/identity/v2/endpoints_client.py
new file mode 100644
index 0000000..770e8ae
--- /dev/null
+++ b/tempest/lib/services/identity/v2/endpoints_client.py
@@ -0,0 +1,49 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.lib.common import rest_client
+
+
+class EndpointsClient(rest_client.RestClient):
+    api_version = "v2.0"
+
+    def create_endpoint(self, **kwargs):
+        """Create an endpoint for service.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-identity-v2-ext.html#createEndpoint
+        """
+
+        post_body = json.dumps({'endpoint': kwargs})
+        resp, body = self.post('/endpoints', post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(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 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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v2/identity_client.py b/tempest/lib/services/identity/v2/identity_client.py
new file mode 100644
index 0000000..6caff0e
--- /dev/null
+++ b/tempest/lib/services/identity/v2/identity_client.py
@@ -0,0 +1,47 @@
+#    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.lib.common import rest_client
+
+
+class IdentityClient(rest_client.RestClient):
+    api_version = "v2.0"
+
+    def show_api_description(self):
+        """Retrieves info about the v2.0 Identity API"""
+        url = ''
+        resp, body = self.get(url)
+        self.expected_success([200, 203], resp.status)
+        body = json.loads(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 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 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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v2/roles_client.py b/tempest/lib/services/identity/v2/roles_client.py
new file mode 100644
index 0000000..635d013
--- /dev/null
+++ b/tempest/lib/services/identity/v2/roles_client.py
@@ -0,0 +1,109 @@
+#    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.lib.common import rest_client
+
+
+class RolesClient(rest_client.RestClient):
+    api_version = "v2.0"
+
+    def create_role(self, **kwargs):
+        """Create a role.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-identity-v2-ext.html#createRole
+        """
+        post_body = json.dumps({'role': kwargs})
+        resp, body = self.post('OS-KSADM/roles', post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_role(self, role_id_or_name):
+        """Get a role by its id or name.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-identity-v2-ext.html#showRoleByID
+        OR
+        http://developer.openstack.org/api-ref-identity-v2-ext.html#showRoleByName
+        """
+        resp, body = self.get('OS-KSADM/roles/%s' % role_id_or_name)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_roles(self, **params):
+        """Returns roles.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-identity-v2-ext.html#listRoles
+        """
+        url = 'OS-KSADM/roles'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_role(self, role_id):
+        """Delete a role.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-identity-v2-ext.html#deleteRole
+        """
+        resp, body = self.delete('OS-KSADM/roles/%s' % role_id)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_user_role_on_project(self, tenant_id, user_id, role_id):
+        """Add roles to a user on a tenant.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-identity-v2-ext.html#grantRoleToUserOnTenant
+        """
+        resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
+                              (tenant_id, user_id, role_id), "")
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_user_roles_on_project(self, tenant_id, user_id, **params):
+        """Returns a list of roles assigned to a user for a tenant."""
+        # TODO(gmann): Need to write API-ref link, Bug# 1592711
+        url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_role_from_user_on_project(self, tenant_id, user_id, role_id):
+        """Removes a role assignment for a user on a tenant.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-identity-v2-ext.html#revokeRoleFromUserOnTenant
+        """
+        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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v2/services_client.py b/tempest/lib/services/identity/v2/services_client.py
new file mode 100644
index 0000000..b3f94aa
--- /dev/null
+++ b/tempest/lib/services/identity/v2/services_client.py
@@ -0,0 +1,65 @@
+# Copyright 2015 Red Hat, Inc.
+#
+# 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.lib.common import rest_client
+
+
+class ServicesClient(rest_client.RestClient):
+    api_version = "v2.0"
+
+    def create_service(self, **kwargs):
+        """Create a service.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v2-ext/?expanded=#create-service-admin-extension
+        """
+        post_body = json.dumps({'OS-KSADM:service': kwargs})
+        resp, body = self.post('/OS-KSADM/services', post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_service(self, service_id):
+        """Get Service."""
+        url = '/OS-KSADM/services/%s' % service_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_services(self, **params):
+        """List Service - Returns Services.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v2-ext/?expanded=#list-services-admin-extension
+        """
+        url = '/OS-KSADM/services'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v2/tenants_client.py b/tempest/lib/services/identity/v2/tenants_client.py
new file mode 100644
index 0000000..b687332
--- /dev/null
+++ b/tempest/lib/services/identity/v2/tenants_client.py
@@ -0,0 +1,103 @@
+# Copyright 2015 Red Hat, Inc.
+#
+# 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.lib.common import rest_client
+
+
+class TenantsClient(rest_client.RestClient):
+    api_version = "v2.0"
+
+    def create_tenant(self, **kwargs):
+        """Create a tenant
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v2-admin/index.html#create-tenant
+        """
+        post_body = json.dumps({'tenant': kwargs})
+        resp, body = self.post('tenants', post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_tenant(self, tenant_id):
+        """Delete a tenant.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-identity-v2-ext.html#deleteTenant
+        """
+        resp, body = self.delete('tenants/%s' % str(tenant_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_tenant(self, tenant_id):
+        """Get tenant details.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-identity-v2-ext.html#admin-showTenantById
+        """
+        resp, body = self.get('tenants/%s' % str(tenant_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_tenants(self, **params):
+        """Returns tenants.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v2-admin/index.html#list-tenants-admin-endpoint
+        """
+        url = 'tenants'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_tenant(self, tenant_id, **kwargs):
+        """Updates a tenant.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v2-admin/index.html#update-tenant
+        """
+        if 'id' not in kwargs:
+            kwargs['id'] = tenant_id
+        post_body = json.dumps({'tenant': kwargs})
+        resp, body = self.post('tenants/%s' % tenant_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_tenant_users(self, tenant_id, **params):
+        """List users for a Tenant.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v2-admin/index.html#list-users-on-a-tenant
+        """
+        url = '/tenants/%s/users' % tenant_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v2/token_client.py b/tempest/lib/services/identity/v2/token_client.py
new file mode 100644
index 0000000..458c862
--- /dev/null
+++ b/tempest/lib/services/identity/v2/token_client.py
@@ -0,0 +1,140 @@
+# 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 oslo_log import log as logging
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions
+
+
+class TokenClient(rest_client.RestClient):
+
+    def __init__(self, auth_url, disable_ssl_certificate_validation=None,
+                 ca_certs=None, trace_requests=None, **kwargs):
+        """Initialises the Token client
+
+        :param auth_url: URL to which the token request is sent
+        :param disable_ssl_certificate_validation: pass-through to rest client
+        :param ca_certs: pass-through to rest client
+        :param trace_requests: pass-through to rest client
+        :param kwargs: any extra parameter to pass through the rest client.
+               region, service and auth_provider will be ignored, if passed,
+               as they are not meaningful for token client
+        """
+        dscv = disable_ssl_certificate_validation
+        # NOTE(andreaf) region, service and auth_provider are passed
+        # positionally with None. Having them in kwargs would raise a
+        # "multiple values for keyword arguments" error
+        for unwanted_kwargs in ['region', 'service', 'auth_provider']:
+            kwargs.pop(unwanted_kwargs, None)
+        super(TokenClient, self).__init__(
+            None, None, None, disable_ssl_certificate_validation=dscv,
+            ca_certs=ca_certs, trace_requests=trace_requests, **kwargs)
+
+        if auth_url is None:
+            raise exceptions.IdentityError("Couldn't determine auth_url")
+
+        # Normalize URI to ensure /tokens is in it.
+        if 'tokens' not in auth_url:
+            auth_url = auth_url.rstrip('/') + '/tokens'
+
+        self.auth_url = auth_url
+
+    def auth(self, user, password, tenant=None):
+        creds = {
+            'auth': {
+                'passwordCredentials': {
+                    'username': user,
+                    'password': password,
+                },
+            }
+        }
+
+        if tenant:
+            creds['auth']['tenantName'] = tenant
+
+        body = json.dumps(creds, sort_keys=True)
+        resp, body = self.post(self.auth_url, body=body)
+        self.expected_success(200, resp.status)
+
+        return rest_client.ResponseBody(resp, body['access'])
+
+    def auth_token(self, token_id, tenant=None):
+        creds = {
+            'auth': {
+                'token': {
+                    'id': token_id,
+                },
+            }
+        }
+
+        if tenant:
+            creds['auth']['tenantName'] = tenant
+
+        body = json.dumps(creds)
+        resp, body = self.post(self.auth_url, body=body)
+        self.expected_success(200, resp.status)
+
+        return rest_client.ResponseBody(resp, body['access'])
+
+    def request(self, method, url, extra_headers=False, headers=None,
+                body=None, chunked=False):
+        """A simple HTTP request interface.
+
+        Note: this overloads the `request` method from the parent class and
+        thus must implement the same method signature.
+        """
+        if headers is None:
+            headers = self.get_headers(accept_type="json")
+        elif extra_headers:
+            try:
+                headers.update(self.get_headers(accept_type="json"))
+            except (ValueError, TypeError):
+                headers = self.get_headers(accept_type="json")
+
+        resp, resp_body = self.raw_request(url, method,
+                                           headers=headers, body=body)
+        self._log_request(method, url, resp, req_headers=headers,
+                          req_body='<omitted>', resp_body=resp_body)
+
+        if resp.status in [401, 403]:
+            resp_body = json.loads(resp_body)
+            raise exceptions.Unauthorized(resp_body['error']['message'])
+        elif resp.status not in [200, 201]:
+            raise exceptions.IdentityError(
+                'Unexpected status code {0}'.format(resp.status))
+
+        return resp, json.loads(resp_body)
+
+    def get_token(self, user, password, tenant, auth_data=False):
+        """Returns (token id, token data) for supplied credentials."""
+        body = self.auth(user, password, tenant)
+
+        if auth_data:
+            return body['token']['id'], body
+        else:
+            return body['token']['id']
+
+
+class TokenClientJSON(TokenClient):
+    LOG = logging.getLogger(__name__)
+
+    def _warn(self):
+        self.LOG.warning("%s class was deprecated and renamed to %s",
+                         self.__class__.__name__, 'TokenClient')
+
+    def __init__(self, *args, **kwargs):
+        self._warn()
+        super(TokenClientJSON, self).__init__(*args, **kwargs)
diff --git a/tempest/lib/services/identity/v2/users_client.py b/tempest/lib/services/identity/v2/users_client.py
new file mode 100644
index 0000000..f20fdc4
--- /dev/null
+++ b/tempest/lib/services/identity/v2/users_client.py
@@ -0,0 +1,158 @@
+#    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.lib.common import rest_client
+
+
+class UsersClient(rest_client.RestClient):
+    api_version = "v2.0"
+
+    def create_user(self, **kwargs):
+        """Create a user.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v2-admin/index.html#create-user-admin-endpoint
+        """
+        post_body = json.dumps({'user': kwargs})
+        resp, body = self.post('users', post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_user(self, user_id, **kwargs):
+        """Updates a user.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v2-admin/index.html#update-user-admin-endpoint
+        """
+        put_body = json.dumps({'user': kwargs})
+        resp, body = self.put('users/%s' % user_id, put_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_user(self, user_id):
+        """GET a user.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-identity-admin-v2.html#admin-showUser
+        """
+        resp, body = self.get("users/%s" % user_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_user(self, user_id):
+        """Delete a user.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-identity-admin-v2.html#admin-deleteUser
+        """
+        resp, body = self.delete("users/%s" % user_id)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_users(self, **params):
+        """Get the list of users.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v2-admin/index.html#list-users-admin-endpoint
+        """
+        url = "users"
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_user_enabled(self, user_id, **kwargs):
+        """Enables or disables a user.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-identity-v2-ext.html#enableUser
+        """
+        # NOTE: The URL (users/<id>/enabled) is different from the api-site
+        # one (users/<id>/OS-KSADM/enabled) , but they are the same API
+        # because of the fact that in keystone/contrib/admin_crud/core.py
+        # both api use same action='set_user_enabled'
+        put_body = json.dumps({'user': kwargs})
+        resp, body = self.put('users/%s/enabled' % user_id, put_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_user_password(self, user_id, **kwargs):
+        """Update User Password."""
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524147
+        put_body = json.dumps({'user': kwargs})
+        resp, body = self.put('users/%s/OS-KSADM/password' % user_id, put_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_user_own_password(self, user_id, **kwargs):
+        """User updates own password"""
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524153
+        # NOTE: This API is used for updating user password by itself.
+        # Ref: http://lists.openstack.org/pipermail/openstack-dev/2015-December
+        #      /081803.html
+        patch_body = json.dumps({'user': kwargs})
+        resp, body = self.patch('OS-KSCRUD/users/%s' % user_id, patch_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_user_ec2_credential(self, user_id, **kwargs):
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        post_body = json.dumps(kwargs)
+        resp, body = self.post('/users/%s/credentials/OS-EC2' % user_id,
+                               post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_user_ec2_credential(self, user_id, access):
+        resp, body = self.delete('/users/%s/credentials/OS-EC2/%s' %
+                                 (user_id, access))
+        self.expected_success(204, resp.status)
+        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 rest_client.ResponseBody(resp, body)
+
+    def show_user_ec2_credential(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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py
new file mode 100644
index 0000000..8058d51
--- /dev/null
+++ b/tempest/lib/services/identity/v3/__init__.py
@@ -0,0 +1,38 @@
+# Copyright (c) 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.
+
+from tempest.lib.services.identity.v3.credentials_client import \
+    CredentialsClient
+from tempest.lib.services.identity.v3.domains_client import DomainsClient
+from tempest.lib.services.identity.v3.endpoints_client import EndPointsClient
+from tempest.lib.services.identity.v3.groups_client import GroupsClient
+from tempest.lib.services.identity.v3.identity_client import IdentityClient
+from tempest.lib.services.identity.v3.inherited_roles_client import \
+    InheritedRolesClient
+from tempest.lib.services.identity.v3.policies_client import PoliciesClient
+from tempest.lib.services.identity.v3.projects_client import ProjectsClient
+from tempest.lib.services.identity.v3.regions_client import RegionsClient
+from tempest.lib.services.identity.v3.role_assignments_client import \
+    RoleAssignmentsClient
+from tempest.lib.services.identity.v3.roles_client import RolesClient
+from tempest.lib.services.identity.v3.services_client import ServicesClient
+from tempest.lib.services.identity.v3.token_client import V3TokenClient
+from tempest.lib.services.identity.v3.trusts_client import TrustsClient
+from tempest.lib.services.identity.v3.users_client import UsersClient
+
+__all__ = ['CredentialsClient', 'DomainsClient', 'EndPointsClient',
+           'GroupsClient', 'IdentityClient', 'InheritedRolesClient',
+           'PoliciesClient', 'ProjectsClient', 'RegionsClient',
+           'RoleAssignmentsClient', 'RolesClient', 'ServicesClient',
+           'V3TokenClient', 'TrustsClient', 'UsersClient', ]
diff --git a/tempest/lib/services/identity/v3/credentials_client.py b/tempest/lib/services/identity/v3/credentials_client.py
new file mode 100644
index 0000000..6e5fd31
--- /dev/null
+++ b/tempest/lib/services/identity/v3/credentials_client.py
@@ -0,0 +1,91 @@
+# Copyright 2013 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.
+
+"""
+http://developer.openstack.org/api-ref/identity/v3/index.html#credentials
+"""
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class CredentialsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_credential(self, **kwargs):
+        """Creates a credential.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#create-credential
+        """
+        post_body = json.dumps({'credential': kwargs})
+        resp, body = self.post('credentials', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_credential(self, credential_id, **kwargs):
+        """Updates a credential.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-credential
+        """
+        post_body = json.dumps({'credential': kwargs})
+        resp, body = self.patch('credentials/%s' % credential_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_credential(self, credential_id):
+        """To GET Details of a credential.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#show-credential-details
+        """
+        resp, body = self.get('credentials/%s' % credential_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_credentials(self, **params):
+        """Lists out all the available credentials.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/#list-credentials
+        """
+        url = 'credentials'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_credential(self, credential_id):
+        """Deletes a credential.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/#delete-credential
+        """
+        resp, body = self.delete('credentials/%s' % credential_id)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/domains_client.py b/tempest/lib/services/identity/v3/domains_client.py
new file mode 100644
index 0000000..43cb62c
--- /dev/null
+++ b/tempest/lib/services/identity/v3/domains_client.py
@@ -0,0 +1,84 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.lib.common import rest_client
+
+
+class DomainsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_domain(self, **kwargs):
+        """Creates a domain.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#create-domain
+        """
+        post_body = json.dumps({'domain': kwargs})
+        resp, body = self.post('domains', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_domain(self, domain_id):
+        """Deletes a domain.
+
+        For APi details, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#delete-domain
+        """
+        resp, body = self.delete('domains/%s' % domain_id)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_domains(self, **params):
+        """List Domains.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#list-domains
+        """
+        url = 'domains'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_domain(self, domain_id, **kwargs):
+        """Updates a domain.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-domain
+        """
+        post_body = json.dumps({'domain': kwargs})
+        resp, body = self.patch('domains/%s' % domain_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_domain(self, domain_id):
+        """Get Domain details.
+
+        For API details, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#show-domain-details
+        """
+        resp, body = self.get('domains/%s' % domain_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/endpoints_client.py b/tempest/lib/services/identity/v3/endpoints_client.py
new file mode 100644
index 0000000..c4c0d8d
--- /dev/null
+++ b/tempest/lib/services/identity/v3/endpoints_client.py
@@ -0,0 +1,72 @@
+# Copyright 2013 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.
+
+"""
+http://developer.openstack.org/api-ref-identity-v3.html#endpoints-v3
+"""
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class EndPointsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def list_endpoints(self):
+        """GET endpoints."""
+        resp, body = self.get('endpoints')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_endpoint(self, **kwargs):
+        """Create endpoint.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#create-endpoint
+        """
+        post_body = json.dumps({'endpoint': kwargs})
+        resp, body = self.post('endpoints', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_endpoint(self, endpoint_id, **kwargs):
+        """Updates an endpoint with given parameters.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-endpoint
+        """
+        post_body = json.dumps({'endpoint': kwargs})
+        resp, body = self.patch('endpoints/%s' % endpoint_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(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 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 rest_client.ResponseBody(resp_header, resp_body)
diff --git a/tempest/lib/services/identity/v3/groups_client.py b/tempest/lib/services/identity/v3/groups_client.py
new file mode 100644
index 0000000..5e68939
--- /dev/null
+++ b/tempest/lib/services/identity/v3/groups_client.py
@@ -0,0 +1,115 @@
+# Copyright 2013 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.
+
+"""
+http://developer.openstack.org/api-ref-identity-v3.html#groups-v3
+"""
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class GroupsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_group(self, **kwargs):
+        """Creates a group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#create-group
+        """
+        post_body = json.dumps({'group': kwargs})
+        resp, body = self.post('groups', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(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 rest_client.ResponseBody(resp, body)
+
+    def list_groups(self, **params):
+        """Lists the groups.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/#list-groups
+        """
+        url = 'groups'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_group(self, group_id, **kwargs):
+        """Updates a group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-group
+        """
+        post_body = json.dumps({'group': kwargs})
+        resp, body = self.patch('groups/%s' % group_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_group(self, group_id):
+        """Delete a group."""
+        resp, body = self.delete('groups/%s' % group_id)
+        self.expected_success(204, resp.status)
+        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 rest_client.ResponseBody(resp, body)
+
+    def list_group_users(self, group_id, **params):
+        """List users in group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/#list-users-in-group
+        """
+        url = 'groups/%s/users' % group_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(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 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 rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/identity/v3/identity_client.py b/tempest/lib/services/identity/v3/identity_client.py
new file mode 100644
index 0000000..8177e35
--- /dev/null
+++ b/tempest/lib/services/identity/v3/identity_client.py
@@ -0,0 +1,45 @@
+# Copyright 2013 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.lib.common import rest_client
+
+
+class IdentityClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def show_api_description(self):
+        """Retrieves info about the v3 Identity API"""
+        url = ''
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_token(self, resp_token):
+        """Get token details."""
+        headers = {'X-Subject-Token': resp_token}
+        resp, body = self.get("auth/tokens", headers=headers)
+        self.expected_success(200, resp.status)
+        body = json.loads(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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/inherited_roles_client.py b/tempest/lib/services/identity/v3/inherited_roles_client.py
new file mode 100644
index 0000000..691c7fd
--- /dev/null
+++ b/tempest/lib/services/identity/v3/inherited_roles_client.py
@@ -0,0 +1,151 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.lib.common import rest_client
+
+
+class InheritedRolesClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_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 delete_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 create_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 delete_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 create_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 delete_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 create_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 delete_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/lib/services/identity/v3/policies_client.py b/tempest/lib/services/identity/v3/policies_client.py
new file mode 100644
index 0000000..0282745
--- /dev/null
+++ b/tempest/lib/services/identity/v3/policies_client.py
@@ -0,0 +1,75 @@
+# Copyright 2013 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.
+
+"""
+http://developer.openstack.org/api-ref-identity-v3.html#policies-v3
+"""
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class PoliciesClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_policy(self, **kwargs):
+        """Creates a Policy.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#create-policy
+        """
+        post_body = json.dumps({'policy': kwargs})
+        resp, body = self.post('policies', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(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 rest_client.ResponseBody(resp, body)
+
+    def show_policy(self, policy_id):
+        """Lists out the given policy."""
+        url = 'policies/%s' % policy_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_policy(self, policy_id, **kwargs):
+        """Updates a policy.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-policy
+        """
+        post_body = json.dumps({'policy': kwargs})
+        url = 'policies/%s' % policy_id
+        resp, body = self.patch(url, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/projects_client.py b/tempest/lib/services/identity/v3/projects_client.py
new file mode 100644
index 0000000..20787da
--- /dev/null
+++ b/tempest/lib/services/identity/v3/projects_client.py
@@ -0,0 +1,75 @@
+# Copyright 2013 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 six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class ProjectsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_project(self, name, **kwargs):
+        """Create a Project.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#create-project
+
+        """
+        # Include the project name to the kwargs parameters
+        kwargs['name'] = name
+        post_body = json.dumps({'project': kwargs})
+        resp, body = self.post('projects', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_projects(self, params=None):
+        url = "projects"
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_project(self, project_id, **kwargs):
+        """Update a Project.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-project
+
+        """
+        post_body = json.dumps({'project': kwargs})
+        resp, body = self.patch('projects/%s' % project_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(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 rest_client.ResponseBody(resp, body)
+
+    def delete_project(self, project_id):
+        """Delete a project."""
+        resp, body = self.delete('projects/%s' % project_id)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/regions_client.py b/tempest/lib/services/identity/v3/regions_client.py
new file mode 100644
index 0000000..33c754a
--- /dev/null
+++ b/tempest/lib/services/identity/v3/regions_client.py
@@ -0,0 +1,83 @@
+# Copyright 2014 Hewlett-Packard Development Company, L.P
+# 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.
+
+"""
+http://developer.openstack.org/api-ref-identity-v3.html#regions-v3
+"""
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class RegionsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_region(self, region_id=None, **kwargs):
+        """Create region.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#create-region
+        """
+        if region_id is not None:
+            method = self.put
+            url = 'regions/%s' % region_id
+        else:
+            method = self.post
+            url = 'regions'
+        req_body = json.dumps({'region': kwargs})
+        resp, body = method(url, req_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_region(self, region_id, **kwargs):
+        """Updates a region.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-region
+        """
+        post_body = json.dumps({'region': kwargs})
+        resp, body = self.patch('regions/%s' % region_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_region(self, region_id):
+        """Get region."""
+        url = 'regions/%s' % region_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_regions(self, params=None):
+        """List regions."""
+        url = 'regions'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/role_assignments_client.py b/tempest/lib/services/identity/v3/role_assignments_client.py
new file mode 100644
index 0000000..10de03f
--- /dev/null
+++ b/tempest/lib/services/identity/v3/role_assignments_client.py
@@ -0,0 +1,48 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.lib.common import rest_client
+
+
+class RoleAssignmentsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def list_role_assignments(self, effective=False, **kwargs):
+        """List role assignments.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/?expanded=list-effective-role-assignments-detail
+
+        :param effective: If True, returns the effective assignments, including
+                          any assignments gained by virtue of group membership
+                          or inherited roles.
+        """
+        url = 'role_assignments'
+        if kwargs:
+            # NOTE(rodrigods): "effective" is a key-only query parameter and
+            # is treated below.
+            if 'effective' in kwargs:
+                del kwargs['effective']
+            url += '?%s' % urllib.urlencode(kwargs)
+        if effective:
+            url += '&effective'
+
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/roles_client.py b/tempest/lib/services/identity/v3/roles_client.py
new file mode 100644
index 0000000..f1339dd
--- /dev/null
+++ b/tempest/lib/services/identity/v3/roles_client.py
@@ -0,0 +1,192 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.lib.common import rest_client
+
+
+class RolesClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_role(self, **kwargs):
+        """Create a Role.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#create-role
+        """
+        post_body = json.dumps({'role': kwargs})
+        resp, body = self.post('roles', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_role(self, role_id):
+        """GET a Role."""
+        resp, body = self.get('roles/%s' % role_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_roles(self, **params):
+        """Get the list of Roles."""
+
+        url = 'roles'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_role(self, role_id, **kwargs):
+        """Update a Role.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-role
+        """
+        post_body = json.dumps({'role': kwargs})
+        resp, body = self.patch('roles/%s' % role_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_role(self, role_id):
+        """Delete a role."""
+        resp, body = self.delete('roles/%s' % role_id)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_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 rest_client.ResponseBody(resp, body)
+
+    def create_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 rest_client.ResponseBody(resp, body)
+
+    def list_user_roles_on_project(self, project_id, user_id):
+        """list roles of a user on a project."""
+        resp, body = self.get('projects/%s/users/%s/roles' %
+                              (project_id, user_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(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."""
+        resp, body = self.get('domains/%s/users/%s/roles' %
+                              (domain_id, user_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(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 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 rest_client.ResponseBody(resp, body)
+
+    def check_user_role_existence_on_project(self, project_id,
+                                             user_id, role_id):
+        """Check role of a user on a project."""
+        resp, body = self.head('projects/%s/users/%s/roles/%s' %
+                               (project_id, user_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def check_user_role_existence_on_domain(self, domain_id,
+                                            user_id, role_id):
+        """Check role of a user on a domain."""
+        resp, body = self.head('domains/%s/users/%s/roles/%s' %
+                               (domain_id, user_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def create_group_role_on_project(self, project_id, group_id, role_id):
+        """Add roles to a group 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 rest_client.ResponseBody(resp, body)
+
+    def create_group_role_on_domain(self, domain_id, group_id, role_id):
+        """Add roles to a group 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 rest_client.ResponseBody(resp, body)
+
+    def list_group_roles_on_project(self, project_id, group_id):
+        """list roles of a group on a project."""
+        resp, body = self.get('projects/%s/groups/%s/roles' %
+                              (project_id, group_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_group_roles_on_domain(self, domain_id, group_id):
+        """list roles of a group on a domain."""
+        resp, body = self.get('domains/%s/groups/%s/roles' %
+                              (domain_id, group_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(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 group 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 rest_client.ResponseBody(resp, body)
+
+    def delete_role_from_group_on_domain(self, domain_id, group_id, role_id):
+        """Delete role of a group 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 rest_client.ResponseBody(resp, body)
+
+    def check_role_from_group_on_project_existence(self, project_id,
+                                                   group_id, role_id):
+        """Check role of a group on a project."""
+        resp, body = self.head('projects/%s/groups/%s/roles/%s' %
+                               (project_id, group_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def check_role_from_group_on_domain_existence(self, domain_id,
+                                                  group_id, role_id):
+        """Check role of a group on a domain."""
+        resp, body = self.head('domains/%s/groups/%s/roles/%s' %
+                               (domain_id, group_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/identity/v3/services_client.py b/tempest/lib/services/identity/v3/services_client.py
new file mode 100644
index 0000000..14c81cc
--- /dev/null
+++ b/tempest/lib/services/identity/v3/services_client.py
@@ -0,0 +1,82 @@
+# Copyright 2013 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.
+
+"""
+http://developer.openstack.org/api-ref-identity-v3.html#service-catalog-v3
+"""
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class ServicesClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def update_service(self, service_id, **kwargs):
+        """Updates a service.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-service
+        """
+        patch_body = json.dumps({'service': kwargs})
+        resp, body = self.patch('services/%s' % service_id, patch_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_service(self, service_id):
+        """Get Service."""
+        url = 'services/%s' % service_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_service(self, **kwargs):
+        """Creates a service.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#create-service
+        """
+        body = json.dumps({'service': kwargs})
+        resp, body = self.post("services", body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_service(self, service_id):
+        url = "services/" + service_id
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_services(self, **params):
+        """List services.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/#list-services
+        """
+        url = 'services'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get('services')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/token_client.py b/tempest/lib/services/identity/v3/token_client.py
new file mode 100644
index 0000000..33f6f16
--- /dev/null
+++ b/tempest/lib/services/identity/v3/token_client.py
@@ -0,0 +1,199 @@
+# 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 oslo_log import log as logging
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions
+
+
+class V3TokenClient(rest_client.RestClient):
+
+    def __init__(self, auth_url, disable_ssl_certificate_validation=None,
+                 ca_certs=None, trace_requests=None, **kwargs):
+        """Initialises the Token client
+
+        :param auth_url: URL to which the token request is sent
+        :param disable_ssl_certificate_validation: pass-through to rest client
+        :param ca_certs: pass-through to rest client
+        :param trace_requests: pass-through to rest client
+        :param kwargs: any extra parameter to pass through the rest client.
+               Three kwargs are forbidden: region, service and auth_provider
+               as they are not meaningful for token client
+        """
+        dscv = disable_ssl_certificate_validation
+        for unwanted_kwargs in ['region', 'service', 'auth_provider']:
+            kwargs.pop(unwanted_kwargs, None)
+        super(V3TokenClient, self).__init__(
+            None, None, None, disable_ssl_certificate_validation=dscv,
+            ca_certs=ca_certs, trace_requests=trace_requests, **kwargs)
+
+        if auth_url is None:
+            raise exceptions.IdentityError("Couldn't determine auth_url")
+
+        if 'auth/tokens' not in auth_url:
+            auth_url = auth_url.rstrip('/') + '/auth/tokens'
+
+        self.auth_url = auth_url
+
+    def auth(self, user_id=None, username=None, password=None, project_id=None,
+             project_name=None, user_domain_id=None, user_domain_name=None,
+             project_domain_id=None, project_domain_name=None, domain_id=None,
+             domain_name=None, token=None):
+        """Obtains a token from the authentication service
+
+        :param user_id: user id
+        :param username: user name
+        :param user_domain_id: the user domain id
+        :param user_domain_name: the user domain name
+        :param project_domain_id: the project domain id
+        :param project_domain_name: the project domain name
+        :param domain_id: a domain id to scope to
+        :param domain_name: a domain name to scope to
+        :param project_id: a project id to scope to
+        :param project_name: a project name to scope to
+        :param token: a token to re-scope.
+
+        Accepts different combinations of credentials.
+        Sample sample valid combinations:
+        - token
+        - token, project_name, project_domain_id
+        - user_id, password
+        - username, password, user_domain_id
+        - username, password, project_name, user_domain_id, project_domain_id
+        Validation is left to the server side.
+        """
+        creds = {
+            'auth': {
+                'identity': {
+                    'methods': [],
+                }
+            }
+        }
+        id_obj = creds['auth']['identity']
+        if token:
+            id_obj['methods'].append('token')
+            id_obj['token'] = {
+                'id': token
+            }
+
+        if (user_id or username) and password:
+            id_obj['methods'].append('password')
+            id_obj['password'] = {
+                'user': {
+                    'password': password,
+                }
+            }
+            if user_id:
+                id_obj['password']['user']['id'] = user_id
+            else:
+                id_obj['password']['user']['name'] = username
+
+            _domain = None
+            if user_domain_id is not None:
+                _domain = dict(id=user_domain_id)
+            elif user_domain_name is not None:
+                _domain = dict(name=user_domain_name)
+            if _domain:
+                id_obj['password']['user']['domain'] = _domain
+
+        if (project_id or project_name):
+            _project = dict()
+
+            if project_id:
+                _project['id'] = project_id
+            elif project_name:
+                _project['name'] = project_name
+
+            if project_domain_id is not None:
+                _project['domain'] = {'id': project_domain_id}
+            elif project_domain_name is not None:
+                _project['domain'] = {'name': project_domain_name}
+
+            creds['auth']['scope'] = dict(project=_project)
+        elif domain_id:
+            creds['auth']['scope'] = dict(domain={'id': domain_id})
+        elif domain_name:
+            creds['auth']['scope'] = dict(domain={'name': domain_name})
+
+        body = json.dumps(creds, sort_keys=True)
+        resp, body = self.post(self.auth_url, body=body)
+        self.expected_success(201, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def request(self, method, url, extra_headers=False, headers=None,
+                body=None, chunked=False):
+        """A simple HTTP request interface.
+
+        Note: this overloads the `request` method from the parent class and
+        thus must implement the same method signature.
+        """
+        if headers is None:
+            # Always accept 'json', for xml token client too.
+            # Because XML response is not easily
+            # converted to the corresponding JSON one
+            headers = self.get_headers(accept_type="json")
+        elif extra_headers:
+            try:
+                headers.update(self.get_headers(accept_type="json"))
+            except (ValueError, TypeError):
+                headers = self.get_headers(accept_type="json")
+
+        resp, resp_body = self.raw_request(url, method,
+                                           headers=headers, body=body)
+        self._log_request(method, url, resp, req_headers=headers,
+                          req_body='<omitted>', resp_body=resp_body)
+
+        if resp.status in [401, 403]:
+            resp_body = json.loads(resp_body)
+            raise exceptions.Unauthorized(resp_body['error']['message'])
+        elif resp.status not in [200, 201, 204]:
+            raise exceptions.IdentityError(
+                'Unexpected status code {0}'.format(resp.status))
+
+        return resp, json.loads(resp_body)
+
+    def get_token(self, **kwargs):
+        """Returns (token id, token data) for supplied credentials"""
+
+        auth_data = kwargs.pop('auth_data', False)
+
+        if not (kwargs.get('user_domain_id') or
+                kwargs.get('user_domain_name')):
+            kwargs['user_domain_name'] = 'Default'
+
+        if not (kwargs.get('project_domain_id') or
+                kwargs.get('project_domain_name')):
+            kwargs['project_domain_name'] = 'Default'
+
+        body = self.auth(**kwargs)
+
+        token = body.response.get('x-subject-token')
+        if auth_data:
+            return token, body['token']
+        else:
+            return token
+
+
+class V3TokenClientJSON(V3TokenClient):
+    LOG = logging.getLogger(__name__)
+
+    def _warn(self):
+        self.LOG.warning("%s class was deprecated and renamed to %s",
+                         self.__class__.__name__, 'V3TokenClient')
+
+    def __init__(self, *args, **kwargs):
+        self._warn()
+        super(V3TokenClientJSON, self).__init__(*args, **kwargs)
diff --git a/tempest/lib/services/identity/v3/trusts_client.py b/tempest/lib/services/identity/v3/trusts_client.py
new file mode 100644
index 0000000..d113905
--- /dev/null
+++ b/tempest/lib/services/identity/v3/trusts_client.py
@@ -0,0 +1,85 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.lib.common import rest_client
+
+
+class TrustsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_trust(self, **kwargs):
+        """Creates a trust.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/index.html#create-trust
+        """
+        post_body = json.dumps({'trust': kwargs})
+        resp, body = self.post('OS-TRUST/trusts', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(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 rest_client.ResponseBody(resp, body)
+
+    def list_trusts(self, **params):
+        """Returns trusts
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/index.html#list-trusts
+        """
+        url = "OS-TRUST/trusts/"
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(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 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 rest_client.ResponseBody(resp, body)
+
+    def show_trust_role(self, trust_id, role_id):
+        """GET role delegated by a trust."""
+        resp, body = self.get("OS-TRUST/trusts/%s/roles/%s"
+                              % (trust_id, role_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/users_client.py b/tempest/lib/services/identity/v3/users_client.py
new file mode 100644
index 0000000..e99a971
--- /dev/null
+++ b/tempest/lib/services/identity/v3/users_client.py
@@ -0,0 +1,120 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.lib.common import rest_client
+
+
+class UsersClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_user(self, **kwargs):
+        """Creates a user.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/#create-user
+        """
+        post_body = json.dumps({'user': kwargs})
+        resp, body = self.post('users', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_user(self, user_id, **kwargs):
+        """Updates a user.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/#update-user
+        """
+        if 'id' not in kwargs:
+            kwargs['id'] = user_id
+        post_body = json.dumps({'user': kwargs})
+        resp, body = self.patch('users/%s' % user_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_user_password(self, user_id, **kwargs):
+        """Update a user password
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#change-password-for-user
+        """
+        update_user = json.dumps({'user': kwargs})
+        resp, _ = self.post('users/%s/password' % user_id, update_user)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def list_user_projects(self, user_id, **params):
+        """Lists the projects on which a user has roles assigned.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/#list-projects-for-user
+        """
+        url = 'users/%s/projects' % user_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_users(self, **params):
+        """Get the list of users.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/#list-users
+        """
+        url = 'users'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(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 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 rest_client.ResponseBody(resp, body)
+
+    def list_user_groups(self, user_id, **params):
+        """Lists groups which a user belongs to.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/#list-groups-to-which-a-user-belongs
+        """
+        url = 'users/%s/groups' % user_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/image/__init__.py b/tempest/lib/services/image/__init__.py
new file mode 100644
index 0000000..4b01663
--- /dev/null
+++ b/tempest/lib/services/image/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (c) 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.
+
+from tempest.lib.services.image import v1
+from tempest.lib.services.image import v2
+
+__all__ = ['v1', 'v2']
diff --git a/tempest/lib/services/image/v1/__init__.py b/tempest/lib/services/image/v1/__init__.py
new file mode 100644
index 0000000..9bd8262
--- /dev/null
+++ b/tempest/lib/services/image/v1/__init__.py
@@ -0,0 +1,19 @@
+# Copyright (c) 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.
+
+from tempest.lib.services.image.v1.image_members_client import \
+    ImageMembersClient
+from tempest.lib.services.image.v1.images_client import ImagesClient
+
+__all__ = ['ImageMembersClient', 'ImagesClient']
diff --git a/tempest/lib/services/image/v1/image_members_client.py b/tempest/lib/services/image/v1/image_members_client.py
new file mode 100644
index 0000000..2318087
--- /dev/null
+++ b/tempest/lib/services/image/v1/image_members_client.py
@@ -0,0 +1,66 @@
+#    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.lib.common import rest_client
+
+
+class ImageMembersClient(rest_client.RestClient):
+    api_version = "v1"
+
+    def list_image_members(self, image_id):
+        """List all members of an image."""
+        url = 'images/%s/members' % image_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_shared_images(self, tenant_id):
+        """List image memberships for the given tenant.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v1/#list-shared-images
+        """
+
+        url = 'shared-images/%s' % tenant_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_image_member(self, image_id, member_id, **kwargs):
+        """Add a member to an image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v1/#add-member-to-image
+        """
+        url = 'images/%s/members/%s' % (image_id, member_id)
+        body = json.dumps({'member': kwargs})
+        resp, __ = self.put(url, body)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def delete_image_member(self, image_id, member_id):
+        """Removes a membership from the image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v1/#remove-member
+        """
+        url = 'images/%s/members/%s' % (image_id, member_id)
+        resp, __ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/image/v1/images_client.py b/tempest/lib/services/image/v1/images_client.py
new file mode 100644
index 0000000..03f4c4b
--- /dev/null
+++ b/tempest/lib/services/image/v1/images_client.py
@@ -0,0 +1,155 @@
+# Copyright 2013 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 functools
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+CHUNKSIZE = 1024 * 64  # 64kB
+
+
+class ImagesClient(rest_client.RestClient):
+    api_version = "v1"
+
+    def _create_with_data(self, headers, data):
+        # We are going to do chunked transfert, so split the input data
+        # info fixed-sized chunks.
+        headers['Content-Type'] = 'application/octet-stream'
+        data = iter(functools.partial(data.read, CHUNKSIZE), b'')
+        resp, body = self.request('POST', 'images',
+                                  headers=headers, body=data, chunked=True)
+        self._error_checker(resp, body)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def _update_with_data(self, image_id, headers, data):
+        # We are going to do chunked transfert, so split the input data
+        # info fixed-sized chunks.
+        headers['Content-Type'] = 'application/octet-stream'
+        data = iter(functools.partial(data.read, CHUNKSIZE), b'')
+        url = 'images/%s' % image_id
+        resp, body = self.request('PUT', url, headers=headers,
+                                  body=data, chunked=True)
+        self._error_checker(resp, body)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    @property
+    def http(self):
+        if self._http is None:
+            self._http = self._get_http()
+        return self._http
+
+    def create_image(self, data=None, headers=None):
+        """Create an image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-image-v1.html#createImage-v1
+        """
+        if headers is None:
+            headers = {}
+
+        if data is not None:
+            return self._create_with_data(headers, data)
+
+        resp, body = self.post('images', None, headers)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_image(self, image_id, data=None, headers=None):
+        """Update an image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-image-v1.html#updateImage-v1
+        """
+        if headers is None:
+            headers = {}
+
+        if data is not None:
+            return self._update_with_data(image_id, headers, data)
+
+        url = 'images/%s' % image_id
+        resp, body = self.put(url, None, headers)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_image(self, image_id):
+        url = 'images/%s' % image_id
+        resp, body = self.delete(url)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_images(self, detail=False, **kwargs):
+        """Return a list of all images filtered by input parameters.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v1/#list-images
+
+        Most parameters except the following are passed to the API without
+        any changes.
+        :param changes_since: The name is changed to changes-since
+        """
+        url = 'images'
+
+        if detail:
+            url += '/detail'
+
+        if 'changes_since' in kwargs:
+            kwargs['changes-since'] = kwargs.pop('changes_since')
+
+        if len(kwargs) > 0:
+            url += '?%s' % urllib.urlencode(kwargs)
+
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def check_image(self, image_id):
+        """Check image metadata."""
+        url = 'images/%s' % image_id
+        resp, body = self.head(url)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_image(self, image_id):
+        """Get image details plus the image itself."""
+        url = 'images/%s' % image_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBodyData(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            resp = self.check_image(id)
+            if resp.response["x-image-meta-status"] == 'deleted':
+                return True
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'image_meta'
diff --git a/tempest/lib/services/image/v2/__init__.py b/tempest/lib/services/image/v2/__init__.py
new file mode 100644
index 0000000..7d973e5
--- /dev/null
+++ b/tempest/lib/services/image/v2/__init__.py
@@ -0,0 +1,31 @@
+# Copyright (c) 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.
+
+from tempest.lib.services.image.v2.image_members_client import \
+    ImageMembersClient
+from tempest.lib.services.image.v2.images_client import ImagesClient
+from tempest.lib.services.image.v2.namespace_objects_client import \
+    NamespaceObjectsClient
+from tempest.lib.services.image.v2.namespace_properties_client import \
+    NamespacePropertiesClient
+from tempest.lib.services.image.v2.namespace_tags_client import \
+    NamespaceTagsClient
+from tempest.lib.services.image.v2.namespaces_client import NamespacesClient
+from tempest.lib.services.image.v2.resource_types_client import \
+    ResourceTypesClient
+from tempest.lib.services.image.v2.schemas_client import SchemasClient
+
+__all__ = ['ImageMembersClient', 'ImagesClient', 'NamespaceObjectsClient',
+           'NamespacePropertiesClient', 'NamespaceTagsClient',
+           'NamespacesClient', 'ResourceTypesClient', 'SchemasClient']
diff --git a/tempest/lib/services/image/v2/image_members_client.py b/tempest/lib/services/image/v2/image_members_client.py
new file mode 100644
index 0000000..e5118a8
--- /dev/null
+++ b/tempest/lib/services/image/v2/image_members_client.py
@@ -0,0 +1,84 @@
+#    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.lib.common import rest_client
+
+
+class ImageMembersClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def list_image_members(self, image_id):
+        """List image members.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#list-image-members
+        """
+        url = 'images/%s/members' % image_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_image_member(self, image_id, **kwargs):
+        """Create an image member.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#create-image-member
+        """
+        url = 'images/%s/members' % image_id
+        data = json.dumps(kwargs)
+        resp, body = self.post(url, data)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_image_member(self, image_id, member_id, **kwargs):
+        """Update an image member.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#update-image-member
+        """
+        url = 'images/%s/members/%s' % (image_id, member_id)
+        data = json.dumps(kwargs)
+        resp, body = self.put(url, data)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_image_member(self, image_id, member_id):
+        """Show an image member.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#show-image-member-details
+        """
+        url = 'images/%s/members/%s' % (image_id, member_id)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, json.loads(body))
+
+    def delete_image_member(self, image_id, member_id):
+        """Delete an image member.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#delete-image-member
+        """
+        url = 'images/%s/members/%s' % (image_id, member_id)
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/image/v2/images_client.py b/tempest/lib/services/image/v2/images_client.py
new file mode 100644
index 0000000..bcdae44
--- /dev/null
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -0,0 +1,189 @@
+# Copyright 2013 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 functools
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+CHUNKSIZE = 1024 * 64  # 64kB
+
+
+class ImagesClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def update_image(self, image_id, patch):
+        """Update an image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/index.html#update-an-image
+        """
+        data = json.dumps(patch)
+        headers = {"Content-Type": "application/openstack-images-v2.0"
+                                   "-json-patch"}
+        resp, body = self.patch('images/%s' % image_id, data, headers)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_image(self, **kwargs):
+        """Create an image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/index.html#create-an-image
+        """
+        data = json.dumps(kwargs)
+        resp, body = self.post('images', data)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def deactivate_image(self, image_id):
+        """Deactivate image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#deactivate-image
+        """
+        url = 'images/%s/actions/deactivate' % image_id
+        resp, body = self.post(url, None)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def reactivate_image(self, image_id):
+        """Reactivate image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#reactivate-image
+        """
+        url = 'images/%s/actions/reactivate' % image_id
+        resp, body = self.post(url, None)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_image(self, image_id):
+        """Delete image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#delete-an-image
+         """
+        url = 'images/%s' % image_id
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def list_images(self, params=None):
+        """List images.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#show-images
+        """
+        url = 'images'
+
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_image(self, image_id):
+        """Show image details.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#show-image-details
+        """
+        url = 'images/%s' % image_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_image(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'image'
+
+    def store_image_file(self, image_id, data):
+        """Upload binary image data.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#upload-binary-image-data
+        """
+        url = 'images/%s/file' % image_id
+
+        # We are going to do chunked transfert, so split the input data
+        # info fixed-sized chunks.
+        headers = {'Content-Type': 'application/octet-stream'}
+        data = iter(functools.partial(data.read, CHUNKSIZE), b'')
+
+        resp, body = self.request('PUT', url, headers=headers,
+                                  body=data, chunked=True)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_image_file(self, image_id):
+        """Download binary image data.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#download-binary-image-data
+        """
+        url = 'images/%s/file' % image_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBodyData(resp, body)
+
+    def add_image_tag(self, image_id, tag):
+        """Add an image tag.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#add-image-tag
+        """
+        url = 'images/%s/tags/%s' % (image_id, tag)
+        resp, body = self.put(url, body=None)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_image_tag(self, image_id, tag):
+        """Delete an image tag.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/#delete-image-tag
+        """
+        url = 'images/%s/tags/%s' % (image_id, tag)
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/image/v2/namespace_objects_client.py b/tempest/lib/services/image/v2/namespace_objects_client.py
new file mode 100644
index 0000000..ac2e63e
--- /dev/null
+++ b/tempest/lib/services/image/v2/namespace_objects_client.py
@@ -0,0 +1,91 @@
+# Copyright 2016 EasyStack.
+# 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 six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class NamespaceObjectsClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def list_namespace_objects(self, namespace, **kwargs):
+        """Lists all namespace objects.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#list-objects
+        """
+        url = 'metadefs/namespaces/%s/objects' % namespace
+        if kwargs:
+            url += '?%s' % urllib.urlencode(kwargs)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_namespace_object(self, namespace, **kwargs):
+        """Create a namespace object
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#create-object
+        """
+        url = 'metadefs/namespaces/%s/objects' % namespace
+        data = json.dumps(kwargs)
+        resp, body = self.post(url, data)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_namespace_object(self, namespace, object_name, **kwargs):
+        """Update a namespace object
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#update-object
+        """
+        url = 'metadefs/namespaces/%s/objects/%s' % (namespace, object_name)
+        data = json.dumps(kwargs)
+        resp, body = self.put(url, data)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_namespace_object(self, namespace, object_name):
+        """Show a namespace object
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#show-object
+        """
+        url = 'metadefs/namespaces/%s/objects/%s' % (namespace, object_name)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_namespace_object(self, namespace, object_name):
+        """Delete a namespace object
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#delete-object
+        """
+        url = 'metadefs/namespaces/%s/objects/%s' % (namespace, object_name)
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/image/v2/namespace_properties_client.py b/tempest/lib/services/image/v2/namespace_properties_client.py
new file mode 100644
index 0000000..1236b2b
--- /dev/null
+++ b/tempest/lib/services/image/v2/namespace_properties_client.py
@@ -0,0 +1,91 @@
+# Copyright 2016 EasyStack.
+# 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.lib.common import rest_client
+
+
+class NamespacePropertiesClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def list_namespace_properties(self, namespace):
+        """Lists property definitions in a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#list-properties
+        """
+        url = 'metadefs/namespaces/%s/properties' % namespace
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_namespace_property(self, namespace, **kwargs):
+        """Creates a property definition in a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#create-property
+        """
+        url = 'metadefs/namespaces/%s/properties' % namespace
+        data = json.dumps(kwargs)
+        resp, body = self.post(url, data)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_namespace_properties(self, namespace, property_name):
+        """Shows the definition for a property.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#show-property-definition
+        """
+        url = 'metadefs/namespaces/%s/properties/%s' % (namespace,
+                                                        property_name)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_namespace_properties(self, namespace, property_name, **kwargs):
+        """Updates a property definition.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#update-property-definition
+        """
+        url = 'metadefs/namespaces/%s/properties/%s' % (namespace,
+                                                        property_name)
+        data = json.dumps(kwargs)
+        resp, body = self.put(url, data)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_namespace_property(self, namespace, property_name):
+        """Removes a property definition from a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#remove-property-definition
+        """
+        url = 'metadefs/namespaces/%s/properties/%s' % (namespace,
+                                                        property_name)
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/image/v2/namespace_tags_client.py b/tempest/lib/services/image/v2/namespace_tags_client.py
new file mode 100644
index 0000000..ac8b569
--- /dev/null
+++ b/tempest/lib/services/image/v2/namespace_tags_client.py
@@ -0,0 +1,119 @@
+# Copyright 2016 EasyStack.
+# 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 six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class NamespaceTagsClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def create_namespace_tag(self, namespace, tag_name):
+        """Adds a tag to the list of namespace tag definitions.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#create-tag-definition
+        """
+        url = 'metadefs/namespaces/%s/tags/%s' % (namespace,
+                                                  tag_name)
+        resp, body = self.post(url, None)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_namespace_tag(self, namespace, tag_name):
+        """Gets a definition for a tag.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#get_tag_definition
+        """
+        url = 'metadefs/namespaces/%s/tags/%s' % (namespace,
+                                                  tag_name)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_namespace_tag(self, namespace, tag_name, **kwargs):
+        """Renames a tag definition.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#update-tag-definition
+        """
+        url = 'metadefs/namespaces/%s/tags/%s' % (namespace,
+                                                  tag_name)
+        data = json.dumps(kwargs)
+        resp, body = self.put(url, data)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_namespace_tag(self, namespace, tag_name):
+        """Deletes a tag definition within a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#delete-tag-definition
+        """
+        url = 'metadefs/namespaces/%s/tags/%s' % (namespace, tag_name)
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def create_namespace_tags(self, namespace, **kwargs):
+        """Creates one or more tag definitions in a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#create-tags
+        """
+        url = 'metadefs/namespaces/%s/tags' % namespace
+        data = json.dumps(kwargs)
+        resp, body = self.post(url, data)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_namespace_tags(self, namespace, **params):
+        """Lists the tag definitions within a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#list-tags
+        """
+        url = 'metadefs/namespaces/%s/tags' % namespace
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_namespace_tags(self, namespace):
+        """Deletes all tag definitions within a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#delete-all-tag-definitions
+        """
+        url = 'metadefs/namespaces/%s/tags' % namespace
+        resp, _ = self.delete(url)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/image/v2/namespaces_client.py b/tempest/lib/services/image/v2/namespaces_client.py
new file mode 100644
index 0000000..b00de89
--- /dev/null
+++ b/tempest/lib/services/image/v2/namespaces_client.py
@@ -0,0 +1,91 @@
+# Copyright 2013 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.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class NamespacesClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def create_namespace(self, **kwargs):
+        """Create a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#create-namespace
+        """
+        data = json.dumps(kwargs)
+        resp, body = self.post('metadefs/namespaces', data)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_namespaces(self):
+        """List namespaces
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#list-namespaces
+        """
+        url = 'metadefs/namespaces'
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_namespace(self, namespace):
+        """Show namespace details.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#get-namespace-details
+        """
+        url = 'metadefs/namespaces/%s' % namespace
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_namespace(self, namespace, **kwargs):
+        """Update a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#update-namespace
+        """
+        # NOTE: On Glance API, we need to pass namespace on both URI
+        # and a request body.
+        params = {'namespace': namespace}
+        params.update(kwargs)
+        data = json.dumps(params)
+        url = 'metadefs/namespaces/%s' % namespace
+        resp, body = self.put(url, body=data)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_namespace(self, namespace):
+        """Delete a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#delete-namespace
+        """
+        url = 'metadefs/namespaces/%s' % namespace
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/image/v2/resource_types_client.py b/tempest/lib/services/image/v2/resource_types_client.py
new file mode 100644
index 0000000..1b6889f
--- /dev/null
+++ b/tempest/lib/services/image/v2/resource_types_client.py
@@ -0,0 +1,75 @@
+# Copyright 2013 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.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class ResourceTypesClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def list_resource_types(self):
+        """Lists all resource types.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html?expanded=#list-resource-types
+        """
+        url = 'metadefs/resource_types'
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_resource_type_association(self, namespace_id, **kwargs):
+        """Creates a resource type association in given namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html?expanded=#create-resource-type-association
+        """
+        url = 'metadefs/namespaces/%s/resource_types' % namespace_id
+        data = json.dumps(kwargs)
+        resp, body = self.post(url, data)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_resource_type_association(self, namespace_id):
+        """Lists resource type associations in given namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html?expanded=#list-resource-type-associations
+        """
+        url = 'metadefs/namespaces/%s/resource_types' % namespace_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_resource_type_association(self, namespace_id, resource_name):
+        """Removes resource type association in given namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html?expanded=#remove-resource-type-association
+        """
+        url = 'metadefs/namespaces/%s/resource_types/%s' % (namespace_id,
+                                                            resource_name)
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/image/v2/schemas_client.py b/tempest/lib/services/image/v2/schemas_client.py
new file mode 100644
index 0000000..0c9db40
--- /dev/null
+++ b/tempest/lib/services/image/v2/schemas_client.py
@@ -0,0 +1,29 @@
+# Copyright 2013 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.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class SchemasClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def show_schema(self, schema):
+        url = 'schemas/%s' % schema
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/network/__init__.py b/tempest/lib/services/network/__init__.py
new file mode 100644
index 0000000..19e5463
--- /dev/null
+++ b/tempest/lib/services/network/__init__.py
@@ -0,0 +1,41 @@
+# Copyright (c) 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.
+
+from tempest.lib.services.network.agents_client import AgentsClient
+from tempest.lib.services.network.extensions_client import ExtensionsClient
+from tempest.lib.services.network.floating_ips_client import FloatingIPsClient
+from tempest.lib.services.network.metering_label_rules_client import \
+    MeteringLabelRulesClient
+from tempest.lib.services.network.metering_labels_client import \
+    MeteringLabelsClient
+from tempest.lib.services.network.networks_client import NetworksClient
+from tempest.lib.services.network.ports_client import PortsClient
+from tempest.lib.services.network.quotas_client import QuotasClient
+from tempest.lib.services.network.routers_client import RoutersClient
+from tempest.lib.services.network.security_group_rules_client import \
+    SecurityGroupRulesClient
+from tempest.lib.services.network.security_groups_client import \
+    SecurityGroupsClient
+from tempest.lib.services.network.service_providers_client import \
+    ServiceProvidersClient
+from tempest.lib.services.network.subnetpools_client import SubnetpoolsClient
+from tempest.lib.services.network.subnets_client import SubnetsClient
+from tempest.lib.services.network.versions_client import NetworkVersionsClient
+
+__all__ = ['AgentsClient', 'ExtensionsClient', 'FloatingIPsClient',
+           'MeteringLabelRulesClient', 'MeteringLabelsClient',
+           'NetworksClient', 'PortsClient', 'QuotasClient', 'RoutersClient',
+           'SecurityGroupRulesClient', 'SecurityGroupsClient',
+           'ServiceProvidersClient', 'SubnetpoolsClient', 'SubnetsClient',
+           'NetworkVersionsClient']
diff --git a/tempest/lib/services/network/agents_client.py b/tempest/lib/services/network/agents_client.py
new file mode 100644
index 0000000..9bdf090
--- /dev/null
+++ b/tempest/lib/services/network/agents_client.py
@@ -0,0 +1,68 @@
+# 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.services.network import base
+
+
+class AgentsClient(base.BaseNetworkClient):
+
+    def update_agent(self, agent_id, **kwargs):
+        """Update agent."""
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526673
+        uri = '/agents/%s' % agent_id
+        return self.update_resource(uri, kwargs)
+
+    def show_agent(self, agent_id, **fields):
+        uri = '/agents/%s' % agent_id
+        return self.show_resource(uri, **fields)
+
+    def list_agents(self, **filters):
+        uri = '/agents'
+        return self.list_resources(uri, **filters)
+
+    def list_routers_on_l3_agent(self, agent_id):
+        uri = '/agents/%s/l3-routers' % agent_id
+        return self.list_resources(uri)
+
+    def create_router_on_l3_agent(self, agent_id, **kwargs):
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526670
+        uri = '/agents/%s/l3-routers' % agent_id
+        return self.create_resource(uri, kwargs, expect_empty_body=True)
+
+    def delete_router_from_l3_agent(self, agent_id, router_id):
+        uri = '/agents/%s/l3-routers/%s' % (agent_id, router_id)
+        return self.delete_resource(uri)
+
+    def list_networks_hosted_by_one_dhcp_agent(self, agent_id):
+        uri = '/agents/%s/dhcp-networks' % agent_id
+        return self.list_resources(uri)
+
+    def delete_network_from_dhcp_agent(self, agent_id, network_id):
+        uri = '/agents/%s/dhcp-networks/%s' % (agent_id,
+                                               network_id)
+        return self.delete_resource(uri)
+
+    def add_dhcp_agent_to_network(self, agent_id, **kwargs):
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526212
+        uri = '/agents/%s/dhcp-networks' % agent_id
+        return self.create_resource(uri, kwargs, expect_empty_body=True)
diff --git a/tempest/lib/services/network/base.py b/tempest/lib/services/network/base.py
new file mode 100644
index 0000000..b6f9c91
--- /dev/null
+++ b/tempest/lib/services/network/base.py
@@ -0,0 +1,83 @@
+#    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.lib.common import rest_client
+
+
+class BaseNetworkClient(rest_client.RestClient):
+
+    """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 rest_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 rest_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 rest_client.ResponseBody(resp, body)
+
+    def create_resource(self, uri, post_data, expect_empty_body=False):
+        req_uri = self.uri_prefix + uri
+        req_post_data = json.dumps(post_data)
+        resp, body = self.post(req_uri, req_post_data)
+        # NOTE: RFC allows both a valid non-empty body and an empty body for
+        # response of POST API. If a body is expected not empty, we decode the
+        # body. Otherwise we returns the body as it is.
+        if not expect_empty_body:
+            body = json.loads(body)
+        else:
+            body = None
+        self.expected_success(201, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_resource(self, uri, post_data, expect_empty_body=False):
+        req_uri = self.uri_prefix + uri
+        req_post_data = json.dumps(post_data)
+        resp, body = self.put(req_uri, req_post_data)
+        # NOTE: RFC allows both a valid non-empty body and an empty body for
+        # response of PUT API. If a body is expected not empty, we decode the
+        # body. Otherwise we returns the body as it is.
+        if not expect_empty_body:
+            body = json.loads(body)
+        else:
+            body = None
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/network/extensions_client.py b/tempest/lib/services/network/extensions_client.py
new file mode 100644
index 0000000..3910c84
--- /dev/null
+++ b/tempest/lib/services/network/extensions_client.py
@@ -0,0 +1,24 @@
+#    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 ExtensionsClient(base.BaseNetworkClient):
+
+    def show_extension(self, ext_alias, **fields):
+        uri = '/extensions/%s' % ext_alias
+        return self.show_resource(uri, **fields)
+
+    def list_extensions(self, **filters):
+        uri = '/extensions'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/floating_ips_client.py b/tempest/lib/services/network/floating_ips_client.py
new file mode 100644
index 0000000..2bb18e0
--- /dev/null
+++ b/tempest/lib/services/network/floating_ips_client.py
@@ -0,0 +1,65 @@
+#    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 FloatingIPsClient(base.BaseNetworkClient):
+
+    def create_floatingip(self, **kwargs):
+        """Creates a floating IP.
+
+        If you specify port information, associates the floating IP with an
+        internal port.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#create-floating-ip
+        """
+        uri = '/floatingips'
+        post_data = {'floatingip': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_floatingip(self, floatingip_id, **kwargs):
+        """Updates a floating IP and its association with an internal port.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#update-floating-ip
+        """
+        uri = '/floatingips/%s' % floatingip_id
+        post_data = {'floatingip': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_floatingip(self, floatingip_id, **fields):
+        """Shows details for a floating IP.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#show-floating-ip-details
+        """
+        uri = '/floatingips/%s' % floatingip_id
+        return self.show_resource(uri, **fields)
+
+    def delete_floatingip(self, floatingip_id):
+        uri = '/floatingips/%s' % floatingip_id
+        return self.delete_resource(uri)
+
+    def list_floatingips(self, **filters):
+        """Lists floating IPs.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#list-floating-ips
+        """
+        uri = '/floatingips'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/metering_label_rules_client.py b/tempest/lib/services/network/metering_label_rules_client.py
new file mode 100644
index 0000000..36cf8e3
--- /dev/null
+++ b/tempest/lib/services/network/metering_label_rules_client.py
@@ -0,0 +1,33 @@
+#    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 MeteringLabelRulesClient(base.BaseNetworkClient):
+
+    def create_metering_label_rule(self, **kwargs):
+        uri = '/metering/metering-label-rules'
+        post_data = {'metering_label_rule': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def show_metering_label_rule(self, metering_label_rule_id, **fields):
+        uri = '/metering/metering-label-rules/%s' % metering_label_rule_id
+        return self.show_resource(uri, **fields)
+
+    def delete_metering_label_rule(self, metering_label_rule_id):
+        uri = '/metering/metering-label-rules/%s' % metering_label_rule_id
+        return self.delete_resource(uri)
+
+    def list_metering_label_rules(self, **filters):
+        uri = '/metering/metering-label-rules'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/metering_labels_client.py b/tempest/lib/services/network/metering_labels_client.py
new file mode 100644
index 0000000..411da1f
--- /dev/null
+++ b/tempest/lib/services/network/metering_labels_client.py
@@ -0,0 +1,57 @@
+#    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 MeteringLabelsClient(base.BaseNetworkClient):
+
+    def create_metering_label(self, **kwargs):
+        """Creates an L3 metering label.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#create-metering-label
+        """
+        uri = '/metering/metering-labels'
+        post_data = {'metering_label': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def show_metering_label(self, metering_label_id, **fields):
+        """Shows details for a metering label.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#show-metering-label-details
+        """
+        uri = '/metering/metering-labels/%s' % metering_label_id
+        return self.show_resource(uri, **fields)
+
+    def delete_metering_label(self, metering_label_id):
+        """Deletes an L3 metering label.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#delete-metering-label
+        """
+        uri = '/metering/metering-labels/%s' % metering_label_id
+        return self.delete_resource(uri)
+
+    def list_metering_labels(self, **filters):
+        """Lists all L3 metering labels that belong to the tenant.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#list-metering-labels
+        """
+        uri = '/metering/metering-labels'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/networks_client.py b/tempest/lib/services/network/networks_client.py
new file mode 100644
index 0000000..77d4823
--- /dev/null
+++ b/tempest/lib/services/network/networks_client.py
@@ -0,0 +1,76 @@
+#    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 NetworksClient(base.BaseNetworkClient):
+
+    def create_network(self, **kwargs):
+        """Creates a network.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#create-network
+        """
+        uri = '/networks'
+        post_data = {'network': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_network(self, network_id, **kwargs):
+        """Updates a network.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#update-network
+        """
+        uri = '/networks/%s' % network_id
+        post_data = {'network': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_network(self, network_id, **fields):
+        """Shows details for a network.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#show-network-details
+        """
+        uri = '/networks/%s' % network_id
+        return self.show_resource(uri, **fields)
+
+    def delete_network(self, network_id):
+        uri = '/networks/%s' % network_id
+        return self.delete_resource(uri)
+
+    def list_networks(self, **filters):
+        """Lists networks to which the tenant has access.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#list-networks
+        """
+        uri = '/networks'
+        return self.list_resources(uri, **filters)
+
+    def create_bulk_networks(self, **kwargs):
+        """Create multiple networks in a single request.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#bulk-create-networks
+        """
+        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
new file mode 100644
index 0000000..93138b9
--- /dev/null
+++ b/tempest/lib/services/network/ports_client.py
@@ -0,0 +1,86 @@
+#    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 import exceptions as lib_exc
+from tempest.lib.services.network import base
+
+
+class PortsClient(base.BaseNetworkClient):
+
+    def create_port(self, **kwargs):
+        """Creates a port on a network.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#create-port
+        """
+        uri = '/ports'
+        post_data = {'port': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_port(self, port_id, **kwargs):
+        """Updates a port.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#update-port
+        """
+        uri = '/ports/%s' % port_id
+        post_data = {'port': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_port(self, port_id, **fields):
+        """Shows details for a port.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#show-port-details
+        """
+        uri = '/ports/%s' % port_id
+        return self.show_resource(uri, **fields)
+
+    def delete_port(self, port_id):
+        """Deletes a port.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#delete-port
+        """
+        uri = '/ports/%s' % port_id
+        return self.delete_resource(uri)
+
+    def list_ports(self, **filters):
+        """Lists ports to which the tenant has access.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#list-ports
+        """
+        uri = '/ports'
+        return self.list_resources(uri, **filters)
+
+    def create_bulk_ports(self, **kwargs):
+        """Create multiple ports in a single request.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html?expanded=#bulk-create-ports
+        """
+        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
new file mode 100644
index 0000000..752b253
--- /dev/null
+++ b/tempest/lib/services/network/quotas_client.py
@@ -0,0 +1,37 @@
+# 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.services.network import base
+
+
+class QuotasClient(base.BaseNetworkClient):
+
+    def update_quotas(self, tenant_id, **kwargs):
+        put_body = {'quota': kwargs}
+        uri = '/quotas/%s' % tenant_id
+        return self.update_resource(uri, put_body)
+
+    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)
+
+    def show_quotas(self, tenant_id, **fields):
+        uri = '/quotas/%s' % tenant_id
+        return self.show_resource(uri, **fields)
+
+    def list_quotas(self, **filters):
+        uri = '/quotas'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/routers_client.py b/tempest/lib/services/network/routers_client.py
new file mode 100644
index 0000000..19b7627
--- /dev/null
+++ b/tempest/lib/services/network/routers_client.py
@@ -0,0 +1,86 @@
+#    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.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#create-router
+        """
+        post_body = {'router': kwargs}
+        uri = '/routers'
+        return self.create_resource(uri, post_body)
+
+    def update_router(self, router_id, **kwargs):
+        """Updates a logical router.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#update-router
+        """
+        uri = '/routers/%s' % router_id
+        update_body = {'router': kwargs}
+        return self.update_resource(uri, update_body)
+
+    def show_router(self, router_id, **fields):
+        """Shows details for a router.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#show-router-details
+        """
+        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):
+        """Lists logical routers.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#list-routers
+        """
+        uri = '/routers'
+        return self.list_resources(uri, **filters)
+
+    def add_router_interface(self, router_id, **kwargs):
+        """Add router interface.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#add-interface-to-router
+        """
+        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.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#remove-interface-from-router
+        """
+        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/lib/services/network/security_group_rules_client.py b/tempest/lib/services/network/security_group_rules_client.py
new file mode 100644
index 0000000..d2bc4a9
--- /dev/null
+++ b/tempest/lib/services/network/security_group_rules_client.py
@@ -0,0 +1,51 @@
+#    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 SecurityGroupRulesClient(base.BaseNetworkClient):
+
+    def create_security_group_rule(self, **kwargs):
+        """Creates an OpenStack Networking security group rule.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#create-security-group-rule
+        """
+        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):
+        """Shows detailed information for a security group rule.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#show-security-group-rule
+        """
+        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):
+        """Lists a summary of all OpenStack Networking security group rules.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#list-security-group-rules
+        """
+        uri = '/security-group-rules'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/security_groups_client.py b/tempest/lib/services/network/security_groups_client.py
new file mode 100644
index 0000000..1f30216
--- /dev/null
+++ b/tempest/lib/services/network/security_groups_client.py
@@ -0,0 +1,68 @@
+#    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 SecurityGroupsClient(base.BaseNetworkClient):
+
+    def create_security_group(self, **kwargs):
+        """Creates an OpenStack Networking security group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#create-security-group
+        """
+        uri = '/security-groups'
+        post_data = {'security_group': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_security_group(self, security_group_id, **kwargs):
+        """Updates a security group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#update-security-group
+        """
+        uri = '/security-groups/%s' % security_group_id
+        post_data = {'security_group': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_security_group(self, security_group_id, **fields):
+        """Shows details for a security group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#show-security-group
+        """
+        uri = '/security-groups/%s' % security_group_id
+        return self.show_resource(uri, **fields)
+
+    def delete_security_group(self, security_group_id):
+        """Deletes an OpenStack Networking security group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#delete-security-group
+        """
+        uri = '/security-groups/%s' % security_group_id
+        return self.delete_resource(uri)
+
+    def list_security_groups(self, **filters):
+        """Lists OpenStack Networking security groups.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#list-security-groups
+        """
+        uri = '/security-groups'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/service_providers_client.py b/tempest/lib/services/network/service_providers_client.py
new file mode 100644
index 0000000..0ee9bc3
--- /dev/null
+++ b/tempest/lib/services/network/service_providers_client.py
@@ -0,0 +1,21 @@
+#    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 ServiceProvidersClient(base.BaseNetworkClient):
+
+    def list_service_providers(self, **filters):
+        """Lists service providers."""
+        uri = '/service-providers'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/subnetpools_client.py b/tempest/lib/services/network/subnetpools_client.py
new file mode 100644
index 0000000..7e77e30
--- /dev/null
+++ b/tempest/lib/services/network/subnetpools_client.py
@@ -0,0 +1,64 @@
+# 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.services.network import base
+
+
+class SubnetpoolsClient(base.BaseNetworkClient):
+
+    def list_subnetpools(self, **filters):
+        """Lists subnet pools to which the tenant has access.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#list-subnet-pools
+        """
+        uri = '/subnetpools'
+        return self.list_resources(uri, **filters)
+
+    def create_subnetpool(self, **kwargs):
+        """Creates a subnet pool.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#create-subnet-pool
+        """
+        uri = '/subnetpools'
+        post_data = {'subnetpool': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def show_subnetpool(self, subnetpool_id, **fields):
+        """Shows information for a subnet pool.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#show-subnet-pool
+        """
+        uri = '/subnetpools/%s' % subnetpool_id
+        return self.show_resource(uri, **fields)
+
+    def update_subnetpool(self, subnetpool_id, **kwargs):
+        """Updates a subnet pool.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#update-subnet-pool
+        """
+        uri = '/subnetpools/%s' % subnetpool_id
+        post_data = {'subnetpool': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def delete_subnetpool(self, subnetpool_id):
+        uri = '/subnetpools/%s' % subnetpool_id
+        return self.delete_resource(uri)
diff --git a/tempest/lib/services/network/subnets_client.py b/tempest/lib/services/network/subnets_client.py
new file mode 100644
index 0000000..b843f84
--- /dev/null
+++ b/tempest/lib/services/network/subnets_client.py
@@ -0,0 +1,72 @@
+#    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 SubnetsClient(base.BaseNetworkClient):
+
+    def create_subnet(self, **kwargs):
+        """Creates a subnet on a network.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#create-subnet
+        """
+        uri = '/subnets'
+        post_data = {'subnet': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_subnet(self, subnet_id, **kwargs):
+        """Updates a subnet.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#update-subnet
+        """
+        uri = '/subnets/%s' % subnet_id
+        post_data = {'subnet': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_subnet(self, subnet_id, **fields):
+        """Shows details for a subnet.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#show-subnet-details
+        """
+        uri = '/subnets/%s' % subnet_id
+        return self.show_resource(uri, **fields)
+
+    def delete_subnet(self, subnet_id):
+        uri = '/subnets/%s' % subnet_id
+        return self.delete_resource(uri)
+
+    def list_subnets(self, **filters):
+        """Lists subnets to which the tenant has access.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#list-subnets
+        """
+        uri = '/subnets'
+        return self.list_resources(uri, **filters)
+
+    def create_bulk_subnets(self, **kwargs):
+        """Create multiple subnets in a single request.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#bulk-create-subnet
+        """
+        uri = '/subnets'
+        return self.create_resource(uri, kwargs)
diff --git a/tempest/lib/services/network/versions_client.py b/tempest/lib/services/network/versions_client.py
new file mode 100644
index 0000000..a9c3bbf
--- /dev/null
+++ b/tempest/lib/services/network/versions_client.py
@@ -0,0 +1,46 @@
+# Copyright 2016 VMware, Inc.  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 time
+
+from oslo_serialization import jsonutils as json
+from six.moves import urllib
+
+from tempest.lib.services.network import base
+
+
+class NetworkVersionsClient(base.BaseNetworkClient):
+
+    def list_versions(self):
+        """Do a GET / to fetch available API version information."""
+
+        endpoint = self.base_url
+        url = urllib.parse.urlparse(endpoint)
+        version_url = '%s://%s/' % (url.scheme, url.netloc)
+
+        # Note: we do a raw_request here because we want to use
+        # an unversioned URL, not "v2/$project_id/".
+        # Since raw_request doesn't log anything, we do that too.
+        start = time.time()
+        self._log_request_start('GET', version_url)
+        response, body = self.raw_request(version_url, 'GET')
+        self._error_checker(response, body)
+        end = time.time()
+        self._log_request('GET', version_url, response,
+                          secs=(end - start), resp_body=body)
+
+        self.response_checker('GET', response, body)
+        self.expected_success(200, response.status)
+        body = json.loads(body)
+        return body
diff --git a/tempest/lib/services/volume/__init__.py b/tempest/lib/services/volume/__init__.py
new file mode 100644
index 0000000..6855d8e
--- /dev/null
+++ b/tempest/lib/services/volume/__init__.py
@@ -0,0 +1,19 @@
+# Copyright (c) 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.
+
+from tempest.lib.services.volume import v1
+from tempest.lib.services.volume import v2
+from tempest.lib.services.volume import v3
+
+__all__ = ['v1', 'v2', 'v3']
diff --git a/tempest/lib/services/volume/v1/__init__.py b/tempest/lib/services/volume/v1/__init__.py
new file mode 100644
index 0000000..7b5991f
--- /dev/null
+++ b/tempest/lib/services/volume/v1/__init__.py
@@ -0,0 +1,33 @@
+# Copyright (c) 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.
+
+from tempest.lib.services.volume.v1.availability_zone_client \
+    import AvailabilityZoneClient
+from tempest.lib.services.volume.v1.backups_client import BackupsClient
+from tempest.lib.services.volume.v1.encryption_types_client import \
+    EncryptionTypesClient
+from tempest.lib.services.volume.v1.extensions_client import ExtensionsClient
+from tempest.lib.services.volume.v1.hosts_client import HostsClient
+from tempest.lib.services.volume.v1.limits_client import LimitsClient
+from tempest.lib.services.volume.v1.qos_client import QosSpecsClient
+from tempest.lib.services.volume.v1.quotas_client import QuotasClient
+from tempest.lib.services.volume.v1.services_client import ServicesClient
+from tempest.lib.services.volume.v1.snapshots_client import SnapshotsClient
+from tempest.lib.services.volume.v1.types_client import TypesClient
+from tempest.lib.services.volume.v1.volumes_client import VolumesClient
+
+__all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
+           'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
+           'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
+           'LimitsClient']
diff --git a/tempest/lib/services/volume/v1/availability_zone_client.py b/tempest/lib/services/volume/v1/availability_zone_client.py
new file mode 100644
index 0000000..be4f539
--- /dev/null
+++ b/tempest/lib/services/volume/v1/availability_zone_client.py
@@ -0,0 +1,28 @@
+# 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.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class AvailabilityZoneClient(rest_client.RestClient):
+    """Volume V1 availability zone client."""
+
+    def list_availability_zones(self):
+        resp, body = self.get('os-availability-zone')
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v1/backups_client.py b/tempest/lib/services/volume/v1/backups_client.py
new file mode 100644
index 0000000..8677913
--- /dev/null
+++ b/tempest/lib/services/volume/v1/backups_client.py
@@ -0,0 +1,104 @@
+# Copyright 2014 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.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class BackupsClient(rest_client.RestClient):
+    """Volume V1 Backups client"""
+    api_version = "v1"
+
+    def create_backup(self, **kwargs):
+        """Creates a backup of volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-backup
+        """
+        post_body = json.dumps({'backup': kwargs})
+        resp, body = self.post('backups', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def restore_backup(self, backup_id, **kwargs):
+        """Restore volume from backup.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#restore-backup
+        """
+        post_body = json.dumps({'restore': kwargs})
+        resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_backup(self, backup_id):
+        """Delete a backup of volume."""
+        resp, body = self.delete('backups/%s' % backup_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_backup(self, backup_id):
+        """Returns the details of a single backup."""
+        url = "backups/%s" % backup_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_backups(self, detail=False):
+        """Information for all the tenant's backups."""
+        url = "backups"
+        if detail:
+            url += "/detail"
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def export_backup(self, backup_id):
+        """Export backup metadata record."""
+        url = "backups/%s/export_record" % backup_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def import_backup(self, **kwargs):
+        """Import backup metadata record."""
+        post_body = json.dumps({'backup-record': kwargs})
+        resp, body = self.post("backups/import_record", post_body)
+        body = json.loads(body)
+        self.expected_success(201, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def reset_backup_status(self, backup_id, status):
+        """Reset the specified backup's status."""
+        post_body = json.dumps({'os-reset_status': {"status": status}})
+        resp, body = self.post('backups/%s/action' % backup_id, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_backup(id)
+        except lib_exc.NotFound:
+            return True
+        return False
diff --git a/tempest/lib/services/volume/v1/encryption_types_client.py b/tempest/lib/services/volume/v1/encryption_types_client.py
new file mode 100755
index 0000000..067b4e8
--- /dev/null
+++ b/tempest/lib/services/volume/v1/encryption_types_client.py
@@ -0,0 +1,68 @@
+# 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.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class EncryptionTypesClient(rest_client.RestClient):
+
+    def is_resource_deleted(self, id):
+        try:
+            body = self.show_encryption_type(id)
+            if not body:
+                return True
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'encryption-type'
+
+    def show_encryption_type(self, volume_type_id):
+        """Get the volume encryption type for the specified volume type.
+
+        volume_type_id: Id of volume_type.
+        """
+        url = "/types/%s/encryption" % volume_type_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_encryption_type(self, volume_type_id, **kwargs):
+        """Create encryption type.
+
+        TODO: Current api-site doesn't contain this API description.
+        After fixing the api-site, we need to fix here also for putting
+        the link to api-site.
+        """
+        url = "/types/%s/encryption" % volume_type_id
+        post_body = json.dumps({'encryption': kwargs})
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_encryption_type(self, volume_type_id):
+        """Delete the encryption type for the specified volume-type."""
+        resp, body = self.delete(
+            "/types/%s/encryption/provider" % volume_type_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v1/extensions_client.py b/tempest/lib/services/volume/v1/extensions_client.py
new file mode 100644
index 0000000..7b849a8
--- /dev/null
+++ b/tempest/lib/services/volume/v1/extensions_client.py
@@ -0,0 +1,29 @@
+# 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.lib.common import rest_client
+
+
+class ExtensionsClient(rest_client.RestClient):
+    """Volume V1 extensions client."""
+
+    def list_extensions(self):
+        url = 'extensions'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v1/hosts_client.py b/tempest/lib/services/volume/v1/hosts_client.py
new file mode 100644
index 0000000..56ba12c
--- /dev/null
+++ b/tempest/lib/services/volume/v1/hosts_client.py
@@ -0,0 +1,35 @@
+# Copyright 2013 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 six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class HostsClient(rest_client.RestClient):
+    """Client class to send CRUD Volume Host API V1 requests"""
+
+    def list_hosts(self, **params):
+        """Lists all hosts."""
+
+        url = 'os-hosts'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v1/limits_client.py b/tempest/lib/services/volume/v1/limits_client.py
new file mode 100644
index 0000000..e14b2dc
--- /dev/null
+++ b/tempest/lib/services/volume/v1/limits_client.py
@@ -0,0 +1,32 @@
+# Copyright 2016 Red Hat, Inc.
+# 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.lib.common import rest_client
+
+
+class LimitsClient(rest_client.RestClient):
+    """Volume V1 limits client."""
+
+    api_version = "v1"
+
+    def show_limits(self):
+        """Returns the details of a volume absolute limits."""
+        url = "limits"
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v1/qos_client.py b/tempest/lib/services/volume/v1/qos_client.py
new file mode 100644
index 0000000..e247b7b
--- /dev/null
+++ b/tempest/lib/services/volume/v1/qos_client.py
@@ -0,0 +1,131 @@
+# 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.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class QosSpecsClient(rest_client.RestClient):
+    """Volume V1 QoS client.
+
+       Client class to send CRUD QoS API requests
+    """
+
+    api_version = "v1"
+
+    def is_resource_deleted(self, qos_id):
+        try:
+            self.show_qos(qos_id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'qos'
+
+    def create_qos(self, **kwargs):
+        """Create a QoS Specification.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-qos-specification
+        """
+        post_body = json.dumps({'qos_specs': kwargs})
+        resp, body = self.post('qos-specs', post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(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" % (qos_id, force))
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_qos(self):
+        """List all the QoS specifications created."""
+        url = 'qos-specs'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_qos(self, qos_id):
+        """Get the specified QoS specification."""
+        url = "qos-specs/%s" % qos_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_qos_key(self, qos_id, **kwargs):
+        """Set the specified keys/values of QoS specification.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#set-keys-in-qos-specification
+        """
+        put_body = json.dumps({"qos_specs": kwargs})
+        resp, body = self.put('qos-specs/%s' % qos_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def unset_qos_key(self, qos_id, keys):
+        """Unset the specified keys of QoS specification.
+
+        :param keys: keys to delete from the QoS specification.
+
+        TODO(jordanP): Add a link once LP #1524877 is fixed.
+        """
+        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 rest_client.ResponseBody(resp, body)
+
+    def associate_qos(self, qos_id, vol_type_id):
+        """Associate the specified QoS with specified volume-type."""
+        url = "qos-specs/%s/associate" % qos_id
+        url += "?vol_type_id=%s" % vol_type_id
+        resp, body = self.get(url)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_association_qos(self, qos_id):
+        """Get the association of the specified QoS specification."""
+        url = "qos-specs/%s/associations" % qos_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def disassociate_qos(self, qos_id, vol_type_id):
+        """Disassociate the specified QoS with specified volume-type."""
+        url = "qos-specs/%s/disassociate" % qos_id
+        url += "?vol_type_id=%s" % vol_type_id
+        resp, body = self.get(url)
+        self.expected_success(202, resp.status)
+        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" % qos_id
+        resp, body = self.get(url)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v1/quotas_client.py b/tempest/lib/services/volume/v1/quotas_client.py
new file mode 100644
index 0000000..678fd82
--- /dev/null
+++ b/tempest/lib/services/volume/v1/quotas_client.py
@@ -0,0 +1,62 @@
+# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
+#
+#    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
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class QuotasClient(rest_client.RestClient):
+    """Client class to send CRUD Volume Quotas API V1 requests"""
+
+    def show_default_quota_set(self, tenant_id):
+        """List the default volume quota set for a tenant."""
+
+        url = 'os-quota-sets/%s/defaults' % tenant_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = jsonutils.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_quota_set(self, tenant_id, params=None):
+        """List the quota set for a tenant."""
+
+        url = 'os-quota-sets/%s' % tenant_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = jsonutils.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_quota_set(self, tenant_id, **kwargs):
+        """Updates quota set
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-blockstorage-v1.html#updateQuota
+        """
+        put_body = jsonutils.dumps({'quota_set': kwargs})
+        resp, body = self.put('os-quota-sets/%s' % tenant_id, put_body)
+        self.expected_success(200, resp.status)
+        body = jsonutils.loads(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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v1/services_client.py b/tempest/lib/services/volume/v1/services_client.py
new file mode 100644
index 0000000..d438a34
--- /dev/null
+++ b/tempest/lib/services/volume/v1/services_client.py
@@ -0,0 +1,33 @@
+# 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.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class ServicesClient(rest_client.RestClient):
+    """Volume V1 volume services client"""
+
+    def list_services(self, **params):
+        url = 'os-services'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v1/snapshots_client.py b/tempest/lib/services/volume/v1/snapshots_client.py
new file mode 100644
index 0000000..3433e68
--- /dev/null
+++ b/tempest/lib/services/volume/v1/snapshots_client.py
@@ -0,0 +1,186 @@
+#    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.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class SnapshotsClient(rest_client.RestClient):
+    """Client class to send CRUD Volume V1 API requests."""
+
+    create_resp = 200
+
+    def list_snapshots(self, detail=False, **params):
+        """List all the snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#list-snapshots-with-details-v1
+        """
+        url = 'snapshots'
+        if detail:
+            url += '/detail'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_snapshot(self, snapshot_id):
+        """Returns the details of a single snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#show-snapshot-details-v1
+        """
+        url = "snapshots/%s" % snapshot_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_snapshot(self, **kwargs):
+        """Creates a new snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#create-snapshot-v1
+        """
+        post_body = json.dumps({'snapshot': kwargs})
+        resp, body = self.post('snapshots', post_body)
+        body = json.loads(body)
+        self.expected_success(self.create_resp, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_snapshot(self, snapshot_id):
+        """Delete Snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#delete-snapshot-v1
+        """
+        resp, body = self.delete("snapshots/%s" % snapshot_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_snapshot(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'volume-snapshot'
+
+    def reset_snapshot_status(self, snapshot_id, status):
+        """Reset the specified snapshot's status."""
+        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 rest_client.ResponseBody(resp, body)
+
+    def update_snapshot_status(self, snapshot_id, **kwargs):
+        """Update the specified snapshot's status."""
+        # TODO(gmann): api-site doesn't contain doc ref
+        # for this API. After fixing the api-site, we need to
+        # add the link here.
+        # Bug https://bugs.launchpad.net/openstack-api-site/+bug/1532645
+
+        post_body = json.dumps({'os-update_snapshot_status': kwargs})
+        url = 'snapshots/%s/action' % snapshot_id
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_snapshot_metadata(self, snapshot_id, metadata):
+        """Create metadata for the snapshot."""
+        put_body = json.dumps({'metadata': metadata})
+        url = "snapshots/%s/metadata" % snapshot_id
+        resp, body = self.post(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_snapshot(self, snapshot_id, **kwargs):
+        """Updates a snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#update-snapshot-v1
+        """
+        put_body = json.dumps({'snapshot': kwargs})
+        resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_snapshot_metadata(self, snapshot_id):
+        """Get metadata of the snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#show-snapshot-metadata-v1
+        """
+        url = "snapshots/%s/metadata" % snapshot_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_snapshot_metadata(self, snapshot_id, **kwargs):
+        """Update metadata for the snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#update-snapshot-metadata-v1
+        """
+        put_body = json.dumps(kwargs)
+        url = "snapshots/%s/metadata" % snapshot_id
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_snapshot_metadata_item(self, snapshot_id, id, **kwargs):
+        """Update metadata item for the snapshot."""
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1529064
+        put_body = json.dumps(kwargs)
+        url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        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" % (snapshot_id, id)
+        resp, body = self.delete(url)
+        self.expected_success(200, resp.status)
+        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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v1/types_client.py b/tempest/lib/services/volume/v1/types_client.py
new file mode 100644
index 0000000..4ae9935
--- /dev/null
+++ b/tempest/lib/services/volume/v1/types_client.py
@@ -0,0 +1,165 @@
+# 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 six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class TypesClient(rest_client.RestClient):
+    """Client class to send CRUD Volume Types API requests"""
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_volume_type(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'volume-type'
+
+    def list_volume_types(self, **params):
+        """List all the volume_types created.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#list-volume-types-v1
+        """
+        url = 'types'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_type(self, volume_type_id):
+        """Returns the details of a single volume_type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#show-volume-type-v1
+        """
+        url = "types/%s" % volume_type_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_volume_type(self, **kwargs):
+        """Create volume type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#create-volume-type-v1
+        """
+        post_body = json.dumps({'volume_type': kwargs})
+        resp, body = self.post('types', post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume_type(self, volume_type_id):
+        """Deletes the Specified Volume_type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#delete-volume-type-v1
+        """
+        resp, body = self.delete("types/%s" % volume_type_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_volume_types_extra_specs(self, volume_type_id, **params):
+        """List all the volume_types extra specs created.
+
+        TODO: Current api-site doesn't contain this API description.
+        After fixing the api-site, we need to fix here also for putting
+        the link to api-site.
+        """
+        url = 'types/%s/extra_specs' % volume_type_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_type_extra_specs(self, volume_type_id, extra_specs_name):
+        """Returns the details of a single volume_type extra spec."""
+        url = "types/%s/extra_specs/%s" % (volume_type_id, extra_specs_name)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_volume_type_extra_specs(self, volume_type_id, extra_specs):
+        """Creates a new Volume_type extra spec.
+
+        volume_type_id: Id of volume_type.
+        extra_specs: A dictionary of values to be used as extra_specs.
+        """
+        url = "types/%s/extra_specs" % volume_type_id
+        post_body = json.dumps({'extra_specs': extra_specs})
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume_type_extra_specs(self, volume_type_id, extra_spec_name):
+        """Deletes the Specified Volume_type extra spec."""
+        resp, body = self.delete("types/%s/extra_specs/%s" % (
+            volume_type_id, extra_spec_name))
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_type(self, volume_type_id, **kwargs):
+        """Updates volume type name, description, and/or is_public.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#update-volume-type-v1
+        """
+        put_body = json.dumps({'volume_type': kwargs})
+        resp, body = self.put('types/%s' % volume_type_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_type_extra_specs(self, volume_type_id, extra_spec_name,
+                                       extra_specs):
+        """Update a volume_type extra spec.
+
+        volume_type_id: Id of volume_type.
+        extra_spec_name: Name of the extra spec to be updated.
+        extra_spec: A dictionary of with key as extra_spec_name and the
+                     updated value.
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#update-extra-specs-for-a-volume-type-v1
+        """
+        url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
+        put_body = json.dumps(extra_specs)
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v1/volumes_client.py b/tempest/lib/services/volume/v1/volumes_client.py
new file mode 100644
index 0000000..7a25697
--- /dev/null
+++ b/tempest/lib/services/volume/v1/volumes_client.py
@@ -0,0 +1,301 @@
+# 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
+import six
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class VolumesClient(rest_client.RestClient):
+    """Client class to send CRUD Volume V1 API requests"""
+
+    def _prepare_params(self, params):
+        """Prepares params for use in get or _ext_get methods.
+
+        If params is a string it will be left as it is, but if it's not it will
+        be urlencoded.
+        """
+        if isinstance(params, six.string_types):
+            return params
+        return urllib.urlencode(params)
+
+    def list_volumes(self, detail=False, params=None):
+        """List all the volumes created.
+
+        Params can be a string (must be urlencoded) or a dictionary.
+        """
+        url = 'volumes'
+        if detail:
+            url += '/detail'
+        if params:
+            url += '?%s' % self._prepare_params(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume(self, volume_id):
+        """Returns the details of a single volume."""
+        url = "volumes/%s" % volume_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_volume(self, **kwargs):
+        """Creates a new Volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#create-volume
+        """
+        post_body = json.dumps({'volume': kwargs})
+        resp, body = self.post('volumes', post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume(self, volume_id, **kwargs):
+        """Updates the Specified Volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#update-volume
+        """
+        put_body = json.dumps({'volume': kwargs})
+        resp, body = self.put('volumes/%s' % volume_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume(self, volume_id):
+        """Deletes the Specified Volume."""
+        resp, body = self.delete("volumes/%s" % volume_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def upload_volume(self, volume_id, **kwargs):
+        """Uploads a volume in Glance."""
+        post_body = json.dumps({'os-volume_upload_image': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def attach_volume(self, volume_id, **kwargs):
+        """Attaches a volume to a given instance on a given mountpoint.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#attach-volume
+        """
+        post_body = json.dumps({'os-attach': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_bootable_volume(self, volume_id, **kwargs):
+        """set a bootable flag for a volume - true or false."""
+        post_body = json.dumps({'os-set_bootable': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def detach_volume(self, volume_id):
+        """Detaches a volume from an instance."""
+        post_body = json.dumps({'os-detach': {}})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def reserve_volume(self, volume_id):
+        """Reserves a volume."""
+        post_body = json.dumps({'os-reserve': {}})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def unreserve_volume(self, volume_id):
+        """Restore a reserved volume ."""
+        post_body = json.dumps({'os-unreserve': {}})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_volume(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'volume'
+
+    def extend_volume(self, volume_id, **kwargs):
+        """Extend a volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#extend-volume
+        """
+        post_body = json.dumps({'os-extend': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def reset_volume_status(self, volume_id, **kwargs):
+        """Reset the Specified Volume's Status.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#reset-volume-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 rest_client.ResponseBody(resp, body)
+
+    def create_volume_transfer(self, **kwargs):
+        """Create a volume transfer.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#create-volume-transfer
+        """
+        post_body = json.dumps({'transfer': kwargs})
+        resp, body = self.post('os-volume-transfer', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_transfer(self, transfer_id):
+        """Returns the details of a volume transfer."""
+        url = "os-volume-transfer/%s" % transfer_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_volume_transfers(self, **params):
+        """List all the volume transfers created.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#list-volume-transfers
+        """
+        url = 'os-volume-transfer'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        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" % transfer_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def accept_volume_transfer(self, transfer_id, **kwargs):
+        """Accept a volume transfer.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#accept-volume-transfer
+        """
+        url = 'os-volume-transfer/%s/accept' % transfer_id
+        post_body = json.dumps({'accept': kwargs})
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_readonly(self, volume_id, **kwargs):
+        """Update the Specified Volume readonly."""
+        post_body = json.dumps({'os-update_readonly_flag': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        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 rest_client.ResponseBody(resp, body)
+
+    def create_volume_metadata(self, volume_id, metadata):
+        """Create metadata for the volume."""
+        put_body = json.dumps({'metadata': metadata})
+        url = "volumes/%s/metadata" % volume_id
+        resp, body = self.post(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_metadata(self, volume_id):
+        """Get metadata of the volume."""
+        url = "volumes/%s/metadata" % volume_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_metadata(self, volume_id, metadata):
+        """Update metadata for the volume."""
+        put_body = json.dumps({'metadata': metadata})
+        url = "volumes/%s/metadata" % volume_id
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_metadata_item(self, volume_id, id, meta_item):
+        """Update metadata item for the volume."""
+        put_body = json.dumps({'meta': meta_item})
+        url = "volumes/%s/metadata/%s" % (volume_id, id)
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        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" % (volume_id, id)
+        resp, body = self.delete(url)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def retype_volume(self, volume_id, **kwargs):
+        """Updates volume with new volume type."""
+        post_body = json.dumps({'os-retype': kwargs})
+        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+        self.expected_success(202, resp.status)
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
new file mode 100644
index 0000000..8acad0f
--- /dev/null
+++ b/tempest/lib/services/volume/v2/__init__.py
@@ -0,0 +1,40 @@
+# Copyright (c) 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.
+
+from tempest.lib.services.volume.v2.availability_zone_client \
+    import AvailabilityZoneClient
+from tempest.lib.services.volume.v2.backups_client import BackupsClient
+from tempest.lib.services.volume.v2.capabilities_client import \
+    CapabilitiesClient
+from tempest.lib.services.volume.v2.encryption_types_client import \
+    EncryptionTypesClient
+from tempest.lib.services.volume.v2.extensions_client import ExtensionsClient
+from tempest.lib.services.volume.v2.hosts_client import HostsClient
+from tempest.lib.services.volume.v2.limits_client import LimitsClient
+from tempest.lib.services.volume.v2.qos_client import QosSpecsClient
+from tempest.lib.services.volume.v2.quotas_client import QuotasClient
+from tempest.lib.services.volume.v2.scheduler_stats_client import \
+    SchedulerStatsClient
+from tempest.lib.services.volume.v2.services_client import ServicesClient
+from tempest.lib.services.volume.v2.snapshot_manage_client import \
+    SnapshotManageClient
+from tempest.lib.services.volume.v2.snapshots_client import SnapshotsClient
+from tempest.lib.services.volume.v2.types_client import TypesClient
+from tempest.lib.services.volume.v2.volumes_client import VolumesClient
+
+__all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
+           'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
+           'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
+           'LimitsClient', 'CapabilitiesClient', 'SchedulerStatsClient',
+           'SnapshotManageClient']
diff --git a/tempest/lib/services/volume/v2/availability_zone_client.py b/tempest/lib/services/volume/v2/availability_zone_client.py
new file mode 100644
index 0000000..bb4a357
--- /dev/null
+++ b/tempest/lib/services/volume/v2/availability_zone_client.py
@@ -0,0 +1,28 @@
+# 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.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class AvailabilityZoneClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def list_availability_zones(self):
+        resp, body = self.get('os-availability-zone')
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/backups_client.py b/tempest/lib/services/volume/v2/backups_client.py
new file mode 100644
index 0000000..ab5eefd
--- /dev/null
+++ b/tempest/lib/services/volume/v2/backups_client.py
@@ -0,0 +1,104 @@
+# Copyright 2014 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.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class BackupsClient(rest_client.RestClient):
+    """Volume V2 Backups client"""
+    api_version = "v2"
+
+    def create_backup(self, **kwargs):
+        """Creates a backup of volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-blockstorage-v2.html#createBackup
+        """
+        post_body = json.dumps({'backup': kwargs})
+        resp, body = self.post('backups', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def restore_backup(self, backup_id, **kwargs):
+        """Restore volume from backup.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-blockstorage-v2.html#restoreBackup
+        """
+        post_body = json.dumps({'restore': kwargs})
+        resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_backup(self, backup_id):
+        """Delete a backup of volume."""
+        resp, body = self.delete('backups/%s' % backup_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_backup(self, backup_id):
+        """Returns the details of a single backup."""
+        url = "backups/%s" % backup_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_backups(self, detail=False):
+        """Information for all the tenant's backups."""
+        url = "backups"
+        if detail:
+            url += "/detail"
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def export_backup(self, backup_id):
+        """Export backup metadata record."""
+        url = "backups/%s/export_record" % backup_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def import_backup(self, **kwargs):
+        """Import backup metadata record."""
+        post_body = json.dumps({'backup-record': kwargs})
+        resp, body = self.post("backups/import_record", post_body)
+        body = json.loads(body)
+        self.expected_success(201, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def reset_backup_status(self, backup_id, status):
+        """Reset the specified backup's status."""
+        post_body = json.dumps({'os-reset_status': {"status": status}})
+        resp, body = self.post('backups/%s/action' % backup_id, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_backup(id)
+        except lib_exc.NotFound:
+            return True
+        return False
diff --git a/tempest/lib/services/volume/v2/capabilities_client.py b/tempest/lib/services/volume/v2/capabilities_client.py
new file mode 100644
index 0000000..b6de5b9
--- /dev/null
+++ b/tempest/lib/services/volume/v2/capabilities_client.py
@@ -0,0 +1,34 @@
+# Copyright 2016 Red Hat, Inc.
+# 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.lib.common import rest_client
+
+
+class CapabilitiesClient(rest_client.RestClient):
+
+    def show_backend_capabilities(self, host):
+        """Shows capabilities for a storage back end.
+
+         Output params: see http://developer.openstack.org/
+                            api-ref-blockstorage-v2.html
+                            #showBackendCapabilities
+        """
+        url = 'capabilities/%s' % host
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/encryption_types_client.py b/tempest/lib/services/volume/v2/encryption_types_client.py
new file mode 100755
index 0000000..8b01f11
--- /dev/null
+++ b/tempest/lib/services/volume/v2/encryption_types_client.py
@@ -0,0 +1,69 @@
+# 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.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class EncryptionTypesClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def is_resource_deleted(self, id):
+        try:
+            body = self.show_encryption_type(id)
+            if not body:
+                return True
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'encryption-type'
+
+    def show_encryption_type(self, volume_type_id):
+        """Get the volume encryption type for the specified volume type.
+
+        volume_type_id: Id of volume_type.
+        """
+        url = "/types/%s/encryption" % volume_type_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_encryption_type(self, volume_type_id, **kwargs):
+        """Create encryption type.
+
+        TODO: Current api-site doesn't contain this API description.
+        After fixing the api-site, we need to fix here also for putting
+        the link to api-site.
+        """
+        url = "/types/%s/encryption" % volume_type_id
+        post_body = json.dumps({'encryption': kwargs})
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_encryption_type(self, volume_type_id):
+        """Delete the encryption type for the specified volume-type."""
+        resp, body = self.delete(
+            "/types/%s/encryption/provider" % volume_type_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/extensions_client.py b/tempest/lib/services/volume/v2/extensions_client.py
new file mode 100644
index 0000000..09279d5
--- /dev/null
+++ b/tempest/lib/services/volume/v2/extensions_client.py
@@ -0,0 +1,30 @@
+# 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.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class ExtensionsClient(rest_client.RestClient):
+    """Volume V2 extensions client."""
+    api_version = "v2"
+
+    def list_extensions(self):
+        url = 'extensions'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/hosts_client.py b/tempest/lib/services/volume/v2/hosts_client.py
new file mode 100644
index 0000000..dd7c482
--- /dev/null
+++ b/tempest/lib/services/volume/v2/hosts_client.py
@@ -0,0 +1,36 @@
+# Copyright 2014 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 six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class HostsClient(rest_client.RestClient):
+    """Client class to send CRUD Volume V2 API requests"""
+    api_version = "v2"
+
+    def list_hosts(self, **params):
+        """Lists all hosts."""
+
+        url = 'os-hosts'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/limits_client.py b/tempest/lib/services/volume/v2/limits_client.py
new file mode 100644
index 0000000..ce9fba9
--- /dev/null
+++ b/tempest/lib/services/volume/v2/limits_client.py
@@ -0,0 +1,32 @@
+# Copyright 2016 Red Hat, Inc.
+# 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.lib.common import rest_client
+
+
+class LimitsClient(rest_client.RestClient):
+    """Volume V2 limits client."""
+
+    api_version = "v2"
+
+    def show_limits(self):
+        """Returns the details of a volume absolute limits."""
+        url = "limits"
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/qos_client.py b/tempest/lib/services/volume/v2/qos_client.py
new file mode 100644
index 0000000..40d4a3f
--- /dev/null
+++ b/tempest/lib/services/volume/v2/qos_client.py
@@ -0,0 +1,139 @@
+# 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.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class QosSpecsClient(rest_client.RestClient):
+    """Volume V2 QoS client.
+
+       Client class to send CRUD QoS API requests
+    """
+
+    api_version = "v2"
+
+    def is_resource_deleted(self, qos_id):
+        try:
+            self.show_qos(qos_id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'qos'
+
+    def create_qos(self, **kwargs):
+        """Create a QoS Specification.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/index.html
+                                ?expanded=create-qos-specification-detail
+                                #quality-of-service-qos-specifications-qos-specs
+        """
+        post_body = json.dumps({'qos_specs': kwargs})
+        resp, body = self.post('qos-specs', post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(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" % (qos_id, force))
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_qos(self):
+        """List all the QoS specifications created."""
+        url = 'qos-specs'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_qos(self, qos_id):
+        """Get the specified QoS specification."""
+        url = "qos-specs/%s" % qos_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_qos_key(self, qos_id, **kwargs):
+        """Set the specified keys/values of QoS specification.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/index.html
+                            ?expanded=set-keys-in-qos-specification-detail
+                            #quality-of-service-qos-specifications-qos-specs
+        """
+        put_body = json.dumps({"qos_specs": kwargs})
+        resp, body = self.put('qos-specs/%s' % qos_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def unset_qos_key(self, qos_id, keys):
+        """Unset the specified keys of QoS specification.
+
+        :param keys: keys to delete from the QoS specification.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/index.html
+                            ?expanded=unset-keys-in-qos-specification-detail
+                            #quality-of-service-qos-specifications-qos-specs
+        """
+        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 rest_client.ResponseBody(resp, body)
+
+    def associate_qos(self, qos_id, vol_type_id):
+        """Associate the specified QoS with specified volume-type."""
+        url = "qos-specs/%s/associate" % qos_id
+        url += "?vol_type_id=%s" % vol_type_id
+        resp, body = self.get(url)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_association_qos(self, qos_id):
+        """Get the association of the specified QoS specification."""
+        url = "qos-specs/%s/associations" % qos_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def disassociate_qos(self, qos_id, vol_type_id):
+        """Disassociate the specified QoS with specified volume-type."""
+        url = "qos-specs/%s/disassociate" % qos_id
+        url += "?vol_type_id=%s" % vol_type_id
+        resp, body = self.get(url)
+        self.expected_success(202, resp.status)
+        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" % qos_id
+        resp, body = self.get(url)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/quotas_client.py b/tempest/lib/services/volume/v2/quotas_client.py
new file mode 100644
index 0000000..430957d
--- /dev/null
+++ b/tempest/lib/services/volume/v2/quotas_client.py
@@ -0,0 +1,64 @@
+# Copyright 2014 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
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class QuotasClient(rest_client.RestClient):
+    """Client class to send CRUD Volume Quotas API V2 requests"""
+    api_version = "v2"
+
+    def show_default_quota_set(self, tenant_id):
+        """List the default volume quota set for a tenant."""
+
+        url = 'os-quota-sets/%s/defaults' % tenant_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = jsonutils.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_quota_set(self, tenant_id, params=None):
+        """List the quota set for a tenant."""
+
+        url = 'os-quota-sets/%s' % tenant_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = jsonutils.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_quota_set(self, tenant_id, **kwargs):
+        """Updates quota set
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-blockstorage-v2.html#updateQuota
+        """
+        put_body = jsonutils.dumps({'quota_set': kwargs})
+        resp, body = self.put('os-quota-sets/%s' % tenant_id, put_body)
+        self.expected_success(200, resp.status)
+        body = jsonutils.loads(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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/scheduler_stats_client.py b/tempest/lib/services/volume/v2/scheduler_stats_client.py
new file mode 100644
index 0000000..637254b
--- /dev/null
+++ b/tempest/lib/services/volume/v2/scheduler_stats_client.py
@@ -0,0 +1,35 @@
+# Copyright 2016 Red Hat, Inc.
+# 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.lib.common import rest_client
+
+
+class SchedulerStatsClient(rest_client.RestClient):
+
+    def list_pools(self, detail=False):
+        """List all the volumes pools (hosts).
+
+        Output params: see http://developer.openstack.org/
+                           api-ref-blockstorage-v2.html#listPools
+        """
+        url = 'scheduler-stats/get_pools'
+        if detail:
+            url += '?detail=True'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/services_client.py b/tempest/lib/services/volume/v2/services_client.py
new file mode 100644
index 0000000..bc55469
--- /dev/null
+++ b/tempest/lib/services/volume/v2/services_client.py
@@ -0,0 +1,34 @@
+# Copyright 2014 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 six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class ServicesClient(rest_client.RestClient):
+    """Client class to send CRUD Volume V2 API requests"""
+    api_version = "v2"
+
+    def list_services(self, **params):
+        url = 'os-services'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/snapshot_manage_client.py b/tempest/lib/services/volume/v2/snapshot_manage_client.py
new file mode 100644
index 0000000..aecd30b
--- /dev/null
+++ b/tempest/lib/services/volume/v2/snapshot_manage_client.py
@@ -0,0 +1,33 @@
+# Copyright 2016 Red Hat, Inc.
+# 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.lib.common import rest_client
+
+
+class SnapshotManageClient(rest_client.RestClient):
+    """Snapshot manage V2 client."""
+
+    api_version = "v2"
+
+    def manage_snapshot(self, **kwargs):
+        """Manage a snapshot."""
+        post_body = json.dumps({'snapshot': kwargs})
+        url = 'os-snapshot-manage'
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/snapshots_client.py b/tempest/lib/services/volume/v2/snapshots_client.py
new file mode 100644
index 0000000..2bdf1b1
--- /dev/null
+++ b/tempest/lib/services/volume/v2/snapshots_client.py
@@ -0,0 +1,194 @@
+#    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.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class SnapshotsClient(rest_client.RestClient):
+    """Client class to send CRUD Volume V2 API requests."""
+    api_version = "v2"
+    create_resp = 202
+
+    def list_snapshots(self, detail=False, **params):
+        """List all the snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots-v2
+        """
+        url = 'snapshots'
+        if detail:
+            url += '/detail'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_snapshot(self, snapshot_id):
+        """Returns the details of a single snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-v2
+        """
+        url = "snapshots/%s" % snapshot_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_snapshot(self, **kwargs):
+        """Creates a new snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-snapshot
+        """
+        post_body = json.dumps({'snapshot': kwargs})
+        resp, body = self.post('snapshots', post_body)
+        body = json.loads(body)
+        self.expected_success(self.create_resp, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_snapshot(self, snapshot_id, **kwargs):
+        """Updates a snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-v2
+        """
+        put_body = json.dumps({'snapshot': kwargs})
+        resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_snapshot(self, snapshot_id):
+        """Delete Snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#delete-snapshot-v2
+        """
+        resp, body = self.delete("snapshots/%s" % snapshot_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_snapshot(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'volume-snapshot'
+
+    def reset_snapshot_status(self, snapshot_id, status):
+        """Reset the specified snapshot's status."""
+        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 rest_client.ResponseBody(resp, body)
+
+    def update_snapshot_status(self, snapshot_id, **kwargs):
+        """Update the specified snapshot's status."""
+        # TODO(gmann): api-site doesn't contain doc ref
+        # for this API. After fixing the api-site, we need to
+        # add the link here.
+        # Bug https://bugs.launchpad.net/openstack-api-site/+bug/1532645
+
+        post_body = json.dumps({'os-update_snapshot_status': kwargs})
+        url = 'snapshots/%s/action' % snapshot_id
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_snapshot_metadata(self, snapshot_id, metadata):
+        """Create metadata for the snapshot."""
+        put_body = json.dumps({'metadata': metadata})
+        url = "snapshots/%s/metadata" % snapshot_id
+        resp, body = self.post(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_snapshot_metadata(self, snapshot_id):
+        """Get metadata of the snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-metadata-v2
+        """
+        url = "snapshots/%s/metadata" % snapshot_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_snapshot_metadata(self, snapshot_id, **kwargs):
+        """Update metadata for the snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-metadata-v2
+        """
+        put_body = json.dumps(kwargs)
+        url = "snapshots/%s/metadata" % snapshot_id
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_snapshot_metadata_item(self, snapshot_id, id, **kwargs):
+        """Update metadata item for the snapshot."""
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1529064
+        put_body = json.dumps(kwargs)
+        url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        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" % (snapshot_id, id)
+        resp, body = self.delete(url)
+        self.expected_success(200, resp.status)
+        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 rest_client.ResponseBody(resp, body)
+
+    def unmanage_snapshot(self, snapshot_id):
+        """Unmanage a snapshot."""
+        post_body = json.dumps({'os-unmanage': {}})
+        url = 'snapshots/%s/action' % (snapshot_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/types_client.py b/tempest/lib/services/volume/v2/types_client.py
new file mode 100644
index 0000000..31597d7
--- /dev/null
+++ b/tempest/lib/services/volume/v2/types_client.py
@@ -0,0 +1,205 @@
+# 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 six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class TypesClient(rest_client.RestClient):
+    """Client class to send CRUD Volume V2 API requests"""
+    api_version = "v2"
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_volume_type(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'volume-type'
+
+    def list_volume_types(self, **params):
+        """List all the volume_types created.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-types-v2
+        """
+        url = 'types'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_type(self, volume_type_id):
+        """Returns the details of a single volume_type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-v2
+        """
+        url = "types/%s" % volume_type_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_volume_type(self, **kwargs):
+        """Create volume type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type-v2
+        """
+        post_body = json.dumps({'volume_type': kwargs})
+        resp, body = self.post('types', post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume_type(self, volume_type_id):
+        """Deletes the Specified Volume_type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#delete-volume-type-v2
+        """
+        resp, body = self.delete("types/%s" % volume_type_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_volume_types_extra_specs(self, volume_type_id, **params):
+        """List all the volume_types extra specs created.
+
+        TODO: Current api-site doesn't contain this API description.
+        After fixing the api-site, we need to fix here also for putting
+        the link to api-site.
+        """
+        url = 'types/%s/extra_specs' % volume_type_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_type_extra_specs(self, volume_type_id, extra_specs_name):
+        """Returns the details of a single volume_type extra spec."""
+        url = "types/%s/extra_specs/%s" % (volume_type_id, extra_specs_name)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_volume_type_extra_specs(self, volume_type_id, extra_specs):
+        """Creates a new Volume_type extra spec.
+
+        volume_type_id: Id of volume_type.
+        extra_specs: A dictionary of values to be used as extra_specs.
+        """
+        url = "types/%s/extra_specs" % volume_type_id
+        post_body = json.dumps({'extra_specs': extra_specs})
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume_type_extra_specs(self, volume_type_id, extra_spec_name):
+        """Deletes the Specified Volume_type extra spec."""
+        resp, body = self.delete("types/%s/extra_specs/%s" % (
+            volume_type_id, extra_spec_name))
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_type(self, volume_type_id, **kwargs):
+        """Updates volume type name, description, and/or is_public.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-v2
+        """
+        put_body = json.dumps({'volume_type': kwargs})
+        resp, body = self.put('types/%s' % volume_type_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_type_extra_specs(self, volume_type_id, extra_spec_name,
+                                       extra_specs):
+        """Update a volume_type extra spec.
+
+        volume_type_id: Id of volume_type.
+        extra_spec_name: Name of the extra spec to be updated.
+        extra_spec: A dictionary of with key as extra_spec_name and the
+                     updated value.
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-extra-specs-v2
+        """
+        url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
+        put_body = json.dumps(extra_specs)
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def add_type_access(self, volume_type_id, **kwargs):
+        """Adds volume type access for the given project.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#add-type-access-v2
+        """
+        post_body = json.dumps({'addProjectAccess': kwargs})
+        url = 'types/%s/action' % volume_type_id
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def remove_type_access(self, volume_type_id, **kwargs):
+        """Removes volume type access for the given project.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#remove-type-access-v2
+        """
+        post_body = json.dumps({'removeProjectAccess': kwargs})
+        url = 'types/%s/action' % volume_type_id
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_type_access(self, volume_type_id):
+        """Print access information about the given volume type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-type-access-v2
+        """
+        url = 'types/%s/os-volume-type-access' % volume_type_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
new file mode 100644
index 0000000..8b8e249
--- /dev/null
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -0,0 +1,353 @@
+# 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 debtcollector import removals
+from oslo_serialization import jsonutils as json
+import six
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class VolumesClient(rest_client.RestClient):
+    """Client class to send CRUD Volume V2 API requests"""
+    api_version = "v2"
+
+    def _prepare_params(self, params):
+        """Prepares params for use in get or _ext_get methods.
+
+        If params is a string it will be left as it is, but if it's not it will
+        be urlencoded.
+        """
+        if isinstance(params, six.string_types):
+            return params
+        return urllib.urlencode(params)
+
+    def list_volumes(self, detail=False, params=None):
+        """List all the volumes created.
+
+        Params can be a string (must be urlencoded) or a dictionary.
+        """
+        url = 'volumes'
+        if detail:
+            url += '/detail'
+        if params:
+            url += '?%s' % self._prepare_params(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume(self, volume_id):
+        """Returns the details of a single volume."""
+        url = "volumes/%s" % volume_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_volume(self, **kwargs):
+        """Creates a new Volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-v2
+        """
+        post_body = json.dumps({'volume': kwargs})
+        resp, body = self.post('volumes', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume(self, volume_id, **kwargs):
+        """Updates the Specified Volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-v2
+        """
+        put_body = json.dumps({'volume': kwargs})
+        resp, body = self.put('volumes/%s' % volume_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume(self, volume_id):
+        """Deletes the Specified Volume."""
+        resp, body = self.delete("volumes/%s" % volume_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def upload_volume(self, volume_id, **kwargs):
+        """Uploads a volume in Glance."""
+        post_body = json.dumps({'os-volume_upload_image': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def attach_volume(self, volume_id, **kwargs):
+        """Attaches a volume to a given instance on a given mountpoint.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#attach-volume-v2
+        """
+        post_body = json.dumps({'os-attach': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_bootable_volume(self, volume_id, **kwargs):
+        """set a bootable flag for a volume - true or false."""
+        post_body = json.dumps({'os-set_bootable': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def detach_volume(self, volume_id):
+        """Detaches a volume from an instance."""
+        post_body = json.dumps({'os-detach': {}})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def reserve_volume(self, volume_id):
+        """Reserves a volume."""
+        post_body = json.dumps({'os-reserve': {}})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def unreserve_volume(self, volume_id):
+        """Restore a reserved volume ."""
+        post_body = json.dumps({'os-unreserve': {}})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_volume(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'volume'
+
+    def extend_volume(self, volume_id, **kwargs):
+        """Extend a volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#extend-volume-v2
+        """
+        post_body = json.dumps({'os-extend': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def reset_volume_status(self, volume_id, **kwargs):
+        """Reset the Specified Volume's Status.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#reset-volume-status-v2
+        """
+        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 rest_client.ResponseBody(resp, body)
+
+    def create_volume_transfer(self, **kwargs):
+        """Create a volume transfer.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-transfer-v2
+        """
+        post_body = json.dumps({'transfer': kwargs})
+        resp, body = self.post('os-volume-transfer', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_transfer(self, transfer_id):
+        """Returns the details of a volume transfer."""
+        url = "os-volume-transfer/%s" % transfer_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_volume_transfers(self, **params):
+        """List all the volume transfers created.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers-v2
+        """
+        url = 'os-volume-transfer'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        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" % transfer_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def accept_volume_transfer(self, transfer_id, **kwargs):
+        """Accept a volume transfer.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#accept-volume-transfer-v2
+        """
+        url = 'os-volume-transfer/%s/accept' % transfer_id
+        post_body = json.dumps({'accept': kwargs})
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_readonly(self, volume_id, **kwargs):
+        """Update the Specified Volume readonly."""
+        post_body = json.dumps({'os-update_readonly_flag': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        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 rest_client.ResponseBody(resp, body)
+
+    def create_volume_metadata(self, volume_id, metadata):
+        """Create metadata for the volume."""
+        put_body = json.dumps({'metadata': metadata})
+        url = "volumes/%s/metadata" % volume_id
+        resp, body = self.post(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_metadata(self, volume_id):
+        """Get metadata of the volume."""
+        url = "volumes/%s/metadata" % volume_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_metadata(self, volume_id, metadata):
+        """Update metadata for the volume."""
+        put_body = json.dumps({'metadata': metadata})
+        url = "volumes/%s/metadata" % volume_id
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_metadata_item(self, volume_id, id, meta_item):
+        """Update metadata item for the volume."""
+        put_body = json.dumps({'meta': meta_item})
+        url = "volumes/%s/metadata/%s" % (volume_id, id)
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        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" % (volume_id, id)
+        resp, body = self.delete(url)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def retype_volume(self, volume_id, **kwargs):
+        """Updates volume with new volume type."""
+        post_body = json.dumps({'os-retype': kwargs})
+        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+        self.expected_success(202, resp.status)
+
+    def update_volume_image_metadata(self, volume_id, **kwargs):
+        """Update image metadata for the volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-image-metadata-v2
+        """
+        post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
+        url = "volumes/%s/action" % (volume_id)
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume_image_metadata(self, volume_id, key_name):
+        """Delete image metadata item for the volume."""
+        post_body = json.dumps({'os-unset_image_metadata': {'key': key_name}})
+        url = "volumes/%s/action" % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    @removals.remove(message="use show_pools from tempest.lib.services."
+                             "volume.v2.scheduler_stats_client")
+    def show_pools(self, detail=False):
+        # List all the volumes pools (hosts)
+        url = 'scheduler-stats/get_pools'
+        if detail:
+            url += '?detail=True'
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    @removals.remove(message="use show_backend_capabilities from tempest.lib."
+                             "services.volume.v2.capabilities_client")
+    def show_backend_capabilities(self, host):
+        """Shows capabilities for a storage back end.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#show_backend_capabilities-v2
+        """
+        url = 'capabilities/%s' % host
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/__init__.py b/tempest/lib/services/volume/v3/__init__.py
new file mode 100644
index 0000000..a4600a8
--- /dev/null
+++ b/tempest/lib/services/volume/v3/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (c) 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.
+
+from tempest.lib.services.volume.v3.base_client import BaseClient
+from tempest.lib.services.volume.v3.messages_client import MessagesClient
+
+__all__ = ['MessagesClient', 'BaseClient']
diff --git a/tempest/lib/services/volume/v3/base_client.py b/tempest/lib/services/volume/v3/base_client.py
new file mode 100644
index 0000000..958212a
--- /dev/null
+++ b/tempest/lib/services/volume/v3/base_client.py
@@ -0,0 +1,46 @@
+# Copyright 2016 Andrew Kerr
+# 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 api_version_utils
+from tempest.lib.common import rest_client
+
+VOLUME_MICROVERSION = None
+
+
+class BaseClient(rest_client.RestClient):
+    """Base class to handle Cinder v3 client microversion support."""
+    api_version = 'v3'
+    api_microversion_header_name = 'Openstack-Api-Version'
+
+    def get_headers(self, accept_type=None, send_type=None):
+        headers = super(BaseClient, self).get_headers(
+            accept_type=accept_type, send_type=send_type)
+        if VOLUME_MICROVERSION:
+            headers[self.api_microversion_header_name] = ('volume %s' %
+                                                          VOLUME_MICROVERSION)
+        return headers
+
+    def request(self, method, url, extra_headers=False, headers=None,
+                body=None, chunked=False):
+
+        resp, resp_body = super(BaseClient, self).request(
+            method, url, extra_headers, headers, body, chunked)
+        if (VOLUME_MICROVERSION and
+            VOLUME_MICROVERSION != api_version_utils.LATEST_MICROVERSION):
+            api_version_utils.assert_version_header_matches_request(
+                self.api_microversion_header_name,
+                'volume %s' % VOLUME_MICROVERSION,
+                resp)
+        return resp, resp_body
diff --git a/tempest/lib/services/volume/v3/messages_client.py b/tempest/lib/services/volume/v3/messages_client.py
new file mode 100644
index 0000000..8a01864
--- /dev/null
+++ b/tempest/lib/services/volume/v3/messages_client.py
@@ -0,0 +1,59 @@
+# Copyright 2016 Andrew Kerr
+# 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.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v3 import base_client
+
+
+class MessagesClient(base_client.BaseClient):
+    """Client class to send user messages API requests."""
+
+    def show_message(self, message_id):
+        """Show details for a single message."""
+        url = 'messages/%s' % str(message_id)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_messages(self):
+        """List all messages."""
+        url = 'messages'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_message(self, message_id):
+        """Delete a single message."""
+        url = 'messages/%s' % str(message_id)
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_message(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'message'
diff --git a/tempest/manager.py b/tempest/manager.py
index 6e86c78..e3174d4 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -13,66 +13,50 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest_lib import auth
+from oslo_log import log as logging
 
-from tempest.common import cred_provider
+from tempest import clients as tempest_clients
 from tempest import config
-from tempest import exceptions
+from tempest.lib.services import clients
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
-class Manager(object):
-    """Base manager class
+class Manager(clients.ServiceClients):
+    """Service client manager class for backward compatibility
 
-    Manager objects are responsible for providing a configuration object
-    and a client object for a test case to use in performing actions.
+    The former manager.Manager is not a stable interface in Tempest,
+    nonetheless it is consumed by a number of plugins already. This class
+    exists to provide some grace time for the move to tempest.lib.
     """
 
-    def __init__(self, credentials):
-        """Initialization of base manager class
-
-        Credentials to be used within the various client classes managed by the
-        Manager object must be defined.
-
-        :param credentials: type Credentials or TestResources
-        """
-        self.credentials = credentials
-        # Check if passed or default credentials are valid
-        if not self.credentials.is_valid():
-            raise exceptions.InvalidCredentials()
-        self.auth_version = CONF.identity.auth_version
-        # Tenant isolation creates TestResources, but
-        # PreProvisionedCredentialProvider and some tests create Credentials
-        if isinstance(credentials, cred_provider.TestResources):
-            creds = self.credentials.credentials
-        else:
-            creds = self.credentials
-        # Creates an auth provider for the credentials
-        self.auth_provider = get_auth_provider(creds, pre_auth=True)
+    def __init__(self, credentials, scope='project'):
+        msg = ("tempest.manager.Manager is not a stable interface and as such "
+               "it should not imported directly. It will be removed as "
+               "soon as the client manager becomes available in tempest.lib.")
+        LOG.warning(msg)
+        dscv = CONF.identity.disable_ssl_certificate_validation
+        _, uri = tempest_clients.get_auth_provider_class(credentials)
+        super(Manager, self).__init__(
+            credentials=credentials, scope=scope,
+            identity_uri=uri,
+            disable_ssl_certificate_validation=dscv,
+            ca_certs=CONF.identity.ca_certificates_file,
+            trace_requests=CONF.debug.trace_requests)
 
 
-def get_auth_provider_class(credentials):
-    if isinstance(credentials, auth.KeystoneV3Credentials):
-        return auth.KeystoneV3AuthProvider, CONF.identity.uri_v3
-    else:
-        return auth.KeystoneV2AuthProvider, CONF.identity.uri
+def get_auth_provider(credentials, pre_auth=False, scope='project'):
+    """Shim to get_auth_provider in clients.py
 
-
-def get_auth_provider(credentials, pre_auth=False):
-    default_params = {
-        'disable_ssl_certificate_validation':
-            CONF.identity.disable_ssl_certificate_validation,
-        'ca_certs': CONF.identity.ca_certificates_file,
-        'trace_requests': CONF.debug.trace_requests
-    }
-    if credentials is None:
-        raise exceptions.InvalidCredentials(
-            'Credentials must be specified')
-    auth_provider_class, auth_url = get_auth_provider_class(
-        credentials)
-    _auth_provider = auth_provider_class(credentials, auth_url,
-                                         **default_params)
-    if pre_auth:
-        _auth_provider.set_auth()
-    return _auth_provider
+    get_auth_provider used to be hosted in this module, but it has been
+    moved to clients.py now as a more permanent location.
+    This module will be removed eventually, and this shim is only
+    maintained for the benefit of plugins already consuming this interface.
+    """
+    msg = ("tempest.manager.get_auth_provider is not a stable interface and "
+           "as such it should not imported directly. It will be removed as "
+           "the client manager becomes available in tempest.lib.")
+    LOG.warning(msg)
+    return tempest_clients.get_auth_provider(credentials=credentials,
+                                             pre_auth=pre_auth, scope=scope)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index a996ffe..8c930c3 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -19,17 +19,18 @@
 import netaddr
 from oslo_log import log
 from oslo_serialization import jsonutils as json
-import six
-from tempest_lib.common.utils import misc as misc_utils
-from tempest_lib import exceptions as lib_exc
+from oslo_utils import netutils
 
 from tempest.common import compute
+from tempest.common import image as common_image
 from tempest.common.utils import data_utils
 from tempest.common.utils.linux import remote_client
+from tempest.common.utils import net_utils
 from tempest.common import waiters
 from tempest import config
 from tempest import exceptions
-from tempest.services.network import resources as net_resources
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions as lib_exc
 import tempest.test
 
 CONF = config.CONF
@@ -50,8 +51,15 @@
         cls.compute_floating_ips_client = (
             cls.manager.compute_floating_ips_client)
         if CONF.service_available.glance:
-            # Glance image client v1
-            cls.image_client = cls.manager.image_client
+            # Check if glance v1 is available to determine which client to use.
+            if CONF.image_feature_enabled.api_v1:
+                cls.image_client = cls.manager.image_client
+            elif CONF.image_feature_enabled.api_v2:
+                cls.image_client = cls.manager.image_client_v2
+            else:
+                raise lib_exc.InvalidConfiguration(
+                    'Either api_v1 or api_v2 must be True in '
+                    '[image-feature-enabled].')
         # Compute image client
         cls.compute_images_client = cls.manager.compute_images_client
         cls.keypairs_client = cls.manager.keypairs_client
@@ -63,96 +71,42 @@
         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
         cls.security_group_rules_client = (
             cls.manager.security_group_rules_client)
-        # Heat client
-        cls.orchestration_client = cls.manager.orchestration_client
 
-        if CONF.volume_feature_enabled.api_v1:
-            cls.volumes_client = cls.manager.volumes_client
-            cls.snapshots_client = cls.manager.snapshots_client
-        else:
+        if CONF.volume_feature_enabled.api_v2:
             cls.volumes_client = cls.manager.volumes_v2_client
             cls.snapshots_client = cls.manager.snapshots_v2_client
-
-    # ## Methods to handle sync and async deletes
-
-    def setUp(self):
-        super(ScenarioTest, self).setUp()
-        self.cleanup_waits = []
-        # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
-        # because scenario tests in the same test class should not share
-        # resources. If resources were shared between test cases then it
-        # should be a single scenario test instead of multiples.
-
-        # NOTE(yfried): this list is cleaned at the end of test_methods and
-        # not at the end of the class
-        self.addCleanup(self._wait_for_cleanups)
-
-    def delete_wrapper(self, delete_thing, *args, **kwargs):
-        """Ignores NotFound exceptions for delete operations.
-
-        @param delete_thing: delete method of a resource. method will be
-            executed as delete_thing(*args, **kwargs)
-
-        """
-        try:
-            # Tempest clients return dicts, so there is no common delete
-            # method available. Using a callable instead
-            delete_thing(*args, **kwargs)
-        except lib_exc.NotFound:
-            # If the resource is already missing, mission accomplished.
-            pass
-
-    def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
-                             cleanup_callable, cleanup_args=None,
-                             cleanup_kwargs=None, waiter_client=None):
-        """Adds wait for async resource deletion at the end of cleanups
-
-        @param waiter_callable: callable to wait for the resource to delete
-            with the following waiter_client if specified.
-        @param thing_id: the id of the resource to be cleaned-up
-        @param thing_id_param: the name of the id param in the waiter
-        @param cleanup_callable: method to load pass to self.addCleanup with
-            the following *cleanup_args, **cleanup_kwargs.
-            usually a delete method.
-        """
-        if cleanup_args is None:
-            cleanup_args = []
-        if cleanup_kwargs is None:
-            cleanup_kwargs = {}
-        self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
-        wait_dict = {
-            'waiter_callable': waiter_callable,
-            thing_id_param: thing_id
-        }
-        if waiter_client:
-            wait_dict['client'] = waiter_client
-        self.cleanup_waits.append(wait_dict)
-
-    def _wait_for_cleanups(self):
-        # To handle async delete actions, a list of waits is added
-        # which will be iterated over as the last step of clearing the
-        # cleanup queue. That way all the delete calls are made up front
-        # and the tests won't succeed unless the deletes are eventually
-        # successful. This is the same basic approach used in the api tests to
-        # limit cleanup execution time except here it is multi-resource,
-        # because of the nature of the scenario tests.
-        for wait in self.cleanup_waits:
-            waiter_callable = wait.pop('waiter_callable')
-            waiter_callable(**wait)
+        else:
+            cls.volumes_client = cls.manager.volumes_client
+            cls.snapshots_client = cls.manager.snapshots_client
 
     # ## Test functions library
     #
     # The create_[resource] functions only return body and discard the
     # resp part which is not used in scenario tests
 
+    def _create_port(self, network_id, client=None, namestart='port-quotatest',
+                     **kwargs):
+        if not client:
+            client = self.ports_client
+        name = data_utils.rand_name(namestart)
+        result = client.create_port(
+            name=name,
+            network_id=network_id,
+            **kwargs)
+        self.assertIsNotNone(result, 'Unable to allocate port')
+        port = result['port']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        client.delete_port, port['id'])
+        return port
+
     def create_keypair(self, client=None):
         if not client:
             client = self.keypairs_client
@@ -164,7 +118,7 @@
 
     def create_server(self, name=None, image_id=None, flavor=None,
                       validatable=False, wait_until=None,
-                      wait_on_delete=True, clients=None, **kwargs):
+                      clients=None, **kwargs):
         """Wrapper utility that returns a test server.
 
         This wrapper utility calls the common create test server and
@@ -186,20 +140,23 @@
         if clients is None:
             clients = self.manager
 
+        if name is None:
+            name = data_utils.rand_name(self.__class__.__name__ + "-server")
+
         vnic_type = CONF.network.port_vnic_type
 
         # If vnic_type is configured create port for
         # every network
         if vnic_type:
             ports = []
-            networks = []
+
             create_port_body = {'binding:vnic_type': vnic_type,
                                 'namestart': 'port-smoke'}
             if kwargs:
                 # Convert security group names to security group ids
                 # to pass to create_port
                 if 'security_groups' in kwargs:
-                    security_groups =\
+                    security_groups = \
                         clients.security_groups_client.list_security_groups(
                         ).get('security_groups')
                     sec_dict = dict([(s['name'], s['id'])
@@ -213,25 +170,30 @@
                     if security_groups_ids:
                         create_port_body[
                             'security_groups'] = security_groups_ids
-                networks = kwargs.pop('networks')
+                networks = kwargs.pop('networks', [])
+            else:
+                networks = []
 
             # If there are no networks passed to us we look up
-            # for the tenant'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
+            # for the project's private networks and create a port.
+            # The same behaviour as we would expect when passing
+            # the call to the clients with no networks
             if not networks:
                 networks = clients.networks_client.list_networks(
-                    filters={'router:external': False})
-                self.assertEqual(1, len(networks),
-                                 "There is more than one"
-                                 " network for the tenant")
+                    **{'router:external': False, 'fields': 'id'})['networks']
+
+            # It's net['uuid'] if networks come from kwargs
+            # and net['id'] if they come from
+            # clients.networks_client.list_networks
             for net in networks:
-                net_id = net['uuid']
-                port = self._create_port(network_id=net_id,
-                                         client=clients.ports_client,
-                                         **create_port_body)
-                ports.append({'port': port.id})
+                net_id = net.get('uuid', net.get('id'))
+                if 'port' not in net:
+                    port = self._create_port(network_id=net_id,
+                                             client=clients.ports_client,
+                                             **create_port_body)
+                    ports.append({'port': port['id']})
+                else:
+                    ports.append({'port': net['port']})
             if ports:
                 kwargs['networks'] = ports
             self.ports = ports
@@ -245,56 +207,68 @@
             name=name, flavor=flavor,
             image_id=image_id, **kwargs)
 
-        # TODO(jlanoux) Move wait_on_delete in compute.py
-        if wait_on_delete:
-            self.addCleanup(waiters.wait_for_server_termination,
-                            clients.servers_client,
-                            body['id'])
-
-        self.addCleanup_with_wait(
-            waiter_callable=waiters.wait_for_server_termination,
-            thing_id=body['id'], thing_id_param='server_id',
-            cleanup_callable=self.delete_wrapper,
-            cleanup_args=[clients.servers_client.delete_server, body['id']],
-            waiter_client=clients.servers_client)
+        self.addCleanup(waiters.wait_for_server_termination,
+                        clients.servers_client, body['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        clients.servers_client.delete_server, body['id'])
         server = clients.servers_client.show_server(body['id'])['server']
         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 size is None:
+            size = CONF.volume.volume_size
+        if imageRef:
+            image = self.compute_images_client.show_image(imageRef)['image']
+            min_disk = image.get('minDisk')
+            size = max(size, min_disk)
         if name is None:
-            name = data_utils.rand_name(self.__class__.__name__)
+            name = data_utils.rand_name(self.__class__.__name__ + "-volume")
         kwargs = {'display_name': name,
                   'snapshot_id': snapshot_id,
                   'imageRef': imageRef,
-                  'volume_type': volume_type}
-        if size is not None:
-            kwargs.update({'size': size})
+                  'volume_type': volume_type,
+                  '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(test_utils.call_and_ignore_notfound_exc,
+                        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']
         return volume
 
+    def create_volume_type(self, client=None, name=None, backend_name=None):
+        if not client:
+            client = self.admin_volume_types_client
+        if not name:
+            class_name = self.__class__.__name__
+            name = data_utils.rand_name(class_name + '-volume-type')
+        randomized_name = data_utils.rand_name('scenario-type-' + name)
+
+        LOG.debug("Creating a volume type: %s on backend %s",
+                  randomized_name, backend_name)
+        extra_specs = {}
+        if backend_name:
+            extra_specs = {"volume_backend_name": backend_name}
+
+        body = client.create_volume_type(name=randomized_name,
+                                         extra_specs=extra_specs)
+        volume_type = body['volume_type']
+        self.assertIn('id', volume_type)
+        self.addCleanup(client.delete_volume_type, volume_type['id'])
+        return volume_type
+
     def _create_loginable_secgroup_rule(self, secgroup_id=None):
         _client = self.compute_security_groups_client
         _client_rules = self.compute_security_group_rules_client
@@ -340,7 +314,7 @@
         self.assertEqual(secgroup['name'], sg_name)
         self.assertEqual(secgroup['description'], sg_desc)
         self.addCleanup(
-            self.delete_wrapper,
+            test_utils.call_and_ignore_notfound_exc,
             self.compute_security_groups_client.delete_security_group,
             secgroup['id'])
 
@@ -379,7 +353,7 @@
             message = ('Initializing SSH connection to %(ip)s failed. '
                        'Error: %(error)s' % {'ip': ip_address,
                                              'error': e})
-            caller = misc_utils.find_test_caller()
+            caller = test_utils.find_test_caller()
             if caller:
                 message = '(%s) %s' % (caller, message)
             LOG.exception(message)
@@ -393,19 +367,28 @@
         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,
             'disk_format': disk_format or fmt,
-            'is_public': 'False',
         }
-        params['properties'] = properties
-        image = self.image_client.create_image(**params)['image']
+        if CONF.image_feature_enabled.api_v1:
+            params['is_public'] = 'False'
+            params['properties'] = properties
+            params = {'headers': common_image.image_meta_to_headers(**params)}
+        else:
+            params['visibility'] = 'private'
+            # Additional properties are flattened out in the v2 API.
+            params.update(properties)
+        body = self.image_client.create_image(**params)
+        image = body['image'] if 'image' in body else body
         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:
+            if CONF.image_feature_enabled.api_v1:
+                self.image_client.update_image(image['id'], data=image_file)
+            else:
+                self.image_client.store_image_file(image['id'], image_file)
         return image['id']
 
     def glance_image_create(self):
@@ -416,10 +399,10 @@
         img_container_format = CONF.scenario.img_container_format
         img_disk_format = CONF.scenario.img_disk_format
         img_properties = CONF.scenario.img_properties
-        LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
-                  "properties: %s, ami: %s, ari: %s, aki: %s" %
-                  (img_path, img_container_format, img_disk_format,
-                   img_properties, ami_img_path, ari_img_path, aki_img_path))
+        LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
+                  "properties: %s, ami: %s, ari: %s, aki: %s",
+                  img_path, img_container_format, img_disk_format,
+                  img_properties, ami_img_path, ari_img_path, aki_img_path)
         try:
             image = self._image_create('scenario-img',
                                        img_container_format,
@@ -434,7 +417,7 @@
             image = self._image_create('scenario-ami', 'ami',
                                        path=ami_img_path,
                                        properties=properties)
-        LOG.debug("image:%s" % image)
+        LOG.debug("image:%s", image)
 
         return image
 
@@ -446,10 +429,14 @@
             servers = self.servers_client.list_servers()
             servers = servers['servers']
         for server in servers:
-            console_output = self.servers_client.get_console_output(
-                server['id'])['output']
-            LOG.debug('Console output for %s\nbody=\n%s',
-                      server['id'], console_output)
+            try:
+                console_output = self.servers_client.get_console_output(
+                    server['id'])['output']
+                LOG.debug('Console output for %s\nbody=\n%s',
+                          server['id'], console_output)
+            except lib_exc.NotFound:
+                LOG.debug("Server %s disappeared(deleted) while looking "
+                          "for the console log", server['id'])
 
     def _log_net_info(self, exc):
         # network debug is called as part of ssh init
@@ -462,19 +449,28 @@
         # Compute client
         _images_client = self.compute_images_client
         if name is None:
-            name = data_utils.rand_name('scenario-snapshot')
+            name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
         LOG.debug("Creating a snapshot image for server: %s", server['name'])
         image = _images_client.create_image(server['id'], name=name)
         image_id = image.response['location'].split('images/')[1]
-        _image_client.wait_for_image_status(image_id, 'active')
-        self.addCleanup_with_wait(
-            waiter_callable=_image_client.wait_for_resource_deletion,
-            thing_id=image_id, thing_id_param='id',
-            cleanup_callable=self.delete_wrapper,
-            cleanup_args=[_image_client.delete_image, image_id])
-        snapshot_image = _image_client.get_image_meta(image_id)
+        waiters.wait_for_image_status(_image_client, image_id, 'active')
 
-        bdm = snapshot_image.get('properties', {}).get('block_device_mapping')
+        self.addCleanup(_image_client.wait_for_resource_deletion,
+                        image_id)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        _image_client.delete_image, image_id)
+
+        if CONF.image_feature_enabled.api_v1:
+            # In glance v1 the additional properties are stored in the headers.
+            resp = _image_client.check_image(image_id)
+            snapshot_image = common_image.get_image_meta_from_headers(resp)
+            image_props = snapshot_image.get('properties', {})
+        else:
+            # In glance v2 the additional properties are flattened.
+            snapshot_image = _image_client.show_image(image_id)
+            image_props = snapshot_image
+
+        bdm = image_props.get('block_device_mapping')
         if bdm:
             bdm = json.loads(bdm)
             if bdm and 'snapshot_id' in bdm[0]:
@@ -482,12 +478,11 @@
                 self.addCleanup(
                     self.snapshots_client.wait_for_resource_deletion,
                     snapshot_id)
-                self.addCleanup(
-                    self.delete_wrapper, self.snapshots_client.delete_snapshot,
-                    snapshot_id)
-                self.snapshots_client.wait_for_snapshot_status(snapshot_id,
-                                                               'available')
-
+                self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                                self.snapshots_client.delete_snapshot,
+                                snapshot_id)
+                waiters.wait_for_snapshot_status(self.snapshots_client,
+                                                 snapshot_id, 'available')
         image_name = snapshot_image['name']
         self.assertEqual(name, image_name)
         LOG.debug("Created snapshot image %s for server %s",
@@ -499,14 +494,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'])
@@ -530,9 +527,18 @@
                                            server_id, 'ACTIVE')
 
     def ping_ip_address(self, ip_address, should_succeed=True,
-                        ping_timeout=None):
+                        ping_timeout=None, mtu=None):
         timeout = ping_timeout or CONF.validation.ping_timeout
-        cmd = ['ping', '-c1', '-w1', ip_address]
+        cmd = ['ping', '-c1', '-w1']
+
+        if mtu:
+            cmd += [
+                # don't fragment
+                '-M', 'do',
+                # ping receives just the size of ICMP payload
+                '-s', str(net_utils.get_ping_payload_size(mtu, 4))
+            ]
+        cmd.append(ip_address)
 
         def ping():
             proc = subprocess.Popen(cmd,
@@ -542,16 +548,16 @@
 
             return (proc.returncode == 0) == should_succeed
 
-        caller = misc_utils.find_test_caller()
+        caller = test_utils.find_test_caller()
         LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
-                  ' expected result is %(should_succeed)s' % {
+                  ' expected result is %(should_succeed)s', {
                       'caller': caller, 'ip': ip_address, 'timeout': timeout,
                       'should_succeed':
                       'reachable' if should_succeed else 'unreachable'
                   })
-        result = tempest.test.call_until_true(ping, timeout, 1)
+        result = test_utils.call_until_true(ping, timeout, 1)
         LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
-                  'ping result is %(result)s' % {
+                  'ping result is %(result)s', {
                       'caller': caller, 'ip': ip_address, 'timeout': timeout,
                       'result': 'expected' if result else 'unexpected'
                   })
@@ -560,7 +566,8 @@
     def check_vm_connectivity(self, ip_address,
                               username=None,
                               private_key=None,
-                              should_connect=True):
+                              should_connect=True,
+                              mtu=None):
         """Check server connectivity
 
         :param ip_address: server to test against
@@ -569,6 +576,7 @@
         :param should_connect: True/False indicates positive/negative test
             positive - attempt ping and ssh
             negative - attempt ping and fail if succeed
+        :param mtu: network MTU to use for connectivity validation
 
         :raises: AssertError if the result of the connectivity check does
             not match the value of the should_connect param
@@ -578,7 +586,8 @@
         else:
             msg = "ip address %s is reachable" % ip_address
         self.assertTrue(self.ping_ip_address(ip_address,
-                                             should_succeed=should_connect),
+                                             should_succeed=should_connect,
+                                             mtu=mtu),
                         msg=msg)
         if should_connect:
             # no need to check ssh for negative connectivity
@@ -586,16 +595,17 @@
 
     def check_public_network_connectivity(self, ip_address, username,
                                           private_key, should_connect=True,
-                                          msg=None, servers=None):
+                                          msg=None, servers=None, mtu=None):
         # The target login is assumed to have been configured for
         # key-based authentication by cloud-init.
-        LOG.debug('checking network connections to IP %s with user: %s' %
-                  (ip_address, username))
+        LOG.debug('checking network connections to IP %s with user: %s',
+                  ip_address, username)
         try:
             self.check_vm_connectivity(ip_address,
                                        username,
                                        private_key,
-                                       should_connect=should_connect)
+                                       should_connect=should_connect,
+                                       mtu=mtu)
         except Exception:
             ex_msg = 'Public network connectivity check failed'
             if msg:
@@ -607,9 +617,11 @@
     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,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.compute_floating_ips_client.delete_floating_ip,
                         floating_ip['id'])
         self.compute_floating_ips_client.associate_floating_ip_to_server(
@@ -651,17 +663,28 @@
         """
         if CONF.validation.connect_method == 'floating':
             # The tests calling this method don't have a floating IP
-            # and can't make use of the validattion resources. So the
+            # and can't make use of the validation resources. So the
             # method is creating the floating IP there.
             return self.create_floating_ip(server)['ip']
         elif CONF.validation.connect_method == 'fixed':
-            addresses = server['addresses'][CONF.validation.network_for_ssh]
+            # Determine the network name to look for based on config or creds
+            # provider network resources.
+            if CONF.validation.network_for_ssh:
+                addresses = server['addresses'][
+                    CONF.validation.network_for_ssh]
+            else:
+                creds_provider = self._get_credentials_provider()
+                net_creds = creds_provider.get_primary_creds()
+                network = getattr(net_creds, 'network', None)
+                addresses = (server['addresses'][network['name']]
+                             if network else [])
             for address in addresses:
-                if address['version'] == CONF.validation.ip_version_for_ssh:
+                if (address['version'] == CONF.validation.ip_version_for_ssh
+                        and address['OS-EXT-IPS:type'] == 'fixed'):
                     return address['addr']
-            raise exceptions.ServerUnreachable()
+            raise exceptions.ServerUnreachable(server_id=server['id'])
         else:
-            raise exceptions.InvalidConfiguration()
+            raise lib_exc.InvalidConfiguration()
 
 
 class NetworkScenarioTest(ScenarioTest):
@@ -684,25 +707,29 @@
         if not CONF.service_available.neutron:
             raise cls.skipException('Neutron not available')
 
-    @classmethod
-    def resource_setup(cls):
-        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-',
+                        port_security_enabled=True):
         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'])
-        self.assertEqual(network.name, name)
-        self.addCleanup(self.delete_wrapper, network.delete)
+        network_kwargs = dict(name=name, tenant_id=tenant_id)
+        # Neutron disables port security by default so we have to check the
+        # config before trying to create the network with port_security_enabled
+        if CONF.network_feature_enabled.port_security:
+            network_kwargs['port_security_enabled'] = port_security_enabled
+        result = networks_client.create_network(**network_kwargs)
+        network = result['network']
+
+        self.assertEqual(network['name'], name)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.networks_client.delete_network,
+                        network['id'])
         return network
 
     def _list_networks(self, *args, **kwargs):
@@ -719,7 +746,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 +762,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 +787,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
@@ -771,13 +799,13 @@
         # blocks until an unallocated block is found.
         for subnet_cidr in tenant_cidr.subnet(num_bits):
             str_cidr = str(subnet_cidr)
-            if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
+            if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
                 continue
 
             subnet = dict(
                 name=data_utils.rand_name(namestart),
-                network_id=network.id,
-                tenant_id=network.tenant_id,
+                network_id=network['id'],
+                tenant_id=network['tenant_id'],
                 cidr=str_cidr,
                 ip_version=ip_version,
                 **kwargs
@@ -790,38 +818,32 @@
                 if not is_overlapping_cidr:
                     raise
         self.assertIsNotNone(result, 'Unable to allocate tenant network')
-        subnet = net_resources.DeletableSubnet(
-            network_client=client, subnets_client=subnets_client,
-            **result['subnet'])
-        self.assertEqual(subnet.cidr, str_cidr)
-        self.addCleanup(self.delete_wrapper, subnet.delete)
-        return subnet
 
-    def _create_port(self, network_id, client=None, namestart='port-quotatest',
-                     **kwargs):
-        if not client:
-            client = self.ports_client
-        name = data_utils.rand_name(namestart)
-        result = client.create_port(
-            name=name,
-            network_id=network_id,
-            **kwargs)
-        self.assertIsNotNone(result, 'Unable to allocate port')
-        port = net_resources.DeletablePort(ports_client=client,
-                                           **result['port'])
-        self.addCleanup(self.delete_wrapper, port.delete)
-        return port
+        subnet = result['subnet']
+        self.assertEqual(subnet['cidr'], str_cidr)
+
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        subnets_client.delete_subnet, subnet['id'])
+
+        return subnet
 
     def _get_server_port_id_and_ip4(self, server, ip_addr=None):
         ports = self._list_ports(device_id=server['id'], fixed_ip=ip_addr)
-        # A port can have more then one IP address in some cases.
+        # A port can have more than one IP address in some cases.
         # If the network is dual-stack (IPv4 + IPv6), this port is associated
         # with 2 subnets
+        p_status = ['ACTIVE']
+        # NOTE(vsaienko) With Ironic, instances live on separate hardware
+        # servers. Neutron does not bind ports for Ironic instances, as a
+        # result the port remains in the DOWN state.
+        # TODO(vsaienko) remove once bug: #1599836 is resolved.
+        if getattr(CONF.service_available, 'ironic', False):
+            p_status.append('DOWN')
         port_map = [(p["id"], fxip["ip_address"])
                     for p in ports
                     for fxip in p["fixed_ips"]
-                    if netaddr.valid_ipv4(fxip["ip_address"])
-                    and p['status'] == 'ACTIVE']
+                    if netutils.is_valid_ipv4(fxip["ip_address"])
+                    and p['status'] in p_status]
         inactive = [p for p in ports if p['status'] != 'ACTIVE']
         if inactive:
             LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
@@ -838,7 +860,7 @@
         net = self._list_networks(name=network_name)
         self.assertNotEqual(len(net), 0,
                             "Unable to get network by name: %s" % network_name)
-        return net_resources.AttributeDict(net[0])
+        return net[0]
 
     def create_floating_ip(self, thing, external_network_id=None,
                            port_id=None, client=None):
@@ -857,43 +879,51 @@
             tenant_id=thing['tenant_id'],
             fixed_ip_address=ip4
         )
-        floating_ip = net_resources.DeletableFloatingIp(
-            client=client,
-            **result['floatingip'])
-        self.addCleanup(self.delete_wrapper, floating_ip.delete)
+        floating_ip = result['floatingip']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.floating_ips_client.delete_floatingip,
+                        floating_ip['id'])
         return floating_ip
 
     def _associate_floating_ip(self, floating_ip, server):
         port_id, _ = self._get_server_port_id_and_ip4(server)
-        floating_ip.update(port_id=port_id)
-        self.assertEqual(port_id, floating_ip.port_id)
+        kwargs = dict(port_id=port_id)
+        floating_ip = self.floating_ips_client.update_floatingip(
+            floating_ip['id'], **kwargs)['floatingip']
+        self.assertEqual(port_id, floating_ip['port_id'])
         return floating_ip
 
     def _disassociate_floating_ip(self, floating_ip):
-        """:param floating_ip: type DeletableFloatingIp"""
-        floating_ip.update(port_id=None)
-        self.assertIsNone(floating_ip.port_id)
+        """:param floating_ip: floating_ips_client.create_floatingip"""
+        kwargs = dict(port_id=None)
+        floating_ip = self.floating_ips_client.update_floatingip(
+            floating_ip['id'], **kwargs)['floatingip']
+        self.assertIsNone(floating_ip['port_id'])
         return floating_ip
 
     def check_floating_ip_status(self, floating_ip, status):
         """Verifies floatingip reaches the given status
 
-        :param floating_ip: net_resources.DeletableFloatingIp floating IP to
-        to check status
+        :param dict floating_ip: floating IP dict to check status
         :param status: target status
         :raises: AssertionError if status doesn't match
         """
-        def refresh():
-            floating_ip.refresh()
-            return status == floating_ip.status
+        floatingip_id = floating_ip['id']
 
-        tempest.test.call_until_true(refresh,
-                                     CONF.network.build_timeout,
-                                     CONF.network.build_interval)
-        self.assertEqual(status, floating_ip.status,
+        def refresh():
+            result = (self.floating_ips_client.
+                      show_floatingip(floatingip_id)['floatingip'])
+            return status == result['status']
+
+        test_utils.call_until_true(refresh,
+                                   CONF.network.build_timeout,
+                                   CONF.network.build_interval)
+        floating_ip = self.floating_ips_client.show_floatingip(
+            floatingip_id)['floatingip']
+        self.assertEqual(status, floating_ip['status'],
                          message="FloatingIP: {fp} is at status: {cst}. "
                                  "failed  to reach status: {st}"
-                         .format(fp=floating_ip, cst=floating_ip.status,
+                         .format(fp=floating_ip, cst=floating_ip['status'],
                                  st=status))
         LOG.info("FloatingIP: {fp} is at status: {st}"
                  .format(fp=floating_ip, st=status))
@@ -903,14 +933,14 @@
                                            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
         # The target login is assumed to have been configured for
         # key-based authentication by cloud-init.
         try:
-            for net_name, ip_addresses in six.iteritems(server['addresses']):
+            for net_name, ip_addresses in server['addresses'].items():
                 for ip_address in ip_addresses:
                     self.check_vm_connectivity(ip_address['addr'],
                                                username,
@@ -938,13 +968,13 @@
                 source.ping_host(dest, nic=nic)
             except lib_exc.SSHExecCommandFailed:
                 LOG.warning('Failed to ping IP: %s via a ssh connection '
-                            'from: %s.' % (dest, source.ssh_client.host))
+                            'from: %s.', dest, source.ssh_client.host)
                 return not should_succeed
             return should_succeed
 
-        return tempest.test.call_until_true(ping_remote,
-                                            CONF.validation.ping_timeout,
-                                            1)
+        return test_utils.call_until_true(ping_remote,
+                                          CONF.validation.ping_timeout,
+                                          1)
 
     def _create_security_group(self, security_group_rules_client=None,
                                tenant_id=None,
@@ -966,8 +996,8 @@
             secgroup=secgroup,
             security_groups_client=security_groups_client)
         for rule in rules:
-            self.assertEqual(tenant_id, rule.tenant_id)
-            self.assertEqual(secgroup.id, rule.security_group_id)
+            self.assertEqual(tenant_id, rule['tenant_id'])
+            self.assertEqual(secgroup['id'], rule['security_group_id'])
         return secgroup
 
     def _create_empty_security_group(self, client=None, tenant_id=None,
@@ -979,7 +1009,7 @@
          - IPv6 egress to any
 
         :param tenant_id: secgroup will be created in this tenant
-        :returns: DeletableSecurityGroup -- containing the secgroup created
+        :returns: the created security group
         """
         if client is None:
             client = self.security_groups_client
@@ -991,33 +1021,32 @@
                        description=sg_desc)
         sg_dict['tenant_id'] = tenant_id
         result = client.create_security_group(**sg_dict)
-        secgroup = net_resources.DeletableSecurityGroup(
-            client=client,
-            **result['security_group']
-        )
-        self.assertEqual(secgroup.name, sg_name)
-        self.assertEqual(tenant_id, secgroup.tenant_id)
-        self.assertEqual(secgroup.description, sg_desc)
-        self.addCleanup(self.delete_wrapper, secgroup.delete)
+
+        secgroup = result['security_group']
+        self.assertEqual(secgroup['name'], sg_name)
+        self.assertEqual(tenant_id, secgroup['tenant_id'])
+        self.assertEqual(secgroup['description'], sg_desc)
+
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        client.delete_security_group, secgroup['id'])
         return secgroup
 
     def _default_security_group(self, client=None, tenant_id=None):
         """Get default secgroup for given tenant_id.
 
-        :returns: DeletableSecurityGroup -- default secgroup for given tenant
+        :returns: default secgroup for given tenant
         """
         if client is None:
             client = self.security_groups_client
         if not tenant_id:
             tenant_id = client.tenant_id
         sgs = [
-            sg for sg in client.list_security_groups().values()[0]
+            sg for sg in list(client.list_security_groups().values())[0]
             if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
         ]
         msg = "No default security group for tenant %s." % (tenant_id)
-        self.assertTrue(len(sgs) > 0, msg)
-        return net_resources.DeletableSecurityGroup(client=client,
-                                                    **sgs[0])
+        self.assertGreater(len(sgs), 0, msg)
+        return sgs[0]
 
     def _create_security_group_rule(self, secgroup=None,
                                     sec_group_rules_client=None,
@@ -1028,7 +1057,7 @@
         Create a rule in a secgroup. if secgroup not defined will search for
         default secgroup in tenant_id.
 
-        :param secgroup: type DeletableSecurityGroup.
+        :param secgroup: the security group.
         :param tenant_id: if secgroup not passed -- the tenant in which to
             search for default secgroup
         :param kwargs: a dictionary containing rule parameters:
@@ -1050,17 +1079,15 @@
             secgroup = self._default_security_group(
                 client=security_groups_client, tenant_id=tenant_id)
 
-        ruleset = dict(security_group_id=secgroup.id,
-                       tenant_id=secgroup.tenant_id)
+        ruleset = dict(security_group_id=secgroup['id'],
+                       tenant_id=secgroup['tenant_id'])
         ruleset.update(kwargs)
 
         sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
-        sg_rule = net_resources.DeletableSecurityGroupRule(
-            client=sec_group_rules_client,
-            **sg_rule['security_group_rule']
-        )
-        self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
-        self.assertEqual(secgroup.id, sg_rule.security_group_id)
+        sg_rule = sg_rule['security_group_rule']
+
+        self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
+        self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
 
         return sg_rule
 
@@ -1069,10 +1096,11 @@
                                         security_groups_client=None):
         """Create loginable security group rule
 
-        These rules are intended to permit inbound ssh and icmp
-        traffic from all sources, so no group_id is provided.
-        Setting a group_id would only permit traffic from ports
-        belonging to the same security group.
+        This function will create:
+        1. egress and ingress tcp port 22 allow rule in order to allow ssh
+        access for ipv4.
+        2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
+        3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
         """
 
         if security_group_rules_client is None:
@@ -1113,7 +1141,7 @@
                     if msg not in ex._error_string:
                         raise ex
                 else:
-                    self.assertEqual(r_direction, sg_rule.direction)
+                    self.assertEqual(r_direction, sg_rule['direction'])
                     rules.append(sg_rule)
 
         return rules
@@ -1128,17 +1156,18 @@
         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
         network_id = CONF.network.public_network_id
         if router_id:
             body = client.show_router(router_id)
-            return net_resources.AttributeDict(**body['router'])
+            return body['router']
         elif network_id:
             router = self._create_router(client, tenant_id)
-            router.set_gateway(network_id)
+            kwargs = {'external_gateway_info': dict(network_id=network_id)}
+            router = client.update_router(router['id'], **kwargs)['router']
             return router
         else:
             raise Exception("Neither of 'public_router_id' or "
@@ -1147,37 +1176,40 @@
     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,
-                                               **result['router'])
-        self.assertEqual(router.name, name)
-        self.addCleanup(self.delete_wrapper, router.delete)
+        router = result['router']
+        self.assertEqual(router['name'], name)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        client.delete_router,
+                        router['id'])
         return router
 
     def _update_router_admin_state(self, router, admin_state_up):
-        router.update(admin_state_up=admin_state_up)
-        self.assertEqual(admin_state_up, router.admin_state_up)
+        kwargs = dict(admin_state_up=admin_state_up)
+        router = self.routers_client.update_router(
+            router['id'], **kwargs)['router']
+        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,
+                        port_security_enabled=True):
         """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
         """
-        if CONF.baremetal.driver_enabled:
+        if CONF.network.shared_physical_network:
             # NOTE(Shrews): This exception is for environments where tenant
             # credential isolation is available, but network separation is
             # not (the current baremetal case). Likely can be removed when
@@ -1185,175 +1217,39 @@
             # https://blueprints.launchpad.net/tempest/+spec/test-accounts
             if not CONF.compute.fixed_network_name:
                 m = 'fixed_network_name must be specified in config'
-                raise exceptions.InvalidConfiguration(m)
+                raise lib_exc.InvalidConfiguration(m)
             network = self._get_network_by_name(
                 CONF.compute.fixed_network_name)
             router = None
             subnet = None
         else:
             network = self._create_network(
-                client=client, networks_client=networks_client,
-                tenant_id=tenant_id)
-            router = self._get_router(client=client, tenant_id=tenant_id)
-
-            subnet_kwargs = dict(network=network, client=client,
-                                 subnets_client=subnets_client)
+                networks_client=networks_client,
+                tenant_id=tenant_id,
+                port_security_enabled=port_security_enabled)
+            router = self._get_router(client=routers_client,
+                                      tenant_id=tenant_id)
+            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
             subnet = self._create_subnet(**subnet_kwargs)
-            subnet.add_to_router(router.id)
+            if not routers_client:
+                routers_client = self.routers_client
+            router_id = router['id']
+            routers_client.add_router_interface(router_id,
+                                                subnet_id=subnet['id'])
+
+            # save a cleanup job to remove this association between
+            # router and subnet
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            routers_client.remove_router_interface, router_id,
+                            subnet_id=subnet['id'])
         return network, subnet, router
 
 
-# power/provision states as of icehouse
-class BaremetalPowerStates(object):
-    """Possible power states of an Ironic node."""
-    POWER_ON = 'power on'
-    POWER_OFF = 'power off'
-    REBOOT = 'rebooting'
-    SUSPEND = 'suspended'
-
-
-class BaremetalProvisionStates(object):
-    """Possible provision states of an Ironic node."""
-    NOSTATE = None
-    INIT = 'initializing'
-    ACTIVE = 'active'
-    BUILDING = 'building'
-    DEPLOYWAIT = 'wait call-back'
-    DEPLOYING = 'deploying'
-    DEPLOYFAIL = 'deploy failed'
-    DEPLOYDONE = 'deploy complete'
-    DELETING = 'deleting'
-    DELETED = 'deleted'
-    ERROR = 'error'
-
-
-class BaremetalScenarioTest(ScenarioTest):
-
-    credentials = ['primary', 'admin']
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaremetalScenarioTest, cls).skip_checks()
-        if (not CONF.service_available.ironic or
-           not CONF.baremetal.driver_enabled):
-            msg = 'Ironic not available or Ironic compute driver not enabled'
-            raise cls.skipException(msg)
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaremetalScenarioTest, cls).setup_clients()
-
-        cls.baremetal_client = cls.admin_manager.baremetal_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(BaremetalScenarioTest, cls).resource_setup()
-        # allow any issues obtaining the node list to raise early
-        cls.baremetal_client.list_nodes()
-
-    def _node_state_timeout(self, node_id, state_attr,
-                            target_states, timeout=10, interval=1):
-        if not isinstance(target_states, list):
-            target_states = [target_states]
-
-        def check_state():
-            node = self.get_node(node_id=node_id)
-            if node.get(state_attr) in target_states:
-                return True
-            return False
-
-        if not tempest.test.call_until_true(
-            check_state, timeout, interval):
-            msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
-                   (node_id, state_attr, target_states))
-            raise exceptions.TimeoutException(msg)
-
-    def wait_provisioning_state(self, node_id, state, timeout):
-        self._node_state_timeout(
-            node_id=node_id, state_attr='provision_state',
-            target_states=state, timeout=timeout)
-
-    def wait_power_state(self, node_id, state):
-        self._node_state_timeout(
-            node_id=node_id, state_attr='power_state',
-            target_states=state, timeout=CONF.baremetal.power_timeout)
-
-    def wait_node(self, instance_id):
-        """Waits for a node to be associated with instance_id."""
-
-        def _get_node():
-            node = None
-            try:
-                node = self.get_node(instance_id=instance_id)
-            except lib_exc.NotFound:
-                pass
-            return node is not None
-
-        if not tempest.test.call_until_true(
-            _get_node, CONF.baremetal.association_timeout, 1):
-            msg = ('Timed out waiting to get Ironic node by instance id %s'
-                   % instance_id)
-            raise exceptions.TimeoutException(msg)
-
-    def get_node(self, node_id=None, instance_id=None):
-        if node_id:
-            _, body = self.baremetal_client.show_node(node_id)
-            return body
-        elif instance_id:
-            _, body = self.baremetal_client.show_node_by_instance_uuid(
-                instance_id)
-            if body['nodes']:
-                return body['nodes'][0]
-
-    def get_ports(self, node_uuid):
-        ports = []
-        _, body = self.baremetal_client.list_node_ports(node_uuid)
-        for port in body['ports']:
-            _, p = self.baremetal_client.show_port(port['uuid'])
-            ports.append(p)
-        return ports
-
-    def add_keypair(self):
-        self.keypair = self.create_keypair()
-
-    def boot_instance(self):
-        self.instance = self.create_server(
-            key_name=self.keypair['name'])
-
-        self.wait_node(self.instance['id'])
-        self.node = self.get_node(instance_id=self.instance['id'])
-
-        self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
-
-        self.wait_provisioning_state(
-            self.node['uuid'],
-            [BaremetalProvisionStates.DEPLOYWAIT,
-             BaremetalProvisionStates.ACTIVE],
-            timeout=15)
-
-        self.wait_provisioning_state(self.node['uuid'],
-                                     BaremetalProvisionStates.ACTIVE,
-                                     timeout=CONF.baremetal.active_timeout)
-
-        waiters.wait_for_server_status(self.servers_client,
-                                       self.instance['id'], 'ACTIVE')
-        self.node = self.get_node(instance_id=self.instance['id'])
-        self.instance = (self.servers_client.show_server(self.instance['id'])
-                         ['server'])
-
-    def terminate_instance(self):
-        self.servers_client.delete_server(self.instance['id'])
-        self.wait_power_state(self.node['uuid'],
-                              BaremetalPowerStates.POWER_OFF)
-        self.wait_provisioning_state(
-            self.node['uuid'],
-            BaremetalProvisionStates.NOSTATE,
-            timeout=CONF.baremetal.unprovision_timeout)
-
-
 class EncryptionScenarioTest(ScenarioTest):
     """Base class for encryption scenario tests"""
 
@@ -1362,29 +1258,20 @@
     @classmethod
     def setup_clients(cls):
         super(EncryptionScenarioTest, cls).setup_clients()
-        if CONF.volume_feature_enabled.api_v1:
-            cls.admin_volume_types_client = cls.os_adm.volume_types_client
-        else:
+        if CONF.volume_feature_enabled.api_v2:
             cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
-
-    def create_volume_type(self, client=None, name=None):
-        if not client:
-            client = self.admin_volume_types_client
-        if not name:
-            name = 'generic'
-        randomized_name = data_utils.rand_name('scenario-type-' + name)
-        LOG.debug("Creating a volume type: %s", randomized_name)
-        body = client.create_volume_type(
-            name=randomized_name)['volume_type']
-        self.assertIn('id', body)
-        self.addCleanup(client.delete_volume_type, body['id'])
-        return body
+            cls.admin_encryption_types_client =\
+                cls.os_adm.encryption_types_v2_client
+        else:
+            cls.admin_volume_types_client = cls.os_adm.volume_types_client
+            cls.admin_encryption_types_client =\
+                cls.os_adm.encryption_types_client
 
     def create_encryption_type(self, client=None, type_id=None, provider=None,
                                key_size=None, cipher=None,
                                control_location=None):
         if not client:
-            client = self.admin_volume_types_client
+            client = self.admin_encryption_types_client
         if not type_id:
             volume_type = self.create_volume_type()
             type_id = volume_type['id']
@@ -1435,21 +1322,21 @@
         self.container_client.create_container(name)
         # look for the container to assure it is created
         self.list_and_check_container_objects(name)
-        LOG.debug('Container %s created' % (name))
-        self.addCleanup(self.delete_wrapper,
+        LOG.debug('Container %s created', name)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.container_client.delete_container,
                         name)
         return name
 
     def delete_container(self, container_name):
         self.container_client.delete_container(container_name)
-        LOG.debug('Container %s deleted' % (container_name))
+        LOG.debug('Container %s deleted', container_name)
 
     def upload_object_to_container(self, container_name, obj_name=None):
         obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
-        obj_data = data_utils.arbitrary_string()
+        obj_data = data_utils.random_bytes()
         self.object_client.create_object(container_name, obj_name, obj_data)
-        self.addCleanup(self.delete_wrapper,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.object_client.delete_object,
                         container_name,
                         obj_name)
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index cace90b..8de3561 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -36,7 +36,6 @@
         super(TestAggregatesBasicOps, cls).setup_clients()
         # Use admin client by default
         cls.manager = cls.admin_manager
-        super(TestAggregatesBasicOps, cls).resource_setup()
         cls.aggregates_client = cls.manager.aggregates_client
         cls.hosts_client = cls.manager.hosts_client
 
@@ -53,7 +52,7 @@
 
     def _get_host_name(self):
         hosts = self.hosts_client.list_hosts()['hosts']
-        self.assertTrue(len(hosts) >= 1)
+        self.assertGreaterEqual(len(hosts), 1)
         computes = [x for x in hosts if x['service'] == 'compute']
         return computes[0]['host_name']
 
@@ -74,7 +73,7 @@
         self.assertEqual(aggregate_name, aggregate['name'])
         self.assertEqual(azone, aggregate['availability_zone'])
         self.assertEqual(hosts, aggregate['hosts'])
-        for meta_key in metadata.keys():
+        for meta_key in metadata:
             self.assertIn(meta_key, aggregate['metadata'])
             self.assertEqual(metadata[meta_key],
                              aggregate['metadata'][meta_key])
diff --git a/tempest/scenario/test_baremetal_basic_ops.py b/tempest/scenario/test_baremetal_basic_ops.py
deleted file mode 100644
index 655d19d..0000000
--- a/tempest/scenario/test_baremetal_basic_ops.py
+++ /dev/null
@@ -1,105 +0,0 @@
-#
-# Copyright 2014 Hewlett-Packard 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.
-
-from oslo_log import log as logging
-
-from tempest import config
-from tempest.scenario import manager
-from tempest import test
-
-CONF = config.CONF
-
-LOG = logging.getLogger(__name__)
-
-
-class BaremetalBasicOps(manager.BaremetalScenarioTest):
-    """This smoke test tests the pxe_ssh Ironic driver.
-
-    It follows this basic set of operations:
-        * Creates a keypair
-        * Boots an instance using the keypair
-        * Monitors the associated Ironic node for power and
-          expected state transitions
-        * Validates Ironic node's port data has been properly updated
-        * Verifies SSH connectivity using created keypair via fixed IP
-        * Associates a floating ip
-        * Verifies SSH connectivity using created keypair via floating IP
-        * Deletes instance
-        * Monitors the associated Ironic node for power and
-          expected state transitions
-    """
-    def verify_partition(self, client, label, mount, gib_size):
-        """Verify a labeled partition's mount point and size."""
-        LOG.info("Looking for partition %s mounted on %s" % (label, mount))
-
-        # Validate we have a device with the given partition label
-        cmd = "/sbin/blkid | grep '%s' | cut -d':' -f1" % label
-        device = client.exec_command(cmd).rstrip('\n')
-        LOG.debug("Partition device is %s" % device)
-        self.assertNotEqual('', device)
-
-        # Validate the mount point for the device
-        cmd = "mount | grep '%s' | cut -d' ' -f3" % device
-        actual_mount = client.exec_command(cmd).rstrip('\n')
-        LOG.debug("Partition mount point is %s" % actual_mount)
-        self.assertEqual(actual_mount, mount)
-
-        # Validate the partition size matches what we expect
-        numbers = '0123456789'
-        devnum = device.replace('/dev/', '')
-        cmd = "cat /sys/block/%s/%s/size" % (devnum.rstrip(numbers), devnum)
-        num_bytes = client.exec_command(cmd).rstrip('\n')
-        num_bytes = int(num_bytes) * 512
-        actual_gib_size = num_bytes / (1024 * 1024 * 1024)
-        LOG.debug("Partition size is %d GiB" % actual_gib_size)
-        self.assertEqual(actual_gib_size, gib_size)
-
-    def get_flavor_ephemeral_size(self):
-        """Returns size of the ephemeral partition in GiB."""
-        f_id = self.instance['flavor']['id']
-        flavor = self.flavors_client.show_flavor(f_id)['flavor']
-        ephemeral = flavor.get('OS-FLV-EXT-DATA:ephemeral')
-        if not ephemeral or ephemeral == 'N/A':
-            return None
-        return int(ephemeral)
-
-    def validate_ports(self):
-        for port in self.get_ports(self.node['uuid']):
-            n_port_id = port['extra']['vif_port_id']
-            body = self.ports_client.show_port(n_port_id)
-            n_port = body['port']
-            self.assertEqual(n_port['device_id'], self.instance['id'])
-            self.assertEqual(n_port['mac_address'], port['address'])
-
-    @test.idempotent_id('549173a5-38ec-42bb-b0e2-c8b9f4a08943')
-    @test.services('baremetal', 'compute', 'image', 'network')
-    def test_baremetal_server_ops(self):
-        self.add_keypair()
-        self.boot_instance()
-        self.validate_ports()
-        ip_address = self.get_server_ip(self.instance)
-        self.get_remote_client(ip_address).validate_authentication()
-        vm_client = self.get_remote_client(ip_address)
-
-        # We expect the ephemeral partition to be mounted on /mnt and to have
-        # the same size as our flavor definition.
-        eph_size = self.get_flavor_ephemeral_size()
-        if eph_size:
-            self.verify_partition(vm_client, 'ephemeral0', '/mnt', eph_size)
-            # Create the test file
-            self.create_timestamp(
-                ip_address, private_key=self.keypair['private_key'])
-
-        self.terminate_instance()
diff --git a/tempest/scenario/test_dashboard_basic_ops.py b/tempest/scenario/test_dashboard_basic_ops.py
deleted file mode 100644
index 5d4f7b3..0000000
--- a/tempest/scenario/test_dashboard_basic_ops.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# 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 six.moves import html_parser as HTMLParser
-from six.moves.urllib import parse
-from six.moves.urllib import request
-
-from tempest import config
-from tempest.scenario import manager
-from tempest import test
-
-CONF = config.CONF
-
-
-class HorizonHTMLParser(HTMLParser.HTMLParser):
-    csrf_token = None
-    region = None
-    login = None
-
-    def _find_name(self, attrs, name):
-        for attrpair in attrs:
-            if attrpair[0] == 'name' and attrpair[1] == name:
-                return True
-        return False
-
-    def _find_value(self, attrs):
-        for attrpair in attrs:
-            if attrpair[0] == 'value':
-                return attrpair[1]
-        return None
-
-    def _find_attr_value(self, attrs, attr_name):
-        for attrpair in attrs:
-            if attrpair[0] == attr_name:
-                return attrpair[1]
-        return None
-
-    def handle_starttag(self, tag, attrs):
-        if tag == 'input':
-            if self._find_name(attrs, 'csrfmiddlewaretoken'):
-                self.csrf_token = self._find_value(attrs)
-            if self._find_name(attrs, 'region'):
-                self.region = self._find_value(attrs)
-        if tag == 'form':
-            self.login = self._find_attr_value(attrs, 'action')
-
-
-class TestDashboardBasicOps(manager.ScenarioTest):
-
-    """The test suite for dashboard basic operations
-
-    This is a basic scenario test:
-    * checks that the login page is available
-    * logs in as a regular user
-    * checks that the user home page loads without error
-    """
-
-    @classmethod
-    def skip_checks(cls):
-        super(TestDashboardBasicOps, cls).skip_checks()
-        if not CONF.service_available.horizon:
-            raise cls.skipException("Horizon support is required")
-
-    @classmethod
-    def setup_credentials(cls):
-        cls.set_network_resources()
-        super(TestDashboardBasicOps, cls).setup_credentials()
-
-    def check_login_page(self):
-        response = request.urlopen(CONF.dashboard.dashboard_url)
-        self.assertIn("id_username", response.read())
-
-    def user_login(self, username, password):
-        self.opener = request.build_opener(request.HTTPCookieProcessor())
-        response = self.opener.open(CONF.dashboard.dashboard_url).read()
-
-        # Grab the CSRF token and default region
-        parser = HorizonHTMLParser()
-        parser.feed(response)
-
-        # construct login url for dashboard, discovery accommodates non-/ web
-        # root for dashboard
-        login_url = parse.urljoin(CONF.dashboard.dashboard_url, parser.login)
-
-        # Prepare login form request
-        req = request.Request(login_url)
-        req.add_header('Content-type', 'application/x-www-form-urlencoded')
-        req.add_header('Referer', CONF.dashboard.dashboard_url)
-
-        # Pass the default domain name regardless of the auth version in order
-        # to test the scenario of when horizon is running with keystone v3
-        params = {'username': username,
-                  'password': password,
-                  'region': parser.region,
-                  'domain': CONF.auth.default_credentials_domain_name,
-                  'csrfmiddlewaretoken': parser.csrf_token}
-        self.opener.open(req, parse.urlencode(params))
-
-    def check_home_page(self):
-        response = self.opener.open(CONF.dashboard.dashboard_url)
-        self.assertIn('Overview', response.read())
-
-    @test.idempotent_id('4f8851b1-0e69-482b-b63b-84c6e76f6c80')
-    @test.services('dashboard')
-    def test_basic_scenario(self):
-        creds = self.os.credentials
-        self.check_login_page()
-        self.user_login(creds.username, creds.password)
-        self.check_home_page()
diff --git a/tempest/scenario/test_encrypted_cinder_volumes.py b/tempest/scenario/test_encrypted_cinder_volumes.py
index dcd77ad..1659ebe 100644
--- a/tempest/scenario/test_encrypted_cinder_volumes.py
+++ b/tempest/scenario/test_encrypted_cinder_volumes.py
@@ -53,7 +53,7 @@
         volume_type = self.create_volume_type(name=volume_type)
         self.create_encryption_type(type_id=volume_type['id'],
                                     provider=encryption_provider,
-                                    key_size=512,
+                                    key_size=256,
                                     cipher='aes-xts-plain64',
                                     control_location='front-end')
         return self.create_volume(volume_type=volume_type['name'])
diff --git a/tempest/scenario/test_large_ops.py b/tempest/scenario/test_large_ops.py
deleted file mode 100644
index 402077f..0000000
--- a/tempest/scenario/test_large_ops.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# Copyright 2013 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 import exceptions as lib_exc
-
-from tempest.common import fixed_network
-from tempest.common.utils import data_utils
-from tempest.common import waiters
-from tempest import config
-from tempest.scenario import manager
-from tempest import test
-
-CONF = config.CONF
-
-
-class TestLargeOpsScenario(manager.ScenarioTest):
-
-    """Test large operations.
-
-    This test below:
-    * Spin up multiple instances in one nova call, and repeat three times
-    * as a regular user
-    * TODO: same thing for cinder
-
-    """
-
-    @classmethod
-    def skip_checks(cls):
-        super(TestLargeOpsScenario, cls).skip_checks()
-        if CONF.scenario.large_ops_number < 1:
-            raise cls.skipException("large_ops_number not set to multiple "
-                                    "instances")
-
-    @classmethod
-    def setup_credentials(cls):
-        cls.set_network_resources()
-        super(TestLargeOpsScenario, cls).setup_credentials()
-
-    @classmethod
-    def resource_setup(cls):
-        super(TestLargeOpsScenario, cls).resource_setup()
-        # list of cleanup calls to be executed in reverse order
-        cls._cleanup_resources = []
-
-    @classmethod
-    def resource_cleanup(cls):
-        while cls._cleanup_resources:
-            function, args, kwargs = cls._cleanup_resources.pop(-1)
-            try:
-                function(*args, **kwargs)
-            except lib_exc.NotFound:
-                pass
-        super(TestLargeOpsScenario, cls).resource_cleanup()
-
-    @classmethod
-    def addCleanupClass(cls, function, *arguments, **keywordArguments):
-        cls._cleanup_resources.append((function, arguments, keywordArguments))
-
-    def _wait_for_server_status(self, status):
-        for server in self.servers:
-            # Make sure nova list keeps working throughout the build process
-            self.servers_client.list_servers()
-            waiters.wait_for_server_status(self.servers_client,
-                                           server['id'], status)
-
-    def nova_boot(self, image):
-        name = data_utils.rand_name('scenario-server')
-        flavor_id = CONF.compute.flavor_ref
-        # Explicitly create secgroup to avoid cleanup at the end of testcases.
-        # Since no traffic is tested, we don't need to actually add rules to
-        # secgroup
-        secgroup = self.compute_security_groups_client.create_security_group(
-            name='secgroup-%s' % name,
-            description='secgroup-desc-%s' % name)['security_group']
-        self.addCleanupClass(
-            self.compute_security_groups_client.delete_security_group,
-            secgroup['id'])
-        create_kwargs = {
-            'min_count': CONF.scenario.large_ops_number,
-            'security_groups': [{'name': secgroup['name']}]
-            }
-        network = self.get_tenant_network()
-        create_kwargs = fixed_network.set_networks_kwarg(network,
-                                                         create_kwargs)
-        self.servers_client.create_server(
-            name=name,
-            imageRef=image,
-            flavorRef=flavor_id,
-            **create_kwargs)
-        # needed because of bug 1199788
-        params = {'name': name}
-        server_list = self.servers_client.list_servers(**params)
-        self.servers = server_list['servers']
-        for server in self.servers:
-            # after deleting all servers - wait for all servers to clear
-            # before cleanup continues
-            self.addCleanupClass(waiters.wait_for_server_termination,
-                                 self.servers_client,
-                                 server['id'])
-        for server in self.servers:
-            self.addCleanupClass(self.servers_client.delete_server,
-                                 server['id'])
-        self._wait_for_server_status('ACTIVE')
-
-    def _large_ops_scenario(self):
-        image = self.glance_image_create()
-        self.nova_boot(image)
-
-    @test.idempotent_id('14ba0e78-2ed9-4d17-9659-a48f4756ecb3')
-    @test.services('compute', 'image')
-    def test_large_ops_scenario_1(self):
-        self._large_ops_scenario()
-
-    @test.idempotent_id('b9b79b88-32aa-42db-8f8f-dcc8f4b4ccfe')
-    @test.services('compute', 'image')
-    def test_large_ops_scenario_2(self):
-        self._large_ops_scenario()
-
-    @test.idempotent_id('3aab7e82-2de3-419a-9da1-9f3a070668fb')
-    @test.services('compute', 'image')
-    def test_large_ops_scenario_3(self):
-        self._large_ops_scenario()
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index f7c7434..c454ae2 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -16,7 +16,8 @@
 from tempest.common import custom_matchers
 from tempest.common import waiters
 from tempest import config
-from tempest import exceptions
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
 from tempest.scenario import manager
 from tempest import test
 
@@ -46,12 +47,6 @@
     10. Check SSH connection to instance after reboot
 
     """
-
-    def nova_list(self):
-        servers = self.servers_client.list_servers()
-        # The list servers in the compute client is inconsistent...
-        return servers['servers']
-
     def nova_show(self, server):
         got_server = (self.servers_client.show_server(server['id'])
                       ['server'])
@@ -71,10 +66,10 @@
         waiters.wait_for_server_status(self.servers_client,
                                        server['id'], 'ACTIVE')
 
-    def check_partitions(self):
+    def check_disks(self):
         # NOTE(andreaf) The device name may be different on different guest OS
-        partitions = self.linux_client.get_partitions()
-        self.assertEqual(1, partitions.count(CONF.compute.volume_device_name))
+        disks = self.linux_client.get_disks()
+        self.assertEqual(1, disks.count(CONF.compute.volume_device_name))
 
     def create_and_add_security_group_to_server(self, server):
         secgroup = self._create_security_group()
@@ -88,13 +83,20 @@
                     ['server'])
             return {'name': secgroup['name']} in body['security_groups']
 
-        if not test.call_until_true(wait_for_secgroup_add,
-                                    CONF.compute.build_timeout,
-                                    CONF.compute.build_interval):
+        if not test_utils.call_until_true(wait_for_secgroup_add,
+                                          CONF.compute.build_timeout,
+                                          CONF.compute.build_interval):
             msg = ('Timed out waiting for adding security group %s to server '
                    '%s' % (secgroup['id'], server['id']))
             raise exceptions.TimeoutException(msg)
 
+    def _get_floating_ip_in_server_addresses(self, floating_ip, server):
+        for network_name, addresses in server['addresses'].items():
+            for address in addresses:
+                if (address['OS-EXT-IPS:type'] == 'floating' and
+                        address['addr'] == floating_ip['ip']):
+                    return address
+
     @test.idempotent_id('bdbb5441-9204-419d-a225-b4fdbfb1a1a8')
     @test.services('compute', 'volume', 'image', 'network')
     def test_minimum_basic_scenario(self):
@@ -104,7 +106,7 @@
         server = self.create_server(image_id=image,
                                     key_name=keypair['name'],
                                     wait_until='ACTIVE')
-        servers = self.nova_list()
+        servers = self.servers_client.list_servers()['servers']
         self.assertIn(server['id'], [x['id'] for x in servers])
 
         self.nova_show(server)
@@ -120,6 +122,16 @@
         self.cinder_show(volume)
 
         floating_ip = self.create_floating_ip(server)
+        # fetch the server again to make sure the addresses were refreshed
+        # after associating the floating IP
+        server = self.servers_client.show_server(server['id'])['server']
+        address = self._get_floating_ip_in_server_addresses(
+            floating_ip, server)
+        self.assertIsNotNone(
+            address,
+            "Failed to find floating IP '%s' in server addresses: %s" %
+            (floating_ip['ip'], server['addresses']))
+
         self.create_and_add_security_group_to_server(server)
 
         # check that we can SSH to the server before reboot
@@ -133,4 +145,22 @@
         self.linux_client = self.get_remote_client(
             floating_ip['ip'], private_key=keypair['private_key'])
 
-        self.check_partitions()
+        self.check_disks()
+
+        # delete the floating IP, this should refresh the server addresses
+        self.compute_floating_ips_client.delete_floating_ip(floating_ip['id'])
+
+        def is_floating_ip_detached_from_server():
+            server_info = self.servers_client.show_server(
+                server['id'])['server']
+            address = self._get_floating_ip_in_server_addresses(
+                floating_ip, server_info)
+            return (not address)
+
+        if not test_utils.call_until_true(
+            is_floating_ip_detached_from_server,
+            CONF.compute.build_timeout,
+            CONF.compute.build_interval):
+            msg = ("Floating IP '%s' should not be in server addresses: %s" %
+                   (floating_ip['ip'], server['addresses']))
+            raise exceptions.TimeoutException(msg)
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 2cbe6dc..1279484 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -15,7 +15,6 @@
 
 import testtools
 
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
 from tempest.scenario import manager
@@ -36,11 +35,17 @@
     """
 
     @classmethod
+    def setup_clients(cls):
+        super(TestNetworkAdvancedServerOps, cls).setup_clients()
+        cls.admin_servers_client = cls.os_adm.servers_client
+        cls.admin_hosts_client = cls.os_adm.hosts_client
+
+    @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)
 
@@ -50,25 +55,28 @@
         cls.set_network_resources()
         super(TestNetworkAdvancedServerOps, cls).setup_credentials()
 
-    def _setup_network_and_servers(self):
-        keypair = self.create_keypair()
-        security_group = self._create_security_group()
+    def _setup_server(self, keypair):
+        security_groups = []
+        if test.is_extension_enabled('security-group', 'network'):
+            security_group = self._create_security_group()
+            security_groups = [{'name': security_group['name']}]
         network, subnet, router = self.create_networks()
-        public_network_id = CONF.network.public_network_id
-        server_name = data_utils.rand_name('server-smoke')
         server = self.create_server(
-            name=server_name,
-            networks=[{'uuid': network.id}],
+            networks=[{'uuid': network['id']}],
             key_name=keypair['name'],
-            security_groups=[{'name': security_group['name']}],
+            security_groups=security_groups,
             wait_until='ACTIVE')
+        return server
+
+    def _setup_network(self, server, keypair):
+        public_network_id = CONF.network.public_network_id
         floating_ip = self.create_floating_ip(server, public_network_id)
         # Verify that we can indeed connect to the server before we mess with
         # it's state
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
 
-        return server, keypair, floating_ip
+        return floating_ip
 
     def _check_network_connectivity(self, server, keypair, floating_ip,
                                     should_connect=True):
@@ -78,7 +86,7 @@
             server, username, private_key,
             should_connect=should_connect,
             servers_for_debug=[server])
-        floating_ip_addr = floating_ip.floating_ip_address
+        floating_ip_addr = floating_ip['floating_ip_address']
         # Check FloatingIP status before checking the connectivity
         self.check_floating_ip_status(floating_ip, 'ACTIVE')
         self.check_public_network_connectivity(floating_ip_addr, username,
@@ -92,11 +100,16 @@
                                        'ACTIVE')
         self._check_network_connectivity(server, keypair, floating_ip)
 
+    def _get_host_for_server(self, server_id):
+        body = self.admin_servers_client.show_server(server_id)['server']
+        return body['OS-EXT-SRV-ATTR:host']
+
     @test.idempotent_id('61f1aa9a-1573-410e-9054-afa557cab021')
-    @test.stresstest(class_setup_per='process')
     @test.services('compute', 'network')
     def test_server_connectivity_stop_start(self):
-        server, keypair, floating_ip = self._setup_network_and_servers()
+        keypair = self.create_keypair()
+        server = self._setup_server(keypair)
+        floating_ip = self._setup_network(server, keypair)
         self.servers_client.stop_server(server['id'])
         waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'SHUTOFF')
@@ -109,7 +122,9 @@
     @test.idempotent_id('7b6860c2-afa3-4846-9522-adeb38dfbe08')
     @test.services('compute', 'network')
     def test_server_connectivity_reboot(self):
-        server, keypair, floating_ip = self._setup_network_and_servers()
+        keypair = self.create_keypair()
+        server = self._setup_server(keypair)
+        floating_ip = self._setup_network(server, keypair)
         self.servers_client.reboot_server(server['id'], type='SOFT')
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
@@ -117,7 +132,9 @@
     @test.idempotent_id('88a529c2-1daa-4c85-9aec-d541ba3eb699')
     @test.services('compute', 'network')
     def test_server_connectivity_rebuild(self):
-        server, keypair, floating_ip = self._setup_network_and_servers()
+        keypair = self.create_keypair()
+        server = self._setup_server(keypair)
+        floating_ip = self._setup_network(server, keypair)
         image_ref_alt = CONF.compute.image_ref_alt
         self.servers_client.rebuild_server(server['id'],
                                            image_ref=image_ref_alt)
@@ -129,7 +146,9 @@
                           'Pause is not available.')
     @test.services('compute', 'network')
     def test_server_connectivity_pause_unpause(self):
-        server, keypair, floating_ip = self._setup_network_and_servers()
+        keypair = self.create_keypair()
+        server = self._setup_server(keypair)
+        floating_ip = self._setup_network(server, keypair)
         self.servers_client.pause_server(server['id'])
         waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'PAUSED')
@@ -144,7 +163,9 @@
                           'Suspend is not available.')
     @test.services('compute', 'network')
     def test_server_connectivity_suspend_resume(self):
-        server, keypair, floating_ip = self._setup_network_and_servers()
+        keypair = self.create_keypair()
+        server = self._setup_server(keypair)
+        floating_ip = self._setup_network(server, keypair)
         self.servers_client.suspend_server(server['id'])
         waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'SUSPENDED')
@@ -163,7 +184,9 @@
         if resize_flavor == CONF.compute.flavor_ref:
             msg = "Skipping test - flavor_ref and flavor_ref_alt are identical"
             raise self.skipException(msg)
-        server, keypair, floating_ip = self._setup_network_and_servers()
+        keypair = self.create_keypair()
+        server = self._setup_server(keypair)
+        floating_ip = self._setup_network(server, keypair)
         self.servers_client.resize_server(server['id'],
                                           flavor_ref=resize_flavor)
         waiters.wait_for_server_status(self.servers_client, server['id'],
@@ -171,3 +194,53 @@
         self.servers_client.confirm_resize_server(server['id'])
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
+
+    @test.idempotent_id('a4858f6c-401e-4155-9a49-d5cd053d1a2f')
+    @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
+                          'Cold migration is not available.')
+    @testtools.skipUnless(CONF.compute.min_compute_nodes > 1,
+                          'Less than 2 compute nodes, skipping multinode '
+                          'tests.')
+    @test.services('compute', 'network')
+    def test_server_connectivity_cold_migration(self):
+        keypair = self.create_keypair()
+        server = self._setup_server(keypair)
+        floating_ip = self._setup_network(server, keypair)
+        src_host = self._get_host_for_server(server['id'])
+        self._wait_server_status_and_check_network_connectivity(
+            server, keypair, floating_ip)
+
+        self.admin_servers_client.migrate_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client, server['id'],
+                                       'VERIFY_RESIZE')
+        self.servers_client.confirm_resize_server(server['id'])
+        self._wait_server_status_and_check_network_connectivity(
+            server, keypair, floating_ip)
+        dst_host = self._get_host_for_server(server['id'])
+
+        self.assertNotEqual(src_host, dst_host)
+
+    @test.idempotent_id('25b188d7-0183-4b1e-a11d-15840c8e2fd6')
+    @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
+                          'Cold migration is not available.')
+    @testtools.skipUnless(CONF.compute.min_compute_nodes > 1,
+                          'Less than 2 compute nodes, skipping multinode '
+                          'tests.')
+    @test.services('compute', 'network')
+    def test_server_connectivity_cold_migration_revert(self):
+        keypair = self.create_keypair()
+        server = self._setup_server(keypair)
+        floating_ip = self._setup_network(server, keypair)
+        src_host = self._get_host_for_server(server['id'])
+        self._wait_server_status_and_check_network_connectivity(
+            server, keypair, floating_ip)
+
+        self.admin_servers_client.migrate_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client, server['id'],
+                                       'VERIFY_RESIZE')
+        self.servers_client.revert_resize_server(server['id'])
+        self._wait_server_status_and_check_network_connectivity(
+            server, keypair, floating_ip)
+        dst_host = self._get_host_for_server(server['id'])
+
+        self.assertEqual(src_host, dst_host)
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 4d03ed7..f9aa3e7 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -19,12 +19,12 @@
 from oslo_log import log as logging
 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.common.utils import test_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions
 from tempest.scenario import manager
-from tempest.services.network import resources as net_resources
 from tempest import test
 
 CONF = config.CONF
@@ -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']:
@@ -104,8 +104,7 @@
 
     def _setup_network_and_servers(self, **kwargs):
         boot_with_port = kwargs.pop('boot_with_port', False)
-        self.security_group = \
-            self._create_security_group(tenant_id=self.tenant_id)
+        self.security_group = self._create_security_group()
         self.network, self.subnet, self.router = self.create_networks(**kwargs)
         self.check_networks()
 
@@ -113,11 +112,10 @@
         self.port_id = None
         if boot_with_port:
             # create a port on the network and boot with that
-            self.port_id = self._create_port(self.network['id']).id
+            self.port_id = self._create_port(self.network['id'])['id']
             self.ports.append({'port': self.port_id})
 
-        name = data_utils.rand_name('server-smoke')
-        server = self._create_server(name, self.network, self.port_id)
+        server = self._create_server(self.network, self.port_id)
         self._check_tenant_network_connectivity()
 
         floating_ip = self.create_floating_ip(server)
@@ -132,35 +130,34 @@
         seen_nets = self._list_networks()
         seen_names = [n['name'] for n in seen_nets]
         seen_ids = [n['id'] for n in seen_nets]
-        self.assertIn(self.network.name, seen_names)
-        self.assertIn(self.network.id, seen_ids)
+        self.assertIn(self.network['name'], seen_names)
+        self.assertIn(self.network['id'], seen_ids)
 
         if self.subnet:
             seen_subnets = self._list_subnets()
             seen_net_ids = [n['network_id'] for n in seen_subnets]
             seen_subnet_ids = [n['id'] for n in seen_subnets]
-            self.assertIn(self.network.id, seen_net_ids)
-            self.assertIn(self.subnet.id, seen_subnet_ids)
+            self.assertIn(self.network['id'], seen_net_ids)
+            self.assertIn(self.subnet['id'], seen_subnet_ids)
 
         if self.router:
             seen_routers = self._list_routers()
             seen_router_ids = [n['id'] for n in seen_routers]
             seen_router_names = [n['name'] for n in seen_routers]
-            self.assertIn(self.router.name,
+            self.assertIn(self.router['name'],
                           seen_router_names)
-            self.assertIn(self.router.id,
+            self.assertIn(self.router['id'],
                           seen_router_ids)
 
-    def _create_server(self, name, network, port_id=None):
+    def _create_server(self, network, port_id=None):
         keypair = self.create_keypair()
         self.keypairs[keypair['name']] = keypair
         security_groups = [{'name': self.security_group['name']}]
-        network = {'uuid': network.id}
+        network = {'uuid': network['id']}
         if port_id is not None:
             network['port'] = port_id
 
         server = self.create_server(
-            name=name,
             networks=[network],
             key_name=keypair['name'],
             security_groups=security_groups,
@@ -182,7 +179,7 @@
 
     def check_public_network_connectivity(
             self, should_connect=True, msg=None,
-            should_check_floating_ip_status=True):
+            should_check_floating_ip_status=True, mtu=None):
         """Verifies connectivty to a VM via public network and floating IP
 
         and verifies floating IP has resource status is correct.
@@ -194,10 +191,11 @@
         to indicate the context of the failure
         :param should_check_floating_ip_status: bool. should status of
         floating_ip be checked or not
+        :param mtu: int. MTU network to use for connectivity validation
         """
         ssh_login = CONF.validation.image_ssh_user
         floating_ip, server = self.floating_ip_tuple
-        ip_address = floating_ip.floating_ip_address
+        ip_address = floating_ip['floating_ip_address']
         private_key = None
         floatingip_status = 'DOWN'
         if should_connect:
@@ -209,7 +207,7 @@
         # call the common method in the parent class
         super(TestNetworkBasicOps, self).check_public_network_connectivity(
             ip_address, ssh_login, private_key, should_connect, msg,
-            self.servers)
+            self.servers, mtu=mtu)
 
     def _disassociate_floating_ips(self):
         floating_ip, server = self.floating_ip_tuple
@@ -219,15 +217,14 @@
 
     def _reassociate_floating_ips(self):
         floating_ip, server = self.floating_ip_tuple
-        name = data_utils.rand_name('new_server-smoke')
         # create a new server for the floating ip
-        server = self._create_server(name, self.network)
+        server = self._create_server(self.network)
         self._associate_floating_ip(floating_ip, server)
         self.floating_ip_tuple = Floating_IP_tuple(
             floating_ip, server)
 
     def _create_new_network(self, create_gateway=False):
-        self.new_net = self._create_network(tenant_id=self.tenant_id)
+        self.new_net = self._create_network()
         if create_gateway:
             self.new_subnet = self._create_subnet(
                 network=self.new_net)
@@ -238,7 +235,7 @@
 
     def _hotplug_server(self):
         old_floating_ip, server = self.floating_ip_tuple
-        ip_address = old_floating_ip.floating_ip_address
+        ip_address = old_floating_ip['floating_ip_address']
         private_key = self._get_server_key(server)
         ssh_client = self.get_remote_client(
             ip_address, private_key=private_key)
@@ -249,11 +246,10 @@
         old_port = port_list[0]
         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.delete_wrapper,
+            net_id=self.new_net['id'])['interfaceAttachment']
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        interface['port_id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.interface_client.delete_interface,
                         server['id'], interface['port_id'])
 
@@ -263,30 +259,32 @@
                                   if port['id'] != old_port['id']]
             return len(self.new_port_list) == 1
 
-        if not test.call_until_true(check_ports, CONF.network.build_timeout,
-                                    CONF.network.build_interval):
+        if not test_utils.call_until_true(
+                check_ports, CONF.network.build_timeout,
+                CONF.network.build_interval):
             raise exceptions.TimeoutException(
                 "No new port attached to the server in time (%s sec)! "
                 "Old port: %s. Number of new ports: %d" % (
                     CONF.network.build_timeout, old_port,
                     len(self.new_port_list)))
-        new_port = net_resources.DeletablePort(ports_client=self.ports_client,
-                                               **self.new_port_list[0])
+        new_port = self.new_port_list[0]
 
         def check_new_nic():
             new_nic_list = self._get_server_nics(ssh_client)
             self.diff_list = [n for n in new_nic_list if n not in old_nic_list]
             return len(self.diff_list) == 1
 
-        if not test.call_until_true(check_new_nic, CONF.network.build_timeout,
-                                    CONF.network.build_interval):
+        if not test_utils.call_until_true(
+                check_new_nic, CONF.network.build_timeout,
+                CONF.network.build_interval):
             raise exceptions.TimeoutException("Interface not visible on the "
                                               "guest after %s sec"
                                               % CONF.network.build_timeout)
 
         num, new_nic = self.diff_list[0]
         ssh_client.assign_static_ip(nic=new_nic,
-                                    addr=new_port.fixed_ips[0]['ip_address'])
+                                    addr=new_port['fixed_ips'][0][
+                                        'ip_address'])
         ssh_client.set_nic_state(nic=new_nic)
 
     def _get_server_nics(self, ssh_client):
@@ -306,7 +304,7 @@
         # get all network ports in the new network
         internal_ips = (p['fixed_ips'][0]['ip_address'] for p in
                         self._list_ports(tenant_id=server['tenant_id'],
-                                         network_id=network.id)
+                                         network_id=network['id'])
                         if p['device_owner'].startswith('network'))
 
         self._check_server_connectivity(floating_ip,
@@ -334,7 +332,7 @@
 
     def _check_server_connectivity(self, floating_ip, address_list,
                                    should_connect=True):
-        ip_address = floating_ip.floating_ip_address
+        ip_address = floating_ip['floating_ip_address']
         private_key = self._get_server_key(self.floating_ip_tuple.server)
         ssh_source = self.get_remote_client(
             ip_address, private_key=private_key)
@@ -408,9 +406,21 @@
                                                msg="after re-associate "
                                                    "floating ip")
 
+    @test.idempotent_id('b158ea55-472e-4086-8fa9-c64ac0c6c1d0')
+    @testtools.skipUnless(test.is_extension_enabled('net-mtu', 'network'),
+                          'No way to calculate MTU for networks')
+    @test.services('compute', 'network')
+    def test_mtu_sized_frames(self):
+        """Validate that network MTU sized frames fit through."""
+        self._setup_network_and_servers()
+        self.check_public_network_connectivity(
+            should_connect=True, mtu=self.network['mtu'])
+
     @test.idempotent_id('1546850e-fbaa-42f5-8b5f-03d8a6a95f15')
-    @testtools.skipIf(CONF.baremetal.driver_enabled,
-                      'Baremetal relies on a shared physical network.')
+    @testtools.skipIf(CONF.network.shared_physical_network,
+                      'Connectivity can only be tested when in a '
+                      'multitenant network environment')
+    @decorators.skip_because(bug="1610994")
     @test.services('compute', 'network')
     def test_connectivity_between_vms_on_different_networks(self):
         """Test connectivity between VMs on different networks
@@ -446,11 +456,16 @@
         self._check_network_internal_connectivity(network=self.network)
         self._check_network_external_connectivity()
         self._create_new_network(create_gateway=True)
-        name = data_utils.rand_name('server-smoke')
-        self._create_server(name, self.new_net)
+        self._create_server(self.new_net)
         self._check_network_internal_connectivity(network=self.new_net,
                                                   should_connect=False)
-        self.new_subnet.add_to_router(self.router.id)
+        router_id = self.router['id']
+        self.routers_client.add_router_interface(
+            router_id, subnet_id=self.new_subnet['id'])
+
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.routers_client.remove_router_interface,
+                        router_id, subnet_id=self.new_subnet['id'])
         self._check_network_internal_connectivity(network=self.new_net,
                                                   should_connect=True)
 
@@ -464,11 +479,11 @@
     def test_hotplug_nic(self):
         """Test hotplug network interface
 
-        1. create a new network, with no gateway (to prevent overwriting VM's
-            gateway)
-        2. connect VM to new network
-        3. set static ip and bring new nic up
-        4. check VM can ping new network dhcp port
+        1. Create a network and a VM.
+        2. Check connectivity to the VM via a public network.
+        3. Create a new network, with no gateway.
+        4. Bring up a new interface
+        5. check the VM reach the new network
 
         """
         self._setup_network_and_servers()
@@ -478,9 +493,9 @@
         self._check_network_internal_connectivity(network=self.new_net)
 
     @test.idempotent_id('04b9fe4e-85e8-4aea-b937-ea93885ac59f')
-    @testtools.skipIf(CONF.baremetal.driver_enabled,
-                      'Router state cannot be altered on a shared baremetal '
-                      'network')
+    @testtools.skipIf(CONF.network.shared_physical_network,
+                      'Router state can be altered only with multitenant '
+                      'networks capabilities')
     @test.services('compute', 'network')
     def test_update_router_admin_state(self):
         """Test to update admin state up of router
@@ -510,8 +525,8 @@
             "admin_state_up of router to True")
 
     @test.idempotent_id('d8bb918e-e2df-48b2-97cd-b73c95450980')
-    @testtools.skipIf(CONF.baremetal.driver_enabled,
-                      'network isolation not available for baremetal nodes')
+    @testtools.skipIf(CONF.network.shared_physical_network,
+                      'network isolation not available')
     @testtools.skipUnless(CONF.scenario.dhcp_client,
                           "DHCP client is not available.")
     @test.services('compute', 'network')
@@ -552,7 +567,7 @@
         self.check_public_network_connectivity(should_connect=True)
 
         floating_ip, server = self.floating_ip_tuple
-        ip_address = floating_ip.floating_ip_address
+        ip_address = floating_ip['floating_ip_address']
         private_key = self._get_server_key(server)
         ssh_client = self.get_remote_client(
             ip_address, private_key=private_key)
@@ -567,9 +582,11 @@
                                  act_serv=servers,
                                  trgt_serv=dns_servers))
 
-        self.subnet.update(dns_nameservers=[alt_dns_server])
+        self.subnet = self.subnets_client.update_subnet(
+            self.subnet['id'], dns_nameservers=[alt_dns_server])['subnet']
+
         # asserts that Neutron DB has updated the nameservers
-        self.assertEqual([alt_dns_server], self.subnet.dns_nameservers,
+        self.assertEqual([alt_dns_server], self.subnet['dns_nameservers'],
                          "Failed to update subnet's nameservers")
 
         def check_new_dns_server():
@@ -584,16 +601,13 @@
                 return False
             return True
 
-        self.assertTrue(test.call_until_true(check_new_dns_server,
-                                             renew_timeout,
-                                             renew_delay),
+        self.assertTrue(test_utils.call_until_true(check_new_dns_server,
+                                                   renew_timeout,
+                                                   renew_delay),
                         msg="DHCP renewal failed to fetch "
                             "new DNS nameservers")
 
     @test.idempotent_id('f5dfcc22-45fd-409f-954c-5bd500d7890b')
-    @testtools.skipIf(CONF.baremetal.driver_enabled,
-                      'admin_state of instance ports cannot be altered '
-                      'for baremetal nodes')
     @testtools.skipUnless(CONF.network_feature_enabled.port_admin_state_change,
                           "Changing a port's admin state is not supported "
                           "by the test environment")
@@ -601,29 +615,44 @@
     def test_update_instance_port_admin_state(self):
         """Test to update admin_state_up attribute of instance port
 
-        1. Check public connectivity before updating
+        1. Check public and project connectivity before updating
                 admin_state_up attribute of instance port to False
-        2. Check public connectivity after updating
+        2. Check public and project connectivity after updating
                 admin_state_up attribute of instance port to False
-        3. Check public connectivity after updating
+        3. Check public and project connectivity after updating
                 admin_state_up attribute of instance port to True
         """
         self._setup_network_and_servers()
         floating_ip, server = self.floating_ip_tuple
         server_id = server['id']
         port_id = self._list_ports(device_id=server_id)[0]['id']
+        server_pip = server['addresses'][self.network['name']][0]['addr']
+
+        server2 = self._create_server(self.network)
+        server2_fip = self.create_floating_ip(server2)
+
+        private_key = self._get_server_key(server2)
+        ssh_client = self.get_remote_client(server2_fip['floating_ip_address'],
+                                            private_key=private_key)
+
         self.check_public_network_connectivity(
             should_connect=True, msg="before updating "
             "admin_state_up of instance port to False")
+        self._check_remote_connectivity(ssh_client, dest=server_pip,
+                                        should_succeed=True)
         self.ports_client.update_port(port_id, admin_state_up=False)
         self.check_public_network_connectivity(
             should_connect=False, msg="after updating "
             "admin_state_up of instance port to False",
             should_check_floating_ip_status=False)
+        self._check_remote_connectivity(ssh_client, dest=server_pip,
+                                        should_succeed=False)
         self.ports_client.update_port(port_id, admin_state_up=True)
         self.check_public_network_connectivity(
             should_connect=True, msg="after updating "
             "admin_state_up of instance port to True")
+        self._check_remote_connectivity(ssh_client, dest=server_pip,
+                                        should_succeed=True)
 
     @test.idempotent_id('759462e1-8535-46b0-ab3a-33aa45c55aaa')
     @test.services('compute', 'network')
@@ -635,6 +664,8 @@
 
         Nova should unbind the port from the instance on delete if the port was
         not created by Nova as part of the boot request.
+
+        We should also be able to boot another server with the same port.
         """
         # Setup the network, create a port and boot the server from that port.
         self._setup_network_and_servers(boot_with_port=True)
@@ -663,6 +694,16 @@
         self.assertEqual('', port['device_id'])
         self.assertEqual('', port['device_owner'])
 
+        # Boot another server with the same port to make sure nothing was
+        # left around that could cause issues.
+        server = self._create_server(self.network, port['id'])
+        port_list = self._list_ports(device_id=server['id'],
+                                     network_id=self.network['id'])
+        self.assertEqual(1, len(port_list),
+                         'There should only be one port created for '
+                         'server %s.' % server['id'])
+        self.assertEqual(port['id'], port_list[0]['id'])
+
     @test.requires_ext(service='network', extension='l3_agent_scheduler')
     @test.idempotent_id('2e788c46-fb3f-4ac9-8f82-0561555bea73')
     @test.services('compute', 'network')
@@ -680,20 +721,22 @@
         # 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)
         unschedule_router = (self.admin_manager.network_agents_client.
                              delete_router_from_l3_agent)
 
-        agent_list = set(a["id"] for a in
-                         self._list_agents(agent_type="L3 agent"))
+        agent_list_alive = set(a["id"] for a in
+                               self._list_agents(agent_type="L3 agent") if
+                               a["alive"] is True)
         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)
@@ -702,33 +745,32 @@
 
         # remove resource from agents
         hosting_agents = set(a["id"] for a in
-                             list_hosts(self.router.id)['agents'])
-        no_migration = agent_list == hosting_agents
+                             list_hosts(self.router['id'])['agents'])
+        no_migration = agent_list_alive == hosting_agents
         LOG.info("Router will be assigned to {mig} hosting agent".
                  format(mig="the same" if no_migration else "a new"))
 
         for hosting_agent in hosting_agents:
-            unschedule_router(hosting_agent, self.router.id)
+            unschedule_router(hosting_agent, self.router['id'])
             self.assertNotIn(hosting_agent,
                              [a["id"] for a in
-                              list_hosts(self.router.id)['agents']],
+                              list_hosts(self.router['id'])['agents']],
                              'unscheduling router failed')
 
         # verify resource is un-functional
         self.check_public_network_connectivity(
             should_connect=False,
             msg='after router unscheduling',
-            should_check_floating_ip_status=False
         )
 
         # schedule resource to new agent
         target_agent = list(hosting_agents if no_migration else
-                            agent_list - hosting_agents)[0]
+                            agent_list_alive - hosting_agents)[0]
         schedule_router(target_agent,
                         router_id=self.router['id'])
         self.assertEqual(
             target_agent,
-            list_hosts(self.router.id)['agents'][0]['id'],
+            list_hosts(self.router['id'])['agents'][0]['id'],
             "Router failed to reschedule. Hosting agent doesn't match "
             "target agent")
 
@@ -753,10 +795,10 @@
         The test steps are :
         1. Create a new network.
         2. Connect (hotplug) the VM to a new network.
-        3. Check the VM can ping the DHCP interface of this network.
+        3. Check the VM can ping a server on the new network ("peer")
         4. Spoof the mac address of the new VM interface.
         5. Check the Security Group enforces mac spoofing and blocks pings via
-           spoofed interface (VM cannot ping the DHCP interface).
+           spoofed interface (VM cannot ping the peer).
         6. Disable port-security of the spoofed port- set the flag to false.
         7. Retest 3rd step and check that the Security Group allows pings via
         the spoofed interface.
@@ -774,21 +816,20 @@
                                      network_id=self.new_net["id"])
         spoof_port = new_ports[0]
         private_key = self._get_server_key(server)
-        ssh_client = self.get_remote_client(fip.floating_ip_address,
+        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"])
-        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"]
-        self._check_remote_connectivity(ssh_client, dest=new_net_dhcp,
+        spoof_nic = ssh_client.get_nic_name_by_mac(spoof_port["mac_address"])
+        peer = self._create_server(self.new_net)
+        peer_address = peer['addresses'][self.new_net['name']][0]['addr']
+        self._check_remote_connectivity(ssh_client, dest=peer_address,
                                         nic=spoof_nic, should_succeed=True)
         ssh_client.set_mac_address(spoof_nic, spoof_mac)
         new_mac = ssh_client.get_mac_address(nic=spoof_nic)
         self.assertEqual(spoof_mac, new_mac)
-        self._check_remote_connectivity(ssh_client, dest=new_net_dhcp,
+        self._check_remote_connectivity(ssh_client, dest=peer_address,
                                         nic=spoof_nic, should_succeed=False)
         self.ports_client.update_port(spoof_port["id"],
                                       port_security_enabled=False,
                                       security_groups=[])
-        self._check_remote_connectivity(ssh_client, dest=new_net_dhcp,
+        self._check_remote_connectivity(ssh_client, dest=peer_address,
                                         nic=spoof_nic, should_succeed=True)
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index fc33dd9..7acf107 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -14,9 +14,8 @@
 #    under the License.
 import functools
 
-import six
-
 from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest.scenario import manager
 from tempest import test
 
@@ -44,13 +43,13 @@
         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:
-            msg = ('Baremetal does not currently support network isolation')
+        if CONF.network.shared_physical_network:
+            msg = 'Deployment uses a shared physical network'
             raise cls.skipException(msg)
 
     @classmethod
@@ -62,7 +61,7 @@
     def setUp(self):
         super(TestGettingAddress, self).setUp()
         self.keypair = self.create_keypair()
-        self.sec_grp = self._create_security_group(tenant_id=self.tenant_id)
+        self.sec_grp = self._create_security_group()
 
     def prepare_network(self, address6_mode, n_subnets6=1, dualnet=False):
         """Prepare network
@@ -73,17 +72,21 @@
         if dualnet - create IPv6 subnets on a different network
         :return: list of created networks
         """
-        self.network = self._create_network(tenant_id=self.tenant_id)
+        self.network = self._create_network()
         if dualnet:
-            self.network_v6 = self._create_network(tenant_id=self.tenant_id)
+            self.network_v6 = self._create_network()
 
         sub4 = self._create_subnet(network=self.network,
                                    namestart='sub4',
                                    ip_version=4)
 
-        router = self._get_router(tenant_id=self.tenant_id)
-        sub4.add_to_router(router_id=router['id'])
-        self.addCleanup(sub4.delete)
+        router = self._get_router()
+        self.routers_client.add_router_interface(router['id'],
+                                                 subnet_id=sub4['id'])
+
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.routers_client.remove_router_interface,
+                        router['id'], subnet_id=sub4['id'])
 
         self.subnets_v6 = []
         for _ in range(n_subnets6):
@@ -94,16 +97,20 @@
                                        ipv6_ra_mode=address6_mode,
                                        ipv6_address_mode=address6_mode)
 
-            sub6.add_to_router(router_id=router['id'])
-            self.addCleanup(sub6.delete)
-            self.subnets_v6.append(sub6)
+            self.routers_client.add_router_interface(router['id'],
+                                                     subnet_id=sub6['id'])
 
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            self.routers_client.remove_router_interface,
+                            router['id'], subnet_id=sub6['id'])
+
+            self.subnets_v6.append(sub6)
         return [self.network, self.network_v6] if dualnet else [self.network]
 
     @staticmethod
     def define_server_ips(srv):
         ips = {'4': None, '6': []}
-        for net_name, nics in six.iteritems(srv['addresses']):
+        for net_name, nics in srv['addresses'].items():
             for nic in nics:
                 if nic['version'] == 6:
                     ips['6'].append(nic['addr'])
@@ -119,12 +126,12 @@
         srv = self.create_server(
             key_name=self.keypair['name'],
             security_groups=[{'name': self.sec_grp['name']}],
-            networks=[{'uuid': n.id} for n in networks],
+            networks=[{'uuid': n['id']} for n in networks],
             wait_until='ACTIVE')
         fip = self.create_floating_ip(thing=srv)
         ips = self.define_server_ips(srv=srv)
         ssh = self.get_remote_client(
-            ip_address=fip.floating_ip_address,
+            ip_address=fip['floating_ip_address'],
             username=username)
         return ssh, ips, srv["id"]
 
@@ -139,13 +146,13 @@
         """
         ports = [p["mac_address"] for p in
                  self._list_ports(device_id=sid,
-                                  network_id=self.network_v6.id)]
+                                  network_id=self.network_v6['id'])]
         self.assertEqual(1, len(ports),
                          message=("Multiple IPv6 ports found on network %s. "
                                   "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,
@@ -177,10 +184,10 @@
             srv2_v6_addr_assigned = functools.partial(
                 guest_has_address, sshv4_2, ips_from_api_2['6'][i])
 
-            self.assertTrue(test.call_until_true(srv1_v6_addr_assigned,
+            self.assertTrue(test_utils.call_until_true(srv1_v6_addr_assigned,
                             CONF.validation.ping_timeout, 1))
 
-            self.assertTrue(test.call_until_true(srv2_v6_addr_assigned,
+            self.assertTrue(test_utils.call_until_true(srv2_v6_addr_assigned,
                             CONF.validation.ping_timeout, 1))
 
         self._check_connectivity(sshv4_1, ips_from_api_2['4'])
@@ -190,11 +197,11 @@
             self._check_connectivity(sshv4_1,
                                      ips_from_api_2['6'][i])
             self._check_connectivity(sshv4_1,
-                                     self.subnets_v6[i].gateway_ip)
+                                     self.subnets_v6[i]['gateway_ip'])
             self._check_connectivity(sshv4_2,
                                      ips_from_api_1['6'][i])
             self._check_connectivity(sshv4_2,
-                                     self.subnets_v6[i].gateway_ip)
+                                     self.subnets_v6[i]['gateway_ip'])
 
     def _check_connectivity(self, source, dest):
         self.assertTrue(
diff --git a/tempest/scenario/test_object_storage_basic_ops.py b/tempest/scenario/test_object_storage_basic_ops.py
index 63ffa0b..1d2b2b6 100644
--- a/tempest/scenario/test_object_storage_basic_ops.py
+++ b/tempest/scenario/test_object_storage_basic_ops.py
@@ -13,31 +13,26 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import config
 from tempest.scenario import manager
 from tempest import test
 
-CONF = config.CONF
-
 
 class TestObjectStorageBasicOps(manager.ObjectStorageScenarioTest):
-    """Test swift basic ops.
-
-     * get swift stat.
-     * create container.
-     * upload a file to the created container.
-     * list container's objects and assure that the uploaded file is present.
-     * download the object and check the content
-     * delete object from container.
-     * list container's objects and assure that the deleted file is gone.
-     * delete a container.
-     * list containers and assure that the deleted container is gone.
-     * change ACL of the container and make sure it works successfully
-    """
-
     @test.idempotent_id('b920faf1-7b8a-4657-b9fe-9c4512bfb381')
     @test.services('object_storage')
     def test_swift_basic_ops(self):
+        """Test swift basic ops.
+
+         * get swift stat.
+         * create container.
+         * upload a file to the created container.
+         * list container's objects and assure that the uploaded file is
+         present.
+         * download the object and check the content
+         * delete object from container.
+         * list container's objects and assure that the deleted file is gone.
+         * delete a container.
+        """
         self.get_swift_stat()
         container_name = self.create_container()
         obj_name, obj_data = self.upload_object_to_container(container_name)
diff --git a/tempest/scenario/test_object_storage_telemetry_middleware.py b/tempest/scenario/test_object_storage_telemetry_middleware.py
deleted file mode 100644
index eee4d3d..0000000
--- a/tempest/scenario/test_object_storage_telemetry_middleware.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# Copyright 2014 Red Hat
-#
-# 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_log import log as logging
-
-from tempest import config
-from tempest.scenario import manager
-from tempest import test
-
-CONF = config.CONF
-
-LOG = logging.getLogger(__name__)
-
-# Loop for up to 120 seconds waiting on notifications
-# NOTE(chdent): The choice of 120 seconds is fairly
-# arbitrary: Long enough to give the notifications the
-# chance to travel across a highly latent bus but not
-# so long as to allow excessive latency to never be visible.
-# TODO(chdent): Ideally this value would come from configuration.
-NOTIFICATIONS_WAIT = 120
-NOTIFICATIONS_SLEEP = 1
-
-
-class TestObjectStorageTelemetry(manager.ObjectStorageScenarioTest):
-    """Test that swift uses the ceilometer middleware.
-
-     * create container.
-     * upload a file to the created container.
-     * retrieve the file from the created container.
-     * wait for notifications from ceilometer.
-    """
-
-    @classmethod
-    def skip_checks(cls):
-        super(TestObjectStorageTelemetry, cls).skip_checks()
-        if not CONF.service_available.ceilometer:
-            skip_msg = ("%s skipped as ceilometer is not available" %
-                        cls.__name__)
-            raise cls.skipException(skip_msg)
-
-    @classmethod
-    def setup_clients(cls):
-        super(TestObjectStorageTelemetry, cls).setup_clients()
-        cls.telemetry_client = cls.os_operator.telemetry_client
-
-    def _confirm_notifications(self, container_name, obj_name):
-        # NOTE: Loop seeking for appropriate notifications about the containers
-        # and objects sent to swift.
-
-        def _check_samples():
-            # NOTE: Return True only if we have notifications about some
-            # containers and some objects and the notifications are about
-            # the expected containers and objects.
-            # Otherwise returning False will case _check_samples to be
-            # called again.
-            results = self.telemetry_client.list_samples(
-                'storage.objects.incoming.bytes')
-            LOG.debug('got samples %s', results)
-
-            # Extract container info from samples.
-            containers, objects = [], []
-            for sample in results:
-                meta = sample['resource_metadata']
-                if meta.get('container') and meta['container'] != 'None':
-                    containers.append(meta['container'])
-                elif (meta.get('target.metadata:container') and
-                      meta['target.metadata:container'] != 'None'):
-                    containers.append(meta['target.metadata:container'])
-
-                if meta.get('object') and meta['object'] != 'None':
-                    objects.append(meta['object'])
-                elif (meta.get('target.metadata:object') and
-                      meta['target.metadata:object'] != 'None'):
-                    objects.append(meta['target.metadata:object'])
-
-            return (container_name in containers and obj_name in objects)
-
-        self.assertTrue(test.call_until_true(_check_samples,
-                                             NOTIFICATIONS_WAIT,
-                                             NOTIFICATIONS_SLEEP),
-                        'Correct notifications were not received after '
-                        '%s seconds.' % NOTIFICATIONS_WAIT)
-
-    @test.idempotent_id('6d6b88e5-3e38-41bc-b34a-79f713a6cb84')
-    @test.services('object_storage', 'telemetry')
-    def test_swift_middleware_notifies(self):
-        container_name = self.create_container()
-        obj_name, _ = self.upload_object_to_container(container_name)
-        self._confirm_notifications(container_name, obj_name)
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 18bd764..f8c5c0a 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -12,14 +12,17 @@
 #    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
+import testtools
 
-from tempest import clients
 from tempest.common.utils import data_utils
+from tempest.common.utils import net_info
 from tempest import config
 from tempest.scenario import manager
 from tempest import test
 
 CONF = config.CONF
+LOG = log.getLogger(__name__)
 
 
 class TestSecurityGroupsBasicOps(manager.NetworkScenarioTest):
@@ -43,6 +46,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
@@ -103,9 +112,9 @@
             access point
         """
 
-        def __init__(self, credentials):
-            self.manager = clients.Manager(credentials)
+        def __init__(self, clients):
             # Credentials from manager are filled with both names and IDs
+            self.manager = clients
             self.creds = self.manager.credentials
             self.network = None
             self.subnet = None
@@ -121,47 +130,56 @@
     @classmethod
     def skip_checks(cls):
         super(TestSecurityGroupsBasicOps, cls).skip_checks()
-        if CONF.baremetal.driver_enabled:
-            msg = ('Not currently supported by baremetal.')
-            raise cls.skipException(msg)
         if CONF.network.port_vnic_type in ['direct', 'macvtap']:
             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'):
             msg = "security-group extension not enabled."
             raise cls.skipException(msg)
+        if CONF.network.shared_physical_network:
+            msg = ('Deployment uses a shared physical network, security '
+                   'groups not supported')
+            raise cls.skipException(msg)
 
     @classmethod
     def setup_credentials(cls):
         # Create no network resources for these tests.
         cls.set_network_resources()
         super(TestSecurityGroupsBasicOps, cls).setup_credentials()
-        # TODO(mnewby) Consider looking up entities as needed instead
-        # of storing them as collections on the class.
-
-        # Credentials from the manager are filled with both IDs and Names
-        cls.alt_creds = cls.alt_manager.credentials
 
     @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
-        cls.primary_tenant = cls.TenantProperties(creds)
-        cls.alt_tenant = cls.TenantProperties(cls.alt_creds)
+        cls.primary_tenant = cls.TenantProperties(cls.os)
+        cls.alt_tenant = cls.TenantProperties(cls.os_alt)
         for tenant in [cls.primary_tenant, cls.alt_tenant]:
             cls.tenants[tenant.creds.tenant_id] = tenant
 
         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 +196,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,
@@ -205,49 +223,66 @@
         seen_names = [n['name'] for n in seen_nets]
         seen_ids = [n['id'] for n in seen_nets]
 
-        self.assertIn(tenant.network.name, seen_names)
-        self.assertIn(tenant.network.id, seen_ids)
+        self.assertIn(tenant.network['name'], seen_names)
+        self.assertIn(tenant.network['id'], seen_ids)
 
         seen_subnets = [(n['id'], n['cidr'], n['network_id'])
                         for n in self._list_subnets()]
-        mysubnet = (tenant.subnet.id, tenant.subnet.cidr, tenant.network.id)
+        mysubnet = (tenant.subnet['id'], tenant.subnet['cidr'],
+                    tenant.network['id'])
         self.assertIn(mysubnet, seen_subnets)
 
         seen_routers = self._list_routers()
         seen_router_ids = [n['id'] for n in seen_routers]
         seen_router_names = [n['name'] for n in seen_routers]
 
-        self.assertIn(tenant.router.name, seen_router_names)
-        self.assertIn(tenant.router.id, seen_router_ids)
+        self.assertIn(tenant.router['name'], seen_router_names)
+        self.assertIn(tenant.router['id'], seen_router_ids)
 
-        myport = (tenant.router.id, tenant.subnet.id)
+        myport = (tenant.router['id'], tenant.subnet['id'])
         router_ports = [(i['device_id'], i['fixed_ips'][0]['subnet_id']) for i
                         in self._list_ports()
-                        if self._is_router_port(i)]
+                        if net_info.is_router_interface_port(i)]
 
         self.assertIn(myport, router_ports)
 
-    def _is_router_port(self, port):
-        """Return True if port is a router interface."""
-        # NOTE(armando-migliaccio): match device owner for both centralized
-        # and distributed routers; 'device_owner' is "" by default.
-        return port['device_owner'].startswith('network:router_interface')
+    def _create_server(self, name, tenant, security_groups, **kwargs):
+        """Creates a server and assigns it to security group.
 
-    def _create_server(self, name, tenant, security_groups=None):
-        """creates a server and assigns to security group"""
-        if security_groups is None:
-            security_groups = [tenant.security_groups['default']]
+        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.
+        """
         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}],
+            networks=[{'uuid': tenant.network["id"]}],
             key_name=tenant.keypair['name'],
             security_groups=security_groups_names,
             wait_until='ACTIVE',
-            clients=tenant.manager)
-        self.assertEqual(
-            sorted([s['name'] for s in security_groups]),
-            sorted([s['name'] for s in server['security_groups']]))
+            clients=tenant.manager,
+            **kwargs)
+        if 'security_groups' in server:
+            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):
@@ -257,12 +292,13 @@
                    num=i
             )
             name = data_utils.rand_name(name)
-            server = self._create_server(name, tenant)
+            server = self._create_server(name, tenant,
+                                         [tenant.security_groups['default']])
             tenant.servers.append(server)
 
     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(
@@ -280,11 +316,12 @@
             client=tenant.manager.floating_ips_client)
         self.floating_ips.setdefault(server['id'], floating_ip)
 
-    def _create_tenant_network(self, tenant):
+    def _create_tenant_network(self, tenant, port_security_enabled=True):
         network, subnet, router = self.create_networks(
-            client=tenant.manager.network_client,
             networks_client=tenant.manager.networks_client,
-            subnets_client=tenant.manager.subnets_client)
+            routers_client=tenant.manager.routers_client,
+            subnets_client=tenant.manager.subnets_client,
+            port_security_enabled=port_security_enabled)
         tenant.set_network(network, subnet, router)
 
     def _deploy_tenant(self, tenant_or_id):
@@ -308,10 +345,10 @@
     def _get_server_ip(self, server, floating=False):
         """returns the ip (floating/internal) of a server"""
         if floating:
-            server_ip = self.floating_ips[server['id']].floating_ip_address
+            server_ip = self.floating_ips[server['id']]['floating_ip_address']
         else:
             server_ip = None
-            network_name = self.tenants[server['tenant_id']].network.name
+            network_name = self.tenants[server['tenant_id']].network['name']
             if network_name in server['addresses']:
                 server_ip = server['addresses'][network_name][0]['addr']
         return server_ip
@@ -319,7 +356,7 @@
     def _connect_to_access_point(self, tenant):
         """create ssh connection to tenant access point"""
         access_point_ssh = \
-            self.floating_ips[tenant.access_point['id']].floating_ip_address
+            self.floating_ips[tenant.access_point['id']]['floating_ip_address']
         private_key = tenant.keypair['private_key']
         access_point_ssh = self.get_remote_client(
             access_point_ssh, private_key=private_key)
@@ -343,11 +380,12 @@
     def _test_in_tenant_allow(self, tenant):
         ruleset = dict(
             protocol='icmp',
-            remote_group_id=tenant.security_groups['default'].id,
+            remote_group_id=tenant.security_groups['default']['id'],
             direction='ingress'
         )
         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)
@@ -418,7 +456,7 @@
             for port in port_list if port['fixed_ips']
         ]
         server_ip = self._get_server_ip(tenant.access_point)
-        subnet_id = tenant.subnet.id
+        subnet_id = tenant.subnet['id']
         self.assertIn((subnet_id, server_ip, mac_addr), port_detail_list)
 
     @test.idempotent_id('e79f879e-debb-440c-a7e4-efeda05b6848')
@@ -427,7 +465,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)
@@ -486,7 +524,8 @@
                tenant=new_tenant.creds.tenant_name
         )
         name = data_utils.rand_name(name)
-        server = self._create_server(name, new_tenant)
+        server = self._create_server(name, new_tenant,
+                                     [new_tenant.security_groups['default']])
 
         # Check connectivity failure with default security group
         try:
@@ -499,7 +538,7 @@
 
             # update port with new security group and check connectivity
             self.ports_client.update_port(port_id, security_groups=[
-                new_tenant.security_groups['new_sg'].id])
+                new_tenant.security_groups['new_sg']['id']])
             self._check_connectivity(
                 access_point=access_point_ssh,
                 ip=self._get_server_ip(server))
@@ -552,7 +591,8 @@
                tenant=new_tenant.creds.tenant_name
         )
         name = data_utils.rand_name(name)
-        server = self._create_server(name, new_tenant)
+        server = self._create_server(name, new_tenant,
+                                     [new_tenant.security_groups['default']])
 
         access_point_ssh = self._connect_to_access_point(new_tenant)
         server_id = server['id']
@@ -577,3 +617,32 @@
             for tenant in self.tenants.values():
                 self._log_console_output(servers=tenant.servers)
             raise
+
+    @test.requires_ext(service='network', extension='port-security')
+    @test.idempotent_id('13ccf253-e5ad-424b-9c4a-97b88a026699')
+    @testtools.skipUnless(
+        CONF.compute_feature_enabled.allow_port_security_disabled,
+        'Port security must be enabled.')
+    # TODO(mriedem): We shouldn't actually need to check this since neutron
+    # disables the port_security extension by default, but the problem is nova
+    # assumes port_security_enabled=True if it's not set on the network
+    # resource, which will mean nova may attempt to apply a security group on
+    # a port on that network which would fail. This is really a bug in nova.
+    @testtools.skipUnless(
+        CONF.network_feature_enabled.port_security,
+        'Port security must be enabled.')
+    @test.services('compute', 'network')
+    def test_boot_into_disabled_port_security_network_without_secgroup(self):
+        tenant = self.primary_tenant
+        self._create_tenant_network(tenant, port_security_enabled=False)
+        self.assertFalse(tenant.network['port_security_enabled'])
+        name = data_utils.rand_name('server-smoke')
+        sec_groups = []
+        server = self._create_server(name, tenant, sec_groups)
+        server_id = server['id']
+        ports = self._list_ports(device_id=server_id)
+        self.assertEqual(1, len(ports))
+        for port in ports:
+            self.assertEmpty(port['security_groups'],
+                             "Neutron shouldn't even use it's default sec "
+                             "group.")
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 4b932ce..504d72b 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -31,7 +31,7 @@
     """The test suite for server advanced operations
 
     This test case stresses some advanced server instance operations:
-     * Resizing an instance
+     * Resizing a volume-backed instance
      * Sequence suspend resume
     """
 
@@ -50,10 +50,10 @@
     @test.idempotent_id('e6c28180-7454-4b59-b188-0257af08a63b')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize is not available.')
-    @test.services('compute')
-    def test_resize_server_confirm(self):
+    @test.services('compute', 'volume')
+    def test_resize_volume_backed_server_confirm(self):
         # We create an instance for use in this test
-        instance = self.create_server(wait_until='ACTIVE')
+        instance = self.create_server(wait_until='ACTIVE', volume_backed=True)
         instance_id = instance['id']
         resize_flavor = CONF.compute.flavor_ref_alt
         LOG.debug("Resizing instance %s from flavor %s to flavor %s",
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index dcb095b..2d2f7df 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -14,21 +14,17 @@
 #    under the License.
 
 import json
+import re
 
-from oslo_log import log as logging
-
+from tempest.common import waiters
 from tempest import config
-from tempest import exceptions
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
 from tempest.scenario import manager
-from tempest.scenario import utils as test_utils
 from tempest import test
 
 CONF = config.CONF
 
-LOG = logging.getLogger(__name__)
-
-load_tests = test_utils.load_tests_input_scenario_utils
-
 
 class TestServerBasicOps(manager.ScenarioTest):
 
@@ -47,27 +43,10 @@
 
     def setUp(self):
         super(TestServerBasicOps, self).setUp()
-        # Setup image and flavor the test instance
-        # Support both configured and injected values
-        if not hasattr(self, 'image_ref'):
-            self.image_ref = CONF.compute.image_ref
-        if not hasattr(self, 'flavor_ref'):
-            self.flavor_ref = CONF.compute.flavor_ref
-        self.image_utils = test_utils.ImageUtils(self.manager)
-        if not self.image_utils.is_flavor_enough(self.flavor_ref,
-                                                 self.image_ref):
-            raise self.skipException(
-                '{image} does not fit in {flavor}'.format(
-                    image=self.image_ref, flavor=self.flavor_ref
-                )
-            )
-        self.run_ssh = CONF.validation.run_validation and \
-            self.image_utils.is_sshable_image(self.image_ref)
-        self.ssh_user = self.image_utils.ssh_user(self.image_ref)
-        LOG.debug('Starting test for i:{image}, f:{flavor}. '
-                  'Run ssh: {ssh}, user: {ssh_user}'.format(
-                      image=self.image_ref, flavor=self.flavor_ref,
-                      ssh=self.run_ssh, ssh_user=self.ssh_user))
+        self.image_ref = CONF.compute.image_ref
+        self.flavor_ref = CONF.compute.flavor_ref
+        self.run_ssh = CONF.validation.run_validation
+        self.ssh_user = CONF.validation.image_ssh_user
 
     def verify_ssh(self, keypair):
         if self.run_ssh:
@@ -76,7 +55,7 @@
             # Check ssh
             self.ssh_client = self.get_remote_client(
                 ip_address=self.fip,
-                username=self.image_utils.ssh_user(self.image_ref),
+                username=self.ssh_user,
                 private_key=keypair['private_key'])
 
     def verify_metadata(self):
@@ -93,46 +72,68 @@
                     self.assertEqual(self.fip, result, msg)
                     return 'Verification is successful!'
 
-            if not test.call_until_true(exec_cmd_and_verify_output,
-                                        CONF.compute.build_timeout,
-                                        CONF.compute.build_interval):
+            if not test_utils.call_until_true(exec_cmd_and_verify_output,
+                                              CONF.compute.build_timeout,
+                                              CONF.compute.build_interval):
                 raise exceptions.TimeoutException('Timed out while waiting to '
                                                   'verify metadata on server. '
                                                   '%s is empty.' % md_url)
 
+    def _mount_config_drive(self):
+        cmd_blkid = 'blkid | grep -i config-2'
+        result = self.ssh_client.exec_command(cmd_blkid)
+        dev_name = re.match('([^:]+)', result).group()
+        self.ssh_client.exec_command('sudo mount %s /mnt' % dev_name)
+
+    def _unmount_config_drive(self):
+        self.ssh_client.exec_command('sudo umount /mnt')
+
     def verify_metadata_on_config_drive(self):
         if self.run_ssh and CONF.compute_feature_enabled.config_drive:
             # Verify metadata on config_drive
-            cmd_blkid = 'blkid -t LABEL=config-2 -o device'
-            dev_name = self.ssh_client.exec_command(cmd_blkid)
-            dev_name = dev_name.rstrip()
-            self.ssh_client.exec_command('sudo mount %s /mnt' % dev_name)
+            self._mount_config_drive()
             cmd_md = 'sudo cat /mnt/openstack/latest/meta_data.json'
             result = self.ssh_client.exec_command(cmd_md)
-            self.ssh_client.exec_command('sudo umount /mnt')
+            self._unmount_config_drive()
             result = json.loads(result)
             self.assertIn('meta', result)
             msg = ('Failed while verifying metadata on config_drive on server.'
                    ' Result of command "%s" is NOT "%s".' % (cmd_md, self.md))
             self.assertEqual(self.md, result['meta'], msg)
 
+    def verify_networkdata_on_config_drive(self):
+        if self.run_ssh and CONF.compute_feature_enabled.config_drive:
+            # Verify network data on config_drive
+            self._mount_config_drive()
+            cmd_md = 'sudo cat /mnt/openstack/latest/network_data.json'
+            result = self.ssh_client.exec_command(cmd_md)
+            self._unmount_config_drive()
+            result = json.loads(result)
+            self.assertIn('services', result)
+            self.assertIn('links', result)
+            self.assertIn('networks', result)
+            # TODO(clarkb) construct network_data from known network
+            # instance info and do direct comparison.
+
     @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']}]
+        security_group = self._create_security_group()
         self.md = {'meta1': 'data1', 'meta2': 'data2', 'metaN': 'dataN'}
         self.instance = self.create_server(
             image_id=self.image_ref,
             flavor=self.flavor_ref,
             key_name=keypair['name'],
-            security_groups=security_groups,
+            security_groups=[{'name': security_group['name']}],
             config_drive=CONF.compute_feature_enabled.config_drive,
             metadata=self.md,
             wait_until='ACTIVE')
         self.verify_ssh(keypair)
         self.verify_metadata()
         self.verify_metadata_on_config_drive()
+        self.verify_networkdata_on_config_drive()
         self.servers_client.delete_server(self.instance['id'])
+        waiters.wait_for_server_termination(
+            self.servers_client, self.instance['id'], ignore_error=False)
diff --git a/tempest/scenario/test_server_multinode.py b/tempest/scenario/test_server_multinode.py
index 0cf72c3..170d220 100644
--- a/tempest/scenario/test_server_multinode.py
+++ b/tempest/scenario/test_server_multinode.py
@@ -15,7 +15,7 @@
 
 
 from tempest import config
-from tempest import exceptions
+from tempest.lib import exceptions
 from tempest.scenario import manager
 from tempest import test
 
@@ -42,15 +42,22 @@
         # this is needed so that we can use the availability_zone:host
         # scheduler hint, which is admin_only by default
         cls.servers_client = cls.admin_manager.servers_client
-        super(TestServerMultinode, cls).resource_setup()
 
     @test.idempotent_id('9cecbe35-b9d4-48da-a37e-7ce70aa43d30')
     @test.attr(type='smoke')
     @test.services('compute', 'network')
     def test_schedule_to_all_nodes(self):
-        host_client = self.manager.hosts_client
-        hosts = host_client.list_hosts()['hosts']
-        hosts = [x for x in hosts if x['service'] == 'compute']
+        available_zone = \
+            self.os_adm.availability_zone_client.list_availability_zones(
+                detail=True)['availabilityZoneInfo']
+        hosts = []
+        for zone in available_zone:
+            if zone['zoneState']['available']:
+                for host in zone['hosts']:
+                    if 'nova-compute' in zone['hosts'][host] and \
+                        zone['hosts'][host]['nova-compute']['available']:
+                        hosts.append({'zone': zone['zoneName'],
+                                      'host_name': host})
 
         # ensure we have at least as many compute hosts as we expect
         if len(hosts) < CONF.compute.min_compute_nodes:
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 77de47e..7f04b0d 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
-
+from tempest.common import compute
 from tempest.common import waiters
 from tempest import config
 from tempest.scenario import manager
@@ -34,19 +33,16 @@
 
     """
 
+    @classmethod
+    def skip_checks(cls):
+        super(TestShelveInstance, cls).skip_checks()
+        if not CONF.compute_feature_enabled.shelve:
+            raise cls.skipException("Shelve is not available.")
+
     def _shelve_then_unshelve_server(self, server):
-        self.servers_client.shelve_server(server['id'])
-        offload_time = CONF.compute.shelved_offload_time
-        if offload_time >= 0:
-            waiters.wait_for_server_status(self.servers_client, server['id'],
-                                           'SHELVED_OFFLOADED',
-                                           extra_timeout=offload_time)
-        else:
-            waiters.wait_for_server_status(self.servers_client,
-                                           server['id'], 'SHELVED')
-            self.servers_client.shelve_offload_server(server['id'])
-            waiters.wait_for_server_status(self.servers_client, server['id'],
-                                           'SHELVED_OFFLOADED')
+        compute.shelve_server(self.servers_client, server['id'],
+                              force_shelve_offload=True)
+
         self.servers_client.unshelve_server(server['id'])
         waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'ACTIVE')
@@ -57,25 +53,12 @@
         security_group = self._create_security_group()
         security_groups = [{'name': security_group['name']}]
 
-        if boot_from_volume:
-            volume = self.create_volume(size=CONF.volume.volume_size,
-                                        imageRef=CONF.compute.image_ref)
-            bd_map = [{
-                'device_name': 'vda',
-                'volume_id': volume['id'],
-                'delete_on_termination': '0'}]
-
-            server = self.create_server(
-                key_name=keypair['name'],
-                security_groups=security_groups,
-                block_device_mapping=bd_map,
-                wait_until='ACTIVE')
-        else:
-            server = self.create_server(
-                image_id=CONF.compute.image_ref,
-                key_name=keypair['name'],
-                security_groups=security_groups,
-                wait_until='ACTIVE')
+        server = self.create_server(
+            image_id=CONF.compute.image_ref,
+            key_name=keypair['name'],
+            security_groups=security_groups,
+            wait_until='ACTIVE',
+            volume_backed=boot_from_volume)
 
         instance_ip = self.get_server_ip(server)
         timestamp = self.create_timestamp(instance_ip,
@@ -91,15 +74,11 @@
         self.assertEqual(timestamp, timestamp2)
 
     @test.idempotent_id('1164e700-0af0-4a4c-8792-35909a88743c')
-    @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
-                          'Shelve is not available.')
     @test.services('compute', 'network', 'image')
     def test_shelve_instance(self):
         self._create_server_then_shelve_and_unshelve()
 
     @test.idempotent_id('c1b6318c-b9da-490b-9c67-9339b627271f')
-    @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
-                          'Shelve is not available.')
     @test.services('compute', 'volume', 'network', 'image')
     def test_shelve_volume_backed_instance(self):
         self._create_server_then_shelve_and_unshelve(boot_from_volume=True)
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index d6528a3..47c6e8d 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
-
 from tempest import config
 from tempest.scenario import manager
 from tempest import test
@@ -33,9 +31,13 @@
 
     """
 
+    @classmethod
+    def skip_checks(cls):
+        super(TestSnapshotPattern, cls).skip_checks()
+        if not CONF.compute_feature_enabled.snapshot:
+            raise cls.skipException("Snapshotting is not available.")
+
     @test.idempotent_id('608e604b-1d63-4a82-8e3e-91bc665c90b4')
-    @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
-                          'Snapshotting is not available.')
     @test.services('compute', 'network', 'image')
     def test_snapshot_pattern(self):
         # prepare for booting an instance
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 1d09fe7..b10be11 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -16,13 +16,14 @@
 import time
 
 from oslo_log import log as logging
-from tempest_lib import decorators
-from tempest_lib import exceptions as lib_exc
 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.common.utils import test_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 from tempest.scenario import manager
 from tempest import test
 
@@ -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
 
@@ -83,14 +85,14 @@
         ssh = self.get_remote_client(ip_address, private_key=private_key)
 
         def _func():
-            part = ssh.get_partitions()
-            LOG.debug("Partitions:%s" % part)
-            return CONF.compute.volume_device_name in part
+            disks = ssh.get_disks()
+            LOG.debug("Disks: %s", disks)
+            return CONF.compute.volume_device_name in disks
 
-        if not test.call_until_true(_func,
-                                    CONF.compute.build_timeout,
-                                    CONF.compute.build_interval):
-            raise exceptions.TimeoutException
+        if not test_utils.call_until_true(_func,
+                                          CONF.compute.build_timeout,
+                                          CONF.compute.build_interval):
+            raise lib_exc.TimeoutException
 
     @decorators.skip_because(bug="1205344")
     @test.idempotent_id('10fd234a-515c-41e5-b092-8323060598c5')
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 4ce57db..2c8b618 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,21 +19,15 @@
 from tempest import test
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
 class TestVolumeBootPattern(manager.ScenarioTest):
 
-    """This test case attempts to reproduce the following steps:
+    # Boot from volume scenario is quite slow, and needs extra
+    # breathing room to get through deletes in the time allotted.
+    TIMEOUT_SCALING_FACTOR = 2
 
-     * Create in Cinder some bootable volume importing a Glance image
-     * Boot an instance from the bootable volume
-     * Write content to the volume
-     * Delete an instance and Boot a new instance from the volume
-     * Check written content in the instance
-     * Create a volume snapshot while the instance is running
-     * Boot an additional instance from the new snapshot based volume
-     * Check written content in the instance booted from snapshot
-    """
     @classmethod
     def skip_checks(cls):
         super(TestVolumeBootPattern, cls).skip_checks()
@@ -40,7 +36,8 @@
 
     def _create_volume_from_image(self):
         img_uuid = CONF.compute.image_ref
-        vol_name = data_utils.rand_name('volume-origin')
+        vol_name = data_utils.rand_name(
+            self.__class__.__name__ + '-volume-origin')
         return self.create_volume(name=vol_name, imageRef=img_uuid)
 
     def _get_bdm(self, vol_id, delete_on_termination=False):
@@ -71,7 +68,8 @@
             **create_kwargs)
 
     def _create_snapshot_from_volume(self, vol_id):
-        snap_name = data_utils.rand_name('snapshot')
+        snap_name = data_utils.rand_name(
+            self.__class__.__name__ + '-snapshot')
         snap = self.snapshots_client.create_snapshot(
             volume_id=vol_id,
             force=True,
@@ -79,7 +77,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:
@@ -89,10 +88,6 @@
 
         return snap
 
-    def _create_volume_from_snapshot(self, snap_id):
-        vol_name = data_utils.rand_name('volume')
-        return self.create_volume(name=vol_name, snapshot_id=snap_id)
-
     def _delete_server(self, server):
         self.servers_client.delete_server(server['id'])
         waiters.wait_for_server_termination(self.servers_client, server['id'])
@@ -101,42 +96,69 @@
     @test.attr(type='smoke')
     @test.services('compute', 'volume', 'image')
     def test_volume_boot_pattern(self):
+
+        """This test case attempts to reproduce the following steps:
+
+        * Create in Cinder some bootable volume importing a Glance image
+        * Boot an instance from the bootable volume
+        * Write content to the volume
+        * Delete an instance and Boot a new instance from the volume
+        * Check written content in the instance
+        * Create a volume snapshot while the instance is running
+        * Boot an additional instance from the new snapshot based volume
+        * Check written content in the instance booted from snapshot
+        """
+
+        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
-        volume = self._create_volume_from_snapshot(snapshot['id'])
+        LOG.info("Creating third instance from snapshot: %s", snapshot['id'])
+        volume = self.create_volume(snapshot_id=snapshot['id'],
+                                    size=snapshot['size'])
+        LOG.info("Booting third instance from snapshot")
         server_from_snapshot = (
             self._boot_instance_from_volume(volume['id'],
                                             keypair, security_group))
+        LOG.info("Booted third instance %s", server_from_snapshot)
 
         # 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'])
@@ -150,8 +172,7 @@
         instance = self._boot_instance_from_volume(volume_origin['id'],
                                                    delete_on_termination=True)
         # create EBS image
-        name = data_utils.rand_name('image')
-        image = self.create_server_snapshot(instance, name=name)
+        image = self.create_server_snapshot(instance)
 
         # delete instance
         self._delete_server(instance)
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
new file mode 100644
index 0000000..dfda18d
--- /dev/null
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -0,0 +1,128 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_log import log as logging
+
+from tempest.common import waiters
+from tempest import config
+from tempest.scenario import manager
+from tempest import test
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+
+class TestVolumeMigrateRetypeAttached(manager.ScenarioTest):
+
+    """This test case attempts to reproduce the following steps:
+
+     * Create 2 volume types representing 2 different backends
+     * Create in Cinder some bootable volume importing a Glance image using
+     *   volume_type_1
+     * Boot an instance from the bootable volume
+     * Write to the volume
+     * Perform a cinder retype --on-demand of the volume to type of backend #2
+     * Check written content of migrated volume
+    """
+
+    credentials = ['primary', 'admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(TestVolumeMigrateRetypeAttached, cls).setup_clients()
+        if CONF.volume_feature_enabled.api_v1:
+            cls.admin_volume_types_client = cls.os_adm.volume_types_client
+        else:
+            cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
+
+    @classmethod
+    def skip_checks(cls):
+        super(TestVolumeMigrateRetypeAttached, cls).skip_checks()
+        if not CONF.volume_feature_enabled.multi_backend:
+            raise cls.skipException("Cinder multi-backend feature disabled")
+
+        if len(set(CONF.volume.backend_names)) < 2:
+            raise cls.skipException("Requires at least two different "
+                                    "backend names")
+
+    def _boot_instance_from_volume(self, vol_id, keypair, security_group):
+
+        key_name = keypair['name']
+        security_groups = [{'name': security_group['name']}]
+        block_device_mapping = [{'device_name': 'vda', 'volume_id': vol_id,
+                                 'delete_on_termination': False}]
+
+        return self.create_server(image_id='', wait_until='ACTIVE',
+                                  key_name=key_name,
+                                  security_groups=security_groups,
+                                  block_device_mapping=block_device_mapping)
+
+    def _create_volume_types(self):
+        backend_names = CONF.volume.backend_names
+
+        backend_source = backend_names[0]
+        backend_dest = backend_names[1]
+
+        source_body = self.create_volume_type(backend_name=backend_source)
+        dest_body = self.create_volume_type(backend_name=backend_dest)
+
+        LOG.info("Created Volume types: %(src)s -> %(src_backend)s, %(dst)s "
+                 "-> %(dst_backend)s", {'src': source_body['name'],
+                                        'src_backend': backend_source,
+                                        'dst': dest_body['name'],
+                                        'dst_backend': backend_dest})
+        return source_body['name'], dest_body['name']
+
+    def _volume_retype_with_migration(self, volume_id, new_volume_type):
+        migration_policy = 'on-demand'
+        self.volumes_client.retype_volume(
+            volume_id, new_type=new_volume_type,
+            migration_policy=migration_policy)
+        waiters.wait_for_volume_retype(self.volumes_client,
+                                       volume_id, new_volume_type)
+
+    @test.idempotent_id('deadd2c2-beef-4dce-98be-f86765ff311b')
+    @test.services('compute', 'volume')
+    def test_volume_migrate_attached(self):
+        LOG.info("Creating keypair and security group")
+        keypair = self.create_keypair()
+        security_group = self._create_security_group()
+
+        # create volume types
+        LOG.info("Creating Volume types")
+        source_type, dest_type = self._create_volume_types()
+
+        # create an instance from volume
+        LOG.info("Booting instance from volume")
+        volume_origin = self.create_volume(imageRef=CONF.compute.image_ref,
+                                           volume_type=source_type)
+
+        instance = self._boot_instance_from_volume(volume_origin['id'],
+                                                   keypair, security_group)
+
+        # write content to volume on instance
+        LOG.info("Setting timestamp in instance %s", instance['id'])
+        ip_instance = self.get_server_ip(instance)
+        timestamp = self.create_timestamp(ip_instance,
+                                          private_key=keypair['private_key'])
+
+        # retype volume with migration from backend #1 to backend #2
+        LOG.info("Retyping Volume %s to new type %s", volume_origin['id'],
+                 dest_type)
+        self._volume_retype_with_migration(volume_origin['id'], dest_type)
+
+        # check the content of written file
+        LOG.info("Getting timestamp in postmigrated instance %s",
+                 instance['id'])
+        timestamp2 = self.get_timestamp(ip_instance,
+                                        private_key=keypair['private_key'])
+        self.assertEqual(timestamp, timestamp2)
diff --git a/tempest/scenario/utils.py b/tempest/scenario/utils.py
deleted file mode 100644
index 3cbb3bc..0000000
--- a/tempest/scenario/utils.py
+++ /dev/null
@@ -1,184 +0,0 @@
-# Copyright 2013 Hewlett-Packard, Ltd.
-#
-#    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 re
-import string
-import unicodedata
-
-from oslo_serialization import jsonutils as json
-from tempest_lib.common.utils import misc
-from tempest_lib import exceptions as exc_lib
-import testscenarios
-import testtools
-
-from tempest import clients
-from tempest.common import credentials_factory as credentials
-from tempest import config
-
-CONF = config.CONF
-
-
-class ImageUtils(object):
-
-    default_ssh_user = 'root'
-
-    def __init__(self, os):
-        # Load configuration items
-        self.ssh_users = json.loads(CONF.input_scenario.ssh_user_regex)
-        self.non_ssh_image_pattern = \
-            CONF.input_scenario.non_ssh_image_regex
-        # Setup clients
-        self.compute_images_client = os.compute_images_client
-        self.flavors_client = os.flavors_client
-
-    def ssh_user(self, image_id):
-        _image = self.compute_images_client.show_image(image_id)['image']
-        for regex, user in self.ssh_users:
-            # First match wins
-            if re.match(regex, _image['name']) is not None:
-                return user
-        else:
-            return self.default_ssh_user
-
-    def _is_sshable_image(self, image):
-        return not re.search(pattern=self.non_ssh_image_pattern,
-                             string=str(image['name']))
-
-    def is_sshable_image(self, image_id):
-        _image = self.compute_images_client.show_image(image_id)['image']
-        return self._is_sshable_image(_image)
-
-    def _is_flavor_enough(self, flavor, image):
-        return image['minDisk'] <= flavor['disk']
-
-    def is_flavor_enough(self, flavor_id, image_id):
-        _image = self.compute_images_client.show_image(image_id)['image']
-        _flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
-        return self._is_flavor_enough(_flavor, _image)
-
-
-@misc.singleton
-class InputScenarioUtils(object):
-
-    """Example usage:
-
-    import testscenarios
-    (...)
-    load_tests = testscenarios.load_tests_apply_scenarios
-
-
-    class TestInputScenario(manager.ScenarioTest):
-
-        scenario_utils = utils.InputScenarioUtils()
-        scenario_flavor = scenario_utils.scenario_flavors
-        scenario_image = scenario_utils.scenario_images
-        scenarios = testscenarios.multiply_scenarios(scenario_image,
-                                                     scenario_flavor)
-
-        def test_create_server_metadata(self):
-            name = rand_name('instance')
-            self.servers_client.create_server(name=name,
-                                              flavorRef=self.flavor_ref,
-                                              imageRef=self.image_ref)
-    """
-    validchars = "-_.{ascii}{digit}".format(ascii=string.ascii_letters,
-                                            digit=string.digits)
-
-    def __init__(self):
-        network_resources = {
-            'network': False,
-            'router': False,
-            'subnet': False,
-            'dhcp': False,
-        }
-        self.cred_provider = credentials.get_credentials_provider(
-            name='InputScenarioUtils',
-            identity_version=CONF.identity.auth_version,
-            network_resources=network_resources)
-        os = clients.Manager(self.cred_provider.get_primary_creds())
-        self.compute_images_client = os.compute_images_client
-        self.flavors_client = os.flavors_client
-        self.image_pattern = CONF.input_scenario.image_regex
-        self.flavor_pattern = CONF.input_scenario.flavor_regex
-
-    def _normalize_name(self, name):
-        nname = unicodedata.normalize('NFKD', name).encode('ASCII', 'ignore')
-        nname = ''.join(c for c in nname if c in self.validchars)
-        return nname
-
-    def clear_creds(self):
-        self.cred_provider.clear_creds()
-
-    @property
-    def scenario_images(self):
-        """:return: a scenario with name and uuid of images"""
-        if not CONF.service_available.glance:
-            return []
-        if not hasattr(self, '_scenario_images'):
-            try:
-                images = self.compute_images_client.list_images()['images']
-                self._scenario_images = [
-                    (self._normalize_name(i['name']), dict(image_ref=i['id']))
-                    for i in images if re.search(self.image_pattern,
-                                                 str(i['name']))
-                ]
-            except Exception:
-                self._scenario_images = []
-        return self._scenario_images
-
-    @property
-    def scenario_flavors(self):
-        """:return: a scenario with name and uuid of flavors"""
-        if not hasattr(self, '_scenario_flavors'):
-            try:
-                flavors = self.flavors_client.list_flavors()['flavors']
-                self._scenario_flavors = [
-                    (self._normalize_name(f['name']), dict(flavor_ref=f['id']))
-                    for f in flavors if re.search(self.flavor_pattern,
-                                                  str(f['name']))
-                ]
-            except Exception:
-                self._scenario_flavors = []
-        return self._scenario_flavors
-
-
-def load_tests_input_scenario_utils(*args):
-    """Wrapper for testscenarios to set the scenarios
-
-    The purpose is to avoid running a getattr on the CONF object at import.
-    """
-
-    if getattr(args[0], 'suiteClass', None) is not None:
-        loader, standard_tests, pattern = args
-    else:
-        standard_tests, module, loader = args
-    output = None
-    scenario_utils = None
-    try:
-        scenario_utils = InputScenarioUtils()
-        scenario_flavor = scenario_utils.scenario_flavors
-        scenario_image = scenario_utils.scenario_images
-    except (exc_lib.InvalidCredentials, TypeError):
-        output = standard_tests
-    finally:
-        if scenario_utils:
-            scenario_utils.clear_creds()
-    if output is not None:
-        return output
-    for test in testtools.iterate_tests(standard_tests):
-        setattr(test, 'scenarios', testscenarios.multiply_scenarios(
-            scenario_image,
-            scenario_flavor))
-    return testscenarios.load_tests_apply_scenarios(*args)
diff --git a/tempest/services/baremetal/__init__.py b/tempest/services/baremetal/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/baremetal/__init__.py
+++ /dev/null
diff --git a/tempest/services/baremetal/base.py b/tempest/services/baremetal/base.py
deleted file mode 100644
index d8cb99d..0000000
--- a/tempest/services/baremetal/base.py
+++ /dev/null
@@ -1,205 +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 functools
-
-from oslo_serialization import jsonutils as json
-import six
-from six.moves.urllib import parse as urllib
-
-from tempest.common import service_client
-
-
-def handle_errors(f):
-    """A decorator that allows to ignore certain types of errors."""
-
-    @functools.wraps(f)
-    def wrapper(*args, **kwargs):
-        param_name = 'ignore_errors'
-        ignored_errors = kwargs.get(param_name, tuple())
-
-        if param_name in kwargs:
-            del kwargs[param_name]
-
-        try:
-            return f(*args, **kwargs)
-        except ignored_errors:
-            # Silently ignore errors
-            pass
-
-    return wrapper
-
-
-class BaremetalClient(service_client.ServiceClient):
-    """Base Tempest REST client for Ironic API."""
-
-    uri_prefix = ''
-
-    def serialize(self, object_dict):
-        """Serialize an Ironic object."""
-
-        return json.dumps(object_dict)
-
-    def deserialize(self, object_str):
-        """Deserialize an Ironic object."""
-
-        return json.loads(object_str)
-
-    def _get_uri(self, resource_name, uuid=None, permanent=False):
-        """Get URI for a specific resource or object.
-
-        :param resource_name: The name of the REST resource, e.g., 'nodes'.
-        :param uuid: The unique identifier of an object in UUID format.
-        :returns: Relative URI for the resource or object.
-
-        """
-        prefix = self.uri_prefix if not permanent else ''
-
-        return '{pref}/{res}{uuid}'.format(pref=prefix,
-                                           res=resource_name,
-                                           uuid='/%s' % uuid if uuid else '')
-
-    def _make_patch(self, allowed_attributes, **kwargs):
-        """Create a JSON patch according to RFC 6902.
-
-        :param allowed_attributes: An iterable object that contains a set of
-            allowed attributes for an object.
-        :param **kwargs: Attributes and new values for them.
-        :returns: A JSON path that sets values of the specified attributes to
-            the new ones.
-
-        """
-        def get_change(kwargs, path='/'):
-            for name, value in six.iteritems(kwargs):
-                if isinstance(value, dict):
-                    for ch in get_change(value, path + '%s/' % name):
-                        yield ch
-                else:
-                    if value is None:
-                        yield {'path': path + name,
-                               'op': 'remove'}
-                    else:
-                        yield {'path': path + name,
-                               'value': value,
-                               'op': 'replace'}
-
-        patch = [ch for ch in get_change(kwargs)
-                 if ch['path'].lstrip('/') in allowed_attributes]
-
-        return patch
-
-    def _list_request(self, resource, permanent=False, **kwargs):
-        """Get the list of objects of the specified type.
-
-        :param resource: The name of the REST resource, e.g., 'nodes'.
-        :param **kwargs: Parameters for the request.
-        :returns: A tuple with the server response and deserialized JSON list
-                 of objects
-
-        """
-        uri = self._get_uri(resource, permanent=permanent)
-        if kwargs:
-            uri += "?%s" % urllib.urlencode(kwargs)
-
-        resp, body = self.get(uri)
-        self.expected_success(200, resp['status'])
-
-        return resp, self.deserialize(body)
-
-    def _show_request(self, resource, uuid, permanent=False, **kwargs):
-        """Gets a specific object of the specified type.
-
-        :param uuid: Unique identifier of the object in UUID format.
-        :returns: Serialized object as a dictionary.
-
-        """
-        if 'uri' in kwargs:
-            uri = kwargs['uri']
-        else:
-            uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
-        resp, body = self.get(uri)
-        self.expected_success(200, resp['status'])
-
-        return resp, self.deserialize(body)
-
-    def _create_request(self, resource, object_dict):
-        """Create an object of the specified type.
-
-        :param resource: The name of the REST resource, e.g., 'nodes'.
-        :param object_dict: A Python dict that represents an object of the
-                            specified type.
-        :returns: A tuple with the server response and the deserialized created
-                 object.
-
-        """
-        body = self.serialize(object_dict)
-        uri = self._get_uri(resource)
-
-        resp, body = self.post(uri, body=body)
-        self.expected_success(201, resp['status'])
-
-        return resp, self.deserialize(body)
-
-    def _delete_request(self, resource, uuid):
-        """Delete specified object.
-
-        :param resource: The name of the REST resource, e.g., 'nodes'.
-        :param uuid: The unique identifier of an object in UUID format.
-        :returns: A tuple with the server response and the response body.
-
-        """
-        uri = self._get_uri(resource, uuid)
-
-        resp, body = self.delete(uri)
-        self.expected_success(204, resp['status'])
-        return resp, body
-
-    def _patch_request(self, resource, uuid, patch_object):
-        """Update specified object with JSON-patch.
-
-        :param resource: The name of the REST resource, e.g., 'nodes'.
-        :param uuid: The unique identifier of an object in UUID format.
-        :returns: A tuple with the server response and the serialized patched
-                 object.
-
-        """
-        uri = self._get_uri(resource, uuid)
-        patch_body = json.dumps(patch_object)
-
-        resp, body = self.patch(uri, body=patch_body)
-        self.expected_success(200, resp['status'])
-        return resp, self.deserialize(body)
-
-    @handle_errors
-    def get_api_description(self):
-        """Retrieves all versions of the Ironic API."""
-
-        return self._list_request('', permanent=True)
-
-    @handle_errors
-    def get_version_description(self, version='v1'):
-        """Retrieves the desctription of the API.
-
-        :param version: The version of the API. Default: 'v1'.
-        :returns: Serialized description of API resources.
-
-        """
-        return self._list_request(version, permanent=True)
-
-    def _put_request(self, resource, put_object):
-        """Update specified object with JSON-patch."""
-        uri = self._get_uri(resource)
-        put_body = json.dumps(put_object)
-
-        resp, body = self.put(uri, body=put_body)
-        self.expected_success(202, resp['status'])
-        return resp, body
diff --git a/tempest/services/baremetal/v1/__init__.py b/tempest/services/baremetal/v1/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/baremetal/v1/__init__.py
+++ /dev/null
diff --git a/tempest/services/baremetal/v1/json/__init__.py b/tempest/services/baremetal/v1/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/baremetal/v1/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/baremetal/v1/json/baremetal_client.py b/tempest/services/baremetal/v1/json/baremetal_client.py
deleted file mode 100644
index f24ef68..0000000
--- a/tempest/services/baremetal/v1/json/baremetal_client.py
+++ /dev/null
@@ -1,349 +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.baremetal import base
-
-
-class BaremetalClient(base.BaremetalClient):
-    """Base Tempest REST client for Ironic API v1."""
-    version = '1'
-    uri_prefix = 'v1'
-
-    @base.handle_errors
-    def list_nodes(self, **kwargs):
-        """List all existing nodes."""
-        return self._list_request('nodes', **kwargs)
-
-    @base.handle_errors
-    def list_chassis(self):
-        """List all existing chassis."""
-        return self._list_request('chassis')
-
-    @base.handle_errors
-    def list_chassis_nodes(self, chassis_uuid):
-        """List all nodes associated with a chassis."""
-        return self._list_request('/chassis/%s/nodes' % chassis_uuid)
-
-    @base.handle_errors
-    def list_ports(self, **kwargs):
-        """List all existing ports."""
-        return self._list_request('ports', **kwargs)
-
-    @base.handle_errors
-    def list_node_ports(self, uuid):
-        """List all ports associated with the node."""
-        return self._list_request('/nodes/%s/ports' % uuid)
-
-    @base.handle_errors
-    def list_nodestates(self, uuid):
-        """List all existing states."""
-        return self._list_request('/nodes/%s/states' % uuid)
-
-    @base.handle_errors
-    def list_ports_detail(self, **kwargs):
-        """Details list all existing ports."""
-        return self._list_request('/ports/detail', **kwargs)
-
-    @base.handle_errors
-    def list_drivers(self):
-        """List all existing drivers."""
-        return self._list_request('drivers')
-
-    @base.handle_errors
-    def show_node(self, uuid):
-        """Gets a specific node.
-
-        :param uuid: Unique identifier of the node in UUID format.
-        :return: Serialized node as a dictionary.
-
-        """
-        return self._show_request('nodes', uuid)
-
-    @base.handle_errors
-    def show_node_by_instance_uuid(self, instance_uuid):
-        """Gets a node associated with given instance uuid.
-
-        :param uuid: Unique identifier of the node in UUID format.
-        :return: Serialized node as a dictionary.
-
-        """
-        uri = '/nodes/detail?instance_uuid=%s' % instance_uuid
-
-        return self._show_request('nodes',
-                                  uuid=None,
-                                  uri=uri)
-
-    @base.handle_errors
-    def show_chassis(self, uuid):
-        """Gets a specific chassis.
-
-        :param uuid: Unique identifier of the chassis in UUID format.
-        :return: Serialized chassis as a dictionary.
-
-        """
-        return self._show_request('chassis', uuid)
-
-    @base.handle_errors
-    def show_port(self, uuid):
-        """Gets a specific port.
-
-        :param uuid: Unique identifier of the port in UUID format.
-        :return: Serialized port as a dictionary.
-
-        """
-        return self._show_request('ports', uuid)
-
-    @base.handle_errors
-    def show_port_by_address(self, address):
-        """Gets a specific port by address.
-
-        :param address: MAC address of the port.
-        :return: Serialized port as a dictionary.
-
-        """
-        uri = '/ports/detail?address=%s' % address
-
-        return self._show_request('ports', uuid=None, uri=uri)
-
-    def show_driver(self, driver_name):
-        """Gets a specific driver.
-
-        :param driver_name: Name of driver.
-        :return: Serialized driver as a dictionary.
-        """
-        return self._show_request('drivers', driver_name)
-
-    @base.handle_errors
-    def create_node(self, chassis_id=None, **kwargs):
-        """Create a baremetal node with the specified parameters.
-
-        :param cpu_arch: CPU architecture of the node. Default: x86_64.
-        :param cpus: Number of CPUs. Default: 8.
-        :param local_gb: Disk size. Default: 1024.
-        :param memory_mb: Available RAM. Default: 4096.
-        :param driver: Driver name. Default: "fake"
-        :return: A tuple with the server response and the created node.
-
-        """
-        node = {'chassis_uuid': chassis_id,
-                'properties': {'cpu_arch': kwargs.get('cpu_arch', 'x86_64'),
-                               'cpus': kwargs.get('cpus', 8),
-                               'local_gb': kwargs.get('local_gb', 1024),
-                               'memory_mb': kwargs.get('memory_mb', 4096)},
-                'driver': kwargs.get('driver', 'fake')}
-
-        return self._create_request('nodes', node)
-
-    @base.handle_errors
-    def create_chassis(self, **kwargs):
-        """Create a chassis with the specified parameters.
-
-        :param description: The description of the chassis.
-            Default: test-chassis
-        :return: A tuple with the server response and the created chassis.
-
-        """
-        chassis = {'description': kwargs.get('description', 'test-chassis')}
-
-        return self._create_request('chassis', chassis)
-
-    @base.handle_errors
-    def create_port(self, node_id, **kwargs):
-        """Create a port with the specified parameters.
-
-        :param node_id: The ID of the node which owns the port.
-        :param address: MAC address of the port.
-        :param extra: Meta data of the port. Default: {'foo': 'bar'}.
-        :param uuid: UUID of the port.
-        :return: A tuple with the server response and the created port.
-
-        """
-        port = {'extra': kwargs.get('extra', {'foo': 'bar'}),
-                'uuid': kwargs['uuid']}
-
-        if node_id is not None:
-            port['node_uuid'] = node_id
-
-        if kwargs['address'] is not None:
-            port['address'] = kwargs['address']
-
-        return self._create_request('ports', port)
-
-    @base.handle_errors
-    def delete_node(self, uuid):
-        """Deletes a node having the specified UUID.
-
-        :param uuid: The unique identifier of the node.
-        :return: A tuple with the server response and the response body.
-
-        """
-        return self._delete_request('nodes', uuid)
-
-    @base.handle_errors
-    def delete_chassis(self, uuid):
-        """Deletes a chassis having the specified UUID.
-
-        :param uuid: The unique identifier of the chassis.
-        :return: A tuple with the server response and the response body.
-
-        """
-        return self._delete_request('chassis', uuid)
-
-    @base.handle_errors
-    def delete_port(self, uuid):
-        """Deletes a port having the specified UUID.
-
-        :param uuid: The unique identifier of the port.
-        :return: A tuple with the server response and the response body.
-
-        """
-        return self._delete_request('ports', uuid)
-
-    @base.handle_errors
-    def update_node(self, uuid, **kwargs):
-        """Update the specified node.
-
-        :param uuid: The unique identifier of the node.
-        :return: A tuple with the server response and the updated node.
-
-        """
-        node_attributes = ('properties/cpu_arch',
-                           'properties/cpus',
-                           'properties/local_gb',
-                           'properties/memory_mb',
-                           'driver',
-                           'instance_uuid')
-
-        patch = self._make_patch(node_attributes, **kwargs)
-
-        return self._patch_request('nodes', uuid, patch)
-
-    @base.handle_errors
-    def update_chassis(self, uuid, **kwargs):
-        """Update the specified chassis.
-
-        :param uuid: The unique identifier of the chassis.
-        :return: A tuple with the server response and the updated chassis.
-
-        """
-        chassis_attributes = ('description',)
-        patch = self._make_patch(chassis_attributes, **kwargs)
-
-        return self._patch_request('chassis', uuid, patch)
-
-    @base.handle_errors
-    def update_port(self, uuid, patch):
-        """Update the specified port.
-
-        :param uuid: The unique identifier of the port.
-        :param patch: List of dicts representing json patches.
-        :return: A tuple with the server response and the updated port.
-
-        """
-
-        return self._patch_request('ports', uuid, patch)
-
-    @base.handle_errors
-    def set_node_power_state(self, node_uuid, state):
-        """Set power state of the specified node.
-
-        :param node_uuid: The unique identifier of the node.
-        :state: desired state to set (on/off/reboot).
-
-        """
-        target = {'target': state}
-        return self._put_request('nodes/%s/states/power' % node_uuid,
-                                 target)
-
-    @base.handle_errors
-    def validate_driver_interface(self, node_uuid):
-        """Get all driver interfaces of a specific node.
-
-        :param uuid: Unique identifier of the node in UUID format.
-
-        """
-
-        uri = '{pref}/{res}/{uuid}/{postf}'.format(pref=self.uri_prefix,
-                                                   res='nodes',
-                                                   uuid=node_uuid,
-                                                   postf='validate')
-
-        return self._show_request('nodes', node_uuid, uri=uri)
-
-    @base.handle_errors
-    def set_node_boot_device(self, node_uuid, boot_device, persistent=False):
-        """Set the boot device of the specified node.
-
-        :param node_uuid: The unique identifier of the node.
-        :param boot_device: The boot device name.
-        :param persistent: Boolean value. True if the boot device will
-                           persist to all future boots, False if not.
-                           Default: False.
-
-        """
-        request = {'boot_device': boot_device, 'persistent': persistent}
-        resp, body = self._put_request('nodes/%s/management/boot_device' %
-                                       node_uuid, request)
-        self.expected_success(204, resp.status)
-        return body
-
-    @base.handle_errors
-    def get_node_boot_device(self, node_uuid):
-        """Get the current boot device of the specified node.
-
-        :param node_uuid: The unique identifier of the node.
-
-        """
-        path = 'nodes/%s/management/boot_device' % node_uuid
-        resp, body = self._list_request(path)
-        self.expected_success(200, resp.status)
-        return body
-
-    @base.handle_errors
-    def get_node_supported_boot_devices(self, node_uuid):
-        """Get the supported boot devices of the specified node.
-
-        :param node_uuid: The unique identifier of the node.
-
-        """
-        path = 'nodes/%s/management/boot_device/supported' % node_uuid
-        resp, body = self._list_request(path)
-        self.expected_success(200, resp.status)
-        return body
-
-    @base.handle_errors
-    def get_console(self, node_uuid):
-        """Get connection information about the console.
-
-        :param node_uuid: Unique identifier of the node in UUID format.
-
-        """
-
-        resp, body = self._show_request('nodes/states/console', node_uuid)
-        self.expected_success(200, resp.status)
-        return resp, body
-
-    @base.handle_errors
-    def set_console_mode(self, node_uuid, enabled):
-        """Start and stop the node console.
-
-        :param node_uuid: Unique identifier of the node in UUID format.
-        :param enabled: Boolean value; whether to enable or disable the
-                        console.
-
-        """
-
-        enabled = {'enabled': enabled}
-        resp, body = self._put_request('nodes/%s/states/console' % node_uuid,
-                                       enabled)
-        self.expected_success(202, resp.status)
-        return resp, body
diff --git a/tempest/services/base_microversion_client.py b/tempest/services/base_microversion_client.py
deleted file mode 100644
index 4c750f5..0000000
--- a/tempest/services/base_microversion_client.py
+++ /dev/null
@@ -1,54 +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.
-
-from tempest_lib.common import rest_client
-
-
-class BaseMicroversionClient(rest_client.RestClient):
-    """Base class to support microversion in service clients
-
-    This class is used to support microversion in service clients.
-    This provides feature to make API request with microversion.
-    Service clients derived from this class will be able to send API
-    request to server with or without microversion.
-    If api_microversion is not set on service client then API request will be
-    normal request without microversion.
-
-    """
-    def __init__(self, auth_provider, service, region,
-                 api_microversion_header_name, **kwargs):
-        """Base Microversion Client __init__
-
-        :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 str api_microversion_header_name: The microversion header name
-                                                 to use for sending API
-                                                 request with microversion
-        :param kwargs: kwargs required by rest_client.RestClient
-        """
-        super(BaseMicroversionClient, self).__init__(
-            auth_provider, service, region, **kwargs)
-        self.api_microversion_header_name = api_microversion_header_name
-        self.api_microversion = None
-
-    def get_headers(self):
-        headers = super(BaseMicroversionClient, self).get_headers()
-        if self.api_microversion:
-            headers[self.api_microversion_header_name] = self.api_microversion
-        return headers
-
-    def set_api_microversion(self, microversion):
-        self.api_microversion = microversion
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/base_compute_client.py b/tempest/services/compute/json/base_compute_client.py
deleted file mode 100644
index 5349af6..0000000
--- a/tempest/services/compute/json/base_compute_client.py
+++ /dev/null
@@ -1,72 +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.common import api_version_request
-from tempest.common import api_version_utils
-from tempest import exceptions
-from tempest.services import base_microversion_client
-
-
-class BaseComputeClient(base_microversion_client.BaseMicroversionClient):
-
-    def __init__(self, auth_provider, service, region,
-                 api_microversion_header_name='X-OpenStack-Nova-API-Version',
-                 **kwargs):
-        super(BaseComputeClient, self).__init__(
-            auth_provider, service, region,
-            api_microversion_header_name, **kwargs)
-
-    def request(self, method, url, extra_headers=False, headers=None,
-                body=None):
-        resp, resp_body = super(BaseComputeClient, self).request(
-            method, url, extra_headers, headers, body)
-        if self.api_microversion and self.api_microversion != 'latest':
-            api_version_utils.assert_version_header_matches_request(
-                self.api_microversion_header_name,
-                self.api_microversion,
-                resp)
-        return resp, resp_body
-
-    def get_schema(self, schema_versions_info):
-        """Get JSON schema
-
-        This method provides the matching schema for requested
-        microversion (self.api_microversion).
-        :param schema_versions_info: List of dict which provides schema
-        information with range of valid versions.
-        Example -
-        schema_versions_info = [
-            {'min': None, 'max': '2.1', 'schema': schemav21},
-            {'min': '2.2', 'max': '2.9', 'schema': schemav22},
-            {'min': '2.10', 'max': None, 'schema': schemav210}]
-        """
-        schema = None
-        version = api_version_request.APIVersionRequest(self.api_microversion)
-        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
-            # 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
-            elif version.matches(min_version, max_version):
-                schema = items['schema']
-                break
-        if schema is None:
-            raise exceptions.JSONSchemaNotFound(
-                version=version.get_string(),
-                schema_versions_info=schema_versions_info)
-        return schema
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/__init__.py b/tempest/services/data_processing/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/data_processing/__init__.py
+++ /dev/null
diff --git a/tempest/services/data_processing/v1_1/__init__.py b/tempest/services/data_processing/v1_1/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/data_processing/v1_1/__init__.py
+++ /dev/null
diff --git a/tempest/services/data_processing/v1_1/data_processing_client.py b/tempest/services/data_processing/v1_1/data_processing_client.py
deleted file mode 100644
index 5aa2622..0000000
--- a/tempest/services/data_processing/v1_1/data_processing_client.py
+++ /dev/null
@@ -1,280 +0,0 @@
-# Copyright (c) 2013 Mirantis Inc.
-#
-#    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.common import service_client
-
-
-class DataProcessingClient(service_client.ServiceClient):
-
-    def _request_and_check_resp(self, request_func, uri, resp_status):
-        """Make a request and check response status code.
-
-        It returns a ResponseBody.
-        """
-        resp, body = request_func(uri)
-        self.expected_success(resp_status, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def _request_and_check_resp_data(self, request_func, uri, resp_status):
-        """Make a request and check response status code.
-
-        It returns pair: resp and response data.
-        """
-        resp, body = request_func(uri)
-        self.expected_success(resp_status, resp.status)
-        return resp, body
-
-    def _request_check_and_parse_resp(self, request_func, uri,
-                                      resp_status, *args, **kwargs):
-        """Make a request, check response status code and parse response body.
-
-        It returns a ResponseBody.
-        """
-        headers = {'Content-Type': 'application/json'}
-        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)
-
-    def list_node_group_templates(self):
-        """List all node group templates for a user."""
-
-        uri = 'node-group-templates'
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def get_node_group_template(self, tmpl_id):
-        """Returns the details of a single node group template."""
-
-        uri = 'node-group-templates/%s' % tmpl_id
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def create_node_group_template(self, name, plugin_name, hadoop_version,
-                                   node_processes, flavor_id,
-                                   node_configs=None, **kwargs):
-        """Creates node group template with specified params.
-
-        It supports passing additional params using kwargs and returns created
-        object.
-        """
-        uri = 'node-group-templates'
-        body = kwargs.copy()
-        body.update({
-            'name': name,
-            'plugin_name': plugin_name,
-            'hadoop_version': hadoop_version,
-            'node_processes': node_processes,
-            'flavor_id': flavor_id,
-            'node_configs': node_configs or dict(),
-        })
-        return self._request_check_and_parse_resp(self.post, uri, 202,
-                                                  body=json.dumps(body))
-
-    def delete_node_group_template(self, tmpl_id):
-        """Deletes the specified node group template by id."""
-
-        uri = 'node-group-templates/%s' % tmpl_id
-        return self._request_and_check_resp(self.delete, uri, 204)
-
-    def list_plugins(self):
-        """List all enabled plugins."""
-
-        uri = 'plugins'
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def get_plugin(self, plugin_name, plugin_version=None):
-        """Returns the details of a single plugin."""
-
-        uri = 'plugins/%s' % plugin_name
-        if plugin_version:
-            uri += '/%s' % plugin_version
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def list_cluster_templates(self):
-        """List all cluster templates for a user."""
-
-        uri = 'cluster-templates'
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def get_cluster_template(self, tmpl_id):
-        """Returns the details of a single cluster template."""
-
-        uri = 'cluster-templates/%s' % tmpl_id
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def create_cluster_template(self, name, plugin_name, hadoop_version,
-                                node_groups, cluster_configs=None,
-                                **kwargs):
-        """Creates cluster template with specified params.
-
-        It supports passing additional params using kwargs and returns created
-        object.
-        """
-        uri = 'cluster-templates'
-        body = kwargs.copy()
-        body.update({
-            'name': name,
-            'plugin_name': plugin_name,
-            'hadoop_version': hadoop_version,
-            'node_groups': node_groups,
-            'cluster_configs': cluster_configs or dict(),
-        })
-        return self._request_check_and_parse_resp(self.post, uri, 202,
-                                                  body=json.dumps(body))
-
-    def delete_cluster_template(self, tmpl_id):
-        """Deletes the specified cluster template by id."""
-
-        uri = 'cluster-templates/%s' % tmpl_id
-        return self._request_and_check_resp(self.delete, uri, 204)
-
-    def list_data_sources(self):
-        """List all data sources for a user."""
-
-        uri = 'data-sources'
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def get_data_source(self, source_id):
-        """Returns the details of a single data source."""
-
-        uri = 'data-sources/%s' % source_id
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def create_data_source(self, name, data_source_type, url, **kwargs):
-        """Creates data source with specified params.
-
-        It supports passing additional params using kwargs and returns created
-        object.
-        """
-        uri = 'data-sources'
-        body = kwargs.copy()
-        body.update({
-            'name': name,
-            'type': data_source_type,
-            'url': url
-        })
-        return self._request_check_and_parse_resp(self.post, uri,
-                                                  202, body=json.dumps(body))
-
-    def delete_data_source(self, source_id):
-        """Deletes the specified data source by id."""
-
-        uri = 'data-sources/%s' % source_id
-        return self._request_and_check_resp(self.delete, uri, 204)
-
-    def list_job_binary_internals(self):
-        """List all job binary internals for a user."""
-
-        uri = 'job-binary-internals'
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def get_job_binary_internal(self, job_binary_id):
-        """Returns the details of a single job binary internal."""
-
-        uri = 'job-binary-internals/%s' % job_binary_id
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def create_job_binary_internal(self, name, data):
-        """Creates job binary internal with specified params."""
-
-        uri = 'job-binary-internals/%s' % name
-        return self._request_check_and_parse_resp(self.put, uri, 202, data)
-
-    def delete_job_binary_internal(self, job_binary_id):
-        """Deletes the specified job binary internal by id."""
-
-        uri = 'job-binary-internals/%s' % job_binary_id
-        return self._request_and_check_resp(self.delete, uri, 204)
-
-    def get_job_binary_internal_data(self, job_binary_id):
-        """Returns data of a single job binary internal."""
-
-        uri = 'job-binary-internals/%s/data' % job_binary_id
-        return self._request_and_check_resp_data(self.get, uri, 200)
-
-    def list_job_binaries(self):
-        """List all job binaries for a user."""
-
-        uri = 'job-binaries'
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def get_job_binary(self, job_binary_id):
-        """Returns the details of a single job binary."""
-
-        uri = 'job-binaries/%s' % job_binary_id
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def create_job_binary(self, name, url, extra=None, **kwargs):
-        """Creates job binary with specified params.
-
-        It supports passing additional params using kwargs and returns created
-        object.
-        """
-        uri = 'job-binaries'
-        body = kwargs.copy()
-        body.update({
-            'name': name,
-            'url': url,
-            'extra': extra or dict(),
-        })
-        return self._request_check_and_parse_resp(self.post, uri,
-                                                  202, body=json.dumps(body))
-
-    def delete_job_binary(self, job_binary_id):
-        """Deletes the specified job binary by id."""
-
-        uri = 'job-binaries/%s' % job_binary_id
-        return self._request_and_check_resp(self.delete, uri, 204)
-
-    def get_job_binary_data(self, job_binary_id):
-        """Returns data of a single job binary."""
-
-        uri = 'job-binaries/%s/data' % job_binary_id
-        return self._request_and_check_resp_data(self.get, uri, 200)
-
-    def list_jobs(self):
-        """List all jobs for a user."""
-
-        uri = 'jobs'
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def get_job(self, job_id):
-        """Returns the details of a single job."""
-
-        uri = 'jobs/%s' % job_id
-        return self._request_check_and_parse_resp(self.get, uri, 200)
-
-    def create_job(self, name, job_type, mains, libs=None, **kwargs):
-        """Creates job with specified params.
-
-        It supports passing additional params using kwargs and returns created
-        object.
-        """
-        uri = 'jobs'
-        body = kwargs.copy()
-        body.update({
-            'name': name,
-            'type': job_type,
-            'mains': mains,
-            'libs': libs or list(),
-        })
-        return self._request_check_and_parse_resp(self.post, uri,
-                                                  202, body=json.dumps(body))
-
-    def delete_job(self, job_id):
-        """Deletes the specified job by id."""
-
-        uri = 'jobs/%s' % job_id
-        return self._request_and_check_resp(self.delete, uri, 204)
diff --git a/tempest/services/database/__init__.py b/tempest/services/database/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/database/__init__.py
+++ /dev/null
diff --git a/tempest/services/database/json/__init__.py b/tempest/services/database/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/database/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/database/json/flavors_client.py b/tempest/services/database/json/flavors_client.py
deleted file mode 100644
index dbb5172..0000000
--- a/tempest/services/database/json/flavors_client.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2014 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 six.moves import urllib
-
-from tempest.common import service_client
-
-
-class DatabaseFlavorsClient(service_client.ServiceClient):
-
-    def list_db_flavors(self, params=None):
-        url = 'flavors'
-        if params:
-            url += '?%s' % urllib.parse.urlencode(params)
-
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
diff --git a/tempest/services/database/json/limits_client.py b/tempest/services/database/json/limits_client.py
deleted file mode 100644
index da495d7..0000000
--- a/tempest/services/database/json/limits_client.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright 2014 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 six.moves.urllib import parse as urllib
-
-from tempest.common import service_client
-
-
-class DatabaseLimitsClient(service_client.ServiceClient):
-
-    def list_db_limits(self, params=None):
-        """List all limits."""
-        url = 'limits'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
diff --git a/tempest/services/database/json/versions_client.py b/tempest/services/database/json/versions_client.py
deleted file mode 100644
index 7a560d9..0000000
--- a/tempest/services/database/json/versions_client.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright 2014 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 six.moves.urllib import parse as urllib
-
-from tempest.common import service_client
-
-
-class DatabaseVersionsClient(service_client.ServiceClient):
-
-    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
-        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)
-        self.skip_path()
-
-    def list_db_versions(self, params=None):
-        """List all versions."""
-        url = ''
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/__init__.py b/tempest/services/identity/v2/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/identity/v2/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/identity/v2/json/endpoints_client.py b/tempest/services/identity/v2/json/endpoints_client.py
deleted file mode 100644
index ff9907d..0000000
--- a/tempest/services/identity/v2/json/endpoints_client.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright 2016 Red Hat, Inc.
-#
-# 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.common import service_client
-
-
-class EndpointsClient(service_client.ServiceClient):
-    api_version = "v2.0"
-
-    def create_endpoint(self, service_id, region_id, **kwargs):
-        """Create an endpoint for service."""
-        post_body = {
-            'service_id': service_id,
-            'region': region_id,
-            'publicurl': kwargs.get('publicurl'),
-            'adminurl': kwargs.get('adminurl'),
-            'internalurl': kwargs.get('internalurl')
-        }
-        post_body = json.dumps({'endpoint': post_body})
-        resp, body = self.post('/endpoints', post_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
diff --git a/tempest/services/identity/v2/json/identity_client.py b/tempest/services/identity/v2/json/identity_client.py
deleted file mode 100644
index f045bb7..0000000
--- a/tempest/services/identity/v2/json/identity_client.py
+++ /dev/null
@@ -1,47 +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 tempest.common import service_client
-
-
-class IdentityClient(service_client.ServiceClient):
-    api_version = "v2.0"
-
-    def show_api_description(self):
-        """Retrieves info about the v2.0 Identity API"""
-        url = ''
-        resp, body = self.get(url)
-        self.expected_success([200, 203], resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
-
-    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)
diff --git a/tempest/services/identity/v2/json/roles_client.py b/tempest/services/identity/v2/json/roles_client.py
deleted file mode 100644
index ef6dfe9..0000000
--- a/tempest/services/identity/v2/json/roles_client.py
+++ /dev/null
@@ -1,74 +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 tempest.common import service_client
-
-
-class RolesClient(service_client.ServiceClient):
-    api_version = "v2.0"
-
-    def create_role(self, **kwargs):
-        """Create a role.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v2-ext.html#createRole
-        """
-        post_body = json.dumps({'role': kwargs})
-        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)
-
-    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)
-
-    def delete_role(self, role_id):
-        """Delete a role."""
-        resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id))
-        self.expected_success(204, resp.status)
-        return resp, body
-
-    def list_user_roles(self, tenant_id, user_id):
-        """Returns a list of roles assigned to a user for a tenant."""
-        url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def assign_user_role(self, tenant_id, user_id, role_id):
-        """Add roles to a user on a tenant."""
-        resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
-                              (tenant_id, user_id, role_id), "")
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
diff --git a/tempest/services/identity/v2/json/services_client.py b/tempest/services/identity/v2/json/services_client.py
deleted file mode 100644
index 436d00d..0000000
--- a/tempest/services/identity/v2/json/services_client.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright 2015 Red Hat, Inc.
-#
-# 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.common import service_client
-
-
-class ServicesClient(service_client.ServiceClient):
-    api_version = "v2.0"
-
-    def create_service(self, name, type, **kwargs):
-        """Create a service."""
-        post_body = {
-            'name': name,
-            'type': type,
-            'description': kwargs.get('description')
-        }
-        post_body = json.dumps({'OS-KSADM:service': post_body})
-        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)
-
-    def show_service(self, service_id):
-        """Get Service."""
-        url = '/OS-KSADM/services/%s' % service_id
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
diff --git a/tempest/services/identity/v2/json/tenants_client.py b/tempest/services/identity/v2/json/tenants_client.py
deleted file mode 100644
index 937ae6f..0000000
--- a/tempest/services/identity/v2/json/tenants_client.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright 2015 Red Hat, Inc.
-#
-# 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.common import service_client
-
-
-class TenantsClient(service_client.ServiceClient):
-    api_version = "v2.0"
-
-    def create_tenant(self, name, **kwargs):
-        """Create a tenant
-
-        name (required): New tenant name
-        description: Description of new tenant (default is none)
-        enabled <true|false>: Initial tenant status (default is true)
-        """
-        post_body = {
-            'name': name,
-            'description': kwargs.get('description', ''),
-            'enabled': kwargs.get('enabled', True),
-        }
-        post_body = json.dumps({'tenant': post_body})
-        resp, body = self.post('tenants', post_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
-
-    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)
-
-    def update_tenant(self, tenant_id, **kwargs):
-        """Updates a tenant."""
-        body = self.show_tenant(tenant_id)['tenant']
-        name = kwargs.get('name', body['name'])
-        desc = kwargs.get('description', body['description'])
-        en = kwargs.get('enabled', body['enabled'])
-        post_body = {
-            'id': tenant_id,
-            'name': name,
-            'description': desc,
-            'enabled': en,
-        }
-        post_body = json.dumps({'tenant': post_body})
-        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)
-
-    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)
diff --git a/tempest/services/identity/v2/json/users_client.py b/tempest/services/identity/v2/json/users_client.py
deleted file mode 100644
index 5327638..0000000
--- a/tempest/services/identity/v2/json/users_client.py
+++ /dev/null
@@ -1,137 +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 tempest.common import service_client
-
-
-class UsersClient(service_client.ServiceClient):
-    api_version = "v2.0"
-
-    def create_user(self, name, password, tenant_id, email, **kwargs):
-        """Create a user."""
-        post_body = {
-            'name': name,
-            'password': password,
-            'email': email
-        }
-        if tenant_id is not None:
-            post_body['tenantId'] = tenant_id
-        if kwargs.get('enabled') is not None:
-            post_body['enabled'] = kwargs.get('enabled')
-        post_body = json.dumps({'user': post_body})
-        resp, body = self.post('users', post_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def update_user(self, user_id, **kwargs):
-        """Updates a user."""
-        put_body = json.dumps({'user': kwargs})
-        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)
-
-    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)
-
-    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)
-
-    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)
-
-    def enable_disable_user(self, user_id, **kwargs):
-        """Enables or disables a user.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v2-ext.html#enableUser
-        """
-        # NOTE: The URL (users/<id>/enabled) is different from the api-site
-        # one (users/<id>/OS-KSADM/enabled) , but they are the same API
-        # because of the fact that in keystone/contrib/admin_crud/core.py
-        # both api use same action='set_user_enabled'
-        put_body = json.dumps({'user': kwargs})
-        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)
-
-    def update_user_password(self, user_id, **kwargs):
-        """Update User Password."""
-        # TODO(piyush): Current api-site doesn't contain this API description.
-        # After fixing the api-site, we need to fix here also for putting the
-        # link to api-site.
-        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524147
-        put_body = json.dumps({'user': kwargs})
-        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)
-
-    def update_user_own_password(self, user_id, **kwargs):
-        """User updates own password"""
-        # TODO(piyush): Current api-site doesn't contain this API description.
-        # After fixing the api-site, we need to fix here also for putting the
-        # link to api-site.
-        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524153
-        # NOTE: This API is used for updating user password by itself.
-        # Ref: http://lists.openstack.org/pipermail/openstack-dev/2015-December
-        #      /081803.html
-        patch_body = json.dumps({'user': kwargs})
-        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)
-
-    def create_user_ec2_credentials(self, user_id, **kwargs):
-        # TODO(piyush): Current api-site doesn't contain this API description.
-        # After fixing the api-site, we need to fix here also for putting the
-        # link to api-site.
-        post_body = json.dumps(kwargs)
-        resp, body = self.post('/users/%s/credentials/OS-EC2' % user_id,
-                               post_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
-
-    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)
diff --git a/tempest/services/identity/v3/json/__init__.py b/tempest/services/identity/v3/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/identity/v3/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/identity/v3/json/credentials_client.py b/tempest/services/identity/v3/json/credentials_client.py
deleted file mode 100644
index 753e960..0000000
--- a/tempest/services/identity/v3/json/credentials_client.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright 2013 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.
-
-"""
-http://developer.openstack.org/api-ref-identity-v3.html#credentials-v3
-"""
-
-from oslo_serialization import jsonutils as json
-
-from tempest.common import service_client
-
-
-class CredentialsClient(service_client.ServiceClient):
-    api_version = "v3"
-
-    def create_credential(self, **kwargs):
-        """Creates a credential.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#createCredential
-        """
-        post_body = json.dumps({'credential': kwargs})
-        resp, body = self.post('credentials', post_body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        body['credential']['blob'] = json.loads(body['credential']['blob'])
-        return service_client.ResponseBody(resp, body)
-
-    def update_credential(self, credential_id, **kwargs):
-        """Updates a credential.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#updateCredential
-        """
-        post_body = json.dumps({'credential': kwargs})
-        resp, body = self.patch('credentials/%s' % credential_id, post_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        body['credential']['blob'] = json.loads(body['credential']['blob'])
-        return service_client.ResponseBody(resp, body)
-
-    def show_credential(self, credential_id):
-        """To GET Details of a credential."""
-        resp, body = self.get('credentials/%s' % credential_id)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        body['credential']['blob'] = json.loads(body['credential']['blob'])
-        return service_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)
-
-    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)
diff --git a/tempest/services/identity/v3/json/endpoints_client.py b/tempest/services/identity/v3/json/endpoints_client.py
deleted file mode 100644
index 8ab7464..0000000
--- a/tempest/services/identity/v3/json/endpoints_client.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright 2013 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.
-
-"""
-http://developer.openstack.org/api-ref-identity-v3.html#endpoints-v3
-"""
-
-from oslo_serialization import jsonutils as json
-
-from tempest.common import service_client
-
-
-class EndPointClient(service_client.ServiceClient):
-    api_version = "v3"
-
-    def list_endpoints(self):
-        """GET endpoints."""
-        resp, body = self.get('endpoints')
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def create_endpoint(self, **kwargs):
-        """Create endpoint.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#createEndpoint
-        """
-        post_body = json.dumps({'endpoint': kwargs})
-        resp, body = self.post('endpoints', post_body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def update_endpoint(self, endpoint_id, **kwargs):
-        """Updates an endpoint with given parameters.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#updateEndpoint
-        """
-        post_body = json.dumps({'endpoint': kwargs})
-        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)
-
-    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)
-
-    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)
diff --git a/tempest/services/identity/v3/json/groups_client.py b/tempest/services/identity/v3/json/groups_client.py
deleted file mode 100644
index 6ed85cf..0000000
--- a/tempest/services/identity/v3/json/groups_client.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# Copyright 2013 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.
-
-"""
-http://developer.openstack.org/api-ref-identity-v3.html#groups-v3
-"""
-
-from oslo_serialization import jsonutils as json
-
-from tempest.common import service_client
-
-
-class GroupsClient(service_client.ServiceClient):
-    api_version = "v3"
-
-    def create_group(self, **kwargs):
-        """Creates a group.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#createGroup
-        """
-        post_body = json.dumps({'group': kwargs})
-        resp, body = self.post('groups', post_body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
-
-    def update_group(self, group_id, **kwargs):
-        """Updates a group.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#updateGroup
-        """
-        post_body = json.dumps({'group': kwargs})
-        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)
-
-    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)
-
-    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)
-
-    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)
-
-    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)
-
-    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)
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
deleted file mode 100644
index 809d0b5..0000000
--- a/tempest/services/identity/v3/json/identity_client.py
+++ /dev/null
@@ -1,326 +0,0 @@
-# Copyright 2013 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 six.moves.urllib import parse as urllib
-
-from tempest.common import service_client
-
-
-class IdentityV3Client(service_client.ServiceClient):
-    api_version = "v3"
-
-    def show_api_description(self):
-        """Retrieves info about the v3 Identity API"""
-        url = ''
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def create_role(self, **kwargs):
-        """Create a Role.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#createRole
-        """
-        post_body = json.dumps({'role': kwargs})
-        resp, body = self.post('roles', post_body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
-
-    def update_role(self, role_id, **kwargs):
-        """Update a Role.
-
-        Available params: see http://developer.openstack.org/
-                          api-ref-identity-v3.html#updateRole
-        """
-        post_body = json.dumps({'role': kwargs})
-        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)
-
-    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)
-
-    def create_domain(self, name, **kwargs):
-        """Creates a domain."""
-        description = kwargs.get('description', None)
-        en = kwargs.get('enabled', True)
-        post_body = {
-            'description': description,
-            'enabled': en,
-            'name': name
-        }
-        post_body = json.dumps({'domain': post_body})
-        resp, body = self.post('domains', post_body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def delete_domain(self, domain_id):
-        """Delete a domain."""
-        resp, body = self.delete('domains/%s' % str(domain_id))
-        self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def list_domains(self, params=None):
-        """List Domains."""
-        url = 'domains'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def update_domain(self, domain_id, **kwargs):
-        """Updates a domain."""
-        body = self.show_domain(domain_id)['domain']
-        description = kwargs.get('description', body['description'])
-        en = kwargs.get('enabled', body['enabled'])
-        name = kwargs.get('name', body['name'])
-        post_body = {
-            'description': description,
-            'enabled': en,
-            'name': name
-        }
-        post_body = json.dumps({'domain': post_body})
-        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)
-
-    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)
-
-    def show_token(self, resp_token):
-        """Get token details."""
-        headers = {'X-Subject-Token': resp_token}
-        resp, body = self.get("auth/tokens", headers=headers)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
-
-    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)
-
-    def list_user_roles_on_project(self, project_id, user_id):
-        """list roles of a user on a project."""
-        resp, body = self.get('projects/%s/users/%s/roles' %
-                              (project_id, user_id))
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def list_user_roles_on_domain(self, domain_id, user_id):
-        """list roles of a user on a domain."""
-        resp, body = self.get('domains/%s/users/%s/roles' %
-                              (domain_id, user_id))
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
-
-    def check_user_role_existence_on_project(self, project_id,
-                                             user_id, role_id):
-        """Check role of a user on a project."""
-        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)
-
-    def check_user_role_existence_on_domain(self, domain_id,
-                                            user_id, role_id):
-        """Check role of a user on a domain."""
-        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)
-
-    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)
-
-    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)
-
-    def list_group_roles_on_project(self, project_id, group_id):
-        """list roles of a user on a project."""
-        resp, body = self.get('projects/%s/groups/%s/roles' %
-                              (project_id, group_id))
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def list_group_roles_on_domain(self, domain_id, group_id):
-        """list roles of a user on a domain."""
-        resp, body = self.get('domains/%s/groups/%s/roles' %
-                              (domain_id, group_id))
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
-
-    def check_role_from_group_on_project_existence(self, project_id,
-                                                   group_id, role_id):
-        """Check role of a user on a project."""
-        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)
-
-    def check_role_from_group_on_domain_existence(self, domain_id,
-                                                  group_id, role_id):
-        """Check role of a user on a domain."""
-        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)
-
-    def create_trust(self, **kwargs):
-        """Creates a trust.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3-ext.html#createTrust
-        """
-        post_body = json.dumps({'trust': kwargs})
-        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)
-
-    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)
-
-    def list_trusts(self, trustor_user_id=None, trustee_user_id=None):
-        """GET trusts."""
-        if trustor_user_id:
-            resp, body = self.get("OS-TRUST/trusts?trustor_user_id=%s"
-                                  % trustor_user_id)
-        elif trustee_user_id:
-            resp, body = self.get("OS-TRUST/trusts?trustee_user_id=%s"
-                                  % trustee_user_id)
-        else:
-            resp, body = self.get("OS-TRUST/trusts")
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
-
-    def show_trust_role(self, trust_id, role_id):
-        """GET role delegated by a trust."""
-        resp, body = self.get("OS-TRUST/trusts/%s/roles/%s"
-                              % (trust_id, role_id))
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
diff --git a/tempest/services/identity/v3/json/policies_client.py b/tempest/services/identity/v3/json/policies_client.py
deleted file mode 100644
index 639ed6d..0000000
--- a/tempest/services/identity/v3/json/policies_client.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright 2013 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.
-
-"""
-http://developer.openstack.org/api-ref-identity-v3.html#policies-v3
-"""
-
-from oslo_serialization import jsonutils as json
-
-from tempest.common import service_client
-
-
-class PoliciesClient(service_client.ServiceClient):
-    api_version = "v3"
-
-    def create_policy(self, **kwargs):
-        """Creates a Policy.
-
-        Available params: see http://developer.openstack.org/
-                          api-ref-identity-v3.html#createPolicy
-        """
-        post_body = json.dumps({'policy': kwargs})
-        resp, body = self.post('policies', post_body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    def show_policy(self, policy_id):
-        """Lists out the given policy."""
-        url = 'policies/%s' % policy_id
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def update_policy(self, policy_id, **kwargs):
-        """Updates a policy.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#updatePolicy
-        """
-        post_body = json.dumps({'policy': kwargs})
-        url = 'policies/%s' % policy_id
-        resp, body = self.patch(url, post_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
diff --git a/tempest/services/identity/v3/json/projects_client.py b/tempest/services/identity/v3/json/projects_client.py
deleted file mode 100644
index 2fa822f..0000000
--- a/tempest/services/identity/v3/json/projects_client.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright 2013 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 six.moves.urllib import parse as urllib
-
-from tempest.common import service_client
-
-
-class ProjectsClient(service_client.ServiceClient):
-    api_version = "v3"
-
-    def create_project(self, name, **kwargs):
-        """Creates a project."""
-        description = kwargs.get('description', None)
-        en = kwargs.get('enabled', True)
-        domain_id = kwargs.get('domain_id', 'default')
-        post_body = {
-            'description': description,
-            'domain_id': domain_id,
-            'enabled': en,
-            'name': name
-        }
-        post_body = json.dumps({'project': post_body})
-        resp, body = self.post('projects', post_body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def list_projects(self, params=None):
-        url = "projects"
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def update_project(self, project_id, **kwargs):
-        body = self.show_project(project_id)['project']
-        name = kwargs.get('name', body['name'])
-        desc = kwargs.get('description', body['description'])
-        en = kwargs.get('enabled', body['enabled'])
-        domain_id = kwargs.get('domain_id', body['domain_id'])
-        post_body = {
-            'id': project_id,
-            'name': name,
-            'description': desc,
-            'enabled': en,
-            'domain_id': domain_id,
-        }
-        post_body = json.dumps({'project': post_body})
-        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)
-
-    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)
-
-    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)
diff --git a/tempest/services/identity/v3/json/regions_client.py b/tempest/services/identity/v3/json/regions_client.py
deleted file mode 100644
index bc4b7a1..0000000
--- a/tempest/services/identity/v3/json/regions_client.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright 2014 Hewlett-Packard Development Company, L.P
-# 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.
-
-"""
-http://developer.openstack.org/api-ref-identity-v3.html#regions-v3
-"""
-
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.common import service_client
-
-
-class RegionsClient(service_client.ServiceClient):
-    api_version = "v3"
-
-    def create_region(self, region_id=None, **kwargs):
-        """Create region.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#createRegion
-
-                          see http://developer.openstack.org/
-                              api-ref-identity-v3.html#createRegionWithID
-        """
-        if region_id is not None:
-            method = self.put
-            url = 'regions/%s' % region_id
-        else:
-            method = self.post
-            url = 'regions'
-        req_body = json.dumps({'region': kwargs})
-        resp, body = method(url, req_body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def update_region(self, region_id, **kwargs):
-        """Updates a region.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#updateRegion
-        """
-        post_body = json.dumps({'region': kwargs})
-        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)
-
-    def show_region(self, region_id):
-        """Get region."""
-        url = 'regions/%s' % region_id
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def list_regions(self, params=None):
-        """List regions."""
-        url = 'regions'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
diff --git a/tempest/services/identity/v3/json/services_client.py b/tempest/services/identity/v3/json/services_client.py
deleted file mode 100644
index dd65f1d..0000000
--- a/tempest/services/identity/v3/json/services_client.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright 2013 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.
-
-"""
-http://developer.openstack.org/api-ref-identity-v3.html#service-catalog-v3
-"""
-
-from oslo_serialization import jsonutils as json
-
-from tempest.common import service_client
-
-
-class ServicesClient(service_client.ServiceClient):
-    api_version = "v3"
-
-    def update_service(self, service_id, **kwargs):
-        """Updates a service.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#updateService
-        """
-        patch_body = json.dumps({'service': kwargs})
-        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)
-
-    def show_service(self, service_id):
-        """Get Service."""
-        url = 'services/%s' % service_id
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def create_service(self, **kwargs):
-        """Creates a service.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#createService
-        """
-        body = json.dumps({'service': kwargs})
-        resp, body = self.post("services", body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
diff --git a/tempest/services/identity/v3/json/users_clients.py b/tempest/services/identity/v3/json/users_clients.py
deleted file mode 100644
index 85c2e79..0000000
--- a/tempest/services/identity/v3/json/users_clients.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# Copyright 2016 Red Hat, Inc.
-#
-# 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 UsersV3Client(service_client.ServiceClient):
-    api_version = "v3"
-
-    def create_user(self, user_name, password=None, project_id=None,
-                    email=None, domain_id='default', **kwargs):
-        """Creates a user."""
-        en = kwargs.get('enabled', True)
-        description = kwargs.get('description', None)
-        default_project_id = kwargs.get('default_project_id')
-        post_body = {
-            'project_id': project_id,
-            'default_project_id': default_project_id,
-            'description': description,
-            'domain_id': domain_id,
-            'email': email,
-            'enabled': en,
-            'name': user_name,
-            'password': password
-        }
-        post_body = json.dumps({'user': post_body})
-        resp, body = self.post('users', post_body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def update_user(self, user_id, name, **kwargs):
-        """Updates a user."""
-        body = self.show_user(user_id)['user']
-        email = kwargs.get('email', body['email'])
-        en = kwargs.get('enabled', body['enabled'])
-        project_id = kwargs.get('project_id', body['project_id'])
-        if 'default_project_id' in body.keys():
-            default_project_id = kwargs.get('default_project_id',
-                                            body['default_project_id'])
-        else:
-            default_project_id = kwargs.get('default_project_id')
-        description = kwargs.get('description', body['description'])
-        domain_id = kwargs.get('domain_id', body['domain_id'])
-        post_body = {
-            'name': name,
-            'email': email,
-            'enabled': en,
-            'project_id': project_id,
-            'default_project_id': default_project_id,
-            'id': user_id,
-            'domain_id': domain_id,
-            'description': description
-        }
-        post_body = json.dumps({'user': post_body})
-        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)
-
-    def update_user_password(self, user_id, **kwargs):
-        """Update a user password
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-identity-v3.html#changeUserPassword
-        """
-        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)
-
-    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)
-
-    def list_users(self, params=None):
-        """Get the list of users."""
-        url = 'users'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
-
-    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)
diff --git a/tempest/services/image/v1/json/__init__.py b/tempest/services/image/v1/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/image/v1/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/image/v1/json/images_client.py b/tempest/services/image/v1/json/images_client.py
deleted file mode 100644
index af2e68c..0000000
--- a/tempest/services/image/v1/json/images_client.py
+++ /dev/null
@@ -1,306 +0,0 @@
-# Copyright 2013 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 copy
-import errno
-import os
-import time
-
-from oslo_log import log as logging
-from oslo_serialization import jsonutils as json
-import six
-from six.moves.urllib import parse as urllib
-from tempest_lib.common.utils import misc as misc_utils
-from tempest_lib import exceptions as lib_exc
-
-from tempest.common import glance_http
-from tempest.common import service_client
-from tempest import exceptions
-
-LOG = logging.getLogger(__name__)
-
-
-class ImagesClient(service_client.ServiceClient):
-
-    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):
-        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)
-        self._http = None
-        self.dscv = disable_ssl_certificate_validation
-        self.ca_certs = ca_certs
-
-    def _image_meta_from_headers(self, headers):
-        meta = {'properties': {}}
-        for key, value in six.iteritems(headers):
-            if key.startswith('x-image-meta-property-'):
-                _key = key[22:]
-                meta['properties'][_key] = value
-            elif key.startswith('x-image-meta-'):
-                _key = key[13:]
-                meta[_key] = value
-
-        for key in ['is_public', 'protected', 'deleted']:
-            if key in meta:
-                meta[key] = meta[key].strip().lower() in ('t', 'true', 'yes',
-                                                          '1')
-        for key in ['size', 'min_ram', 'min_disk']:
-            if key in meta:
-                try:
-                    meta[key] = int(meta[key])
-                except ValueError:
-                    pass
-        return meta
-
-    def _image_meta_to_headers(self, fields):
-        headers = {}
-        fields_copy = copy.deepcopy(fields)
-        copy_from = fields_copy.pop('copy_from', None)
-        if copy_from is not None:
-            headers['x-glance-api-copy-from'] = copy_from
-        for key, value in six.iteritems(fields_copy.pop('properties', {})):
-            headers['x-image-meta-property-%s' % key] = str(value)
-        for key, value in six.iteritems(fields_copy.pop('api', {})):
-            headers['x-glance-api-property-%s' % key] = str(value)
-        for key, value in six.iteritems(fields_copy):
-            headers['x-image-meta-%s' % key] = str(value)
-        return headers
-
-    def _get_file_size(self, obj):
-        """Analyze file-like object and attempt to determine its size.
-
-        :param obj: file-like object, typically redirected from stdin.
-        :retval The file's size or None if it cannot be determined.
-        """
-        # For large images, we need to supply the size of the
-        # image file. See LP Bugs #827660 and #845788.
-        if hasattr(obj, 'seek') and hasattr(obj, 'tell'):
-            try:
-                obj.seek(0, os.SEEK_END)
-                obj_size = obj.tell()
-                obj.seek(0)
-                return obj_size
-            except IOError as e:
-                if e.errno == errno.ESPIPE:
-                    # Illegal seek. This means the user is trying
-                    # to pipe image data to the client, e.g.
-                    # echo testdata | bin/glance add blah..., or
-                    # that stdin is empty, or that a file-like
-                    # object which doesn't support 'seek/tell' has
-                    # been supplied.
-                    return None
-                else:
-                    raise
-        else:
-            # Cannot determine size of input image
-            return None
-
-    def _get_http(self):
-        return glance_http.HTTPClient(auth_provider=self.auth_provider,
-                                      filters=self.filters,
-                                      insecure=self.dscv,
-                                      ca_certs=self.ca_certs)
-
-    def _create_with_data(self, headers, data):
-        resp, body_iter = self.http.raw_request('POST', '/v1/images',
-                                                headers=headers, body=data)
-        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)
-
-    def _update_with_data(self, image_id, headers, data):
-        url = '/v1/images/%s' % image_id
-        resp, body_iter = self.http.raw_request('PUT', url, headers=headers,
-                                                body=data)
-        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)
-
-    @property
-    def http(self):
-        if self._http is None:
-            self._http = self._get_http()
-        return self._http
-
-    def create_image(self, **kwargs):
-        headers = {}
-        data = kwargs.pop('data', None)
-        headers.update(self._image_meta_to_headers(kwargs))
-
-        if data is not None:
-            return self._create_with_data(headers, data)
-
-        resp, body = self.post('v1/images', None, headers)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def update_image(self, image_id, **kwargs):
-        headers = {}
-        data = kwargs.pop('data', None)
-        headers.update(self._image_meta_to_headers(kwargs))
-
-        if data is not None:
-            return self._update_with_data(image_id, headers, data)
-
-        url = 'v1/images/%s' % image_id
-        resp, body = self.put(url, None, headers)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    def list_images(self, detail=False, **kwargs):
-        """Return a list of all images filtered by input parameters.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-image-v1.html#listImage-v1
-
-        Most parameters except the following are passed to the API without
-        any changes.
-        :param changes_since: The name is changed to changes-since
-        """
-        url = 'v1/images'
-
-        if detail:
-            url += '/detail'
-
-        properties = kwargs.pop('properties', {})
-        for key, value in six.iteritems(properties):
-            kwargs['property-%s' % key] = value
-
-        if kwargs.get('changes_since'):
-            kwargs['changes-since'] = kwargs.pop('changes_since')
-
-        if len(kwargs) > 0:
-            url += '?%s' % urllib.urlencode(kwargs)
-
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
-
-    def is_resource_deleted(self, id):
-        try:
-            self.get_image_meta(id)
-        except lib_exc.NotFound:
-            return True
-        return False
-
-    @property
-    def resource_type(self):
-        """Returns the primary type of resource this client works with."""
-        return 'image_meta'
-
-    def list_image_members(self, image_id):
-        url = 'v1/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)
-
-    def list_shared_images(self, tenant_id):
-        """List shared images with the specified tenant"""
-        url = 'v1/shared-images/%s' % tenant_id
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def add_member(self, member_id, image_id, **kwargs):
-        """Add a member to an image.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-image-v1.html#addMember-v1
-        """
-        url = 'v1/images/%s/members/%s' % (image_id, member_id)
-        body = json.dumps({'member': kwargs})
-        resp, __ = self.put(url, body)
-        self.expected_success(204, resp.status)
-        return service_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)
-
-    # NOTE(afazekas): just for the wait function
-    def _get_image_status(self, image_id):
-        meta = self.get_image_meta(image_id)
-        status = meta['status']
-        return status
-
-    # NOTE(afazkas): Wait reinvented again. It is not in the correct layer
-    def wait_for_image_status(self, image_id, status):
-        """Waits for a Image to reach a given status."""
-        start_time = time.time()
-        old_value = value = self._get_image_status(image_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 value == 'killed':
-                raise exceptions.ImageKilledException(image_id=image_id,
-                                                      status=status)
-            if dtime > self.build_timeout:
-                message = ('Time Limit Exceeded! (%ds)'
-                           'while waiting for %s, '
-                           'but we got %s.' %
-                           (self.build_timeout, status, value))
-                caller = misc_utils.find_test_caller()
-                if caller:
-                    message = '(%s) %s' % (caller, message)
-                raise exceptions.TimeoutException(message)
-            time.sleep(self.build_interval)
-            old_value = value
-            value = self._get_image_status(image_id)
diff --git a/tempest/services/image/v2/json/__init__.py b/tempest/services/image/v2/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/image/v2/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/image/v2/json/images_client.py b/tempest/services/image/v2/json/images_client.py
deleted file mode 100644
index 72b203a..0000000
--- a/tempest/services/image/v2/json/images_client.py
+++ /dev/null
@@ -1,256 +0,0 @@
-# Copyright 2013 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.
-
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-from tempest_lib import exceptions as lib_exc
-
-from tempest.common import glance_http
-from tempest.common import service_client
-
-
-class ImagesClientV2(service_client.ServiceClient):
-
-    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):
-        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)
-        self._http = None
-        self.dscv = disable_ssl_certificate_validation
-        self.ca_certs = ca_certs
-
-    def _get_http(self):
-        return glance_http.HTTPClient(auth_provider=self.auth_provider,
-                                      filters=self.filters,
-                                      insecure=self.dscv,
-                                      ca_certs=self.ca_certs)
-
-    @property
-    def http(self):
-        if self._http is None:
-            self._http = self._get_http()
-        return self._http
-
-    def update_image(self, image_id, patch):
-        """Update an image.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-image-v2.html#updateImage-v2
-        """
-        data = json.dumps(patch)
-        headers = {"Content-Type": "application/openstack-images-v2.0"
-                                   "-json-patch"}
-        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)
-
-    def create_image(self, **kwargs):
-        """Create an image.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-image-v2.html#createImage-v2
-        """
-        data = json.dumps(kwargs)
-        resp, body = self.post('v2/images', data)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    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)
-
-    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)
-
-    def list_images(self, params=None):
-        url = 'v2/images'
-
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    def is_resource_deleted(self, id):
-        try:
-            self.show_image(id)
-        except lib_exc.NotFound:
-            return True
-        return False
-
-    @property
-    def resource_type(self):
-        """Returns the primary type of resource this client works with."""
-        return 'image'
-
-    def store_image_file(self, image_id, data):
-        url = 'v2/images/%s/file' % image_id
-        headers = {'Content-Type': 'application/octet-stream'}
-        resp, body = self.http.raw_request('PUT', url, headers=headers,
-                                           body=data)
-        self.expected_success(204, resp.status)
-        return service_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)
-
-    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)
-
-    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)
-
-    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)
-
-    def create_image_member(self, image_id, **kwargs):
-        """Create an image member.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-image-v2.html#createImageMember-v2
-        """
-        url = 'v2/images/%s/members' % image_id
-        data = json.dumps(kwargs)
-        resp, body = self.post(url, data)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def update_image_member(self, image_id, member_id, **kwargs):
-        """Update an image member.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-image-v2.html#updateImageMember-v2
-        """
-        url = 'v2/images/%s/members/%s' % (image_id, member_id)
-        data = json.dumps(kwargs)
-        resp, body = self.put(url, data)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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))
-
-    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)
-
-    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)
-
-    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)
-
-    def create_namespace(self, **kwargs):
-        """Create a namespace.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-image-v2.html#createNamespace-v2
-        """
-        data = json.dumps(kwargs)
-        resp, body = self.post('/v2/metadefs/namespaces', data)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    def update_namespace(self, namespace, **kwargs):
-        """Update a namespace.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-image-v2.html#updateNamespace-v2
-        """
-        # NOTE: On Glance API, we need to pass namespace on both URI
-        # and a request body.
-        params = {'namespace': namespace}
-        params.update(kwargs)
-        data = json.dumps(params)
-        url = '/v2/metadefs/namespaces/%s' % namespace
-        resp, body = self.put(url, body=data)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
diff --git a/tempest/services/network/json/__init__.py b/tempest/services/network/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/network/json/__init__.py
+++ /dev/null
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 c6b22df..0000000
--- a/tempest/services/network/json/network_client.py
+++ /dev/null
@@ -1,230 +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_lib.common.utils import misc
-from tempest_lib import exceptions as lib_exc
-
-from tempest import exceptions
-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/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
deleted file mode 100644
index 0a7da92..0000000
--- a/tempest/services/network/resources.py
+++ /dev/null
@@ -1,188 +0,0 @@
-# Copyright 2013 Hewlett-Packard Development Company, L.P.
-# 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 abc
-
-import six
-
-
-class AttributeDict(dict):
-    """Provide attribute access (dict.key) to dictionary values."""
-
-    def __getattr__(self, name):
-        """Allow attribute access for all keys in the dict."""
-        if name in self:
-            return self[name]
-        return super(AttributeDict, self).__getattribute__(name)
-
-
-@six.add_metaclass(abc.ABCMeta)
-class DeletableResource(AttributeDict):
-    """Support deletion of neutron resources (networks, subnets)
-
-    via a delete() method, as is supported by keystone and nova resources.
-    """
-
-    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.subnets_client = kwargs.pop('subnets_client', None)
-        self.ports_client = kwargs.pop('ports_client', None)
-        super(DeletableResource, self).__init__(*args, **kwargs)
-
-    def __str__(self):
-        return '<%s id="%s" name="%s">' % (self.__class__.__name__,
-                                           self.id, self.name)
-
-    @abc.abstractmethod
-    def delete(self):
-        return
-
-    @abc.abstractmethod
-    def refresh(self):
-        return
-
-    def __hash__(self):
-        return hash(self.id)
-
-    def wait_for_status(self, status):
-        if not hasattr(self, 'status'):
-            return
-
-        def helper_get():
-            self.refresh()
-            return self
-
-        return self.client.wait_for_resource_status(helper_get, status)
-
-
-class DeletableNetwork(DeletableResource):
-
-    def delete(self):
-        self.networks_client.delete_network(self.id)
-
-
-class DeletableSubnet(DeletableResource):
-
-    def __init__(self, *args, **kwargs):
-        super(DeletableSubnet, self).__init__(*args, **kwargs)
-        self._router_ids = set()
-
-    def update(self, *args, **kwargs):
-        result = self.subnets_client.update_subnet(self.id,
-                                                   *args,
-                                                   **kwargs)
-        return super(DeletableSubnet, self).update(**result['subnet'])
-
-    def add_to_router(self, router_id):
-        self._router_ids.add(router_id)
-        self.network_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,
-                                                        subnet_id=self.id)
-            self._router_ids.remove(router_id)
-        self.subnets_client.delete_subnet(self.id)
-
-
-class DeletableRouter(DeletableResource):
-
-    def set_gateway(self, network_id):
-        return self.update(external_gateway_info=dict(network_id=network_id))
-
-    def unset_gateway(self):
-        return self.update(external_gateway_info=dict())
-
-    def update(self, *args, **kwargs):
-        result = self.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)
-
-
-class DeletableFloatingIp(DeletableResource):
-
-    def refresh(self, *args, **kwargs):
-        result = self.client.show_floatingip(self.id,
-                                             *args,
-                                             **kwargs)
-        super(DeletableFloatingIp, self).update(**result['floatingip'])
-
-    def update(self, *args, **kwargs):
-        result = self.client.update_floatingip(self.id,
-                                               *args,
-                                               **kwargs)
-        super(DeletableFloatingIp, self).update(**result['floatingip'])
-
-    def __repr__(self):
-        return '<%s addr="%s">' % (self.__class__.__name__,
-                                   self.floating_ip_address)
-
-    def __str__(self):
-        return '<"FloatingIP" addr="%s" id="%s">' % (self.floating_ip_address,
-                                                     self.id)
-
-    def delete(self):
-        self.client.delete_floatingip(self.id)
-
-
-class DeletablePort(DeletableResource):
-
-    def delete(self):
-        self.ports_client.delete_port(self.id)
-
-
-class DeletableSecurityGroup(DeletableResource):
-
-    def delete(self):
-        self.client.delete_security_group(self.id)
-
-
-class DeletableSecurityGroupRule(DeletableResource):
-
-    def __repr__(self):
-        return '<%s id="%s">' % (self.__class__.__name__, self.id)
-
-    def delete(self):
-        self.client.delete_security_group_rule(self.id)
-
-
-class DeletablePool(DeletableResource):
-
-    def delete(self):
-        self.client.delete_pool(self.id)
-
-
-class DeletableMember(DeletableResource):
-
-    def delete(self):
-        self.client.delete_member(self.id)
-
-
-class DeletableVip(DeletableResource):
-
-    def delete(self):
-        self.client.delete_vip(self.id)
-
-    def refresh(self):
-        result = self.client.show_vip(self.id)
-        super(DeletableVip, self).update(**result['vip'])
diff --git a/tempest/services/object_storage/__init__.py b/tempest/services/object_storage/__init__.py
index e69de29..d1a61d6 100644
--- a/tempest/services/object_storage/__init__.py
+++ b/tempest/services/object_storage/__init__.py
@@ -0,0 +1,22 @@
+# Copyright (c) 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.
+
+from tempest.services.object_storage.account_client import AccountClient
+from tempest.services.object_storage.capabilities_client import \
+    CapabilitiesClient
+from tempest.services.object_storage.container_client import ContainerClient
+from tempest.services.object_storage.object_client import ObjectClient
+
+__all__ = ['AccountClient', 'CapabilitiesClient', 'ContainerClient',
+           'ObjectClient']
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 2c7fe29..9932b4a 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,
@@ -144,13 +144,3 @@
             body = body.strip().splitlines()
         self.expected_success([200, 204], resp.status)
         return resp, body
-
-    def list_extensions(self):
-        self.skip_path()
-        try:
-            resp, body = self.get('info')
-        finally:
-            self.reset_path()
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return resp, body
diff --git a/tempest/services/object_storage/capabilities_client.py b/tempest/services/object_storage/capabilities_client.py
new file mode 100644
index 0000000..0fe437f
--- /dev/null
+++ b/tempest/services/object_storage/capabilities_client.py
@@ -0,0 +1,31 @@
+# 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.lib.common import rest_client
+
+
+class CapabilitiesClient(rest_client.RestClient):
+
+    def list_capabilities(self):
+        self.skip_path()
+        try:
+            resp, body = self.get('info')
+        finally:
+            self.reset_path()
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return resp, body
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
index 73c25db..afedd36 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,
@@ -96,28 +96,6 @@
         self.expected_success(204, resp.status)
         return resp, body
 
-    def list_all_container_objects(self, container, params=None):
-        """Returns complete list of all objects in the container
-
-        even if item count is beyond 10,000 item listing limit.
-        Does not require any parameters aside from container name.
-        """
-        # TODO(dwalleck): Rewrite using json format to avoid newlines at end of
-        # obj names. Set limit to API limit - 1 (max returned items = 9999)
-        limit = 9999
-        if params is not None:
-            if 'limit' in params:
-                limit = params['limit']
-
-            if 'marker' in params:
-                limit = params['marker']
-
-        resp, objlist = self.list_container_contents(
-            container,
-            params={'limit': limit, 'format': 'json'})
-        self.expected_success(200, resp.status)
-        return objlist
-
     def list_container_contents(self, container, params=None):
         """List the objects in a container, given the container name
 
@@ -162,5 +140,11 @@
             body = json.loads(body)
         elif params and params.get('format') == 'xml':
             body = etree.fromstring(body)
+        # Else the content-type is plain/text
+        else:
+            body = [
+                obj_name for obj_name in body.decode().split('\n') if obj_name
+            ]
+
         self.expected_success([200, 204], resp.status)
         return resp, body
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 5890e33..6d656ec 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -13,14 +13,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
 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
+from tempest.lib import exceptions
 
 
-class ObjectClient(service_client.ServiceClient):
+class ObjectClient(rest_client.RestClient):
 
     def create_object(self, container, object_name, data,
                       params=None, metadata=None, headers=None):
@@ -41,12 +41,6 @@
         self.expected_success(201, resp.status)
         return resp, body
 
-    def update_object(self, container, object_name, data):
-        """Upload data to replace current storage object."""
-        resp, body = self.create_object(container, object_name, data)
-        self.expected_success(201, resp.status)
-        return resp, body
-
     def delete_object(self, container, object_name, params=None):
         """Delete storage object."""
         url = "%s/%s" % (str(container), str(object_name))
@@ -148,111 +142,90 @@
         self.expected_success(201, resp.status)
         return resp, body
 
-    def put_object_with_chunk(self, container, name, contents, chunk_size):
-        """Put an object with Transfer-Encoding header"""
-        if self.base_url is None:
-            self._set_auth()
+    def put_object_with_chunk(self, container, name, contents):
+        """Put an object with Transfer-Encoding header
 
+        :param container: name of the container
+        :type container: string
+        :param name: name of the object
+        :type name: string
+        :param contents: object data
+        :type contents: iterable
+        """
         headers = {'Transfer-Encoding': 'chunked'}
         if self.token:
             headers['X-Auth-Token'] = self.token
 
-        conn = put_object_connection(self.base_url, container, name, contents,
-                                     chunk_size, headers)
+        url = "%s/%s" % (container, name)
+        resp, body = self.put(
+            url, headers=headers,
+            body=contents,
+            chunked=True
+        )
 
-        resp = conn.getresponse()
-        body = resp.read()
-
-        resp_headers = {}
-        for header, value in resp.getheaders():
-            resp_headers[header.lower()] = value
-
-        self._error_checker('PUT', None, headers, contents, resp, body)
+        self._error_checker(resp, body)
         self.expected_success(201, resp.status)
-        return resp.status, resp.reason, resp_headers
+        return resp.status, resp.reason, resp
 
     def create_object_continue(self, container, object_name,
                                data, metadata=None):
-        """Create storage object."""
+        """Put an object using Expect:100-continue"""
         headers = {}
         if metadata:
             for key in metadata:
                 headers[str(key)] = metadata[key]
 
-        if not data:
-            headers['content-length'] = '0'
-
-        if self.base_url is None:
-            self._set_auth()
         headers['X-Auth-Token'] = self.token
+        headers['content-length'] = 0 if data is None else len(data)
+        headers['Expect'] = '100-continue'
 
-        conn = put_object_connection(self.base_url, str(container),
-                                     str(object_name), data, None, headers)
+        parsed = urlparse.urlparse(self.base_url)
+        path = str(parsed.path) + "/"
+        path += "%s/%s" % (str(container), str(object_name))
 
-        response = conn.response_class(conn.sock,
-                                       strict=conn.strict,
-                                       method=conn._method)
-        version, status, reason = response._read_status()
-        resp = {'version': version,
-                'status': str(status),
-                'reason': reason}
+        conn = create_connection(parsed)
 
-        return resp
-
-
-def put_object_connection(base_url, container, name, contents=None,
-                          chunk_size=65536, headers=None, query_string=None):
-    """Helper function to make connection to put object with httplib
-
-    :param base_url: base_url of an object client
-    :param container: container name that the object is in
-    :param name: object name to put
-    :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'
-                       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
-    """
-    parsed = urlparse.urlparse(base_url)
-    if parsed.scheme == 'https':
-        conn = httplib.HTTPSConnection(parsed.netloc)
-    else:
-        conn = httplib.HTTPConnection(parsed.netloc)
-    path = str(parsed.path) + "/"
-    path += "%s/%s" % (str(container), str(name))
-
-    if query_string:
-        path += '?' + query_string
-    if headers:
-        headers = dict(headers)
-    else:
-        headers = {}
-    if hasattr(contents, 'read'):
+        # Send the PUT request and the headers including the "Expect" header
         conn.putrequest('PUT', path)
-        for header, value in six.iteritems(headers):
+
+        for header, value in headers.items():
             conn.putheader(header, value)
-        if 'Content-Length' not in headers:
-            if 'Transfer-Encoding' not in headers:
-                conn.putheader('Transfer-Encoding', 'chunked')
-            conn.endheaders()
-            chunk = contents.read(chunk_size)
-            while chunk:
-                conn.send('%x\r\n%s\r\n' % (len(chunk), chunk))
-                chunk = contents.read(chunk_size)
-            conn.send('0\r\n\r\n')
-        else:
-            conn.endheaders()
-            left = headers['Content-Length']
-            while left > 0:
-                size = chunk_size
-                if size > left:
-                    size = left
-                chunk = contents.read(size)
-                conn.send(chunk)
-                left -= len(chunk)
+        conn.endheaders()
+
+        # Read the 100 status prior to sending the data
+        response = conn.response_class(conn.sock,
+                                       method=conn._method)
+        _, status, _ = response._read_status()
+
+        # toss the CRLF at the end of the response
+        response._safe_read(2)
+
+        # Expecting a 100 here, if not close and throw an exception
+        if status != 100:
+            conn.close()
+            pattern = "%s %s" % (
+                """Unexpected http success status code {0}.""",
+                """The expected status code is {1}""")
+            details = pattern.format(status, 100)
+            raise exceptions.UnexpectedResponseCode(details)
+
+        # If a continue was received go ahead and send the data
+        # and get the final response
+        conn.send(data)
+
+        resp = conn.getresponse()
+
+        return resp.status, resp.reason
+
+
+def create_connection(parsed_url):
+    """Helper function to create connection with httplib
+
+    :param parsed_url: parsed url of the remote location
+    """
+    if parsed_url.scheme == 'https':
+        conn = httplib.HTTPSConnection(parsed_url.netloc)
     else:
-        conn.request('PUT', path, contents, headers)
+        conn = httplib.HTTPConnection(parsed_url.netloc)
 
     return conn
diff --git a/tempest/services/orchestration/__init__.py b/tempest/services/orchestration/__init__.py
index e69de29..5a1ffcc 100644
--- a/tempest/services/orchestration/__init__.py
+++ b/tempest/services/orchestration/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (c) 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.
+
+from tempest.services.orchestration.json.orchestration_client import \
+    OrchestrationClient
+
+__all__ = ['OrchestrationClient']
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index 22e53f5..9fec548 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -18,13 +18,13 @@
 
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
-from tempest_lib import exceptions as lib_exc
 
-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$'):
@@ -214,7 +178,7 @@
                            'within the required time (%s s).' %
                            (stack_name, status, stack_status,
                             self.build_timeout))
-                raise exceptions.TimeoutException(message)
+                raise lib_exc.TimeoutException(message)
             time.sleep(self.build_interval)
 
     def show_resource_metadata(self, stack_identifier, resource_name):
@@ -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/__init__.py b/tempest/services/telemetry/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/telemetry/__init__.py
+++ /dev/null
diff --git a/tempest/services/telemetry/json/__init__.py b/tempest/services/telemetry/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/telemetry/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/telemetry/json/alarming_client.py b/tempest/services/telemetry/json/alarming_client.py
deleted file mode 100644
index ce14211..0000000
--- a/tempest/services/telemetry/json/alarming_client.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# Copyright 2014 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 six.moves.urllib import parse as urllib
-
-from tempest.common import service_client
-
-
-class AlarmingClient(service_client.ServiceClient):
-
-    version = '2'
-    uri_prefix = "v2"
-
-    def deserialize(self, body):
-        return json.loads(body.replace("\n", ""))
-
-    def serialize(self, body):
-        return json.dumps(body)
-
-    def list_alarms(self, query=None):
-        uri = '%s/alarms' % self.uri_prefix
-        uri_dict = {}
-        if query:
-            uri_dict = {'q.field': query[0],
-                        'q.op': query[1],
-                        'q.value': query[2]}
-        if uri_dict:
-            uri += "?%s" % urllib.urlencode(uri_dict)
-        resp, body = self.get(uri)
-        self.expected_success(200, resp.status)
-        body = self.deserialize(body)
-        return service_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)
-
-    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)
-
-    def delete_alarm(self, alarm_id):
-        uri = "%s/alarms/%s" % (self.uri_prefix, alarm_id)
-        resp, body = self.delete(uri)
-        self.expected_success(204, resp.status)
-        if body:
-            body = self.deserialize(body)
-        return service_client.ResponseBody(resp, body)
-
-    def create_alarm(self, **kwargs):
-        uri = "%s/alarms" % self.uri_prefix
-        body = self.serialize(kwargs)
-        resp, body = self.post(uri, body)
-        self.expected_success(201, resp.status)
-        body = self.deserialize(body)
-        return service_client.ResponseBody(resp, body)
-
-    def update_alarm(self, alarm_id, **kwargs):
-        uri = "%s/alarms/%s" % (self.uri_prefix, alarm_id)
-        body = self.serialize(kwargs)
-        resp, body = self.put(uri, body)
-        self.expected_success(200, resp.status)
-        body = self.deserialize(body)
-        return service_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)
-
-    def alarm_set_state(self, alarm_id, state):
-        uri = "%s/alarms/%s/state" % (self.uri_prefix, alarm_id)
-        body = self.serialize(state)
-        resp, body = self.put(uri, body)
-        self.expected_success(200, resp.status)
-        body = self.deserialize(body)
-        return service_client.ResponseBodyData(resp, body)
diff --git a/tempest/services/telemetry/json/telemetry_client.py b/tempest/services/telemetry/json/telemetry_client.py
deleted file mode 100644
index abdeba2..0000000
--- a/tempest/services/telemetry/json/telemetry_client.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright 2014 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 six.moves.urllib import parse as urllib
-
-from tempest.common import service_client
-
-
-class TelemetryClient(service_client.ServiceClient):
-
-    version = '2'
-    uri_prefix = "v2"
-
-    def deserialize(self, body):
-        return json.loads(body.replace("\n", ""))
-
-    def serialize(self, body):
-        return json.dumps(body)
-
-    def create_sample(self, meter_name, sample_list):
-        uri = "%s/meters/%s" % (self.uri_prefix, meter_name)
-        body = self.serialize(sample_list)
-        resp, body = self.post(uri, body)
-        self.expected_success(200, resp.status)
-        body = self.deserialize(body)
-        return service_client.ResponseBody(resp, body)
-
-    def _helper_list(self, uri, query=None, period=None):
-        uri_dict = {}
-        if query:
-            uri_dict = {'q.field': query[0],
-                        'q.op': query[1],
-                        'q.value': query[2]}
-        if period:
-            uri_dict['period'] = period
-        if uri_dict:
-            uri += "?%s" % urllib.urlencode(uri_dict)
-        resp, body = self.get(uri)
-        self.expected_success(200, resp.status)
-        body = self.deserialize(body)
-        return service_client.ResponseBodyList(resp, body)
-
-    def list_resources(self, query=None):
-        uri = '%s/resources' % self.uri_prefix
-        return self._helper_list(uri, query)
-
-    def list_meters(self, query=None):
-        uri = '%s/meters' % self.uri_prefix
-        return self._helper_list(uri, query)
-
-    def list_statistics(self, meter, period=None, query=None):
-        uri = "%s/meters/%s/statistics" % (self.uri_prefix, meter)
-        return self._helper_list(uri, query, period)
-
-    def list_samples(self, meter_id, query=None):
-        uri = '%s/meters/%s' % (self.uri_prefix, meter_id)
-        return self._helper_list(uri, query)
-
-    def list_events(self, query=None):
-        uri = '%s/events' % self.uri_prefix
-        return self._helper_list(uri, query)
-
-    def show_resource(self, resource_id):
-        uri = '%s/resources/%s' % (self.uri_prefix, resource_id)
-        resp, body = self.get(uri)
-        self.expected_success(200, resp.status)
-        body = self.deserialize(body)
-        return service_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/__init__.py b/tempest/services/volume/base/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/base/__init__.py
+++ /dev/null
diff --git a/tempest/services/volume/base/admin/__init__.py b/tempest/services/volume/base/admin/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/base/admin/__init__.py
+++ /dev/null
diff --git a/tempest/services/volume/base/admin/base_hosts_client.py b/tempest/services/volume/base/admin/base_hosts_client.py
deleted file mode 100644
index 074f87f..0000000
--- a/tempest/services/volume/base/admin/base_hosts_client.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright 2013 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 six.moves.urllib import parse as urllib
-
-from tempest.common import service_client
-
-
-class BaseHostsClient(service_client.ServiceClient):
-    """Client class to send CRUD Volume Hosts API requests"""
-
-    def list_hosts(self, **params):
-        """Lists all hosts."""
-
-        url = 'os-hosts'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_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
deleted file mode 100644
index e063a31..0000000
--- a/tempest/services/volume/base/admin/base_quotas_client.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
-#
-#    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
-from six.moves.urllib import parse as urllib
-
-from tempest.common import service_client
-
-
-class BaseQuotasClient(service_client.ServiceClient):
-    """Client class to send CRUD Volume Quotas API requests"""
-
-    TYPE = "json"
-
-    def show_default_quota_set(self, tenant_id):
-        """List the default volume quota set for a tenant."""
-
-        url = 'os-quota-sets/%s/defaults' % tenant_id
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = jsonutils.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def show_quota_set(self, tenant_id, params=None):
-        """List the quota set for a tenant."""
-
-        url = 'os-quota-sets/%s' % tenant_id
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = jsonutils.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def show_quota_usage(self, tenant_id):
-        """List the quota set for a tenant."""
-
-        body = self.show_quota_set(tenant_id, params={'usage': True})
-        return body
-
-    def update_quota_set(self, tenant_id, **kwargs):
-        """Updates quota set
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#updateQuotas-v2
-        """
-        put_body = jsonutils.dumps({'quota_set': kwargs})
-        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)
-
-    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)
diff --git a/tempest/services/volume/base/admin/base_services_client.py b/tempest/services/volume/base/admin/base_services_client.py
deleted file mode 100644
index 3626469..0000000
--- a/tempest/services/volume/base/admin/base_services_client.py
+++ /dev/null
@@ -1,32 +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.
-
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.common import service_client
-
-
-class BaseServicesClient(service_client.ServiceClient):
-
-    def list_services(self, **params):
-        url = 'os-services'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_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
deleted file mode 100644
index 867273e..0000000
--- a/tempest/services/volume/base/admin/base_types_client.py
+++ /dev/null
@@ -1,181 +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 six.moves.urllib import parse as urllib
-from tempest_lib import exceptions as lib_exc
-
-from tempest.common import service_client
-
-
-class BaseTypesClient(service_client.ServiceClient):
-    """Client class to send CRUD Volume Types API requests"""
-
-    def is_resource_deleted(self, resource):
-        # to use this method self.resource must be defined to respective value
-        # Resource is a dictionary containing resource id and type
-        # Resource : {"id" : resource_id
-        #             "type": resource_type}
-        try:
-            if resource['type'] == "volume-type":
-                self.show_volume_type(resource['id'])
-            elif resource['type'] == "encryption-type":
-                body = self.show_encryption_type(resource['id'])
-                if not body:
-                    return True
-            else:
-                msg = (" resource value is either not defined or incorrect.")
-                raise lib_exc.UnprocessableEntity(msg)
-        except lib_exc.NotFound:
-            return True
-        return False
-
-    @property
-    def resource_type(self):
-        """Returns the primary type of resource this client works with."""
-        return 'volume-type/encryption-type'
-
-    def list_volume_types(self, **params):
-        """List all the volume_types created."""
-        url = 'types'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def show_volume_type(self, volume_id):
-        """Returns the details of a single volume_type."""
-        url = "types/%s" % str(volume_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def create_volume_type(self, **kwargs):
-        """Create volume type.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#createVolumeType
-        """
-        post_body = json.dumps({'volume_type': kwargs})
-        resp, body = self.post('types', post_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_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)
-
-    def list_volume_types_extra_specs(self, vol_type_id, **params):
-        """List all the volume_types extra specs created.
-
-        TODO: Current api-site doesn't contain this API description.
-        After fixing the api-site, we need to fix here also for putting
-        the link to api-site.
-
-
-        """
-        url = 'types/%s/extra_specs' % str(vol_type_id)
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_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."""
-        url = "types/%s/extra_specs/%s" % (str(vol_type_id),
-                                           str(extra_specs_name))
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def create_volume_type_extra_specs(self, vol_type_id, extra_specs):
-        """Creates a new Volume_type extra spec.
-
-        vol_type_id: Id of volume_type.
-        extra_specs: A dictionary of values to be used as extra_specs.
-        """
-        url = "types/%s/extra_specs" % str(vol_type_id)
-        post_body = json.dumps({'extra_specs': extra_specs})
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_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)
-
-    def update_volume_type_extra_specs(self, vol_type_id, extra_spec_name,
-                                       extra_specs):
-        """Update a volume_type extra spec.
-
-        vol_type_id: Id of volume_type.
-        extra_spec_name: Name of the extra spec to be updated.
-        extra_spec: A dictionary of with key as extra_spec_name and the
-                     updated value.
-        """
-        url = "types/%s/extra_specs/%s" % (str(vol_type_id),
-                                           str(extra_spec_name))
-        put_body = json.dumps(extra_specs)
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def show_encryption_type(self, vol_type_id):
-        """Get the volume encryption type for the specified volume type.
-
-        vol_type_id: Id of volume_type.
-        """
-        url = "/types/%s/encryption" % str(vol_type_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def create_encryption_type(self, vol_type_id, **kwargs):
-        """Create encryption type.
-
-        TODO: Current api-site doesn't contain this API description.
-        After fixing the api-site, we need to fix here also for putting
-        the link to api-site.
-        """
-        url = "/types/%s/encryption" % str(vol_type_id)
-        post_body = json.dumps({'encryption': kwargs})
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_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)
diff --git a/tempest/services/volume/base/base_availability_zone_client.py b/tempest/services/volume/base/base_availability_zone_client.py
deleted file mode 100644
index b63fdc2..0000000
--- a/tempest/services/volume/base/base_availability_zone_client.py
+++ /dev/null
@@ -1,27 +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.
-
-from oslo_serialization import jsonutils as json
-
-from tempest.common import service_client
-
-
-class BaseAvailabilityZoneClient(service_client.ServiceClient):
-
-    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)
diff --git a/tempest/services/volume/base/base_backups_client.py b/tempest/services/volume/base/base_backups_client.py
deleted file mode 100644
index fc9a40a..0000000
--- a/tempest/services/volume/base/base_backups_client.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# Copyright 2014 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 time
-
-from oslo_serialization import jsonutils as json
-from tempest_lib import exceptions as lib_exc
-
-from tempest.common import service_client
-from tempest import exceptions
-
-
-class BaseBackupsClient(service_client.ServiceClient):
-    """Client class to send CRUD Volume backup API requests"""
-
-    def create_backup(self, **kwargs):
-        """Creates a backup of volume."""
-        post_body = json.dumps({'backup': kwargs})
-        resp, body = self.post('backups', post_body)
-        body = json.loads(body)
-        self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def restore_backup(self, backup_id, **kwargs):
-        """Restore volume from backup."""
-        post_body = json.dumps({'restore': kwargs})
-        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)
-
-    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)
-
-    def show_backup(self, backup_id):
-        """Returns the details of a single backup."""
-        url = "backups/%s" % str(backup_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def list_backups(self, detail=False):
-        """Information for all the tenant's backups."""
-        url = "backups"
-        if detail:
-            url += "/detail"
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def export_backup(self, backup_id):
-        """Export backup metadata record."""
-        url = "backups/%s/export_record" % backup_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def import_backup(self, **kwargs):
-        """Import backup metadata record."""
-        post_body = json.dumps({'backup-record': kwargs})
-        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)
-
-    def wait_for_backup_status(self, backup_id, status):
-        """Waits for a Backup to reach a given status."""
-        body = self.show_backup(backup_id)['backup']
-        backup_status = body['status']
-        start = int(time.time())
-
-        while backup_status != status:
-            time.sleep(self.build_interval)
-            body = self.show_backup(backup_id)['backup']
-            backup_status = body['status']
-            if backup_status == 'error':
-                raise exceptions.VolumeBackupException(backup_id=backup_id)
-
-            if int(time.time()) - start >= self.build_timeout:
-                message = ('Volume backup %s failed to reach %s status '
-                           '(current %s) within the required time (%s s).' %
-                           (backup_id, status, backup_status,
-                            self.build_timeout))
-                raise exceptions.TimeoutException(message)
-
-    def wait_for_backup_deletion(self, backup_id):
-        """Waits for backup deletion"""
-        start_time = int(time.time())
-        while True:
-            try:
-                self.show_backup(backup_id)
-            except lib_exc.NotFound:
-                return
-            if int(time.time()) - start_time >= self.build_timeout:
-                raise exceptions.TimeoutException
-            time.sleep(self.build_interval)
diff --git a/tempest/services/volume/base/base_extensions_client.py b/tempest/services/volume/base/base_extensions_client.py
deleted file mode 100644
index afc3f6b..0000000
--- a/tempest/services/volume/base/base_extensions_client.py
+++ /dev/null
@@ -1,28 +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.common import service_client
-
-
-class BaseExtensionsClient(service_client.ServiceClient):
-
-    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)
diff --git a/tempest/services/volume/base/base_qos_client.py b/tempest/services/volume/base/base_qos_client.py
deleted file mode 100644
index 697e902..0000000
--- a/tempest/services/volume/base/base_qos_client.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# 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 time
-
-from oslo_serialization import jsonutils as json
-from tempest_lib import exceptions as lib_exc
-
-from tempest.common import service_client
-from tempest import exceptions
-
-
-class BaseQosSpecsClient(service_client.ServiceClient):
-    """Client class to send CRUD QoS API requests"""
-
-    def is_resource_deleted(self, qos_id):
-        try:
-            self.show_qos(qos_id)
-        except lib_exc.NotFound:
-            return True
-        return False
-
-    @property
-    def resource_type(self):
-        """Returns the primary type of resource this client works with."""
-        return 'qos'
-
-    def wait_for_qos_operations(self, qos_id, operation, args=None):
-        """Waits for a qos operations to be completed.
-
-        NOTE : operation value is required for  wait_for_qos_operations()
-        operation = 'qos-key' / 'disassociate' / 'disassociate-all'
-        args = keys[] when operation = 'qos-key'
-        args = volume-type-id disassociated when operation = 'disassociate'
-        args = None when operation = 'disassociate-all'
-        """
-        start_time = int(time.time())
-        while True:
-            if operation == 'qos-key-unset':
-                body = self.show_qos(qos_id)['qos_specs']
-                if not any(key in body['specs'] for key in args):
-                    return
-            elif operation == 'disassociate':
-                body = self.show_association_qos(qos_id)['qos_associations']
-                if not any(args in body[i]['id'] for i in range(0, len(body))):
-                    return
-            elif operation == 'disassociate-all':
-                body = self.show_association_qos(qos_id)['qos_associations']
-                if not body:
-                    return
-            else:
-                msg = (" operation value is either not defined or incorrect.")
-                raise lib_exc.UnprocessableEntity(msg)
-
-            if int(time.time()) - start_time >= self.build_timeout:
-                raise exceptions.TimeoutException
-            time.sleep(self.build_interval)
-
-    def create_qos(self, **kwargs):
-        """Create a QoS Specification.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#createQoSSpec
-        """
-        post_body = json.dumps({'qos_specs': kwargs})
-        resp, body = self.post('qos-specs', post_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_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)
-
-    def list_qos(self):
-        """List all the QoS specifications created."""
-        url = 'qos-specs'
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def show_qos(self, qos_id):
-        """Get the specified QoS specification."""
-        url = "qos-specs/%s" % str(qos_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def set_qos_key(self, qos_id, **kwargs):
-        """Set the specified keys/values of QoS specification.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#setQoSKey
-        """
-        put_body = json.dumps({"qos_specs": kwargs})
-        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)
-
-    def unset_qos_key(self, qos_id, keys):
-        """Unset the specified keys of QoS specification.
-
-        :param keys: keys to delete from the QoS specification.
-
-        TODO(jordanP): Add a link once LP #1524877 is fixed.
-        """
-        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)
-
-    def associate_qos(self, qos_id, vol_type_id):
-        """Associate the specified QoS with specified volume-type."""
-        url = "qos-specs/%s/associate" % str(qos_id)
-        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)
-
-    def show_association_qos(self, qos_id):
-        """Get the association of the specified QoS specification."""
-        url = "qos-specs/%s/associations" % str(qos_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def disassociate_qos(self, qos_id, vol_type_id):
-        """Disassociate the specified QoS with specified volume-type."""
-        url = "qos-specs/%s/disassociate" % str(qos_id)
-        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)
-
-    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)
diff --git a/tempest/services/volume/base/base_snapshots_client.py b/tempest/services/volume/base/base_snapshots_client.py
deleted file mode 100644
index 1388e9c..0000000
--- a/tempest/services/volume/base/base_snapshots_client.py
+++ /dev/null
@@ -1,203 +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 oslo_log import log as logging
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-from tempest_lib import exceptions as lib_exc
-
-from tempest.common import service_client
-from tempest import exceptions
-
-
-LOG = logging.getLogger(__name__)
-
-
-class BaseSnapshotsClient(service_client.ServiceClient):
-    """Base Client class to send CRUD Volume API requests."""
-
-    create_resp = 200
-
-    def list_snapshots(self, detail=False, **params):
-        """List all the snapshot."""
-        url = 'snapshots'
-        if detail:
-            url += '/detail'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def show_snapshot(self, snapshot_id):
-        """Returns the details of a single snapshot."""
-        url = "snapshots/%s" % str(snapshot_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def create_snapshot(self, **kwargs):
-        """Creates a new snapshot.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#createSnapshot
-        """
-        post_body = json.dumps({'snapshot': kwargs})
-        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)
-
-    def update_snapshot(self, snapshot_id, **kwargs):
-        """Updates a snapshot."""
-        put_body = json.dumps({'snapshot': kwargs})
-        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)
-
-    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)
-
-    def is_resource_deleted(self, id):
-        try:
-            self.show_snapshot(id)
-        except lib_exc.NotFound:
-            return True
-        return False
-
-    @property
-    def resource_type(self):
-        """Returns the primary type of resource this client works with."""
-        return 'volume-snapshot'
-
-    def reset_snapshot_status(self, snapshot_id, status):
-        """Reset the specified snapshot's status."""
-        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)
-
-    def update_snapshot_status(self, snapshot_id, **kwargs):
-        """Update the specified snapshot's status."""
-        # TODO(gmann): api-site doesn't contain doc ref
-        # for this API. After fixing the api-site, we need to
-        # add the link here.
-        # Bug https://bugs.launchpad.net/openstack-api-site/+bug/1532645
-
-        post_body = json.dumps({'os-update_snapshot_status': kwargs})
-        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)
-
-    def create_snapshot_metadata(self, snapshot_id, metadata):
-        """Create metadata for the snapshot."""
-        put_body = json.dumps({'metadata': metadata})
-        url = "snapshots/%s/metadata" % str(snapshot_id)
-        resp, body = self.post(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def show_snapshot_metadata(self, snapshot_id):
-        """Get metadata of the snapshot."""
-        url = "snapshots/%s/metadata" % str(snapshot_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def update_snapshot_metadata(self, snapshot_id, **kwargs):
-        """Update metadata for the snapshot."""
-        # TODO(piyush): Current api-site doesn't contain this API description.
-        # After fixing the api-site, we need to fix here also for putting the
-        # link to api-site.
-        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1529063
-        put_body = json.dumps(kwargs)
-        url = "snapshots/%s/metadata" % str(snapshot_id)
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def update_snapshot_metadata_item(self, snapshot_id, id, **kwargs):
-        """Update metadata item for the snapshot."""
-        # TODO(piyush): Current api-site doesn't contain this API description.
-        # After fixing the api-site, we need to fix here also for putting the
-        # link to api-site.
-        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1529064
-        put_body = json.dumps(kwargs)
-        url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_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)
-
-    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)
diff --git a/tempest/services/volume/base/base_volumes_client.py b/tempest/services/volume/base/base_volumes_client.py
deleted file mode 100644
index d4435bc..0000000
--- a/tempest/services/volume/base/base_volumes_client.py
+++ /dev/null
@@ -1,300 +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
-import six
-from six.moves.urllib import parse as urllib
-from tempest_lib import exceptions as lib_exc
-
-from tempest.common import service_client
-from tempest.common import waiters
-
-
-class BaseVolumesClient(service_client.ServiceClient):
-    """Base client class to send CRUD Volume API requests"""
-
-    create_resp = 200
-
-    def __init__(self, auth_provider, service, region,
-                 default_volume_size=1, **kwargs):
-        super(BaseVolumesClient, self).__init__(
-            auth_provider, service, region, **kwargs)
-        self.default_volume_size = default_volume_size
-
-    def get_attachment_from_volume(self, volume):
-        """Return the element 'attachment' from input volumes."""
-        return volume['attachments'][0]
-
-    def _prepare_params(self, params):
-        """Prepares params for use in get or _ext_get methods.
-
-        If params is a string it will be left as it is, but if it's not it will
-        be urlencoded.
-        """
-        if isinstance(params, six.string_types):
-            return params
-        return urllib.urlencode(params)
-
-    def list_volumes(self, detail=False, params=None):
-        """List all the volumes created.
-
-        Params can be a string (must be urlencoded) or a dictionary.
-        """
-        url = 'volumes'
-        if detail:
-            url += '/detail'
-        if params:
-            url += '?%s' % self._prepare_params(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def show_volume(self, volume_id):
-        """Returns the details of a single volume."""
-        url = "volumes/%s" % str(volume_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def create_volume(self, **kwargs):
-        """Creates a new Volume.
-
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#createVolume
-        """
-        if 'size' not in kwargs:
-            kwargs['size'] = self.default_volume_size
-        post_body = json.dumps({'volume': kwargs})
-        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)
-
-    def update_volume(self, volume_id, **kwargs):
-        """Updates the Specified Volume."""
-        put_body = json.dumps({'volume': kwargs})
-        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)
-
-    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)
-
-    def upload_volume(self, volume_id, **kwargs):
-        """Uploads a volume in Glance."""
-        post_body = json.dumps({'os-volume_upload_image': kwargs})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def attach_volume(self, volume_id, **kwargs):
-        """Attaches a volume to a given instance on a given mountpoint."""
-        post_body = json.dumps({'os-attach': kwargs})
-        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 set_bootable_volume(self, volume_id, **kwargs):
-        """set a bootable flag for a volume - true or false."""
-        post_body = json.dumps({'os-set_bootable': kwargs})
-        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)
-
-    def detach_volume(self, volume_id):
-        """Detaches a volume from an instance."""
-        post_body = json.dumps({'os-detach': {}})
-        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 reserve_volume(self, volume_id):
-        """Reserves a volume."""
-        post_body = json.dumps({'os-reserve': {}})
-        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 unreserve_volume(self, volume_id):
-        """Restore a reserved volume ."""
-        post_body = json.dumps({'os-unreserve': {}})
-        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)
-
-    def is_resource_deleted(self, id):
-        try:
-            self.show_volume(id)
-        except lib_exc.NotFound:
-            return True
-        return False
-
-    @property
-    def resource_type(self):
-        """Returns the primary type of resource this client works with."""
-        return 'volume'
-
-    def extend_volume(self, volume_id, **kwargs):
-        """Extend a volume."""
-        post_body = json.dumps({'os-extend': kwargs})
-        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 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)
-
-    def volume_begin_detaching(self, volume_id):
-        """Volume Begin Detaching."""
-        # ref cinder/api/contrib/volume_actions.py#L158
-        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)
-
-    def volume_roll_detaching(self, volume_id):
-        """Volume Roll Detaching."""
-        # cinder/api/contrib/volume_actions.py#L170
-        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)
-
-    def create_volume_transfer(self, **kwargs):
-        """Create a volume transfer."""
-        post_body = json.dumps({'transfer': kwargs})
-        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)
-
-    def show_volume_transfer(self, transfer_id):
-        """Returns the details of a volume transfer."""
-        url = "os-volume-transfer/%s" % str(transfer_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def list_volume_transfers(self, **params):
-        """List all the volume transfers created."""
-        url = 'os-volume-transfer'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_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)
-
-    def accept_volume_transfer(self, transfer_id, **kwargs):
-        """Accept a volume transfer."""
-        url = 'os-volume-transfer/%s/accept' % transfer_id
-        post_body = json.dumps({'accept': kwargs})
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        self.expected_success(202, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def update_volume_readonly(self, volume_id, **kwargs):
-        """Update the Specified Volume readonly."""
-        post_body = json.dumps({'os-update_readonly_flag': kwargs})
-        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 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)
-
-    def create_volume_metadata(self, volume_id, metadata):
-        """Create metadata for the volume."""
-        put_body = json.dumps({'metadata': metadata})
-        url = "volumes/%s/metadata" % str(volume_id)
-        resp, body = self.post(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def show_volume_metadata(self, volume_id):
-        """Get metadata of the volume."""
-        url = "volumes/%s/metadata" % str(volume_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def update_volume_metadata(self, volume_id, metadata):
-        """Update metadata for the volume."""
-        put_body = json.dumps({'metadata': metadata})
-        url = "volumes/%s/metadata" % str(volume_id)
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def update_volume_metadata_item(self, volume_id, id, meta_item):
-        """Update metadata item for the volume."""
-        put_body = json.dumps({'meta': meta_item})
-        url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return service_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)
-
-    def retype_volume(self, volume_id, **kwargs):
-        """Updates volume with new volume type."""
-        post_body = json.dumps({'os-retype': kwargs})
-        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        self.expected_success(202, resp.status)
diff --git a/tempest/services/volume/v1/json/__init__.py b/tempest/services/volume/v1/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/v1/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/volume/v1/json/admin/__init__.py b/tempest/services/volume/v1/json/admin/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/v1/json/admin/__init__.py
+++ /dev/null
diff --git a/tempest/services/volume/v1/json/admin/hosts_client.py b/tempest/services/volume/v1/json/admin/hosts_client.py
deleted file mode 100644
index 3b52968..0000000
--- a/tempest/services/volume/v1/json/admin/hosts_client.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright 2013 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 tempest.services.volume.base.admin import base_hosts_client
-
-
-class HostsClient(base_hosts_client.BaseHostsClient):
-    """Client class to send CRUD Volume Host API V1 requests"""
diff --git a/tempest/services/volume/v1/json/admin/quotas_client.py b/tempest/services/volume/v1/json/admin/quotas_client.py
deleted file mode 100644
index 27fc301..0000000
--- a/tempest/services/volume/v1/json/admin/quotas_client.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
-#
-#    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.volume.base.admin import base_quotas_client
-
-
-class QuotasClient(base_quotas_client.BaseQuotasClient):
-    """Client class to send CRUD Volume Type API V1 requests"""
diff --git a/tempest/services/volume/v1/json/admin/services_client.py b/tempest/services/volume/v1/json/admin/services_client.py
deleted file mode 100644
index 2bffd55..0000000
--- a/tempest/services/volume/v1/json/admin/services_client.py
+++ /dev/null
@@ -1,20 +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.
-
-from tempest.services.volume.base.admin import base_services_client
-
-
-class ServicesClient(base_services_client.BaseServicesClient):
-    """Volume V1 volume services client"""
diff --git a/tempest/services/volume/v1/json/admin/types_client.py b/tempest/services/volume/v1/json/admin/types_client.py
deleted file mode 100644
index 0e84296..0000000
--- a/tempest/services/volume/v1/json/admin/types_client.py
+++ /dev/null
@@ -1,20 +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 tempest.services.volume.base.admin import base_types_client
-
-
-class TypesClient(base_types_client.BaseTypesClient):
-    """Volume V1 Volume Types client"""
diff --git a/tempest/services/volume/v1/json/availability_zone_client.py b/tempest/services/volume/v1/json/availability_zone_client.py
deleted file mode 100644
index 3a27027..0000000
--- a/tempest/services/volume/v1/json/availability_zone_client.py
+++ /dev/null
@@ -1,21 +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.
-
-from tempest.services.volume.base import base_availability_zone_client
-
-
-class AvailabilityZoneClient(
-        base_availability_zone_client.BaseAvailabilityZoneClient):
-    """Volume V1 availability zone client."""
diff --git a/tempest/services/volume/v1/json/backups_client.py b/tempest/services/volume/v1/json/backups_client.py
deleted file mode 100644
index ac6db6a..0000000
--- a/tempest/services/volume/v1/json/backups_client.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright 2014 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 tempest.services.volume.base import base_backups_client
-
-
-class BackupsClient(base_backups_client.BaseBackupsClient):
-    """Volume V1 Backups client"""
diff --git a/tempest/services/volume/v1/json/extensions_client.py b/tempest/services/volume/v1/json/extensions_client.py
deleted file mode 100644
index f99d0f5..0000000
--- a/tempest/services/volume/v1/json/extensions_client.py
+++ /dev/null
@@ -1,20 +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 tempest.services.volume.base import base_extensions_client
-
-
-class ExtensionsClient(base_extensions_client.BaseExtensionsClient):
-    """Volume V1 extensions client."""
diff --git a/tempest/services/volume/v1/json/qos_client.py b/tempest/services/volume/v1/json/qos_client.py
deleted file mode 100644
index b2b2195..0000000
--- a/tempest/services/volume/v1/json/qos_client.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# 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.services.volume.base import base_qos_client
-
-
-class QosSpecsClient(base_qos_client.BaseQosSpecsClient):
-    """Volume V1 QoS client."""
diff --git a/tempest/services/volume/v1/json/snapshots_client.py b/tempest/services/volume/v1/json/snapshots_client.py
deleted file mode 100644
index b039c2b..0000000
--- a/tempest/services/volume/v1/json/snapshots_client.py
+++ /dev/null
@@ -1,17 +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.volume.base import base_snapshots_client
-
-
-class SnapshotsClient(base_snapshots_client.BaseSnapshotsClient):
-    """Client class to send CRUD Volume V1 API requests."""
diff --git a/tempest/services/volume/v1/json/volumes_client.py b/tempest/services/volume/v1/json/volumes_client.py
deleted file mode 100644
index 7782043..0000000
--- a/tempest/services/volume/v1/json/volumes_client.py
+++ /dev/null
@@ -1,20 +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 tempest.services.volume.base import base_volumes_client
-
-
-class VolumesClient(base_volumes_client.BaseVolumesClient):
-    """Client class to send CRUD Volume V1 API requests"""
diff --git a/tempest/services/volume/v2/json/__init__.py b/tempest/services/volume/v2/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/v2/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/volume/v2/json/admin/__init__.py b/tempest/services/volume/v2/json/admin/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/v2/json/admin/__init__.py
+++ /dev/null
diff --git a/tempest/services/volume/v2/json/admin/hosts_client.py b/tempest/services/volume/v2/json/admin/hosts_client.py
deleted file mode 100644
index e092c6a..0000000
--- a/tempest/services/volume/v2/json/admin/hosts_client.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2014 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 tempest.services.volume.base.admin import base_hosts_client
-
-
-class HostsClient(base_hosts_client.BaseHostsClient):
-    """Client class to send CRUD Volume V2 API requests"""
-    api_version = "v2"
diff --git a/tempest/services/volume/v2/json/admin/quotas_client.py b/tempest/services/volume/v2/json/admin/quotas_client.py
deleted file mode 100644
index 11e0e22..0000000
--- a/tempest/services/volume/v2/json/admin/quotas_client.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2014 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 tempest.services.volume.base.admin import base_quotas_client
-
-
-class QuotasClient(base_quotas_client.BaseQuotasClient):
-    """Client class to send CRUD Volume V2 API requests"""
-    api_version = "v2"
diff --git a/tempest/services/volume/v2/json/admin/services_client.py b/tempest/services/volume/v2/json/admin/services_client.py
deleted file mode 100644
index db19ba9..0000000
--- a/tempest/services/volume/v2/json/admin/services_client.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2014 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 tempest.services.volume.base.admin import base_services_client
-
-
-class ServicesClient(base_services_client.BaseServicesClient):
-    """Client class to send CRUD Volume V2 API requests"""
-    api_version = "v2"
diff --git a/tempest/services/volume/v2/json/admin/types_client.py b/tempest/services/volume/v2/json/admin/types_client.py
deleted file mode 100644
index ecf5131..0000000
--- a/tempest/services/volume/v2/json/admin/types_client.py
+++ /dev/null
@@ -1,21 +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 tempest.services.volume.base.admin import base_types_client
-
-
-class TypesClient(base_types_client.BaseTypesClient):
-    """Client class to send CRUD Volume V2 API requests"""
-    api_version = "v2"
diff --git a/tempest/services/volume/v2/json/availability_zone_client.py b/tempest/services/volume/v2/json/availability_zone_client.py
deleted file mode 100644
index 905ebdc..0000000
--- a/tempest/services/volume/v2/json/availability_zone_client.py
+++ /dev/null
@@ -1,21 +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.
-
-from tempest.services.volume.base import base_availability_zone_client
-
-
-class AvailabilityZoneClient(
-        base_availability_zone_client.BaseAvailabilityZoneClient):
-    api_version = "v2"
diff --git a/tempest/services/volume/v2/json/backups_client.py b/tempest/services/volume/v2/json/backups_client.py
deleted file mode 100644
index 78bab82..0000000
--- a/tempest/services/volume/v2/json/backups_client.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2014 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 tempest.services.volume.base import base_backups_client
-
-
-class BackupsClient(base_backups_client.BaseBackupsClient):
-    """Client class to send CRUD Volume V2 API requests"""
-    api_version = "v2"
diff --git a/tempest/services/volume/v2/json/extensions_client.py b/tempest/services/volume/v2/json/extensions_client.py
deleted file mode 100644
index 245906f..0000000
--- a/tempest/services/volume/v2/json/extensions_client.py
+++ /dev/null
@@ -1,20 +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.
-
-from tempest.services.volume.base import base_extensions_client
-
-
-class ExtensionsClient(base_extensions_client.BaseExtensionsClient):
-    api_version = "v2"
diff --git a/tempest/services/volume/v2/json/qos_client.py b/tempest/services/volume/v2/json/qos_client.py
deleted file mode 100644
index 3c0f74f..0000000
--- a/tempest/services/volume/v2/json/qos_client.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# 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.services.volume.base import base_qos_client
-
-
-class QosSpecsClient(base_qos_client.BaseQosSpecsClient):
-    api_version = "v2"
diff --git a/tempest/services/volume/v2/json/snapshots_client.py b/tempest/services/volume/v2/json/snapshots_client.py
deleted file mode 100644
index a2d415f..0000000
--- a/tempest/services/volume/v2/json/snapshots_client.py
+++ /dev/null
@@ -1,19 +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.volume.base import base_snapshots_client
-
-
-class SnapshotsClient(base_snapshots_client.BaseSnapshotsClient):
-    """Client class to send CRUD Volume V2 API requests."""
-    api_version = "v2"
-    create_resp = 202
diff --git a/tempest/services/volume/v2/json/volumes_client.py b/tempest/services/volume/v2/json/volumes_client.py
deleted file mode 100644
index b7d9dfb..0000000
--- a/tempest/services/volume/v2/json/volumes_client.py
+++ /dev/null
@@ -1,22 +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 tempest.services.volume.base import base_volumes_client
-
-
-class VolumesClient(base_volumes_client.BaseVolumesClient):
-    """Client class to send CRUD Volume V2 API requests"""
-    api_version = "v2"
-    create_resp = 202
diff --git a/tempest/stress/README.rst b/tempest/stress/README.rst
deleted file mode 100644
index 33842fd..0000000
--- a/tempest/stress/README.rst
+++ /dev/null
@@ -1,60 +0,0 @@
-.. _stress_field_guide:
-
-Tempest Field Guide to Stress Tests
-===================================
-
-OpenStack is a distributed, asynchronous system that is prone to race condition
-bugs. These bugs will not be easily found during
-functional testing but will be encountered by users in large deployments in a
-way that is hard to debug. The stress test tries to cause these bugs to happen
-in a more controlled environment.
-
-
-Environment
------------
-This particular framework assumes your working Nova cluster understands Nova
-API 2.0. The stress tests can read the logs from the cluster. To enable this
-you have to provide the hostname to call 'nova-manage' and
-the private key and user name for ssh to the cluster in the
-[stress] section of tempest.conf. You also need to provide the
-location of the log files:
-
-	target_logfiles = "regexp to all log files to be checked for errors"
-	target_private_key_path = "private ssh key for controller and log file nodes"
-	target_ssh_user = "username for controller and log file nodes"
-	target_controller = "hostname or ip of controller node (for nova-manage)
-	log_check_interval = "time between checking logs for errors (default 60s)"
-
-To activate logging on your console please make sure that you activate `use_stderr`
-in tempest.conf or use the default `logging.conf.sample` file.
-
-Running default stress test set
--------------------------------
-
-The stress test framework can automatically discover test inside the tempest
-test suite. All test flag with the `@stresstest` decorator will be executed.
-In order to use this discovery you have to install tempest CLI, be in the
-tempest root directory and execute the following:
-
-	tempest run-stress -a -d 30
-
-Running the sample test
------------------------
-
-To test installation, do the following:
-
-	tempest run-stress -t tempest/stress/etc/server-create-destroy-test.json -d 30
-
-This sample test tries to create a few VMs and kill a few VMs.
-
-
-Additional Tools
-----------------
-
-Sometimes the tests don't finish, or there are failures. In these
-cases, you may want to clean out the nova cluster. We have provided
-some scripts to do this in the ``tools`` subdirectory.
-You can use the following script to destroy any keypairs,
-floating ips, and servers:
-
-tempest/stress/tools/cleanup.py
diff --git a/tempest/stress/actions/__init__.py b/tempest/stress/actions/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/stress/actions/__init__.py
+++ /dev/null
diff --git a/tempest/stress/actions/server_create_destroy.py b/tempest/stress/actions/server_create_destroy.py
deleted file mode 100644
index 44b6f62..0000000
--- a/tempest/stress/actions/server_create_destroy.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2013 Quanta Research Cambridge, Inc.
-#
-#    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.common.utils import data_utils
-from tempest.common import waiters
-from tempest import config
-import tempest.stress.stressaction as stressaction
-
-CONF = config.CONF
-
-
-class ServerCreateDestroyTest(stressaction.StressAction):
-
-    def setUp(self, **kwargs):
-        self.image = CONF.compute.image_ref
-        self.flavor = CONF.compute.flavor_ref
-
-    def run(self):
-        name = data_utils.rand_name("instance")
-        self.logger.info("creating %s" % name)
-        server = self.manager.servers_client.create_server(
-            name=name, imageRef=self.image, flavorRef=self.flavor)['server']
-        server_id = server['id']
-        waiters.wait_for_server_status(self.manager.servers_client, server_id,
-                                       'ACTIVE')
-        self.logger.info("created %s" % server_id)
-        self.logger.info("deleting %s" % name)
-        self.manager.servers_client.delete_server(server_id)
-        waiters.wait_for_server_termination(self.manager.servers_client,
-                                            server_id)
-        self.logger.info("deleted %s" % server_id)
diff --git a/tempest/stress/actions/ssh_floating.py b/tempest/stress/actions/ssh_floating.py
deleted file mode 100644
index 4f8c6bd..0000000
--- a/tempest/stress/actions/ssh_floating.py
+++ /dev/null
@@ -1,199 +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 socket
-import subprocess
-
-from tempest.common.utils import data_utils
-from tempest.common import waiters
-from tempest import config
-import tempest.stress.stressaction as stressaction
-import tempest.test
-
-CONF = config.CONF
-
-
-class FloatingStress(stressaction.StressAction):
-
-    # from the scenario manager
-    def ping_ip_address(self, ip_address):
-        cmd = ['ping', '-c1', '-w1', ip_address]
-
-        proc = subprocess.Popen(cmd,
-                                stdout=subprocess.PIPE,
-                                stderr=subprocess.PIPE)
-        proc.communicate()
-        success = proc.returncode == 0
-        return success
-
-    def tcp_connect_scan(self, addr, port):
-        # like tcp
-        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        try:
-            s.connect((addr, port))
-        except socket.error as exc:
-            self.logger.info("%s(%s): %s", self.server_id, self.floating['ip'],
-                             str(exc))
-            return False
-        self.logger.info("%s(%s): Connected :)", self.server_id,
-                         self.floating['ip'])
-        s.close()
-        return True
-
-    def check_port_ssh(self):
-        def func():
-            return self.tcp_connect_scan(self.floating['ip'], 22)
-        if not tempest.test.call_until_true(func, self.check_timeout,
-                                            self.check_interval):
-            raise RuntimeError("Cannot connect to the ssh port.")
-
-    def check_icmp_echo(self):
-        self.logger.info("%s(%s): Pinging..",
-                         self.server_id, self.floating['ip'])
-
-        def func():
-            return self.ping_ip_address(self.floating['ip'])
-        if not tempest.test.call_until_true(func, self.check_timeout,
-                                            self.check_interval):
-            raise RuntimeError("%s(%s): Cannot ping the machine.",
-                               self.server_id, self.floating['ip'])
-        self.logger.info("%s(%s): pong :)",
-                         self.server_id, self.floating['ip'])
-
-    def _create_vm(self):
-        self.name = name = data_utils.rand_name("instance")
-        servers_client = self.manager.servers_client
-        self.logger.info("creating %s" % name)
-        vm_args = self.vm_extra_args.copy()
-        vm_args['security_groups'] = [self.sec_grp]
-        server = servers_client.create_server(name=name, imageRef=self.image,
-                                              flavorRef=self.flavor,
-                                              **vm_args)['server']
-        self.server_id = server['id']
-        if self.wait_after_vm_create:
-            waiters.wait_for_server_status(self.manager.servers_client,
-                                           self.server_id, 'ACTIVE')
-
-    def _destroy_vm(self):
-        self.logger.info("deleting %s" % self.server_id)
-        self.manager.servers_client.delete_server(self.server_id)
-        waiters.wait_for_server_termination(self.manager.servers_client,
-                                            self.server_id)
-        self.logger.info("deleted %s" % self.server_id)
-
-    def _create_sec_group(self):
-        sec_grp_cli = self.manager.compute_security_groups_client
-        s_name = data_utils.rand_name('sec_grp')
-        s_description = data_utils.rand_name('desc')
-        self.sec_grp = sec_grp_cli.create_security_group(
-            name=s_name, description=s_description)['security_group']
-        create_rule = sec_grp_cli.create_security_group_rule
-        create_rule(parent_group_id=self.sec_grp['id'], ip_protocol='tcp',
-                    from_port=22, to_port=22)
-        create_rule(parent_group_id=self.sec_grp['id'], ip_protocol='icmp',
-                    from_port=-1, to_port=-1)
-
-    def _destroy_sec_grp(self):
-        sec_grp_cli = self.manager.compute_security_groups_client
-        sec_grp_cli.delete_security_group(self.sec_grp['id'])
-
-    def _create_floating_ip(self):
-        floating_cli = self.manager.compute_floating_ips_client
-        self.floating = (floating_cli.create_floating_ip(self.floating_pool)
-                         ['floating_ip'])
-
-    def _destroy_floating_ip(self):
-        cli = self.manager.compute_floating_ips_client
-        cli.delete_floating_ip(self.floating['id'])
-        cli.wait_for_resource_deletion(self.floating['id'])
-        self.logger.info("Deleted Floating IP %s", str(self.floating['ip']))
-
-    def setUp(self, **kwargs):
-        self.image = CONF.compute.image_ref
-        self.flavor = CONF.compute.flavor_ref
-        self.vm_extra_args = kwargs.get('vm_extra_args', {})
-        self.wait_after_vm_create = kwargs.get('wait_after_vm_create',
-                                               True)
-        self.new_vm = kwargs.get('new_vm', False)
-        self.new_sec_grp = kwargs.get('new_sec_group', False)
-        self.new_floating = kwargs.get('new_floating', False)
-        self.reboot = kwargs.get('reboot', False)
-        self.floating_pool = kwargs.get('floating_pool', None)
-        self.verify = kwargs.get('verify', ('check_port_ssh',
-                                            'check_icmp_echo'))
-        self.check_timeout = kwargs.get('check_timeout', 120)
-        self.check_interval = kwargs.get('check_interval', 1)
-        self.wait_for_disassociate = kwargs.get('wait_for_disassociate',
-                                                True)
-
-        # allocate floating
-        if not self.new_floating:
-            self._create_floating_ip()
-        # add security group
-        if not self.new_sec_grp:
-            self._create_sec_group()
-        # create vm
-        if not self.new_vm:
-            self._create_vm()
-
-    def wait_disassociate(self):
-        cli = self.manager.compute_floating_ips_client
-
-        def func():
-            floating = (cli.show_floating_ip(self.floating['id'])
-                        ['floating_ip'])
-            return floating['instance_id'] is None
-
-        if not tempest.test.call_until_true(func, self.check_timeout,
-                                            self.check_interval):
-            raise RuntimeError("IP disassociate timeout!")
-
-    def run_core(self):
-        cli = self.manager.compute_floating_ips_client
-        cli.associate_floating_ip_to_server(self.floating['ip'],
-                                            self.server_id)
-        for method in self.verify:
-            m = getattr(self, method)
-            m()
-        cli.disassociate_floating_ip_from_server(self.floating['ip'],
-                                                 self.server_id)
-        if self.wait_for_disassociate:
-            self.wait_disassociate()
-
-    def run(self):
-        if self.new_sec_grp:
-            self._create_sec_group()
-        if self.new_floating:
-            self._create_floating_ip()
-        if self.new_vm:
-            self._create_vm()
-        if self.reboot:
-            self.manager.servers_client.reboot(self.server_id, 'HARD')
-            waiters.wait_for_server_status(self.manager.servers_client,
-                                           self.server_id, 'ACTIVE')
-
-        self.run_core()
-
-        if self.new_vm:
-            self._destroy_vm()
-        if self.new_floating:
-            self._destroy_floating_ip()
-        if self.new_sec_grp:
-            self._destroy_sec_grp()
-
-    def tearDown(self):
-        if not self.new_vm:
-            self._destroy_vm()
-        if not self.new_floating:
-            self._destroy_floating_ip()
-        if not self.new_sec_grp:
-            self._destroy_sec_grp()
diff --git a/tempest/stress/actions/unit_test.py b/tempest/stress/actions/unit_test.py
deleted file mode 100644
index 3b27885..0000000
--- a/tempest/stress/actions/unit_test.py
+++ /dev/null
@@ -1,94 +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_log import log as logging
-from oslo_utils import importutils
-
-from tempest import config
-import tempest.stress.stressaction as stressaction
-
-CONF = config.CONF
-
-
-class SetUpClassRunTime(object):
-
-    process = 'process'
-    action = 'action'
-    application = 'application'
-
-    allowed = set((process, action, application))
-
-    @classmethod
-    def validate(cls, name):
-        if name not in cls.allowed:
-            raise KeyError("\'%s\' not a valid option" % name)
-
-
-class UnitTest(stressaction.StressAction):
-    """This is a special action for running existing unittests as stress test.
-
-       You need to pass ``test_method`` and ``class_setup_per``
-       using ``kwargs`` in the JSON descriptor;
-       ``test_method`` should be the fully qualified name of a unittest,
-       ``class_setup_per`` should be one from:
-           ``application``: once in the stress job lifetime
-           ``process``: once in the worker process lifetime
-           ``action``: on each action
-       Not all combination working in every case.
-    """
-
-    def setUp(self, **kwargs):
-        method = kwargs['test_method'].split('.')
-        self.test_method = method.pop()
-        self.klass = importutils.import_class('.'.join(method))
-        self.logger = logging.getLogger('.'.join(method))
-        # valid options are 'process', 'application' , 'action'
-        self.class_setup_per = kwargs.get('class_setup_per',
-                                          SetUpClassRunTime.process)
-        SetUpClassRunTime.validate(self.class_setup_per)
-
-        if self.class_setup_per == SetUpClassRunTime.application:
-            self.klass.setUpClass()
-        self.setupclass_called = False
-
-    @property
-    def action(self):
-        if self.test_method:
-            return self.test_method
-        return super(UnitTest, self).action
-
-    def run_core(self):
-        res = self.klass(self.test_method).run()
-        if res.errors:
-            raise RuntimeError(res.errors)
-
-    def run(self):
-        if self.class_setup_per != SetUpClassRunTime.application:
-            if (self.class_setup_per == SetUpClassRunTime.action
-                or self.setupclass_called is False):
-                self.klass.setUpClass()
-                self.setupclass_called = True
-
-            try:
-                self.run_core()
-            except Exception as e:
-                raise e
-            finally:
-                if (CONF.stress.leave_dirty_stack is False
-                    and self.class_setup_per == SetUpClassRunTime.action):
-                    self.klass.tearDownClass()
-        else:
-            self.run_core()
-
-    def tearDown(self):
-        if self.class_setup_per != SetUpClassRunTime.action:
-            self.klass.tearDownClass()
diff --git a/tempest/stress/actions/volume_attach_delete.py b/tempest/stress/actions/volume_attach_delete.py
deleted file mode 100644
index 847f342..0000000
--- a/tempest/stress/actions/volume_attach_delete.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# (c) 2013 Deutsche Telekom AG
-#    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.common.utils import data_utils
-from tempest.common import waiters
-from tempest import config
-import tempest.stress.stressaction as stressaction
-
-CONF = config.CONF
-
-
-class VolumeAttachDeleteTest(stressaction.StressAction):
-
-    def setUp(self, **kwargs):
-        self.image = CONF.compute.image_ref
-        self.flavor = CONF.compute.flavor_ref
-
-    def run(self):
-        # Step 1: create volume
-        name = data_utils.rand_name("volume")
-        self.logger.info("creating volume: %s" % name)
-        volume = self.manager.volumes_client.create_volume(
-            display_name=name)['volume']
-        self.manager.volumes_client.wait_for_volume_status(volume['id'],
-                                                           'available')
-        self.logger.info("created volume: %s" % volume['id'])
-
-        # Step 2: create vm instance
-        vm_name = data_utils.rand_name("instance")
-        self.logger.info("creating vm: %s" % vm_name)
-        server = self.manager.servers_client.create_server(
-            name=vm_name, imageRef=self.image, flavorRef=self.flavor)['server']
-        server_id = server['id']
-        waiters.wait_for_server_status(self.manager.servers_client, server_id,
-                                       'ACTIVE')
-        self.logger.info("created vm %s" % server_id)
-
-        # Step 3: attach volume to vm
-        self.logger.info("attach volume (%s) to vm %s" %
-                         (volume['id'], server_id))
-        self.manager.servers_client.attach_volume(server_id,
-                                                  volumeId=volume['id'],
-                                                  device='/dev/vdc')
-        self.manager.volumes_client.wait_for_volume_status(volume['id'],
-                                                           'in-use')
-        self.logger.info("volume (%s) attached to vm %s" %
-                         (volume['id'], server_id))
-
-        # Step 4: delete vm
-        self.logger.info("deleting vm: %s" % vm_name)
-        self.manager.servers_client.delete_server(server_id)
-        waiters.wait_for_server_termination(self.manager.servers_client,
-                                            server_id)
-        self.logger.info("deleted vm: %s" % server_id)
-
-        # Step 5: delete volume
-        self.logger.info("deleting volume: %s" % volume['id'])
-        self.manager.volumes_client.delete_volume(volume['id'])
-        self.manager.volumes_client.wait_for_resource_deletion(volume['id'])
-        self.logger.info("deleted volume: %s" % volume['id'])
diff --git a/tempest/stress/actions/volume_attach_verify.py b/tempest/stress/actions/volume_attach_verify.py
deleted file mode 100644
index 8bbbfc4..0000000
--- a/tempest/stress/actions/volume_attach_verify.py
+++ /dev/null
@@ -1,232 +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 re
-
-from tempest.common.utils import data_utils
-from tempest.common.utils.linux import remote_client
-from tempest.common import waiters
-from tempest import config
-import tempest.stress.stressaction as stressaction
-import tempest.test
-
-CONF = config.CONF
-
-
-class VolumeVerifyStress(stressaction.StressAction):
-
-    def _create_keypair(self):
-        keyname = data_utils.rand_name("key")
-        self.key = (self.manager.keypairs_client.create_keypair(name=keyname)
-                    ['keypair'])
-
-    def _delete_keypair(self):
-        self.manager.keypairs_client.delete_keypair(self.key['name'])
-
-    def _create_vm(self):
-        self.name = name = data_utils.rand_name("instance")
-        servers_client = self.manager.servers_client
-        self.logger.info("creating %s" % name)
-        vm_args = self.vm_extra_args.copy()
-        vm_args['security_groups'] = [self.sec_grp]
-        vm_args['key_name'] = self.key['name']
-        server = servers_client.create_server(name=name, imageRef=self.image,
-                                              flavorRef=self.flavor,
-                                              **vm_args)['server']
-        self.server_id = server['id']
-        waiters.wait_for_server_status(self.manager.servers_client,
-                                       self.server_id, 'ACTIVE')
-
-    def _destroy_vm(self):
-        self.logger.info("deleting server: %s" % self.server_id)
-        self.manager.servers_client.delete_server(self.server_id)
-        waiters.wait_for_server_termination(self.manager.servers_client,
-                                            self.server_id)
-        self.logger.info("deleted server: %s" % self.server_id)
-
-    def _create_sec_group(self):
-        sec_grp_cli = self.manager.compute_security_groups_client
-        s_name = data_utils.rand_name('sec_grp')
-        s_description = data_utils.rand_name('desc')
-        self.sec_grp = sec_grp_cli.create_security_group(
-            name=s_name, description=s_description)['security_group']
-        create_rule = sec_grp_cli.create_security_group_rule
-        create_rule(parent_group_id=self.sec_grp['id'], ip_protocol='tcp',
-                    from_port=22, to_port=22)
-        create_rule(parent_group_id=self.sec_grp['id'], ip_protocol='icmp',
-                    from_port=-1, to_port=-1)
-
-    def _destroy_sec_grp(self):
-        sec_grp_cli = self.manager.compute_security_groups_client
-        sec_grp_cli.delete_security_group(self.sec_grp['id'])
-
-    def _create_floating_ip(self):
-        floating_cli = self.manager.compute_floating_ips_client
-        self.floating = (floating_cli.create_floating_ip(self.floating_pool)
-                         ['floating_ip'])
-
-    def _destroy_floating_ip(self):
-        cli = self.manager.compute_floating_ips_client
-        cli.delete_floating_ip(self.floating['id'])
-        cli.wait_for_resource_deletion(self.floating['id'])
-        self.logger.info("Deleted Floating IP %s", str(self.floating['ip']))
-
-    def _create_volume(self):
-        name = data_utils.rand_name("volume")
-        self.logger.info("creating volume: %s" % name)
-        volumes_client = self.manager.volumes_client
-        self.volume = volumes_client.create_volume(
-            display_name=name)['volume']
-        volumes_client.wait_for_volume_status(self.volume['id'],
-                                              'available')
-        self.logger.info("created volume: %s" % self.volume['id'])
-
-    def _delete_volume(self):
-        self.logger.info("deleting volume: %s" % self.volume['id'])
-        volumes_client = self.manager.volumes_client
-        volumes_client.delete_volume(self.volume['id'])
-        volumes_client.wait_for_resource_deletion(self.volume['id'])
-        self.logger.info("deleted volume: %s" % self.volume['id'])
-
-    def _wait_disassociate(self):
-        cli = self.manager.compute_floating_ips_client
-
-        def func():
-            floating = (cli.show_floating_ip(self.floating['id'])
-                        ['floating_ip'])
-            return floating['instance_id'] is None
-
-        if not tempest.test.call_until_true(func, CONF.compute.build_timeout,
-                                            CONF.compute.build_interval):
-            raise RuntimeError("IP disassociate timeout!")
-
-    def new_server_ops(self):
-        self._create_vm()
-        cli = self.manager.compute_floating_ips_client
-        cli.associate_floating_ip_to_server(self.floating['ip'],
-                                            self.server_id)
-        if self.ssh_test_before_attach and self.enable_ssh_verify:
-            self.logger.info("Scanning for block devices via ssh on %s"
-                             % self.server_id)
-            self.part_wait(self.detach_match_count)
-
-    def setUp(self, **kwargs):
-        """Note able configuration combinations:
-
-            Closest options to the test_stamp_pattern:
-                new_server = True
-                new_volume = True
-                enable_ssh_verify = True
-                ssh_test_before_attach = False
-            Just attaching:
-                new_server = False
-                new_volume = False
-                enable_ssh_verify = True
-                ssh_test_before_attach = True
-            Mostly API load by repeated attachment:
-                new_server = False
-                new_volume = False
-                enable_ssh_verify = False
-                ssh_test_before_attach = False
-            Minimal Nova load, but cinder load not decreased:
-                new_server = False
-                new_volume = True
-                enable_ssh_verify = True
-                ssh_test_before_attach = True
-        """
-        self.image = CONF.compute.image_ref
-        self.flavor = CONF.compute.flavor_ref
-        self.vm_extra_args = kwargs.get('vm_extra_args', {})
-        self.floating_pool = kwargs.get('floating_pool', None)
-        self.new_volume = kwargs.get('new_volume', True)
-        self.new_server = kwargs.get('new_server', False)
-        self.enable_ssh_verify = kwargs.get('enable_ssh_verify', True)
-        self.ssh_test_before_attach = kwargs.get('ssh_test_before_attach',
-                                                 False)
-        self.part_line_re = re.compile(kwargs.get('part_line_re', '.*vd.*'))
-        self.detach_match_count = kwargs.get('detach_match_count', 1)
-        self.attach_match_count = kwargs.get('attach_match_count', 2)
-        self.part_name = kwargs.get('part_name', '/dev/vdc')
-
-        self._create_floating_ip()
-        self._create_sec_group()
-        self._create_keypair()
-        private_key = self.key['private_key']
-        username = CONF.validation.image_ssh_user
-        self.remote_client = remote_client.RemoteClient(self.floating['ip'],
-                                                        username,
-                                                        pkey=private_key)
-        if not self.new_volume:
-            self._create_volume()
-        if not self.new_server:
-            self.new_server_ops()
-
-    # now we just test that the number of partitions has increased or decreased
-    def part_wait(self, num_match):
-        def _part_state():
-            self.partitions = self.remote_client.get_partitions().split('\n')
-            matching = 0
-            for part_line in self.partitions[1:]:
-                if self.part_line_re.match(part_line):
-                    matching += 1
-            return matching == num_match
-        if tempest.test.call_until_true(_part_state,
-                                        CONF.compute.build_timeout,
-                                        CONF.compute.build_interval):
-            return
-        else:
-            raise RuntimeError("Unexpected partitions: %s",
-                               str(self.partitions))
-
-    def run(self):
-        if self.new_server:
-            self.new_server_ops()
-        if self.new_volume:
-            self._create_volume()
-        servers_client = self.manager.servers_client
-        self.logger.info("attach volume (%s) to vm %s" %
-                         (self.volume['id'], self.server_id))
-        servers_client.attach_volume(self.server_id,
-                                     volumeId=self.volume['id'],
-                                     device=self.part_name)
-        self.manager.volumes_client.wait_for_volume_status(self.volume['id'],
-                                                           'in-use')
-        if self.enable_ssh_verify:
-            self.logger.info("Scanning for new block device on %s"
-                             % self.server_id)
-            self.part_wait(self.attach_match_count)
-
-        servers_client.detach_volume(self.server_id,
-                                     self.volume['id'])
-        self.manager.volumes_client.wait_for_volume_status(self.volume['id'],
-                                                           'available')
-        if self.enable_ssh_verify:
-            self.logger.info("Scanning for block device disappearance on %s"
-                             % self.server_id)
-            self.part_wait(self.detach_match_count)
-        if self.new_volume:
-            self._delete_volume()
-        if self.new_server:
-            self._destroy_vm()
-
-    def tearDown(self):
-        cli = self.manager.compute_floating_ips_client
-        cli.disassociate_floating_ip_from_server(self.floating['ip'],
-                                                 self.server_id)
-        self._wait_disassociate()
-        if not self.new_server:
-            self._destroy_vm()
-        self._delete_keypair()
-        self._destroy_floating_ip()
-        self._destroy_sec_grp()
-        if not self.new_volume:
-            self._delete_volume()
diff --git a/tempest/stress/actions/volume_create_delete.py b/tempest/stress/actions/volume_create_delete.py
deleted file mode 100644
index 3986748..0000000
--- a/tempest/stress/actions/volume_create_delete.py
+++ /dev/null
@@ -1,30 +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.common.utils import data_utils
-import tempest.stress.stressaction as stressaction
-
-
-class VolumeCreateDeleteTest(stressaction.StressAction):
-
-    def run(self):
-        name = data_utils.rand_name("volume")
-        self.logger.info("creating %s" % name)
-        volumes_client = self.manager.volumes_client
-        volume = volumes_client.create_volume(display_name=name)['volume']
-        vol_id = volume['id']
-        volumes_client.wait_for_volume_status(vol_id, 'available')
-        self.logger.info("created %s" % volume['id'])
-        self.logger.info("deleting %s" % name)
-        volumes_client.delete_volume(vol_id)
-        volumes_client.wait_for_resource_deletion(vol_id)
-        self.logger.info("deleted %s" % vol_id)
diff --git a/tempest/stress/cleanup.py b/tempest/stress/cleanup.py
deleted file mode 100644
index 1c1fb46..0000000
--- a/tempest/stress/cleanup.py
+++ /dev/null
@@ -1,118 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright 2013 Quanta Research Cambridge, Inc.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-
-from oslo_log import log as logging
-
-from tempest.common import credentials_factory as credentials
-from tempest.common import waiters
-
-LOG = logging.getLogger(__name__)
-
-
-def cleanup():
-    admin_manager = credentials.AdminManager()
-
-    body = admin_manager.servers_client.list_servers(all_tenants=True)
-    LOG.info("Cleanup::remove %s servers" % len(body['servers']))
-    for s in body['servers']:
-        try:
-            admin_manager.servers_client.delete_server(s['id'])
-        except Exception:
-            pass
-
-    for s in body['servers']:
-        try:
-            waiters.wait_for_server_termination(admin_manager.servers_client,
-                                                s['id'])
-        except Exception:
-            pass
-
-    keypairs = admin_manager.keypairs_client.list_keypairs()['keypairs']
-    LOG.info("Cleanup::remove %s keypairs" % len(keypairs))
-    for k in keypairs:
-        try:
-            admin_manager.keypairs_client.delete_keypair(k['name'])
-        except Exception:
-            pass
-
-    secgrp_client = admin_manager.compute_security_groups_client
-    secgrp = (secgrp_client.list_security_groups(all_tenants=True)
-              ['security_groups'])
-    secgrp_del = [grp for grp in secgrp if grp['name'] != 'default']
-    LOG.info("Cleanup::remove %s Security Group" % len(secgrp_del))
-    for g in secgrp_del:
-        try:
-            secgrp_client.delete_security_group(g['id'])
-        except Exception:
-            pass
-
-    admin_floating_ips_client = admin_manager.compute_floating_ips_client
-    floating_ips = (admin_floating_ips_client.list_floating_ips()
-                    ['floating_ips'])
-    LOG.info("Cleanup::remove %s floating ips" % len(floating_ips))
-    for f in floating_ips:
-        try:
-            admin_floating_ips_client.delete_floating_ip(f['id'])
-        except Exception:
-            pass
-
-    users = admin_manager.users_client.list_users()['users']
-    LOG.info("Cleanup::remove %s users" % len(users))
-    for user in users:
-        if user['name'].startswith("stress_user"):
-            admin_manager.users_client.delete_user(user['id'])
-    tenants = admin_manager.tenants_client.list_tenants()['tenants']
-    LOG.info("Cleanup::remove %s tenants" % len(tenants))
-    for tenant in tenants:
-        if tenant['name'].startswith("stress_tenant"):
-            admin_manager.tenants_client.delete_tenant(tenant['id'])
-
-    # We have to delete snapshots first or
-    # volume deletion may block
-
-    _, snaps = admin_manager.snapshots_client.list_snapshots(
-        all_tenants=True)['snapshots']
-    LOG.info("Cleanup::remove %s snapshots" % len(snaps))
-    for v in snaps:
-        try:
-            admin_manager.snapshots_client.\
-                wait_for_snapshot_status(v['id'], 'available')
-            admin_manager.snapshots_client.delete_snapshot(v['id'])
-        except Exception:
-            pass
-
-    for v in snaps:
-        try:
-            admin_manager.snapshots_client.wait_for_resource_deletion(v['id'])
-        except Exception:
-            pass
-
-    vols = admin_manager.volumes_client.list_volumes(
-        params={"all_tenants": True})
-    LOG.info("Cleanup::remove %s volumes" % len(vols))
-    for v in vols:
-        try:
-            admin_manager.volumes_client.\
-                wait_for_volume_status(v['id'], 'available')
-            admin_manager.volumes_client.delete_volume(v['id'])
-        except Exception:
-            pass
-
-    for v in vols:
-        try:
-            admin_manager.volumes_client.wait_for_resource_deletion(v['id'])
-        except Exception:
-            pass
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
deleted file mode 100644
index 3c69a8b..0000000
--- a/tempest/stress/driver.py
+++ /dev/null
@@ -1,261 +0,0 @@
-# Copyright 2013 Quanta Research Cambridge, Inc.
-#
-#    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 multiprocessing
-import os
-import signal
-import time
-
-from oslo_log import log as logging
-from oslo_utils import importutils
-import six
-from six import moves
-from tempest_lib.common import ssh
-
-
-from tempest import clients
-from tempest.common import cred_client
-from tempest.common import credentials_factory as credentials
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest import exceptions
-from tempest.stress import cleanup
-
-CONF = config.CONF
-
-LOG = logging.getLogger(__name__)
-processes = []
-
-
-def do_ssh(command, host, ssh_user, ssh_key=None):
-    ssh_client = ssh.Client(host, ssh_user, key_filename=ssh_key)
-    try:
-        return ssh_client.exec_command(command)
-    except exceptions.SSHExecCommandFailed:
-        LOG.error('do_ssh raise exception. command:%s, host:%s.'
-                  % (command, host))
-        return None
-
-
-def _get_compute_nodes(controller, ssh_user, ssh_key=None):
-    """Returns a list of active compute nodes.
-
-    List is generated by running nova-manage on the controller.
-    """
-    nodes = []
-    cmd = "nova-manage service list | grep ^nova-compute"
-    output = do_ssh(cmd, controller, ssh_user, ssh_key)
-    if not output:
-        return nodes
-    # For example: nova-compute xg11eth0 nova enabled :-) 2011-10-31 18:57:46
-    # This is fragile but there is, at present, no other way to get this info.
-    for line in output.split('\n'):
-        words = line.split()
-        if len(words) > 0 and words[4] == ":-)":
-            nodes.append(words[1])
-    return nodes
-
-
-def _has_error_in_logs(logfiles, nodes, ssh_user, ssh_key=None,
-                       stop_on_error=False):
-    """Detect errors in nova log files on the controller and compute nodes."""
-    grep = 'egrep "ERROR|TRACE" %s' % logfiles
-    ret = False
-    for node in nodes:
-        errors = do_ssh(grep, node, ssh_user, ssh_key)
-        if len(errors) > 0:
-            LOG.error('%s: %s' % (node, errors))
-            ret = True
-            if stop_on_error:
-                break
-    return ret
-
-
-def sigchld_handler(signalnum, frame):
-    """Signal handler (only active if stop_on_error is True)."""
-    for process in processes:
-        if (not process['process'].is_alive() and
-                process['process'].exitcode != 0):
-            signal.signal(signalnum, signal.SIG_DFL)
-            terminate_all_processes()
-            break
-
-
-def terminate_all_processes(check_interval=20):
-    """Goes through the process list and terminates all child processes."""
-    LOG.info("Stopping all processes.")
-    for process in processes:
-        if process['process'].is_alive():
-            try:
-                process['process'].terminate()
-            except Exception:
-                pass
-    time.sleep(check_interval)
-    for process in processes:
-        if process['process'].is_alive():
-            try:
-                pid = process['process'].pid
-                LOG.warning("Process %d hangs. Send SIGKILL." % pid)
-                os.kill(pid, signal.SIGKILL)
-            except Exception:
-                pass
-        process['process'].join()
-
-
-def stress_openstack(tests, duration, max_runs=None, stop_on_error=False):
-    """Workload driver. Executes an action function against a nova-cluster."""
-    admin_manager = credentials.AdminManager()
-
-    ssh_user = CONF.stress.target_ssh_user
-    ssh_key = CONF.stress.target_private_key_path
-    logfiles = CONF.stress.target_logfiles
-    log_check_interval = int(CONF.stress.log_check_interval)
-    default_thread_num = int(CONF.stress.default_thread_number_per_action)
-    if logfiles:
-        controller = CONF.stress.target_controller
-        computes = _get_compute_nodes(controller, ssh_user, ssh_key)
-        for node in computes:
-            do_ssh("rm -f %s" % logfiles, node, ssh_user, ssh_key)
-    skip = False
-    for test in tests:
-        for service in test.get('required_services', []):
-            if not CONF.service_available.get(service):
-                skip = True
-                break
-        if skip:
-            break
-        if test.get('use_admin', False):
-            manager = admin_manager
-        else:
-            manager = credentials.ConfiguredUserManager()
-        for p_number in moves.xrange(test.get('threads', default_thread_num)):
-            if test.get('use_isolated_tenants', False):
-                username = data_utils.rand_name("stress_user")
-                tenant_name = data_utils.rand_name("stress_tenant")
-                password = "pass"
-                if CONF.identity.auth_version == 'v2':
-                    identity_client = admin_manager.identity_client
-                    projects_client = admin_manager.tenants_client
-                    roles_client = admin_manager.roles_client
-                    users_client = admin_manager.users_client
-                else:
-                    identity_client = admin_manager.identity_v3_client
-                    projects_client = admin_manager.projects_client
-                    roles_client = None
-                    users_client = admin_manager.users_v3_client
-                domain = (identity_client.auth_provider.credentials.
-                          get('project_domain_name', 'Default'))
-                credentials_client = cred_client.get_creds_client(
-                    identity_client, projects_client, users_client,
-                    roles_client, project_domain_name=domain)
-                project = credentials_client.create_project(
-                    name=tenant_name, description=tenant_name)
-                user = credentials_client.create_user(username, password,
-                                                      project, "email")
-                # Add roles specified in config file
-                for conf_role in CONF.auth.tempest_roles:
-                    credentials_client.assign_user_role(user, project,
-                                                        conf_role)
-                creds = credentials_client.get_credentials(user, project,
-                                                           password)
-                manager = clients.Manager(credentials=creds)
-
-            test_obj = importutils.import_class(test['action'])
-            test_run = test_obj(manager, max_runs, stop_on_error)
-
-            kwargs = test.get('kwargs', {})
-            test_run.setUp(**dict(six.iteritems(kwargs)))
-
-            LOG.debug("calling Target Object %s" %
-                      test_run.__class__.__name__)
-
-            mp_manager = multiprocessing.Manager()
-            shared_statistic = mp_manager.dict()
-            shared_statistic['runs'] = 0
-            shared_statistic['fails'] = 0
-
-            p = multiprocessing.Process(target=test_run.execute,
-                                        args=(shared_statistic,))
-
-            process = {'process': p,
-                       'p_number': p_number,
-                       'action': test_run.action,
-                       'statistic': shared_statistic}
-
-            processes.append(process)
-            p.start()
-    if stop_on_error:
-        # NOTE(mkoderer): only the parent should register the handler
-        signal.signal(signal.SIGCHLD, sigchld_handler)
-    end_time = time.time() + duration
-    had_errors = False
-    try:
-        while True:
-            if max_runs is None:
-                remaining = end_time - time.time()
-                if remaining <= 0:
-                    break
-            else:
-                remaining = log_check_interval
-                all_proc_term = True
-                for process in processes:
-                    if process['process'].is_alive():
-                        all_proc_term = False
-                        break
-                if all_proc_term:
-                    break
-
-            time.sleep(min(remaining, log_check_interval))
-            if stop_on_error:
-                if any([True for proc in processes
-                        if proc['statistic']['fails'] > 0]):
-                    break
-
-            if not logfiles:
-                continue
-            if _has_error_in_logs(logfiles, computes, ssh_user, ssh_key,
-                                  stop_on_error):
-                had_errors = True
-                break
-    except KeyboardInterrupt:
-        LOG.warning("Interrupted, going to print statistics and exit ...")
-
-    if stop_on_error:
-        signal.signal(signal.SIGCHLD, signal.SIG_DFL)
-    terminate_all_processes()
-
-    sum_fails = 0
-    sum_runs = 0
-
-    LOG.info("Statistics (per process):")
-    for process in processes:
-        if process['statistic']['fails'] > 0:
-            had_errors = True
-        sum_runs += process['statistic']['runs']
-        sum_fails += process['statistic']['fails']
-        print ("Process %d (%s): Run %d actions (%d failed)" % (
-               process['p_number'],
-               process['action'],
-               process['statistic']['runs'],
-               process['statistic']['fails']))
-    print ("Summary:")
-    print ("Run %d actions (%d failed)" % (sum_runs, sum_fails))
-
-    if not had_errors and CONF.stress.full_clean_stack:
-        LOG.info("cleaning up")
-        cleanup.cleanup()
-    if had_errors:
-        return 1
-    else:
-        return 0
diff --git a/tempest/stress/etc/sample-unit-test.json b/tempest/stress/etc/sample-unit-test.json
deleted file mode 100644
index 54433d5..0000000
--- a/tempest/stress/etc/sample-unit-test.json
+++ /dev/null
@@ -1,8 +0,0 @@
-[{"action": "tempest.stress.actions.unit_test.UnitTest",
-  "threads": 8,
-  "use_admin": true,
-  "use_isolated_tenants": true,
-  "kwargs": {"test_method": "tempest.cli.simple_read_only.test_glance.SimpleReadOnlyGlanceClientTest.test_glance_fake_action",
-             "class_setup_per": "process"}
-  }
-]
diff --git a/tempest/stress/etc/server-create-destroy-test.json b/tempest/stress/etc/server-create-destroy-test.json
deleted file mode 100644
index bbb5352..0000000
--- a/tempest/stress/etc/server-create-destroy-test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-[{"action": "tempest.stress.actions.server_create_destroy.ServerCreateDestroyTest",
-  "threads": 8,
-  "use_admin": true,
-  "use_isolated_tenants": true,
-  "kwargs": {}
-  }
-]
diff --git a/tempest/stress/etc/ssh_floating.json b/tempest/stress/etc/ssh_floating.json
deleted file mode 100644
index c502e96..0000000
--- a/tempest/stress/etc/ssh_floating.json
+++ /dev/null
@@ -1,16 +0,0 @@
-[{"action": "tempest.stress.actions.ssh_floating.FloatingStress",
-  "threads": 8,
-  "use_admin": true,
-  "use_isolated_tenants": true,
-  "kwargs": {"vm_extra_args": {},
-             "new_vm": true,
-             "new_sec_group": true,
-             "new_floating": true,
-             "verify": ["check_icmp_echo", "check_port_ssh"],
-             "check_timeout": 120,
-             "check_interval": 1,
-             "wait_after_vm_create": true,
-             "wait_for_disassociate": true,
-             "reboot": false}
-}
-]
diff --git a/tempest/stress/etc/stress-tox-job.json b/tempest/stress/etc/stress-tox-job.json
deleted file mode 100644
index bfa448d..0000000
--- a/tempest/stress/etc/stress-tox-job.json
+++ /dev/null
@@ -1,28 +0,0 @@
-[{"action": "tempest.stress.actions.server_create_destroy.ServerCreateDestroyTest",
-  "threads": 8,
-  "use_admin": true,
-  "use_isolated_tenants": true,
-  "kwargs": {}
-  },
-  {"action": "tempest.stress.actions.volume_create_delete.VolumeCreateDeleteTest",
-  "threads": 4,
-  "use_admin": true,
-  "use_isolated_tenants": true,
-  "kwargs": {}
-  },
-  {"action": "tempest.stress.actions.volume_attach_delete.VolumeAttachDeleteTest",
-  "threads": 2,
-  "use_admin": true,
-  "use_isolated_tenants": true,
-  "kwargs": {}
-  },
-  {"action": "tempest.stress.actions.unit_test.UnitTest",
-  "threads": 4,
-  "use_admin": true,
-  "use_isolated_tenants": true,
-  "required_services": ["neutron"],
-  "kwargs": {"test_method": "tempest.scenario.test_network_advanced_server_ops.TestNetworkAdvancedServerOps.test_server_connectivity_stop_start",
-             "class_setup_per": "process"}
-  }
-]
-
diff --git a/tempest/stress/etc/volume-attach-delete-test.json b/tempest/stress/etc/volume-attach-delete-test.json
deleted file mode 100644
index d468967..0000000
--- a/tempest/stress/etc/volume-attach-delete-test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-[{"action": "tempest.stress.actions.volume_attach_delete.VolumeAttachDeleteTest",
-  "threads": 4,
-  "use_admin": true,
-  "use_isolated_tenants": true,
-  "kwargs": {}
-  }
-]
diff --git a/tempest/stress/etc/volume-attach-verify.json b/tempest/stress/etc/volume-attach-verify.json
deleted file mode 100644
index d8c96fd..0000000
--- a/tempest/stress/etc/volume-attach-verify.json
+++ /dev/null
@@ -1,11 +0,0 @@
-[{"action": "tempest.stress.actions.volume_attach_verify.VolumeVerifyStress",
-  "threads": 1,
-  "use_admin": true,
-  "use_isolated_tenants": true,
-  "kwargs": {"vm_extra_args": {},
-             "new_volume": true,
-             "new_server": false,
-             "ssh_test_before_attach": false,
-             "enable_ssh_verify": true}
-}
-]
diff --git a/tempest/stress/etc/volume-create-delete-test.json b/tempest/stress/etc/volume-create-delete-test.json
deleted file mode 100644
index a60cde6..0000000
--- a/tempest/stress/etc/volume-create-delete-test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-[{"action": "tempest.stress.actions.volume_create_delete.VolumeCreateDeleteTest",
-  "threads": 4,
-  "use_admin": true,
-  "use_isolated_tenants": true,
-  "kwargs": {}
-  }
-]
diff --git a/tempest/stress/stressaction.py b/tempest/stress/stressaction.py
deleted file mode 100644
index c8bd652..0000000
--- a/tempest/stress/stressaction.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# (c) Copyright 2013 Hewlett-Packard 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.
-
-import abc
-import signal
-import sys
-
-import six
-
-from oslo_log import log as logging
-
-
-@six.add_metaclass(abc.ABCMeta)
-class StressAction(object):
-
-    def __init__(self, manager, max_runs=None, stop_on_error=False):
-        full_cname = self.__module__ + "." + self.__class__.__name__
-        self.logger = logging.getLogger(full_cname)
-        self.manager = manager
-        self.max_runs = max_runs
-        self.stop_on_error = stop_on_error
-
-    def _shutdown_handler(self, signal, frame):
-        try:
-            self.tearDown()
-        except Exception:
-            self.logger.exception("Error while tearDown")
-        sys.exit(0)
-
-    @property
-    def action(self):
-        """This methods returns the action.
-
-        Overload this if you create a stress test wrapper.
-        """
-        return self.__class__.__name__
-
-    def setUp(self, **kwargs):
-        """Initialize test structures/resources
-
-        This method is called before "run" method to help the test
-        initialize any structures. kwargs contains arguments passed
-        in from the configuration json file.
-
-        setUp doesn't count against the time duration.
-        """
-        self.logger.debug("setUp")
-
-    def tearDown(self):
-        """Cleanup test structures/resources
-
-        This method is called to do any cleanup after the test is complete.
-        """
-        self.logger.debug("tearDown")
-
-    def execute(self, shared_statistic):
-        """This is the main execution entry point called by the driver.
-
-        We register a signal handler to allow us to tearDown gracefully,
-        and then exit. We also keep track of how many runs we do.
-        """
-        signal.signal(signal.SIGHUP, self._shutdown_handler)
-        signal.signal(signal.SIGTERM, self._shutdown_handler)
-
-        while self.max_runs is None or (shared_statistic['runs'] <
-                                        self.max_runs):
-            self.logger.debug("Trigger new run (run %d)" %
-                              shared_statistic['runs'])
-            try:
-                self.run()
-            except Exception:
-                shared_statistic['fails'] += 1
-                self.logger.exception("Failure in run")
-            finally:
-                shared_statistic['runs'] += 1
-                if self.stop_on_error and (shared_statistic['fails'] > 1):
-                    self.logger.warn("Stop process due to"
-                                     "\"stop-on-error\" argument")
-                    self.tearDown()
-                    sys.exit(1)
-
-    @abc.abstractmethod
-    def run(self):
-        """This method is where the stress test code runs."""
-        return
diff --git a/tempest/stress/tools/cleanup.py b/tempest/stress/tools/cleanup.py
deleted file mode 100755
index 3885ba0..0000000
--- a/tempest/stress/tools/cleanup.py
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright 2013 Quanta Research Cambridge, Inc.
-#
-#    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.stress import cleanup
-
-cleanup.cleanup()
diff --git a/tempest/test.py b/tempest/test.py
index ee3288c..039afa1 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -16,29 +16,24 @@
 import atexit
 import functools
 import os
-import re
 import sys
-import time
-import uuid
 
+import debtcollector.moves
 import fixtures
 from oslo_log import log as logging
-from oslo_serialization import jsonutils as json
-from oslo_utils import importutils
 import six
-from six.moves import urllib
-from tempest_lib import decorators
-import testscenarios
 import testtools
 
 from tempest import clients
-from tempest.common import cred_client
 from tempest.common import credentials_factory as credentials
 from tempest.common import fixed_network
-import tempest.common.generator.valid_generator as valid
 import tempest.common.validation_resources as vresources
 from tempest import config
 from tempest import exceptions
+from tempest.lib.common import cred_client
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 LOG = logging.getLogger(__name__)
 
@@ -69,18 +64,10 @@
     service_list = {
         'compute': CONF.service_available.nova,
         'image': CONF.service_available.glance,
-        'baremetal': CONF.service_available.ironic,
         'volume': CONF.service_available.cinder,
-        'orchestration': CONF.service_available.heat,
-        # NOTE(mtreinish) nova-network will provide networking functionality
-        # if neutron isn't available, so always set to True.
         'network': True,
         'identity': True,
         'object_storage': CONF.service_available.swift,
-        'dashboard': CONF.service_available.horizon,
-        'telemetry': CONF.service_available.ceilometer,
-        'data_processing': CONF.service_available.sahara,
-        'database': CONF.service_available.trove
     }
     return service_list
 
@@ -92,9 +79,8 @@
     exercised by a test case.
     """
     def decorator(f):
-        services = ['compute', 'image', 'baremetal', 'volume', 'orchestration',
-                    'network', 'identity', 'object_storage', 'dashboard',
-                    'telemetry', 'data_processing', 'database']
+        services = ['compute', 'image', 'baremetal', 'volume',
+                    'network', 'identity', 'object_storage']
         for service in args:
             if service not in services:
                 raise exceptions.InvalidServiceTag('%s is not a valid '
@@ -115,32 +101,6 @@
     return decorator
 
 
-def stresstest(**kwargs):
-    """Add stress test decorator
-
-    For all functions with this decorator a attr stress will be
-    set automatically.
-
-    @param class_setup_per: allowed values are application, process, action
-           ``application``: once in the stress job lifetime
-           ``process``: once in the worker process lifetime
-           ``action``: on each action
-    @param allow_inheritance: allows inheritance of this attribute
-    """
-    def decorator(f):
-        if 'class_setup_per' in kwargs:
-            setattr(f, "st_class_setup_per", kwargs['class_setup_per'])
-        else:
-            setattr(f, "st_class_setup_per", 'process')
-        if 'allow_inheritance' in kwargs:
-            setattr(f, "st_allow_inheritance", kwargs['allow_inheritance'])
-        else:
-            setattr(f, "st_allow_inheritance", False)
-        attr(type='stress')(f)
-        return f
-    return decorator
-
-
 def requires_ext(**kwargs):
     """A decorator to skip tests if an extension is not enabled
 
@@ -180,6 +140,49 @@
     return False
 
 
+def related_bug(bug, status_code=None):
+    """A decorator useful to know solutions from launchpad bug reports
+
+    @param bug: The launchpad bug number causing the test
+    @param status_code: The status code related to the bug report
+    """
+    def decorator(f):
+        @functools.wraps(f)
+        def wrapper(self, *func_args, **func_kwargs):
+            try:
+                return f(self, *func_args, **func_kwargs)
+            except Exception as exc:
+                exc_status_code = getattr(exc, 'status_code', None)
+                if status_code is None or status_code == exc_status_code:
+                    LOG.error('Hints: This test was made for the bug %s. '
+                              'The failure could be related to '
+                              'https://launchpad.net/bugs/%s', bug, bug)
+                raise exc
+        return wrapper
+    return decorator
+
+
+def is_scheduler_filter_enabled(filter_name):
+    """Check the list of enabled compute scheduler filters from config.
+
+    This function checks whether the given compute scheduler filter is
+    available and configured in the config file. If the
+    scheduler_available_filters option is set to 'all' (Default value. which
+    means default filters are configured in nova) in tempest.conf then, this
+    function returns True with assumption that requested filter 'filter_name'
+    is one of available filter in nova ("nova.scheduler.filters.all_filters").
+    """
+
+    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()
 
 
@@ -216,7 +219,6 @@
     """
 
     setUpClassCalled = False
-    _service = None
 
     # NOTE(andreaf) credentials holds a list of the credentials to be allocated
     # at class setup time. Credential types can be 'primary', 'alt', 'admin' or
@@ -226,7 +228,6 @@
     # Resources required to validate a server using ssh
     validation_resources = {}
     network_resources = {}
-    services_microversion = {}
 
     # NOTE(sdague): log_format is defined inline here instead of using the oslo
     # default because going through the config path recouples config to the
@@ -237,6 +238,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
@@ -258,8 +262,8 @@
             cls.resource_setup()
         except Exception:
             etype, value, trace = sys.exc_info()
-            LOG.info("%s raised in %s.setUpClass. Invoking tearDownClass." % (
-                     etype, cls.__name__))
+            LOG.info("%s raised in %s.setUpClass. Invoking tearDownClass.",
+                     etype, cls.__name__)
             cls.tearDownClass()
             try:
                 six.reraise(etype, value, trace)
@@ -291,9 +295,9 @@
                 # resources that were successfully setup in resource_cleanup,
                 # log AttributeError as info instead of exception.
                 if tetype is AttributeError and name == 'resources':
-                    LOG.info("tearDownClass of %s failed: %s" % (name, te))
+                    LOG.info("tearDownClass of %s failed: %s", name, te)
                 else:
-                    LOG.exception("teardown of %s failed: %s" % (name, te))
+                    LOG.exception("teardown of %s failed: %s", name, te)
                 if not etype:
                     etype, value, trace = sys_exec_info
         # If exceptions were raised during teardown, and not before, re-raise
@@ -335,11 +339,14 @@
 
     @classmethod
     def setup_credentials(cls):
-        """Allocate credentials and the client managers from them.
+        """Allocate credentials and create the client managers from them.
 
-        A test class that requires network resources must override
-        setup_credentials and defined the required resources before super
-        is invoked.
+        For every element of credentials param function creates tenant/user,
+        Then it creates client manager for that credential.
+
+        Network related tests must override this function with
+        set_network_resources() method, otherwise it will create
+        network resources(network resources are created in a later step).
         """
         for credentials_type in cls.credentials:
             # This may raise an exception in case credentials are not available
@@ -407,7 +414,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:
@@ -443,11 +450,13 @@
             users_client = self.os_admin.users_client
             project_client = self.os_admin.tenants_client
             roles_client = self.os_admin.roles_client
+            domains_client = None
         else:
             client = self.os_admin.identity_v3_client
-            project_client = self.os_adm.projects_client
             users_client = self.os_admin.users_v3_client
-            roles_client = None
+            project_client = self.os_admin.projects_client
+            roles_client = self.os_admin.roles_v3_client
+            domains_client = self.os_admin.domains_client
 
         try:
             domain = client.auth_provider.credentials.project_domain_name
@@ -457,6 +466,7 @@
         return cred_client.get_creds_client(client, project_client,
                                             users_client,
                                             roles_client,
+                                            domains_client,
                                             project_domain_name=domain)
 
     @classmethod
@@ -520,10 +530,13 @@
             if hasattr(cred_provider, credentials_method):
                 creds = getattr(cred_provider, credentials_method)()
             else:
-                raise exceptions.InvalidCredentials(
+                raise lib_exc.InvalidCredentials(
                     "Invalid credentials type %s" % credential_type)
-        return cls.client_manager(credentials=creds, service=cls._service,
-                                  api_microversions=cls.services_microversion)
+        manager = cls.client_manager(credentials=creds.credentials)
+        # NOTE(andreaf) Ensure credentials have user and project id fields.
+        # It may not be the case when using pre-provisioned credentials.
+        manager.auth_provider.set_auth()
+        return manager
 
     @classmethod
     def clear_credentials(cls):
@@ -550,18 +563,17 @@
         """
         if not CONF.validation.run_validation:
             return
+
         if keypair is None:
-            if CONF.validation.auth_method.lower() == "keypair":
-                keypair = True
-            else:
-                keypair = False
+            keypair = (CONF.validation.auth_method.lower() == "keypair")
+
         if floating_ip is None:
-            if CONF.validation.connect_method.lower() == "floating":
-                floating_ip = True
-            else:
-                floating_ip = False
+            floating_ip = (CONF.validation.connect_method.lower() ==
+                           "floating")
+
         if security_group is None:
             security_group = CONF.validation.security_group
+
         if security_group_rules is None:
             security_group_rules = CONF.validation.security_group_rules
 
@@ -594,13 +606,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
@@ -610,253 +634,18 @@
                 credentials.is_admin_available(
                     identity_version=cls.get_identity_version())):
             admin_creds = cred_provider.get_admin_creds()
-            admin_manager = clients.Manager(
-                admin_creds, api_microversions=cls.services_microversion)
+            admin_manager = clients.Manager(admin_creds.credentials)
             networks_client = admin_manager.compute_networks_client
         return fixed_network.get_tenant_network(
             cred_provider, networks_client, CONF.compute.fixed_network_name)
 
     def assertEmpty(self, list, msg=None):
-        self.assertTrue(len(list) == 0, msg)
+        self.assertEqual(0, len(list), msg)
 
     def assertNotEmpty(self, list, msg=None):
-        self.assertTrue(len(list) > 0, msg)
+        self.assertGreater(len(list), 0, msg)
 
 
-class NegativeAutoTest(BaseTestCase):
-
-    _resources = {}
-
-    @classmethod
-    def setUpClass(cls):
-        super(NegativeAutoTest, cls).setUpClass()
-        os = cls.get_client_manager(credential_type='primary')
-        cls.client = os.negative_client
-
-    @staticmethod
-    def load_tests(*args):
-        """Wrapper for testscenarios
-
-        To set the mandatory scenarios variable only in case a real test
-        loader is in place. Will be automatically called in case the variable
-        "load_tests" is set.
-        """
-        if getattr(args[0], 'suiteClass', None) is not None:
-            loader, standard_tests, pattern = args
-        else:
-            standard_tests, module, loader = args
-        for test in testtools.iterate_tests(standard_tests):
-            schema = getattr(test, '_schema', None)
-            if schema is not None:
-                setattr(test, 'scenarios',
-                        NegativeAutoTest.generate_scenario(schema))
-        return testscenarios.load_tests_apply_scenarios(*args)
-
-    @staticmethod
-    def generate_scenario(description):
-        """Generates the test scenario list for a given description.
-
-        :param description: A file or dictionary with the following entries:
-            name (required) name for the api
-            http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
-            url (required) the url to be appended to the catalog url with '%s'
-                for each resource mentioned
-            resources: (optional) A list of resource names such as "server",
-                "flavor", etc. with an element for each '%s' in the url. This
-                method will call self.get_resource for each element when
-                constructing the positive test case template so negative
-                subclasses are expected to return valid resource ids when
-                appropriate.
-            json-schema (optional) A valid json schema that will be used to
-                create invalid data for the api calls. For "GET" and "HEAD",
-                the data is used to generate query strings appended to the url,
-                otherwise for the body of the http call.
-        """
-        LOG.debug(description)
-        generator = importutils.import_class(
-            CONF.negative.test_generator)()
-        generator.validate_schema(description)
-        schema = description.get("json-schema", None)
-        resources = description.get("resources", [])
-        scenario_list = []
-        expected_result = None
-        for resource in resources:
-            if isinstance(resource, dict):
-                expected_result = resource['expected_result']
-                resource = resource['name']
-            LOG.debug("Add resource to test %s" % resource)
-            scn_name = "inv_res_%s" % (resource)
-            scenario_list.append((scn_name, {"resource": (resource,
-                                                          str(uuid.uuid4())),
-                                             "expected_result": expected_result
-                                             }))
-        if schema is not None:
-            for scenario in generator.generate_scenarios(schema):
-                scenario_list.append((scenario['_negtest_name'],
-                                      scenario))
-        LOG.debug(scenario_list)
-        return scenario_list
-
-    def execute(self, description):
-        """Execute a http call
-
-        Execute a http call on an api that are expected to
-        result in client errors. First it uses invalid resources that are part
-        of the url, and then invalid data for queries and http request bodies.
-
-        :param description: A json file or dictionary with the following
-        entries:
-            name (required) name for the api
-            http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
-            url (required) the url to be appended to the catalog url with '%s'
-                for each resource mentioned
-            resources: (optional) A list of resource names such as "server",
-                "flavor", etc. with an element for each '%s' in the url. This
-                method will call self.get_resource for each element when
-                constructing the positive test case template so negative
-                subclasses are expected to return valid resource ids when
-                appropriate.
-            json-schema (optional) A valid json schema that will be used to
-                create invalid data for the api calls. For "GET" and "HEAD",
-                the data is used to generate query strings appended to the url,
-                otherwise for the body of the http call.
-
-        """
-        LOG.info("Executing %s" % description["name"])
-        LOG.debug(description)
-        generator = importutils.import_class(
-            CONF.negative.test_generator)()
-        schema = description.get("json-schema", None)
-        method = description["http-method"]
-        url = description["url"]
-        expected_result = None
-        if "default_result_code" in description:
-            expected_result = description["default_result_code"]
-
-        resources = [self.get_resource(r) for
-                     r in description.get("resources", [])]
-
-        if hasattr(self, "resource"):
-            # Note(mkoderer): The resources list already contains an invalid
-            # entry (see get_resource).
-            # We just send a valid json-schema with it
-            valid_schema = None
-            if schema:
-                valid_schema = \
-                    valid.ValidTestGenerator().generate_valid(schema)
-            new_url, body = self._http_arguments(valid_schema, url, method)
-        elif hasattr(self, "_negtest_name"):
-            schema_under_test = \
-                valid.ValidTestGenerator().generate_valid(schema)
-            local_expected_result = \
-                generator.generate_payload(self, schema_under_test)
-            if local_expected_result is not None:
-                expected_result = local_expected_result
-            new_url, body = \
-                self._http_arguments(schema_under_test, url, method)
-        else:
-            raise Exception("testscenarios are not active. Please make sure "
-                            "that your test runner supports the load_tests "
-                            "mechanism")
-
-        if "admin_client" in description and description["admin_client"]:
-            if not credentials.is_admin_available(
-                    identity_version=self.get_identity_version()):
-                msg = ("Missing Identity Admin API credentials in"
-                       "configuration.")
-                raise self.skipException(msg)
-            creds = self.credentials_provider.get_admin_creds()
-            os_adm = clients.Manager(credentials=creds)
-            client = os_adm.negative_client
-        else:
-            client = self.client
-        resp, resp_body = client.send_request(method, new_url,
-                                              resources, body=body)
-        self._check_negative_response(expected_result, resp.status, resp_body)
-
-    def _http_arguments(self, json_dict, url, method):
-        LOG.debug("dict: %s url: %s method: %s" % (json_dict, url, method))
-        if not json_dict:
-            return url, None
-        elif method in ["GET", "HEAD", "PUT", "DELETE"]:
-            return "%s?%s" % (url, urllib.parse.urlencode(json_dict)), None
-        else:
-            return url, json.dumps(json_dict)
-
-    def _check_negative_response(self, expected_result, result, body):
-        self.assertTrue(result >= 400 and result < 500 and result != 413,
-                        "Expected client error, got %s:%s" %
-                        (result, body))
-        self.assertTrue(expected_result is None or expected_result == result,
-                        "Expected %s, got %s:%s" %
-                        (expected_result, result, body))
-
-    @classmethod
-    def set_resource(cls, name, resource):
-        """Register a resource for a test
-
-        This function can be used in setUpClass context to register a resource
-        for a test.
-
-        :param name: The name of the kind of resource such as "flavor", "role",
-            etc.
-        :resource: The id of the resource
-        """
-        cls._resources[name] = resource
-
-    def get_resource(self, name):
-        """Return a valid uuid for a type of resource.
-
-        If a real resource is needed as part of a url then this method should
-        return one. Otherwise it can return None.
-
-        :param name: The name of the kind of resource such as "flavor", "role",
-            etc.
-        """
-        if isinstance(name, dict):
-            name = name['name']
-        if hasattr(self, "resource") and self.resource[0] == name:
-            LOG.debug("Return invalid resource (%s) value: %s" %
-                      (self.resource[0], self.resource[1]))
-            return self.resource[1]
-        if name in self._resources:
-            return self._resources[name]
-        return None
-
-
-def SimpleNegativeAutoTest(klass):
-    """This decorator registers a test function on basis of the class name."""
-    @attr(type=['negative'])
-    def generic_test(self):
-        if hasattr(self, '_schema'):
-            self.execute(self._schema)
-
-    cn = klass.__name__
-    cn = cn.replace('JSON', '')
-    cn = cn.replace('Test', '')
-    # NOTE(mkoderer): replaces uppercase chars inside the class name with '_'
-    lower_cn = re.sub('(?<!^)(?=[A-Z])', '_', cn).lower()
-    func_name = 'test_%s' % lower_cn
-    setattr(klass, func_name, generic_test)
-    return klass
-
-
-def call_until_true(func, duration, sleep_for):
-    """Call the given function until it returns True (and return True)
-
-    or until the specified duration (in seconds) elapses (and return False).
-
-    :param func: A zero argument callable that returns True on success.
-    :param duration: The number of seconds for which to attempt a
-        successful call of the function.
-    :param sleep_for: The number of seconds to sleep after an unsuccessful
-                      invocation of the function.
-    """
-    now = time.time()
-    timeout = now + duration
-    while now < timeout:
-        if func():
-            return True
-        time.sleep(sleep_for)
-        now = time.time()
-    return False
+call_until_true = debtcollector.moves.moved_function(
+    test_utils.call_until_true, 'call_until_true', __name__,
+    version='Newton', removal_version='Ocata')
diff --git a/tempest/test_discover/plugins.py b/tempest/test_discover/plugins.py
index 108b50d..abe2b73 100644
--- a/tempest/test_discover/plugins.py
+++ b/tempest/test_discover/plugins.py
@@ -13,12 +13,13 @@
 # under the License.
 
 import abc
-import logging
 
+from oslo_log import log as logging
 import six
 import stevedore
-from tempest_lib.common.utils import misc
 
+from tempest.lib.common.utils import misc
+from tempest.lib.services import clients
 
 LOG = logging.getLogger(__name__)
 
@@ -62,6 +63,54 @@
         """
         return []
 
+    def get_service_clients(self):
+        """Get a list of the service clients for registration
+
+        If the plugin implements service clients for one or more APIs, it
+        may return their details by this method for automatic registration
+        in any ServiceClients object instantiated by tests.
+        The default implementation returns an empty list.
+
+        :return list of dictionaries. Each element of the list represents
+            the service client for an API. Each dict must define all
+            parameters required for the invocation of
+            `service_clients.ServiceClients.register_service_client_module`.
+        :rtype: list
+
+        Example:
+
+            >>>  # Example implementation with one service client
+            >>>  myservice_config = config.service_client_config('myservice')
+            >>>  params = {
+            >>>     'name': 'myservice',
+            >>>     'service_version': 'myservice',
+            >>>     'module_path': 'myservice_tempest_tests.services',
+            >>>     'client_names': ['API1Client', 'API2Client'],
+            >>>  }
+            >>>  params.update(myservice_config)
+            >>>  return [params]
+
+            >>>  # Example implementation with two service clients
+            >>>  foo1_config = config.service_client_config('foo')
+            >>>  params_foo1 = {
+            >>>     'name': 'foo_v1',
+            >>>     'service_version': 'foo.v1',
+            >>>     'module_path': 'bar_tempest_tests.services.foo.v1',
+            >>>     'client_names': ['API1Client', 'API2Client'],
+            >>>  }
+            >>>  params_foo1.update(foo_config)
+            >>>  foo2_config = config.service_client_config('foo')
+            >>>  params_foo2 = {
+            >>>     'name': 'foo_v2',
+            >>>     'service_version': 'foo.v2',
+            >>>     'module_path': 'bar_tempest_tests.services.foo.v2',
+            >>>     'client_names': ['API1Client', 'API2Client'],
+            >>>  }
+            >>>  params_foo2.update(foo2_config)
+            >>>  return [params_foo1, params_foo2]
+        """
+        return []
+
 
 @misc.singleton
 class TempestTestPluginManager(object):
@@ -75,6 +124,7 @@
             'tempest.test_plugins', invoke_on_load=True,
             propagate_map_exceptions=True,
             on_load_failure_callback=self.failure_hook)
+        self._register_service_clients()
 
     @staticmethod
     def failure_hook(_, ep, err):
@@ -93,7 +143,7 @@
                 plug.obj.register_opts(conf)
             except Exception:
                 LOG.exception('Plugin %s raised an exception trying to run '
-                              'register_opts' % plug.name)
+                              'register_opts', plug.name)
 
     def get_plugin_options_list(self):
         plugin_options = []
@@ -102,3 +152,15 @@
             if opt_list:
                 plugin_options.extend(opt_list)
         return plugin_options
+
+    def _register_service_clients(self):
+        registry = clients.ClientsRegistry()
+        for plug in self.ext_plugins:
+            try:
+                service_clients = plug.obj.get_service_clients()
+                if service_clients:
+                    registry.register_service_client(
+                        plug.name, service_clients)
+            except Exception:
+                LOG.exception('Plugin %s raised an exception trying to run '
+                              'get_service_clients', plug.name)
diff --git a/tempest/tests/base.py b/tempest/tests/base.py
index fe9268e..ca81d4d 100644
--- a/tempest/tests/base.py
+++ b/tempest/tests/base.py
@@ -14,17 +14,10 @@
 
 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.
 
@@ -42,3 +35,15 @@
         m = p.start()
         self.addCleanup(p.stop)
         return m
+
+    def patchobject(self, target, attribute, new=mock.DEFAULT):
+        """Convenient wrapper around `mock.patch.object`
+
+        Returns a started mock that will be automatically stopped after the
+        test ran.
+        """
+
+        p = mock.patch.object(target, attribute, new)
+        m = p.start()
+        self.addCleanup(p.stop)
+        return m
diff --git a/tempest/tests/cmd/sample_streams/calls.subunit b/tempest/tests/cmd/sample_streams/calls.subunit
new file mode 100644
index 0000000..d5b4790
--- /dev/null
+++ b/tempest/tests/cmd/sample_streams/calls.subunit
Binary files differ
diff --git a/tempest/tests/cmd/test_account_generator.py b/tempest/tests/cmd/test_account_generator.py
new file mode 100644
index 0000000..6773b2f
--- /dev/null
+++ b/tempest/tests/cmd/test_account_generator.py
@@ -0,0 +1,346 @@
+# Copyright (c) 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.
+
+import fixtures
+import mock
+from oslo_config import cfg
+
+from tempest.cmd import account_generator
+from tempest import config
+from tempest.tests import base
+from tempest.tests import fake_config
+from tempest.tests.lib import fake_identity
+
+
+class FakeOpts(object):
+
+    def __init__(self, version=3):
+        self.os_username = 'fake_user'
+        self.os_password = 'fake_password'
+        self.os_project_name = 'fake_project_name'
+        self.os_tenant_name = None
+        self.os_domain_name = 'fake_domain'
+        self.tag = 'fake'
+        self.concurrency = 2
+        self.with_admin = True
+        self.identity_version = version
+        self.accounts = 'fake_accounts.yml'
+
+
+class MockHelpersMixin(object):
+
+    def mock_config_and_opts(self, identity_version):
+        self.useFixture(fake_config.ConfigFixture())
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
+        self.opts = FakeOpts(version=identity_version)
+
+    def mock_resource_creation(self):
+        fake_resource = dict(id='id', name='name')
+        self.user_create_fixture = self.useFixture(fixtures.MockPatch(
+            self.cred_client + '.create_user', return_value=fake_resource))
+        self.useFixture(fixtures.MockPatch(
+            self.cred_client + '.create_project',
+            return_value=fake_resource))
+        self.useFixture(fixtures.MockPatch(
+            self.cred_client + '.assign_user_role'))
+        self.useFixture(fixtures.MockPatch(
+            self.cred_client + '._check_role_exists',
+            return_value=fake_resource))
+        self.useFixture(fixtures.MockPatch(
+            self.dynamic_creds + '._create_network',
+            return_value=fake_resource))
+        self.useFixture(fixtures.MockPatch(
+            self.dynamic_creds + '._create_subnet',
+            return_value=fake_resource))
+        self.useFixture(fixtures.MockPatch(
+            self.dynamic_creds + '._create_router',
+            return_value=fake_resource))
+        self.useFixture(fixtures.MockPatch(
+            self.dynamic_creds + '._add_router_interface',
+            return_value=fake_resource))
+
+    def mock_domains(self):
+        fake_domain_list = {'domains': [{'id': 'fake_domain',
+                                         'name': 'Fake_Domain'}]}
+        self.useFixture(fixtures.MockPatch(''.join([
+            'tempest.lib.services.identity.v3.domains_client.'
+            'DomainsClient.list_domains']),
+            return_value=fake_domain_list))
+        self.useFixture(fixtures.MockPatch(
+            self.cred_client + '.assign_user_role_on_domain'))
+
+
+class TestAccountGeneratorV2(base.TestCase, MockHelpersMixin):
+
+    identity_version = 2
+    identity_response = fake_identity._fake_v2_response
+
+    def setUp(self):
+        super(TestAccountGeneratorV2, self).setUp()
+        self.mock_config_and_opts(self.identity_version)
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.auth.AuthProvider.set_auth',
+            return_value=self.identity_response))
+
+    def test_get_credential_provider(self):
+        cp = account_generator.get_credential_provider(self.opts)
+        admin_creds = cp.default_admin_creds
+        self.assertEqual(self.opts.tag, cp.name)
+        self.assertIn(str(self.opts.identity_version), cp.identity_version)
+        self.assertEqual(self.opts.os_username, admin_creds.username)
+        self.assertEqual(self.opts.os_project_name, admin_creds.tenant_name)
+        self.assertEqual(self.opts.os_password, admin_creds.password)
+        self.assertFalse(hasattr(admin_creds, 'domain_name'))
+
+    def test_get_credential_provider_with_tenant(self):
+        self.opts.os_project_name = None
+        self.opts.os_tenant_name = 'fake_tenant'
+        cp = account_generator.get_credential_provider(self.opts)
+        admin_creds = cp.default_admin_creds
+        self.assertEqual(self.opts.os_tenant_name, admin_creds.tenant_name)
+
+
+class TestAccountGeneratorV3(TestAccountGeneratorV2):
+
+    identity_version = 3
+    identity_response = fake_identity._fake_v3_response
+
+    def setUp(self):
+        super(TestAccountGeneratorV3, self).setUp()
+        fake_domain_list = {'domains': [{'id': 'fake_domain'}]}
+        self.useFixture(fixtures.MockPatch(''.join([
+            'tempest.lib.services.identity.v3.domains_client.'
+            'DomainsClient.list_domains']),
+            return_value=fake_domain_list))
+
+    def test_get_credential_provider(self):
+        cp = account_generator.get_credential_provider(self.opts)
+        admin_creds = cp.default_admin_creds
+        self.assertEqual(self.opts.tag, cp.name)
+        self.assertIn(str(self.opts.identity_version), cp.identity_version)
+        self.assertEqual(self.opts.os_username, admin_creds.username)
+        self.assertEqual(self.opts.os_project_name, admin_creds.tenant_name)
+        self.assertEqual(self.opts.os_password, admin_creds.password)
+        self.assertEqual(self.opts.os_domain_name, admin_creds.domain_name)
+
+    def test_get_credential_provider_without_domain(self):
+        self.opts.os_domain_name = None
+        cp = account_generator.get_credential_provider(self.opts)
+        admin_creds = cp.default_admin_creds
+        self.assertIsNotNone(admin_creds.domain_name)
+
+
+class TestGenerateResourcesV2(base.TestCase, MockHelpersMixin):
+
+    identity_version = 2
+    identity_response = fake_identity._fake_v2_response
+    cred_client = 'tempest.lib.common.cred_client.V2CredsClient'
+    dynamic_creds = 'tempest.common.dynamic_creds.DynamicCredentialProvider'
+
+    def setUp(self):
+        super(TestGenerateResourcesV2, self).setUp()
+        self.mock_config_and_opts(self.identity_version)
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.auth.AuthProvider.set_auth',
+            return_value=self.identity_response))
+        self.cred_provider = account_generator.get_credential_provider(
+            self.opts)
+        self.mock_resource_creation()
+
+    def test_generate_resources_no_admin(self):
+        cfg.CONF.set_default('swift', False, group='service_available')
+        cfg.CONF.set_default('heat', False, group='service_available')
+        cfg.CONF.set_default('operator_role', 'fake_operator',
+                             group='object-storage')
+        cfg.CONF.set_default('reseller_admin_role', 'fake_reseller',
+                             group='object-storage')
+        cfg.CONF.set_default('stack_owner_role', 'fake_owner',
+                             group='orchestration')
+        resources = account_generator.generate_resources(
+            self.cred_provider, admin=False)
+        resource_types = [k for k, _ in resources]
+        # No admin, no heat, no swift, expect two credentials only
+        self.assertEqual(2, len(resources))
+        # Ensure create_user was invoked twice (two distinct users)
+        self.assertEqual(2, self.user_create_fixture.mock.call_count)
+        self.assertIn('primary', resource_types)
+        self.assertIn('alt', resource_types)
+        self.assertNotIn('admin', resource_types)
+        self.assertNotIn(['fake_operator'], resource_types)
+        self.assertNotIn(['fake_reseller'], resource_types)
+        self.assertNotIn(['fake_owner'], resource_types)
+        for resource in resources:
+            self.assertIsNotNone(resource[1].network)
+            self.assertIsNotNone(resource[1].router)
+            self.assertIsNotNone(resource[1].subnet)
+
+    def test_generate_resources_admin(self):
+        cfg.CONF.set_default('swift', False, group='service_available')
+        cfg.CONF.set_default('heat', False, group='service_available')
+        cfg.CONF.set_default('operator_role', 'fake_operator',
+                             group='object-storage')
+        cfg.CONF.set_default('reseller_admin_role', 'fake_reseller',
+                             group='object-storage')
+        cfg.CONF.set_default('stack_owner_role', 'fake_owner',
+                             group='orchestration')
+        resources = account_generator.generate_resources(
+            self.cred_provider, admin=True)
+        resource_types = [k for k, _ in resources]
+        # Admin, no heat, no swift, expect three credentials only
+        self.assertEqual(3, len(resources))
+        # Ensure create_user was invoked 3 times (3 distinct users)
+        self.assertEqual(3, self.user_create_fixture.mock.call_count)
+        self.assertIn('primary', resource_types)
+        self.assertIn('alt', resource_types)
+        self.assertIn('admin', resource_types)
+        self.assertNotIn(['fake_operator'], resource_types)
+        self.assertNotIn(['fake_reseller'], resource_types)
+        self.assertNotIn(['fake_owner'], resource_types)
+        for resource in resources:
+            self.assertIsNotNone(resource[1].network)
+            self.assertIsNotNone(resource[1].router)
+            self.assertIsNotNone(resource[1].subnet)
+
+    def test_generate_resources_swift_heat_admin(self):
+        cfg.CONF.set_default('swift', True, group='service_available')
+        cfg.CONF.set_default('heat', True, group='service_available')
+        cfg.CONF.set_default('operator_role', 'fake_operator',
+                             group='object-storage')
+        cfg.CONF.set_default('reseller_admin_role', 'fake_reseller',
+                             group='object-storage')
+        cfg.CONF.set_default('stack_owner_role', 'fake_owner',
+                             group='orchestration')
+        resources = account_generator.generate_resources(
+            self.cred_provider, admin=True)
+        resource_types = [k for k, _ in resources]
+        # all options on, expect six credentials
+        self.assertEqual(6, len(resources))
+        # Ensure create_user was invoked 6 times (6 distinct users)
+        self.assertEqual(6, self.user_create_fixture.mock.call_count)
+        self.assertIn('primary', resource_types)
+        self.assertIn('alt', resource_types)
+        self.assertIn('admin', resource_types)
+        self.assertIn(['fake_operator'], resource_types)
+        self.assertIn(['fake_reseller'], resource_types)
+        self.assertIn(['fake_owner', 'fake_operator'], resource_types)
+        for resource in resources:
+            self.assertIsNotNone(resource[1].network)
+            self.assertIsNotNone(resource[1].router)
+            self.assertIsNotNone(resource[1].subnet)
+
+
+class TestGenerateResourcesV3(TestGenerateResourcesV2):
+
+    identity_version = 3
+    identity_response = fake_identity._fake_v3_response
+    cred_client = 'tempest.lib.common.cred_client.V3CredsClient'
+
+    def setUp(self):
+        self.mock_domains()
+        super(TestGenerateResourcesV3, self).setUp()
+
+
+class TestDumpAccountsV2(base.TestCase, MockHelpersMixin):
+
+    identity_version = 2
+    identity_response = fake_identity._fake_v2_response
+    cred_client = 'tempest.lib.common.cred_client.V2CredsClient'
+    dynamic_creds = 'tempest.common.dynamic_creds.DynamicCredentialProvider'
+    domain_is_in = False
+
+    def setUp(self):
+        super(TestDumpAccountsV2, self).setUp()
+        self.mock_config_and_opts(self.identity_version)
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.auth.AuthProvider.set_auth',
+            return_value=self.identity_response))
+        self.cred_provider = account_generator.get_credential_provider(
+            self.opts)
+        self.mock_resource_creation()
+        cfg.CONF.set_default('swift', True, group='service_available')
+        cfg.CONF.set_default('heat', True, group='service_available')
+        self.resources = account_generator.generate_resources(
+            self.cred_provider, admin=True)
+
+    def test_dump_accounts(self):
+        self.useFixture(fixtures.MockPatch('os.path.exists',
+                                           return_value=False))
+        mocked_open = mock.mock_open()
+        with mock.patch('{}.open'.format(account_generator.__name__),
+                        mocked_open, create=True):
+            with mock.patch('yaml.safe_dump') as yaml_dump_mock:
+                account_generator.setup_logging()
+                account_generator.dump_accounts(self.resources,
+                                                self.opts.identity_version,
+                                                self.opts.accounts)
+        mocked_open.assert_called_once_with(self.opts.accounts, 'w')
+        handle = mocked_open()
+        # Ordered args in [0], keyword args in [1]
+        accounts, f = yaml_dump_mock.call_args[0]
+        self.assertEqual(handle, f)
+        self.assertEqual(6, len(accounts))
+        if self.domain_is_in:
+            self.assertIn('domain_name', accounts[0].keys())
+        else:
+            self.assertNotIn('domain_name', accounts[0].keys())
+        self.assertEqual(1, len([x for x in accounts if
+                                 x.get('types') == ['admin']]))
+        self.assertEqual(3, len([x for x in accounts if 'roles' in x]))
+        for account in accounts:
+            self.assertIn('resources', account)
+            self.assertIn('network', account.get('resources'))
+
+    def test_dump_accounts_existing_file(self):
+        self.useFixture(fixtures.MockPatch('os.path.exists',
+                                           return_value=True))
+        rename_mock = self.useFixture(fixtures.MockPatch('os.rename')).mock
+        backup_file = '.'.join((self.opts.accounts, 'bak'))
+        mocked_open = mock.mock_open()
+        with mock.patch('{}.open'.format(account_generator.__name__),
+                        mocked_open, create=True):
+            with mock.patch('yaml.safe_dump') as yaml_dump_mock:
+                account_generator.setup_logging()
+                account_generator.dump_accounts(self.resources,
+                                                self.opts.identity_version,
+                                                self.opts.accounts)
+        rename_mock.assert_called_once_with(self.opts.accounts, backup_file)
+        mocked_open.assert_called_once_with(self.opts.accounts, 'w')
+        handle = mocked_open()
+        # Ordered args in [0], keyword args in [1]
+        accounts, f = yaml_dump_mock.call_args[0]
+        self.assertEqual(handle, f)
+        self.assertEqual(6, len(accounts))
+        if self.domain_is_in:
+            self.assertIn('domain_name', accounts[0].keys())
+        else:
+            self.assertNotIn('domain_name', accounts[0].keys())
+        self.assertEqual(1, len([x for x in accounts if
+                                 x.get('types') == ['admin']]))
+        self.assertEqual(3, len([x for x in accounts if 'roles' in x]))
+        for account in accounts:
+            self.assertIn('resources', account)
+            self.assertIn('network', account.get('resources'))
+
+
+class TestDumpAccountsV3(TestDumpAccountsV2):
+
+    identity_version = 3
+    identity_response = fake_identity._fake_v3_response
+    cred_client = 'tempest.lib.common.cred_client.V3CredsClient'
+    domain_is_in = True
+
+    def setUp(self):
+        self.mock_domains()
+        super(TestDumpAccountsV3, self).setUp()
diff --git a/tempest/tests/cmd/test_javelin.py b/tempest/tests/cmd/test_javelin.py
deleted file mode 100644
index ab6a7a0..0000000
--- a/tempest/tests/cmd/test_javelin.py
+++ /dev/null
@@ -1,422 +0,0 @@
-#!/usr/bin/env python
-#
-#    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 mockpatch
-from tempest_lib import exceptions as lib_exc
-
-from tempest.cmd import javelin
-from tempest.tests import base
-
-
-class JavelinUnitTest(base.TestCase):
-
-    def setUp(self):
-        super(JavelinUnitTest, self).setUp()
-        javelin.LOG = mock.MagicMock()
-        self.fake_client = mock.MagicMock()
-        self.fake_object = mock.MagicMock()
-
-    def test_load_resources(self):
-        with mock.patch('six.moves.builtins.open', mock.mock_open(),
-                        create=True) as open_mock:
-            with mock.patch('yaml.load', mock.MagicMock(),
-                            create=True) as load_mock:
-                javelin.load_resources(self.fake_object)
-                load_mock.assert_called_once_with(open_mock(self.fake_object))
-
-    def test_keystone_admin(self):
-        self.useFixture(mockpatch.PatchObject(javelin, "OSClient"))
-        javelin.OPTS = self.fake_object
-        javelin.keystone_admin()
-        javelin.OSClient.assert_called_once_with(
-            self.fake_object.os_username,
-            self.fake_object.os_password,
-            self.fake_object.os_tenant_name)
-
-    def test_client_for_user(self):
-        fake_user = mock.MagicMock()
-        javelin.USERS = {fake_user['name']: fake_user}
-        self.useFixture(mockpatch.PatchObject(javelin, "OSClient"))
-        javelin.client_for_user(fake_user['name'])
-        javelin.OSClient.assert_called_once_with(
-            fake_user['name'], fake_user['pass'], fake_user['tenant'])
-
-    def test_client_for_non_existing_user(self):
-        fake_non_existing_user = self.fake_object
-        fake_user = mock.MagicMock()
-        javelin.USERS = {fake_user['name']: fake_user}
-        self.useFixture(mockpatch.PatchObject(javelin, "OSClient"))
-        javelin.client_for_user(fake_non_existing_user['name'])
-        self.assertFalse(javelin.OSClient.called)
-
-    def test_attach_volumes(self):
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-
-        self.useFixture(mockpatch.PatchObject(
-            javelin, "_get_volume_by_name",
-            return_value=self.fake_object.volume))
-
-        self.useFixture(mockpatch.PatchObject(
-            javelin, "_get_server_by_name",
-            return_value=self.fake_object.server))
-
-        javelin.attach_volumes([self.fake_object])
-
-        mocked_function = self.fake_client.volumes.attach_volume
-        mocked_function.assert_called_once_with(
-            self.fake_object.volume['id'],
-            instance_uuid=self.fake_object.server['id'],
-            mountpoint=self.fake_object['device'])
-
-
-class TestCreateResources(JavelinUnitTest):
-    def test_create_tenants(self):
-
-        self.fake_client.tenants.list_tenants.return_value = {'tenants': []}
-        self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
-                                              return_value=self.fake_client))
-
-        javelin.create_tenants([self.fake_object['name']])
-
-        mocked_function = self.fake_client.tenants.create_tenant
-        mocked_function.assert_called_once_with(self.fake_object['name'])
-
-    def test_create_duplicate_tenant(self):
-        self.fake_client.tenants.list_tenants.return_value = {'tenants': [
-            {'name': self.fake_object['name']}]}
-        self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
-                                              return_value=self.fake_client))
-
-        javelin.create_tenants([self.fake_object['name']])
-
-        mocked_function = self.fake_client.tenants.create_tenant
-        self.assertFalse(mocked_function.called)
-
-    def test_create_users(self):
-        self.useFixture(mockpatch.Patch(
-                        'tempest.common.identity.get_tenant_by_name',
-                        return_value=self.fake_object['tenant']))
-        self.useFixture(mockpatch.Patch(
-                        'tempest.common.identity.get_user_by_username',
-                        side_effect=lib_exc.NotFound("user is not found")))
-        self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
-                                              return_value=self.fake_client))
-
-        javelin.create_users([self.fake_object])
-
-        fake_tenant_id = self.fake_object['tenant']['id']
-        fake_email = "%s@%s" % (self.fake_object['user'], fake_tenant_id)
-        mocked_function = self.fake_client.users.create_user
-        mocked_function.assert_called_once_with(self.fake_object['name'],
-                                                self.fake_object['password'],
-                                                fake_tenant_id,
-                                                fake_email,
-                                                enabled=True)
-
-    def test_create_user_missing_tenant(self):
-        self.useFixture(mockpatch.Patch(
-                        'tempest.common.identity.get_tenant_by_name',
-                        side_effect=lib_exc.NotFound("tenant is not found")))
-        self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
-                                              return_value=self.fake_client))
-
-        javelin.create_users([self.fake_object])
-
-        mocked_function = self.fake_client.users.create_user
-        self.assertFalse(mocked_function.called)
-
-    def test_create_objects(self):
-
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-        self.useFixture(mockpatch.PatchObject(javelin, "_assign_swift_role"))
-        self.useFixture(mockpatch.PatchObject(javelin, "_file_contents",
-                        return_value=self.fake_object.content))
-
-        javelin.create_objects([self.fake_object])
-
-        mocked_function = self.fake_client.containers.create_container
-        mocked_function.assert_called_once_with(self.fake_object['container'])
-        mocked_function = self.fake_client.objects.create_object
-        mocked_function.assert_called_once_with(self.fake_object['container'],
-                                                self.fake_object['name'],
-                                                self.fake_object.content)
-
-    def test_create_images(self):
-        self.fake_client.images.create_image.return_value = \
-            self.fake_object['body']
-
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-        self.useFixture(mockpatch.PatchObject(javelin, "_get_image_by_name",
-                                              return_value=[]))
-        self.useFixture(mockpatch.PatchObject(javelin, "_resolve_image",
-                                              return_value=(None, None)))
-
-        with mock.patch('six.moves.builtins.open', mock.mock_open(),
-                        create=True) as open_mock:
-            javelin.create_images([self.fake_object])
-
-        mocked_function = self.fake_client.images.create_image
-        mocked_function.assert_called_once_with(self.fake_object['name'],
-                                                self.fake_object['format'],
-                                                self.fake_object['format'])
-
-        mocked_function = self.fake_client.images.store_image_file
-        fake_image_id = self.fake_object['body'].get('id')
-        mocked_function.assert_called_once_with(fake_image_id, open_mock())
-
-    def test_create_networks(self):
-        self.fake_client.networks.list_networks.return_value = {
-            'networks': []}
-
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-
-        javelin.create_networks([self.fake_object])
-
-        mocked_function = self.fake_client.networks.create_network
-        mocked_function.assert_called_once_with(name=self.fake_object['name'])
-
-    def test_create_subnet(self):
-
-        fake_network = self.fake_object['network']
-
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-        self.useFixture(mockpatch.PatchObject(javelin, "_get_resource_by_name",
-                                              return_value=fake_network))
-
-        fake_netaddr = mock.MagicMock()
-        self.useFixture(mockpatch.PatchObject(javelin, "netaddr",
-                                              return_value=fake_netaddr))
-        fake_version = javelin.netaddr.IPNetwork().version
-
-        javelin.create_subnets([self.fake_object])
-
-        mocked_function = self.fake_client.networks.create_subnet
-        mocked_function.assert_called_once_with(network_id=fake_network['id'],
-                                                cidr=self.fake_object['range'],
-                                                name=self.fake_object['name'],
-                                                ip_version=fake_version)
-
-    def test_create_volumes(self):
-
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-        self.useFixture(mockpatch.PatchObject(javelin, "_get_volume_by_name",
-                                              return_value=None))
-        self.fake_client.volumes.create_volume.return_value = \
-            self.fake_object.body
-
-        javelin.create_volumes([self.fake_object])
-
-        mocked_function = self.fake_client.volumes.create_volume
-        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'],
-            'available')
-
-    def test_create_volume_existing(self):
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-        self.useFixture(mockpatch.PatchObject(javelin, "_get_volume_by_name",
-                                              return_value=self.fake_object))
-        self.fake_client.volumes.create_volume.return_value = \
-            self.fake_object.body
-
-        javelin.create_volumes([self.fake_object])
-
-        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)
-
-    def test_create_router(self):
-
-        self.fake_client.networks.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'])
-
-    def test_create_router_existing(self):
-        self.fake_client.networks.list_routers.return_value = {
-            'routers': [self.fake_object]}
-        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
-        self.assertFalse(mocked_function.called)
-
-    def test_create_secgroup(self):
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-        self.fake_client.secgroups.list_security_groups.return_value = (
-            {'security_groups': []})
-        self.fake_client.secgroups.create_security_group.return_value = \
-            {'security_group': {'id': self.fake_object['secgroup_id']}}
-
-        javelin.create_secgroups([self.fake_object])
-
-        mocked_function = self.fake_client.secgroups.create_security_group
-        mocked_function.assert_called_once_with(
-            name=self.fake_object['name'],
-            description=self.fake_object['description'])
-
-
-class TestDestroyResources(JavelinUnitTest):
-
-    def test_destroy_tenants(self):
-
-        fake_tenant = self.fake_object['tenant']
-        fake_auth = self.fake_client
-        self.useFixture(mockpatch.Patch(
-                        'tempest.common.identity.get_tenant_by_name',
-                        return_value=fake_tenant))
-        self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
-                                              return_value=fake_auth))
-        javelin.destroy_tenants([fake_tenant])
-
-        mocked_function = fake_auth.tenants.delete_tenant
-        mocked_function.assert_called_once_with(fake_tenant['id'])
-
-    def test_destroy_users(self):
-
-        fake_user = self.fake_object['user']
-        fake_tenant = self.fake_object['tenant']
-
-        fake_auth = self.fake_client
-        fake_auth.tenants.list_tenants.return_value = \
-            {'tenants': [fake_tenant]}
-        fake_auth.users.list_users.return_value = {'users': [fake_user]}
-
-        self.useFixture(mockpatch.Patch(
-                        'tempest.common.identity.get_user_by_username',
-                        return_value=fake_user))
-        self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
-                                              return_value=fake_auth))
-
-        javelin.destroy_users([fake_user])
-
-        mocked_function = fake_auth.users.delete_user
-        mocked_function.assert_called_once_with(fake_user['id'])
-
-    def test_destroy_objects(self):
-
-        self.fake_client.objects.delete_object.return_value = \
-            {'status': "200"}, ""
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-        javelin.destroy_objects([self.fake_object])
-
-        mocked_function = self.fake_client.objects.delete_object
-        mocked_function.asswert_called_once(self.fake_object['container'],
-                                            self.fake_object['name'])
-
-    def test_destroy_images(self):
-
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-        self.useFixture(mockpatch.PatchObject(javelin, "_get_image_by_name",
-                        return_value=self.fake_object['image']))
-
-        javelin.destroy_images([self.fake_object])
-
-        mocked_function = self.fake_client.images.delete_image
-        mocked_function.assert_called_once_with(
-            self.fake_object['image']['id'])
-
-    def test_destroy_networks(self):
-
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-        self.useFixture(mockpatch.PatchObject(
-            javelin, "_get_resource_by_name",
-            return_value=self.fake_object['resource']))
-
-        javelin.destroy_networks([self.fake_object])
-
-        mocked_function = self.fake_client.networks.delete_network
-        mocked_function.assert_called_once_with(
-            self.fake_object['resource']['id'])
-
-    def test_destroy_volumes(self):
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-
-        self.useFixture(mockpatch.PatchObject(
-            javelin, "_get_volume_by_name",
-            return_value=self.fake_object.volume))
-
-        javelin.destroy_volumes([self.fake_object])
-
-        mocked_function = self.fake_client.volumes.detach_volume
-        mocked_function.assert_called_once_with(self.fake_object.volume['id'])
-        mocked_function = self.fake_client.volumes.delete_volume
-        mocked_function.assert_called_once_with(self.fake_object.volume['id'])
-
-    def test_destroy_subnets(self):
-
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-        fake_subnet_id = self.fake_object['subnet_id']
-        self.useFixture(mockpatch.PatchObject(javelin, "_get_resource_by_name",
-                                              return_value={
-                                                  'id': fake_subnet_id}))
-
-        javelin.destroy_subnets([self.fake_object])
-
-        mocked_function = self.fake_client.subnets.delete_subnet
-        mocked_function.assert_called_once_with(fake_subnet_id)
-
-    def test_destroy_routers(self):
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-
-        # this function is used on 2 different occasions in the code
-        def _fake_get_resource_by_name(*args):
-            if args[1] == "routers":
-                return {"id": self.fake_object['router_id']}
-            elif args[1] == "subnets":
-                return {"id": self.fake_object['subnet_id']}
-        javelin._get_resource_by_name = _fake_get_resource_by_name
-
-        javelin.destroy_routers([self.fake_object])
-
-        mocked_function = self.fake_client.networks.delete_router
-        mocked_function.assert_called_once_with(
-            self.fake_object['router_id'])
-
-    def test_destroy_secgroup(self):
-        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
-                                              return_value=self.fake_client))
-        fake_secgroup = {'id': self.fake_object['id']}
-        self.useFixture(mockpatch.PatchObject(javelin, "_get_resource_by_name",
-                                              return_value=fake_secgroup))
-
-        javelin.destroy_secgroups([self.fake_object])
-
-        mocked_function = self.fake_client.secgroups.delete_security_group
-        mocked_function.assert_called_once_with(self.fake_object['id'])
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
new file mode 100644
index 0000000..7ac347d
--- /dev/null
+++ b/tempest/tests/cmd/test_run.py
@@ -0,0 +1,154 @@
+# Copyright 2015 Hewlett-Packard 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.
+
+import argparse
+import os
+import shutil
+import subprocess
+import tempfile
+
+import fixtures
+import mock
+
+from tempest.cmd import run
+from tempest.tests import base
+
+DEVNULL = open(os.devnull, 'wb')
+
+
+class TestTempestRun(base.TestCase):
+
+    def setUp(self):
+        super(TestTempestRun, self).setUp()
+        self.run_cmd = run.TempestRun(None, None)
+
+    def test_build_options(self):
+        args = mock.Mock(spec=argparse.Namespace)
+        setattr(args, "subunit", True)
+        setattr(args, "parallel", False)
+        setattr(args, "concurrency", 10)
+        options = self.run_cmd._build_options(args)
+        self.assertEqual(['--subunit',
+                          '--concurrency=10'],
+                         options)
+
+    def test__build_regex_default(self):
+        args = mock.Mock(spec=argparse.Namespace)
+        setattr(args, 'smoke', False)
+        setattr(args, 'regex', '')
+        setattr(args, 'whitelist_file', None)
+        setattr(args, 'blacklist_file', None)
+        self.assertEqual('', self.run_cmd._build_regex(args))
+
+    def test__build_regex_smoke(self):
+        args = mock.Mock(spec=argparse.Namespace)
+        setattr(args, "smoke", True)
+        setattr(args, 'regex', '')
+        setattr(args, 'whitelist_file', None)
+        setattr(args, 'blacklist_file', None)
+        self.assertEqual('smoke', self.run_cmd._build_regex(args))
+
+    def test__build_regex_regex(self):
+        args = mock.Mock(spec=argparse.Namespace)
+        setattr(args, 'smoke', False)
+        setattr(args, "regex", 'i_am_a_fun_little_regex')
+        setattr(args, 'whitelist_file', None)
+        setattr(args, 'blacklist_file', None)
+        self.assertEqual('i_am_a_fun_little_regex',
+                         self.run_cmd._build_regex(args))
+
+
+class TestRunReturnCode(base.TestCase):
+    def setUp(self):
+        super(TestRunReturnCode, self).setUp()
+        # Setup test dirs
+        self.directory = tempfile.mkdtemp(prefix='tempest-unit')
+        self.addCleanup(shutil.rmtree, self.directory)
+        self.test_dir = os.path.join(self.directory, 'tests')
+        os.mkdir(self.test_dir)
+        # Setup Test files
+        self.testr_conf_file = os.path.join(self.directory, '.testr.conf')
+        self.setup_cfg_file = os.path.join(self.directory, 'setup.cfg')
+        self.passing_file = os.path.join(self.test_dir, 'test_passing.py')
+        self.failing_file = os.path.join(self.test_dir, 'test_failing.py')
+        self.init_file = os.path.join(self.test_dir, '__init__.py')
+        self.setup_py = os.path.join(self.directory, 'setup.py')
+        shutil.copy('tempest/tests/files/testr-conf', self.testr_conf_file)
+        shutil.copy('tempest/tests/files/passing-tests', self.passing_file)
+        shutil.copy('tempest/tests/files/failing-tests', self.failing_file)
+        shutil.copy('setup.py', self.setup_py)
+        shutil.copy('tempest/tests/files/setup.cfg', self.setup_cfg_file)
+        shutil.copy('tempest/tests/files/__init__.py', self.init_file)
+        # Change directory, run wrapper and check result
+        self.addCleanup(os.chdir, os.path.abspath(os.curdir))
+        os.chdir(self.directory)
+
+    def assertRunExit(self, cmd, expected):
+        p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE)
+        out, err = p.communicate()
+        msg = ("Running %s got an unexpected returncode\n"
+               "Stdout: %s\nStderr: %s" % (' '.join(cmd), out, err))
+        self.assertEqual(p.returncode, expected, msg)
+
+    def test_tempest_run_passes(self):
+        # Git init is required for the pbr testr command. pbr requires a git
+        # version or an sdist to work. so make the test directory a git repo
+        # too.
+        subprocess.call(['git', 'init'], stderr=DEVNULL)
+        self.assertRunExit(['tempest', 'run', '--regex', 'passing'], 0)
+
+    def test_tempest_run_passes_with_testrepository(self):
+        # Git init is required for the pbr testr command. pbr requires a git
+        # version or an sdist to work. so make the test directory a git repo
+        # too.
+        subprocess.call(['git', 'init'], stderr=DEVNULL)
+        subprocess.call(['testr', 'init'])
+        self.assertRunExit(['tempest', 'run', '--regex', 'passing'], 0)
+
+    def test_tempest_run_fails(self):
+        # Git init is required for the pbr testr command. pbr requires a git
+        # version or an sdist to work. so make the test directory a git repo
+        # too.
+        subprocess.call(['git', 'init'], stderr=DEVNULL)
+        self.assertRunExit(['tempest', 'run'], 1)
+
+
+class TestTakeAction(base.TestCase):
+    def test_workspace_not_registered(self):
+        class Exception_(Exception):
+            pass
+
+        m_exit = self.useFixture(fixtures.MockPatch('sys.exit')).mock
+        # sys.exit must not continue (or exit)
+        m_exit.side_effect = Exception_
+
+        workspace = self.getUniqueString()
+
+        tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
+        parsed_args = mock.Mock()
+        parsed_args.config_file = []
+
+        # Override $HOME so that empty workspace gets created in temp dir.
+        self.useFixture(fixtures.TempHomeDir())
+
+        # Force use of the temporary home directory.
+        parsed_args.workspace_path = None
+
+        # Simulate --workspace argument.
+        parsed_args.workspace = workspace
+
+        self.assertRaises(Exception_, tempest_run.take_action, parsed_args)
+        exit_msg = m_exit.call_args[0][0]
+        self.assertIn(workspace, exit_msg)
diff --git a/tempest/tests/cmd/test_subunit_describe_calls.py b/tempest/tests/cmd/test_subunit_describe_calls.py
new file mode 100644
index 0000000..1c24c37
--- /dev/null
+++ b/tempest/tests/cmd/test_subunit_describe_calls.py
@@ -0,0 +1,196 @@
+# Copyright 2016 Rackspace
+#
+# 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 os
+import subprocess
+import tempfile
+
+from tempest.cmd import subunit_describe_calls
+from tempest.tests import base
+
+
+class TestSubunitDescribeCalls(base.TestCase):
+    def test_return_code(self):
+        subunit_file = os.path.join(
+            os.path.dirname(os.path.abspath(__file__)),
+            'sample_streams/calls.subunit')
+        p = subprocess.Popen([
+            'subunit-describe-calls', '-s', subunit_file,
+            '-o', tempfile.mkstemp()[1]], stdin=subprocess.PIPE)
+        p.communicate()
+        self.assertEqual(0, p.returncode)
+
+    def test_parse(self):
+        subunit_file = os.path.join(
+            os.path.dirname(os.path.abspath(__file__)),
+            'sample_streams/calls.subunit')
+        parser = subunit_describe_calls.parse(
+            open(subunit_file), "pythonlogging", None)
+        expected_result = {
+            'bar': [{
+                'name': 'AgentsAdminTestJSON:setUp',
+                'request_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
+                '"hypervisor": "common", "md5hash": '
+                '"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
+                '"architecture": "tempest-x86_64-424013832", "os": "linux"}}',
+                'request_headers': "{'Content-Type': 'application/json', "
+                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+                'response_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
+                '"hypervisor": "common", "md5hash": '
+                '"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
+                '"architecture": "tempest-x86_64-424013832", "os": "linux", '
+                '"agent_id": 1}}',
+                'response_headers': "{'status': '200', 'content-length': "
+                "'203', 'x-compute-request-id': "
+                "'req-25ddaae2-0ef1-40d1-8228-59bd64a7e75b', 'vary': "
+                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
+                "'x-openstack-nova-api-version': '2.1', 'date': "
+                "'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': "
+                "'application/json'}",
+                'service': 'Nova',
+                'status_code': '200',
+                'url': 'v2.1/<id>/os-agents',
+                'verb': 'POST'}, {
+                'name': 'AgentsAdminTestJSON:test_create_agent',
+                'request_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
+                '"hypervisor": "kvm", "md5hash": '
+                '"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
+                '"architecture": "tempest-x86-252246646", "os": "win"}}',
+                'request_headers': "{'Content-Type': 'application/json', "
+                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+                'response_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
+                '"hypervisor": "kvm", "md5hash": '
+                '"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
+                '"architecture": "tempest-x86-252246646", "os": "win", '
+                '"agent_id": 2}}',
+                'response_headers': "{'status': '200', 'content-length': "
+                "'195', 'x-compute-request-id': "
+                "'req-b4136f06-c015-4e7e-995f-c43831e3ecce', 'vary': "
+                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
+                "'x-openstack-nova-api-version': '2.1', 'date': "
+                "'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': "
+                "'application/json'}",
+                'service': 'Nova',
+                'status_code': '200',
+                'url': 'v2.1/<id>/os-agents',
+                'verb': 'POST'}, {
+                'name': 'AgentsAdminTestJSON:tearDown',
+                'request_body': 'None',
+                'request_headers': "{'Content-Type': 'application/json', "
+                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+                'response_body': '',
+                'response_headers': "{'status': '200', 'content-length': "
+                "'0', 'x-compute-request-id': "
+                "'req-ee905fd6-a5b5-4da4-8c37-5363cb25bd9d', 'vary': "
+                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
+                "'x-openstack-nova-api-version': '2.1', 'date': "
+                "'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': "
+                "'application/json'}",
+                'service': 'Nova',
+                'status_code': '200',
+                'url': 'v2.1/<id>/os-agents/1',
+                'verb': 'DELETE'}, {
+                'name': 'AgentsAdminTestJSON:_run_cleanups',
+                'request_body': 'None',
+                'request_headers': "{'Content-Type': 'application/json', "
+                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+                'response_headers': "{'status': '200', 'content-length': "
+                "'0', 'x-compute-request-id': "
+                "'req-e912cac0-63e0-4679-a68a-b6d18ddca074', 'vary': "
+                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
+                "'x-openstack-nova-api-version': '2.1', 'date': "
+                "'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': "
+                "'application/json'}",
+                'service': 'Nova',
+                'status_code': '200',
+                'url': 'v2.1/<id>/os-agents/2',
+                'verb': 'DELETE'}],
+            'foo': [{
+                'name': 'AgentsAdminTestJSON:setUp',
+                'request_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
+                '"hypervisor": "common", "md5hash": '
+                '"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
+                '"architecture": "tempest-x86_64-948635295", "os": "linux"}}',
+                'request_headers': "{'Content-Type': 'application/json', "
+                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+                'response_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
+                '"hypervisor": "common", "md5hash": '
+                '"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
+                '"architecture": "tempest-x86_64-948635295", "os": "linux", '
+                '"agent_id": 3}}',
+                'response_headers': "{'status': '200', 'content-length': "
+                "'203', 'x-compute-request-id': "
+                "'req-ccd2116d-04b1-4ffe-ae32-fb623f68bf1c', 'vary': "
+                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
+                "'x-openstack-nova-api-version': '2.1', 'date': "
+                "'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': "
+                "'application/json'}",
+                'service': 'Nova',
+                'status_code': '200',
+                'url': 'v2.1/<id>/os-agents',
+                'verb': 'POST'}, {
+                'name': 'AgentsAdminTestJSON:test_delete_agent',
+                'request_body': 'None',
+                'request_headers': "{'Content-Type': 'application/json', "
+                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+                'response_body': '',
+                'response_headers': "{'status': '200', 'content-length': "
+                "'0', 'x-compute-request-id': "
+                "'req-6e7fa28f-ae61-4388-9a78-947c58bc0588', 'vary': "
+                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
+                "'x-openstack-nova-api-version': '2.1', 'date': "
+                "'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': "
+                "'application/json'}",
+                'service': 'Nova',
+                'status_code': '200',
+                'url': 'v2.1/<id>/os-agents/3',
+                'verb': 'DELETE'}, {
+                'name': 'AgentsAdminTestJSON:test_delete_agent',
+                'request_body': 'None',
+                'request_headers': "{'Content-Type': 'application/json', "
+                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+                'response_body': '{"agents": []}',
+                'response_headers': "{'status': '200', 'content-length': "
+                "'14', 'content-location': "
+                "'http://23.253.76.97:8774/v2.1/"
+                "cf6b1933fe5b476fbbabb876f6d1b924/os-agents', "
+                "'x-compute-request-id': "
+                "'req-e41aa9b4-41a6-4138-ae04-220b768eb644', 'vary': "
+                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
+                "'x-openstack-nova-api-version': '2.1', 'date': "
+                "'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': "
+                "'application/json'}",
+                'service': 'Nova',
+                'status_code': '200',
+                'url': 'v2.1/<id>/os-agents',
+                'verb': 'GET'}, {
+                'name': 'AgentsAdminTestJSON:tearDown',
+                'request_body': 'None',
+                'request_headers': "{'Content-Type': 'application/json', "
+                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+                'response_headers': "{'status': '404', 'content-length': "
+                "'82', 'x-compute-request-id': "
+                "'req-e297aeea-91cf-4f26-b49c-8f46b1b7a926', 'vary': "
+                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
+                "'x-openstack-nova-api-version': '2.1', 'date': "
+                "'Tue, 02 Feb 2016 03:27:02 GMT', 'content-type': "
+                "'application/json; charset=UTF-8'}",
+                'service': 'Nova',
+                'status_code': '404',
+                'url': 'v2.1/<id>/os-agents/3',
+                'verb': 'DELETE'}]}
+
+        self.assertEqual(expected_result, parser.test_logs)
diff --git a/tempest/tests/cmd/test_tempest_init.py b/tempest/tests/cmd/test_tempest_init.py
index 1048a52..79510be 100644
--- a/tempest/tests/cmd/test_tempest_init.py
+++ b/tempest/tests/cmd/test_tempest_init.py
@@ -13,7 +13,6 @@
 # under the License.
 
 import os
-import shutil
 
 import fixtures
 
@@ -36,28 +35,71 @@
         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())
         etc_dir_path = os.path.join(local_dir.path, 'etc/')
         os.mkdir(etc_dir_path)
-        tmp_dir = self.useFixture(fixtures.TempDir())
-        config_dir = os.path.join(tmp_dir.path, 'config/')
-        shutil.copytree('etc/', config_dir)
         init_cmd = init.TempestInit(None, None)
         local_sample_conf_file = os.path.join(etc_dir_path,
                                               'tempest.conf.sample')
+
         # Verify no sample config file exist
         self.assertFalse(os.path.isfile(local_sample_conf_file))
-        init_cmd.generate_sample_config(local_dir.path, config_dir)
+        init_cmd.generate_sample_config(local_dir.path)
 
         # Verify sample config file exist with some content
         self.assertTrue(os.path.isfile(local_sample_conf_file))
         self.assertGreater(os.path.getsize(local_sample_conf_file), 0)
 
+    def test_update_local_conf(self):
+        local_dir = self.useFixture(fixtures.TempDir())
+        etc_dir_path = os.path.join(local_dir.path, 'etc/')
+        os.mkdir(etc_dir_path)
+        lock_dir = os.path.join(local_dir.path, 'tempest_lock')
+        config_path = os.path.join(etc_dir_path, 'tempest.conf')
+        log_dir = os.path.join(local_dir.path, 'logs')
+
+        init_cmd = init.TempestInit(None, None)
+
+        # Generate the config file
+        init_cmd.generate_sample_config(local_dir.path)
+
+        # Create a conf file with populated values
+        config_parser_pre = init_cmd.get_configparser(config_path)
+        with open(config_path, 'w+') as conf_file:
+            # create the same section init will check for and add values to
+            config_parser_pre.add_section('oslo_concurrency')
+            config_parser_pre.set('oslo_concurrency', 'TEST', local_dir.path)
+            # create a new section
+            config_parser_pre.add_section('TEST')
+            config_parser_pre.set('TEST', 'foo', "bar")
+            config_parser_pre.write(conf_file)
+
+        # Update the config file the same way tempest init does
+        init_cmd.update_local_conf(config_path, lock_dir, log_dir)
+
+        # parse the new config file to verify it
+        config_parser_post = init_cmd.get_configparser(config_path)
+
+        # check that our value in oslo_concurrency wasn't overwritten
+        self.assertTrue(config_parser_post.has_section('oslo_concurrency'))
+        self.assertEqual(config_parser_post.get('oslo_concurrency', 'TEST'),
+                         local_dir.path)
+        # check that the lock directory was set correctly
+        self.assertEqual(config_parser_post.get('oslo_concurrency',
+                                                'lock_path'), lock_dir)
+
+        # check that our new section still exists and wasn't modified
+        self.assertTrue(config_parser_post.has_section('TEST'))
+        self.assertEqual(config_parser_post.get('TEST', 'foo'), 'bar')
+
+        # check that the DEFAULT values are correct
+        # NOTE(auggy): has_section ignores DEFAULT
+        self.assertEqual(config_parser_post.get('DEFAULT', 'log_dir'), log_dir)
+
     def test_create_working_dir_with_existing_local_dir_non_empty(self):
         fake_local_dir = self.useFixture(fixtures.TempDir())
         fake_local_conf_dir = self.useFixture(fixtures.TempDir())
@@ -95,3 +137,18 @@
         self.assertTrue(os.path.isfile(fake_file_moved))
         self.assertTrue(os.path.isfile(local_conf_file))
         self.assertTrue(os.path.isfile(local_testr_conf))
+
+    def test_take_action_fails(self):
+        class ParsedArgs(object):
+            workspace_dir = self.useFixture(fixtures.TempDir()).path
+            workspace_path = os.path.join(workspace_dir, 'workspace.yaml')
+            name = 'test'
+            dir_base = self.useFixture(fixtures.TempDir()).path
+            dir = os.path.join(dir_base, 'foo', 'bar')
+            config_dir = self.useFixture(fixtures.TempDir()).path
+            show_global_dir = False
+        pa = ParsedArgs()
+        init_cmd = init.TempestInit(None, None)
+        self.assertRaises(OSError, init_cmd.take_action, pa)
+        # one more trying should be a same error not "workspace already exists"
+        self.assertRaises(OSError, init_cmd.take_action, pa)
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 193abc7..1af0d95 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -12,28 +12,56 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-
 import mock
 from oslo_serialization import jsonutils as json
 from oslotest import mockpatch
 
 from tempest.cmd import verify_tempest_config
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.tests import base
 from tempest.tests import fake_config
 
 
 class TestGetAPIVersions(base.TestCase):
 
+    def test_remove_version_project(self):
+        f = verify_tempest_config._remove_version_project
+        self.assertEqual('/', f('/v2.1/%s/' % data_utils.rand_uuid_hex()))
+        self.assertEqual('', f('/v2.1/tenant_id'))
+        self.assertEqual('', f('/v3'))
+        self.assertEqual('/', f('/v3/'))
+        self.assertEqual('/something/', f('/something/v2.1/tenant_id/'))
+        self.assertEqual('/something', f('/something/v2.1/tenant_id'))
+        self.assertEqual('/something', f('/something/v3'))
+        self.assertEqual('/something/', f('/something/v3/'))
+        self.assertEqual('/', f('/'))  # http://localhost/
+        self.assertEqual('', f(''))  # http://localhost
+
     def test_url_grab_versioned_nova_nossl(self):
         base_url = 'http://127.0.0.1:8774/v2/'
         endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
-        self.assertEqual('http://127.0.0.1:8774', endpoint)
+        self.assertEqual('http://127.0.0.1:8774/', endpoint)
 
     def test_url_grab_versioned_nova_ssl(self):
         base_url = 'https://127.0.0.1:8774/v3/'
         endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
-        self.assertEqual('https://127.0.0.1:8774', endpoint)
+        self.assertEqual('https://127.0.0.1:8774/', endpoint)
+
+    def test_get_unversioned_endpoint_base(self):
+        base_url = 'https://127.0.0.1:5000/'
+        endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
+        self.assertEqual('https://127.0.0.1:5000/', endpoint)
+
+    def test_get_unversioned_endpoint_subpath(self):
+        base_url = 'https://127.0.0.1/identity/v3'
+        endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
+        self.assertEqual('https://127.0.0.1/identity', endpoint)
+
+    def test_get_unversioned_endpoint_subpath_trailing_solidus(self):
+        base_url = 'https://127.0.0.1/identity/v3/'
+        endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
+        self.assertEqual('https://127.0.0.1/identity/', endpoint)
 
 
 class TestDiscovery(base.TestCase):
@@ -41,7 +69,8 @@
     def setUp(self):
         super(TestDiscovery, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
-        self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
 
     def test_get_keystone_api_versions(self):
         self.useFixture(mockpatch.PatchObject(
@@ -49,8 +78,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 +92,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 +106,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 +155,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 +171,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,35 +187,55 @@
                                            '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_v3(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 = {'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)))
+        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:
             verify_tempest_config.verify_cinder_api_versions(fake_os, True)
-        print_mock.assert_called_once_with('api_v2', 'volume-feature-enabled',
-                                           False, True)
+        print_mock.assert_not_called()
 
-    def test_verify_cinder_api_versions_no_v1(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': 'v2.0'}]}
+        fake_resp = {'versions': [{'id': 'v1.0'}, {'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:
             verify_tempest_config.verify_cinder_api_versions(fake_os, True)
-        print_mock.assert_called_once_with('api_v1', 'volume-feature-enabled',
-                                           False, True)
+        print_mock.assert_any_call('api_v2', 'volume-feature-enabled',
+                                   False, True)
+        print_mock.assert_any_call('api_v3', 'volume-feature-enabled',
+                                   True, True)
+        self.assertEqual(2, print_mock.call_count)
+
+    @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'}, {'id': 'v3.0'}]}
+        fake_resp = json.dumps(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:
+            verify_tempest_config.verify_cinder_api_versions(fake_os, True)
+        print_mock.assert_any_call('api_v1', 'volume-feature-enabled',
+                                   False, True)
+        print_mock.assert_any_call('api_v3', 'volume-feature-enabled',
+                                   True, True)
+        self.assertEqual(2, print_mock.call_count)
 
     def test_verify_glance_version_no_v2_with_v1_1(self):
         def fake_get_versions():
@@ -326,7 +401,7 @@
                            'not_fake': 'metadata',
                            'swift': 'metadata'})
         fake_os = mock.MagicMock()
-        fake_os.account_client.list_extensions = fake_list_extensions
+        fake_os.capabilities_client.list_capabilities = fake_list_extensions
         self.useFixture(mockpatch.PatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['fake1', 'fake2', 'fake3'])))
@@ -348,7 +423,7 @@
                            'not_fake': 'metadata',
                            'swift': 'metadata'})
         fake_os = mock.MagicMock()
-        fake_os.account_client.list_extensions = fake_list_extensions
+        fake_os.capabilities_client.list_capabilities = fake_list_extensions
         self.useFixture(mockpatch.PatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['all'])))
diff --git a/tempest/tests/cmd/test_workspace.py b/tempest/tests/cmd/test_workspace.py
new file mode 100644
index 0000000..6ca4d42
--- /dev/null
+++ b/tempest/tests/cmd/test_workspace.py
@@ -0,0 +1,126 @@
+# Copyright 2016 Rackspace
+#
+# 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 shutil
+import subprocess
+import tempfile
+
+from tempest.cmd import workspace
+from tempest.lib.common.utils import data_utils
+from tempest.tests import base
+
+
+class TestTempestWorkspaceBase(base.TestCase):
+    def setUp(self):
+        super(TestTempestWorkspaceBase, self).setUp()
+        self.name = data_utils.rand_uuid()
+        self.path = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, self.path, ignore_errors=True)
+        store_dir = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, store_dir, ignore_errors=True)
+        self.store_file = os.path.join(store_dir, 'workspace.yaml')
+        self.workspace_manager = workspace.WorkspaceManager(
+            path=self.store_file)
+        self.workspace_manager.register_new_workspace(self.name, self.path)
+
+
+class TestTempestWorkspace(TestTempestWorkspaceBase):
+    def _run_cmd_gets_return_code(self, cmd, expected):
+        process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                                   stderr=subprocess.PIPE)
+        stdout, stderr = process.communicate()
+        return_code = process.returncode
+        msg = ("%s failed with:\nstdout: %s\nstderr: %s" % (' '.join(cmd),
+               stdout, stderr))
+        self.assertEqual(return_code, expected, msg)
+
+    def test_run_workspace_list(self):
+        cmd = ['tempest', 'workspace', '--workspace-path',
+               self.store_file, 'list']
+        self._run_cmd_gets_return_code(cmd, 0)
+
+    def test_run_workspace_register(self):
+        name = data_utils.rand_uuid()
+        path = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, path, ignore_errors=True)
+        cmd = ['tempest', 'workspace', '--workspace-path', self.store_file,
+               'register', '--name', name, '--path', path]
+        self._run_cmd_gets_return_code(cmd, 0)
+        self.assertIsNotNone(self.workspace_manager.get_workspace(name))
+
+    def test_run_workspace_rename(self):
+        new_name = data_utils.rand_uuid()
+        cmd = ['tempest', 'workspace', '--workspace-path', self.store_file,
+               'rename', "--old-name", self.name, '--new-name', new_name]
+        self._run_cmd_gets_return_code(cmd, 0)
+        self.assertIsNone(self.workspace_manager.get_workspace(self.name))
+        self.assertIsNotNone(self.workspace_manager.get_workspace(new_name))
+
+    def test_run_workspace_move(self):
+        new_path = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, new_path, ignore_errors=True)
+        cmd = ['tempest', 'workspace', '--workspace-path', self.store_file,
+               'move', '--name', self.name, '--path', new_path]
+        self._run_cmd_gets_return_code(cmd, 0)
+        self.assertEqual(
+            self.workspace_manager.get_workspace(self.name), new_path)
+
+    def test_run_workspace_remove(self):
+        cmd = ['tempest', 'workspace', '--workspace-path', self.store_file,
+               'remove', '--name', self.name]
+        self._run_cmd_gets_return_code(cmd, 0)
+        self.assertIsNone(self.workspace_manager.get_workspace(self.name))
+
+
+class TestTempestWorkspaceManager(TestTempestWorkspaceBase):
+    def setUp(self):
+        super(TestTempestWorkspaceManager, self).setUp()
+        self.name = data_utils.rand_uuid()
+        self.path = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, self.path, ignore_errors=True)
+        store_dir = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, store_dir, ignore_errors=True)
+        self.store_file = os.path.join(store_dir, 'workspace.yaml')
+        self.workspace_manager = workspace.WorkspaceManager(
+            path=self.store_file)
+        self.workspace_manager.register_new_workspace(self.name, self.path)
+
+    def test_workspace_manager_get(self):
+        self.assertIsNotNone(self.workspace_manager.get_workspace(self.name))
+
+    def test_workspace_manager_rename(self):
+        new_name = data_utils.rand_uuid()
+        self.workspace_manager.rename_workspace(self.name, new_name)
+        self.assertIsNone(self.workspace_manager.get_workspace(self.name))
+        self.assertIsNotNone(self.workspace_manager.get_workspace(new_name))
+
+    def test_workspace_manager_move(self):
+        new_path = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, new_path, ignore_errors=True)
+        self.workspace_manager.move_workspace(self.name, new_path)
+        self.assertEqual(
+            self.workspace_manager.get_workspace(self.name), new_path)
+
+    def test_workspace_manager_remove(self):
+        self.workspace_manager.remove_workspace(self.name)
+        self.assertIsNone(self.workspace_manager.get_workspace(self.name))
+
+    def test_path_expansion(self):
+        name = data_utils.rand_uuid()
+        path = os.path.join("~", name)
+        os.makedirs(os.path.expanduser(path))
+        self.addCleanup(shutil.rmtree, path, ignore_errors=True)
+        self.workspace_manager.register_new_workspace(name, path)
+        self.assertIsNotNone(self.workspace_manager.get_workspace(name))
diff --git a/tempest/tests/common/test_admin_available.py b/tempest/tests/common/test_admin_available.py
index 75401db..01a9cd0 100644
--- a/tempest/tests/common/test_admin_available.py
+++ b/tempest/tests/common/test_admin_available.py
@@ -28,7 +28,8 @@
     def setUp(self):
         super(TestAdminAvailable, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
-        self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
 
     def run_test(self, dynamic_creds, use_accounts_file, admin_creds):
 
@@ -36,19 +37,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 +64,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..27db95c 100644
--- a/tempest/tests/common/test_alt_available.py
+++ b/tempest/tests/common/test_alt_available.py
@@ -28,7 +28,8 @@
     def setUp(self):
         super(TestAltAvailable, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
-        self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
 
     def run_test(self, dynamic_creds, use_accounts_file, creds):
 
@@ -36,7 +37,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',
@@ -48,28 +49,6 @@
         else:
             self.useFixture(mockpatch.Patch('os.path.isfile',
                                             return_value=False))
-            cred_prefix = ['', 'alt_']
-            for ii in range(0, 2):
-                if len(creds) > ii:
-                    username = 'u%s' % creds[ii]
-                    tenant = 't%s' % creds[ii]
-                    password = 'p'
-                    domain = 'd'
-                else:
-                    username = None
-                    tenant = 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('%spassword' % cred_prefix[ii], password,
-                                     group='identity')
-                cfg.CONF.set_default('%sdomain_name' % cred_prefix[ii], domain,
-                                     group='identity')
-
         expected = len(set(creds)) > 1 or dynamic_creds
         observed = credentials.is_alt_available(
             identity_version=self.identity_version)
@@ -96,21 +75,6 @@
                       use_accounts_file=True,
                       creds=['1', '1'])
 
-    def test__no_dynamic_creds__no_accounts_file__one_user(self):
-        self.run_test(dynamic_creds=False,
-                      use_accounts_file=False,
-                      creds=['1'])
-
-    def test__no_dynamic_creds__no_accounts_file__two_users(self):
-        self.run_test(dynamic_creds=False,
-                      use_accounts_file=False,
-                      creds=['1', '2'])
-
-    def test__no_dynamic_creds__no_accounts_file__two_users_identical(self):
-        self.run_test(dynamic_creds=False,
-                      use_accounts_file=False,
-                      creds=['1', '1'])
-
 
 class TestAltAvailableV3(TestAltAvailable):
 
diff --git a/tempest/tests/common/test_api_version_request.py b/tempest/tests/common/test_api_version_request.py
deleted file mode 100644
index 38fbfc1..0000000
--- a/tempest/tests/common/test_api_version_request.py
+++ /dev/null
@@ -1,146 +0,0 @@
-# Copyright 2014 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.
-
-from tempest.common import api_version_request
-from tempest import exceptions
-from tempest.tests import base
-
-
-class APIVersionRequestTests(base.TestCase):
-    def test_valid_version_strings(self):
-        def _test_string(version, exp_major, exp_minor):
-            v = api_version_request.APIVersionRequest(version)
-            self.assertEqual(v.ver_major, exp_major)
-            self.assertEqual(v.ver_minor, exp_minor)
-
-        _test_string("1.1", 1, 1)
-        _test_string("2.10", 2, 10)
-        _test_string("5.234", 5, 234)
-        _test_string("12.5", 12, 5)
-        _test_string("2.0", 2, 0)
-        _test_string("2.200", 2, 200)
-
-    def test_null_version(self):
-        v = api_version_request.APIVersionRequest()
-        self.assertTrue(v.is_null())
-
-    def test_invalid_version_strings(self):
-        self.assertRaises(exceptions.InvalidAPIVersionString,
-                          api_version_request.APIVersionRequest, "2")
-
-        self.assertRaises(exceptions.InvalidAPIVersionString,
-                          api_version_request.APIVersionRequest, "200")
-
-        self.assertRaises(exceptions.InvalidAPIVersionString,
-                          api_version_request.APIVersionRequest, "2.1.4")
-
-        self.assertRaises(exceptions.InvalidAPIVersionString,
-                          api_version_request.APIVersionRequest, "200.23.66.3")
-
-        self.assertRaises(exceptions.InvalidAPIVersionString,
-                          api_version_request.APIVersionRequest, "5 .3")
-
-        self.assertRaises(exceptions.InvalidAPIVersionString,
-                          api_version_request.APIVersionRequest, "5. 3")
-
-        self.assertRaises(exceptions.InvalidAPIVersionString,
-                          api_version_request.APIVersionRequest, "5.03")
-
-        self.assertRaises(exceptions.InvalidAPIVersionString,
-                          api_version_request.APIVersionRequest, "02.1")
-
-        self.assertRaises(exceptions.InvalidAPIVersionString,
-                          api_version_request.APIVersionRequest, "2.001")
-
-        self.assertRaises(exceptions.InvalidAPIVersionString,
-                          api_version_request.APIVersionRequest, "")
-
-        self.assertRaises(exceptions.InvalidAPIVersionString,
-                          api_version_request.APIVersionRequest, " 2.1")
-
-        self.assertRaises(exceptions.InvalidAPIVersionString,
-                          api_version_request.APIVersionRequest, "2.1 ")
-
-    def test_version_comparisons(self):
-        vers2_0 = api_version_request.APIVersionRequest("2.0")
-        vers2_5 = api_version_request.APIVersionRequest("2.5")
-        vers5_23 = api_version_request.APIVersionRequest("5.23")
-        v_null = api_version_request.APIVersionRequest()
-        v_latest = api_version_request.APIVersionRequest('latest')
-
-        self.assertTrue(v_null < vers2_5)
-        self.assertTrue(vers2_0 < vers2_5)
-        self.assertTrue(vers2_0 <= vers2_5)
-        self.assertTrue(vers2_0 <= vers2_0)
-        self.assertTrue(vers2_5 > v_null)
-        self.assertTrue(vers5_23 > vers2_5)
-        self.assertTrue(vers2_0 >= vers2_0)
-        self.assertTrue(vers5_23 >= vers2_5)
-        self.assertTrue(vers2_0 != vers2_5)
-        self.assertTrue(vers2_0 == vers2_0)
-        self.assertTrue(vers2_0 != v_null)
-        self.assertTrue(v_null == v_null)
-        self.assertTrue(vers2_0 <= v_latest)
-        self.assertTrue(vers2_0 != v_latest)
-        self.assertTrue(v_latest == v_latest)
-        self.assertRaises(TypeError, vers2_0.__lt__, "2.1")
-
-    def test_version_matches(self):
-        vers2_0 = api_version_request.APIVersionRequest("2.0")
-        vers2_5 = api_version_request.APIVersionRequest("2.5")
-        vers2_45 = api_version_request.APIVersionRequest("2.45")
-        vers3_3 = api_version_request.APIVersionRequest("3.3")
-        vers3_23 = api_version_request.APIVersionRequest("3.23")
-        vers4_0 = api_version_request.APIVersionRequest("4.0")
-        v_null = api_version_request.APIVersionRequest()
-        v_latest = api_version_request.APIVersionRequest('latest')
-
-        def _check_version_matches(version, version1, version2, check=True):
-            if check:
-                msg = "Version %s does not matches with [%s - %s] range"
-                self.assertTrue(version.matches(version1, version2),
-                                msg % (version.get_string(),
-                                       version1.get_string(),
-                                       version2.get_string()))
-            else:
-                msg = "Version %s matches with [%s - %s] range"
-                self.assertFalse(version.matches(version1, version2),
-                                 msg % (version.get_string(),
-                                        version1.get_string(),
-                                        version2.get_string()))
-
-        _check_version_matches(vers2_5, vers2_0, vers2_45)
-        _check_version_matches(vers2_5, vers2_0, v_null)
-        _check_version_matches(vers2_0, vers2_0, vers2_5)
-        _check_version_matches(vers3_3, vers2_5, vers3_3)
-        _check_version_matches(vers3_3, v_null, vers3_3)
-        _check_version_matches(vers3_3, v_null, vers4_0)
-        _check_version_matches(vers2_0, vers2_5, vers2_45, False)
-        _check_version_matches(vers3_23, vers2_5, vers3_3, False)
-        _check_version_matches(vers2_5, vers2_45, vers2_0, False)
-        _check_version_matches(vers2_5, vers2_0, v_latest)
-        _check_version_matches(v_latest, v_latest, v_latest)
-        _check_version_matches(vers2_5, v_latest, v_latest, False)
-        _check_version_matches(v_latest, vers2_0, vers4_0, False)
-
-        self.assertRaises(ValueError, v_null.matches, vers2_0, vers2_45)
-
-    def test_get_string(self):
-        vers_string = ["3.23", "latest"]
-        for ver in vers_string:
-            ver_obj = api_version_request.APIVersionRequest(ver)
-            self.assertEqual(ver, ver_obj.get_string())
-
-        self.assertIsNotNone(
-            api_version_request.APIVersionRequest().get_string)
diff --git a/tempest/tests/common/test_api_version_utils.py b/tempest/tests/common/test_api_version_utils.py
deleted file mode 100644
index 501f954..0000000
--- a/tempest/tests/common/test_api_version_utils.py
+++ /dev/null
@@ -1,114 +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 testtools
-
-from tempest.common import api_version_utils
-from tempest import exceptions
-from tempest.tests import base
-
-
-class TestVersionSkipLogic(base.TestCase):
-
-    def _test_version(self, test_min_version, test_max_version,
-                      cfg_min_version, cfg_max_version, expected_skip=False):
-        try:
-            api_version_utils.check_skip_with_microversion(test_min_version,
-                                                           test_max_version,
-                                                           cfg_min_version,
-                                                           cfg_max_version)
-        except testtools.TestCase.skipException as e:
-            if not expected_skip:
-                raise testtools.TestCase.failureException(e.message)
-
-    def test_version_min_in_range(self):
-        self._test_version('2.2', '2.10', '2.1', '2.7')
-
-    def test_version_max_in_range(self):
-        self._test_version('2.1', '2.3', '2.2', '2.7')
-
-    def test_version_cfg_in_range(self):
-        self._test_version('2.2', '2.9', '2.3', '2.7')
-
-    def test_version_equal(self):
-        self._test_version('2.2', '2.2', '2.2', '2.2')
-
-    def test_version_below_cfg_min(self):
-        self._test_version('2.2', '2.4', '2.5', '2.7', expected_skip=True)
-
-    def test_version_above_cfg_max(self):
-        self._test_version('2.8', '2.9', '2.3', '2.7', expected_skip=True)
-
-    def test_version_min_greater_than_max(self):
-        self.assertRaises(exceptions.InvalidAPIVersionRange,
-                          self._test_version, '2.8', '2.7', '2.3', '2.7')
-
-    def test_cfg_version_min_greater_than_max(self):
-        self.assertRaises(exceptions.InvalidAPIVersionRange,
-                          self._test_version, '2.2', '2.7', '2.9', '2.7')
-
-
-class TestSelectRequestMicroversion(base.TestCase):
-
-    def _test_request_version(self, test_min_version,
-                              cfg_min_version, expected_version):
-        selected_version = api_version_utils.select_request_microversion(
-            test_min_version, cfg_min_version)
-        self.assertEqual(expected_version, selected_version)
-
-    def test_cfg_min_version_greater(self):
-        self._test_request_version('2.1', '2.3', expected_version='2.3')
-
-    def test_class_min_version_greater(self):
-        self._test_request_version('2.5', '2.3', expected_version='2.5')
-
-    def test_cfg_min_version_none(self):
-        self._test_request_version('2.5', None, expected_version='2.5')
-
-    def test_class_min_version_none(self):
-        self._test_request_version(None, '2.3', expected_version='2.3')
-
-    def test_both_min_version_none(self):
-        self._test_request_version(None, None, expected_version=None)
-
-    def test_both_min_version_equal(self):
-        self._test_request_version('2.3', '2.3', expected_version='2.3')
-
-
-class TestMicroversionHeaderMatches(base.TestCase):
-
-    def test_header_matches(self):
-        microversion_header_name = 'x-openstack-xyz-api-version'
-        request_microversion = '2.1'
-        test_respose = {microversion_header_name: request_microversion}
-        api_version_utils.assert_version_header_matches_request(
-            microversion_header_name, request_microversion, test_respose)
-
-    def test_header_does_not_match(self):
-        microversion_header_name = 'x-openstack-xyz-api-version'
-        request_microversion = '2.1'
-        test_respose = {microversion_header_name: '2.2'}
-        self.assertRaises(
-            exceptions.InvalidHTTPResponseHeader,
-            api_version_utils.assert_version_header_matches_request,
-            microversion_header_name, request_microversion, test_respose)
-
-    def test_header_not_present(self):
-        microversion_header_name = 'x-openstack-xyz-api-version'
-        request_microversion = '2.1'
-        test_respose = {}
-        self.assertRaises(
-            exceptions.InvalidHTTPResponseHeader,
-            api_version_utils.assert_version_header_matches_request,
-            microversion_header_name, request_microversion, test_respose)
diff --git a/tempest/tests/common/test_configured_creds.py b/tempest/tests/common/test_configured_creds.py
deleted file mode 100644
index 96b75fd..0000000
--- a/tempest/tests/common/test_configured_creds.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# Copyright 2015 Hewlett-Packard 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.
-
-from oslo_config import cfg
-from tempest_lib import auth
-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.common import credentials_factory as common_creds
-from tempest.common import tempest_fixtures as fixtures
-from tempest import config
-from tempest.tests import base
-from tempest.tests import fake_config
-from tempest.tests import fake_identity
-
-
-class ConfiguredV2CredentialsTests(base.TestCase):
-    attributes = {
-        'username': 'fake_username',
-        'password': 'fake_password',
-        'tenant_name': 'fake_tenant_name'
-    }
-
-    identity_response = fake_identity._fake_v2_response
-    credentials_class = auth.KeystoneV2Credentials
-    tokenclient_class = v2_client.TokenClient
-    identity_version = 'v2'
-
-    def setUp(self):
-        super(ConfiguredV2CredentialsTests, self).setUp()
-        self.useFixture(fake_config.ConfigFixture())
-        self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
-        self.stubs.Set(self.tokenclient_class, 'raw_request',
-                       self.identity_response)
-
-    def _get_credentials(self, attributes=None):
-        if attributes is None:
-            attributes = self.attributes
-        return self.credentials_class(**attributes)
-
-    def _check(self, credentials, credentials_class, filled):
-        # Check the right version of credentials has been returned
-        self.assertIsInstance(credentials, credentials_class)
-        # Check the id attributes are filled in
-        attributes = [x for x in credentials.ATTRIBUTES if (
-            '_id' in x and x != 'domain_id')]
-        for attr in attributes:
-            if filled:
-                self.assertIsNotNone(getattr(credentials, attr))
-            else:
-                self.assertIsNone(getattr(credentials, attr))
-
-    def _verify_credentials(self, credentials_class, filled=True,
-                            identity_version=None):
-        for ctype in common_creds.CREDENTIAL_TYPES:
-            if identity_version is None:
-                creds = common_creds.get_configured_credentials(
-                    credential_type=ctype, fill_in=filled)
-            else:
-                creds = common_creds.get_configured_credentials(
-                    credential_type=ctype, fill_in=filled,
-                    identity_version=identity_version)
-            self._check(creds, credentials_class, filled)
-
-    def test_create(self):
-        creds = self._get_credentials()
-        self.assertEqual(self.attributes, creds._initial)
-
-    def test_create_invalid_attr(self):
-        self.assertRaises(lib_exc.InvalidCredentials,
-                          self._get_credentials,
-                          attributes=dict(invalid='fake'))
-
-    def test_get_configured_credentials(self):
-        self.useFixture(fixtures.LockFixture('auth_version'))
-        self._verify_credentials(credentials_class=self.credentials_class)
-
-    def test_get_configured_credentials_unfilled(self):
-        self.useFixture(fixtures.LockFixture('auth_version'))
-        self._verify_credentials(credentials_class=self.credentials_class,
-                                 filled=False)
-
-    def test_get_configured_credentials_version(self):
-        # version specified and not loaded from config
-        self.useFixture(fixtures.LockFixture('auth_version'))
-        self._verify_credentials(credentials_class=self.credentials_class,
-                                 identity_version=self.identity_version)
-
-    def test_is_valid(self):
-        creds = self._get_credentials()
-        self.assertTrue(creds.is_valid())
-
-
-class ConfiguredV3CredentialsTests(ConfiguredV2CredentialsTests):
-    attributes = {
-        'username': 'fake_username',
-        'password': 'fake_password',
-        'project_name': 'fake_project_name',
-        'user_domain_name': 'fake_domain_name'
-    }
-
-    credentials_class = auth.KeystoneV3Credentials
-    identity_response = fake_identity._fake_v3_response
-    tokenclient_class = v3_client.V3TokenClient
-    identity_version = 'v3'
-
-    def setUp(self):
-        super(ConfiguredV3CredentialsTests, self).setUp()
-        # Additional config items reset by cfg fixture after each test
-        cfg.CONF.set_default('auth_version', 'v3', group='identity')
-        # Identity group items
-        for prefix in ['', 'alt_', 'admin_']:
-            if prefix == 'admin_':
-                group = 'auth'
-            else:
-                group = 'identity'
-            cfg.CONF.set_default(prefix + 'domain_name', 'fake_domain_name',
-                                 group=group)
diff --git a/tempest/tests/common/test_credentials.py b/tempest/tests/common/test_credentials.py
deleted file mode 100644
index 136ac02..0000000
--- a/tempest/tests/common/test_credentials.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright 2015 Hewlett-Packard 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.
-
-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
-
-
-class TestLegacyCredentialsProvider(base.TestCase):
-
-    fixed_params = {'identity_version': 'v2'}
-
-    def setUp(self):
-        super(TestLegacyCredentialsProvider, self).setUp()
-        self.useFixture(fake_config.ConfigFixture())
-        self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
-
-    def test_get_creds_roles_legacy_invalid(self):
-        test_accounts_class = credentials.LegacyCredentialProvider(
-            **self.fixed_params)
-        self.assertRaises(exceptions.InvalidConfiguration,
-                          test_accounts_class.get_creds_by_roles,
-                          ['fake_role'])
diff --git a/tempest/tests/common/test_custom_matchers.py b/tempest/tests/common/test_custom_matchers.py
index 2656a47..1053d86 100644
--- a/tempest/tests/common/test_custom_matchers.py
+++ b/tempest/tests/common/test_custom_matchers.py
@@ -16,11 +16,47 @@
 from tempest.common import custom_matchers
 from tempest.tests import base
 
-from testtools.tests.matchers import helpers
+
+# Stolen from testtools/testtools/tests/matchers/helpers.py
+class TestMatchersInterface(object):
+
+    def test_matches_match(self):
+        matcher = self.matches_matcher
+        matches = self.matches_matches
+        mismatches = self.matches_mismatches
+        for candidate in matches:
+            self.assertIsNone(matcher.match(candidate))
+        for candidate in mismatches:
+            mismatch = matcher.match(candidate)
+            self.assertIsNotNone(mismatch)
+            self.assertIsNotNone(getattr(mismatch, 'describe', None))
+
+    def test__str__(self):
+        # [(expected, object to __str__)].
+        from testtools.matchers._doctest import DocTestMatches
+        examples = self.str_examples
+        for expected, matcher in examples:
+            self.assertThat(matcher, DocTestMatches(expected))
+
+    def test_describe_difference(self):
+        # [(expected, matchee, matcher), ...]
+        examples = self.describe_examples
+        for difference, matchee, matcher in examples:
+            mismatch = matcher.match(matchee)
+            self.assertEqual(difference, mismatch.describe())
+
+    def test_mismatch_details(self):
+        # The mismatch object must provide get_details, which must return a
+        # dictionary mapping names to Content objects.
+        examples = self.describe_examples
+        for difference, matchee, matcher in examples:
+            mismatch = matcher.match(matchee)
+            details = mismatch.get_details()
+            self.assertEqual(dict(details), details)
 
 
 class TestMatchesDictExceptForKeys(base.TestCase,
-                                   helpers.TestMatchersInterface):
+                                   TestMatchersInterface):
 
     matches_matcher = custom_matchers.MatchesDictExceptForKeys(
         {'a': 1, 'b': 2, 'c': 3, 'd': 4}, ['c', 'd'])
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index 7a21d96..b4fbd50 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -15,26 +15,31 @@
 import mock
 from oslo_config import cfg
 from oslotest import mockpatch
-from tempest_lib.services.identity.v2 import token_client as json_token_client
 
 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.services.identity.v2.json import identity_client as \
-    json_iden_client
-from tempest.services.identity.v2.json import roles_client as \
-    json_roles_client
-from tempest.services.identity.v2.json import tenants_client as \
-    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.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.identity.v2 import identity_client as v2_iden_client
+from tempest.lib.services.identity.v2 import roles_client as v2_roles_client
+from tempest.lib.services.identity.v2 import tenants_client as \
+    v2_tenants_client
+from tempest.lib.services.identity.v2 import token_client as v2_token_client
+from tempest.lib.services.identity.v2 import users_client as v2_users_client
+from tempest.lib.services.identity.v3 import domains_client
+from tempest.lib.services.identity.v3 import identity_client as v3_iden_client
+from tempest.lib.services.identity.v3 import projects_client as \
+    v3_projects_client
+from tempest.lib.services.identity.v3 import roles_client as v3_roles_client
+from tempest.lib.services.identity.v3 import token_client as v3_token_client
+from tempest.lib.services.identity.v3 import users_client as \
+    v3_users_client
+from tempest.lib.services.network import routers_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 fake_http
+from tempest.tests.lib import fake_identity
 
 
 class TestDynamicCredentialProvider(base.TestCase):
@@ -43,13 +48,23 @@
                     'identity_version': 'v2',
                     'admin_role': 'admin'}
 
+    token_client = v2_token_client
+    iden_client = v2_iden_client
+    roles_client = v2_roles_client
+    tenants_client = v2_tenants_client
+    users_client = v2_users_client
+    token_client_class = token_client.TokenClient
+    fake_response = fake_identity._fake_v2_response
+    tenants_client_class = tenants_client.TenantsClient
+    delete_tenant = 'delete_tenant'
+
     def setUp(self):
         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)
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
+        self.patchobject(self.token_client_class, 'raw_request',
+                         self.fake_response)
         cfg.CONF.set_default('operator_role', 'FakeRole',
                              group='object-storage')
         self._mock_list_ec2_credentials('fake_user_id', 'fake_tenant_id')
@@ -58,10 +73,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,
+                              self.iden_client.IdentityClient)
 
     def _get_fake_admin_creds(self):
         return credentials.get_credentials(
@@ -72,25 +85,25 @@
 
     def _mock_user_create(self, id, name):
         user_fix = self.useFixture(mockpatch.PatchObject(
-            json_users_client.UsersClient,
+            self.users_client.UsersClient,
             'create_user',
-            return_value=(service_client.ResponseBody
+            return_value=(rest_client.ResponseBody
                           (200, {'user': {'id': id, 'name': name}}))))
         return user_fix
 
     def _mock_tenant_create(self, id, name):
         tenant_fix = self.useFixture(mockpatch.PatchObject(
-            json_tenants_client.TenantsClient,
+            self.tenants_client.TenantsClient,
             'create_tenant',
-            return_value=(service_client.ResponseBody
+            return_value=(rest_client.ResponseBody
                           (200, {'tenant': {'id': id, 'name': name}}))))
         return tenant_fix
 
     def _mock_list_roles(self, id, name):
         roles_fix = self.useFixture(mockpatch.PatchObject(
-            json_roles_client.RolesClient,
+            self.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'},
@@ -99,9 +112,9 @@
 
     def _mock_list_2_roles(self):
         roles_fix = self.useFixture(mockpatch.PatchObject(
-            json_roles_client.RolesClient,
+            self.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'},
@@ -110,26 +123,27 @@
 
     def _mock_assign_user_role(self):
         tenant_fix = self.useFixture(mockpatch.PatchObject(
-            json_roles_client.RolesClient,
-            'assign_user_role',
-            return_value=(service_client.ResponseBody
+            self.roles_client.RolesClient,
+            'create_user_role_on_project',
+            return_value=(rest_client.ResponseBody
                           (200, {}))))
         return tenant_fix
 
     def _mock_list_role(self):
         roles_fix = self.useFixture(mockpatch.PatchObject(
-            json_roles_client.RolesClient,
+            self.roles_client.RolesClient,
             'list_roles',
-            return_value=(service_client.ResponseBody
-                          (200, {'roles': [{'id': '1',
-                                 'name': 'FakeRole'}]}))))
+            return_value=(rest_client.ResponseBody
+                          (200, {'roles': [
+                              {'id': '1', 'name': 'FakeRole'},
+                              {'id': '2', 'name': 'Member'}]}))))
         return roles_fix
 
     def _mock_list_ec2_credentials(self, user_id, tenant_id):
         ec2_creds_fix = self.useFixture(mockpatch.PatchObject(
-            json_users_client.UsersClient,
+            self.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,14 +168,13 @@
 
     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
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_primary_creds(self, MockRestClient):
-        cfg.CONF.set_default('neutron', False, 'service_available')
         creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
         self._mock_assign_user_role()
         self._mock_list_role()
@@ -174,20 +187,19 @@
         self.assertEqual(primary_creds.tenant_id, '1234')
         self.assertEqual(primary_creds.user_id, '1234')
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_admin_creds(self, MockRestClient):
-        cfg.CONF.set_default('neutron', False, 'service_available')
         creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
         self._mock_list_roles('1234', 'admin')
         self._mock_user_create('1234', 'fake_admin_user')
         self._mock_tenant_create('1234', 'fake_admin_tenant')
 
-        user_mock = mock.patch.object(json_roles_client.RolesClient,
-                                      'assign_user_role')
+        user_mock = mock.patch.object(self.roles_client.RolesClient,
+                                      'create_user_role_on_project')
         user_mock.start()
         self.addCleanup(user_mock.stop)
-        with mock.patch.object(json_roles_client.RolesClient,
-                               'assign_user_role') as user_mock:
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_project') as user_mock:
             admin_creds = creds.get_admin_creds()
         user_mock.assert_has_calls([
             mock.call('1234', '1234', '1234')])
@@ -197,20 +209,19 @@
         self.assertEqual(admin_creds.tenant_id, '1234')
         self.assertEqual(admin_creds.user_id, '1234')
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_role_creds(self, MockRestClient):
-        cfg.CONF.set_default('neutron', False, 'service_available')
         creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
         self._mock_list_2_roles()
         self._mock_user_create('1234', 'fake_role_user')
         self._mock_tenant_create('1234', 'fake_role_tenant')
 
-        user_mock = mock.patch.object(json_roles_client.RolesClient,
-                                      'assign_user_role')
+        user_mock = mock.patch.object(self.roles_client.RolesClient,
+                                      'create_user_role_on_project')
         user_mock.start()
         self.addCleanup(user_mock.stop)
-        with mock.patch.object(json_roles_client.RolesClient,
-                               'assign_user_role') as user_mock:
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_project') as user_mock:
             role_creds = creds.get_creds_by_roles(
                 roles=['role1', 'role2'])
         calls = user_mock.mock_calls
@@ -226,9 +237,8 @@
         self.assertEqual(role_creds.tenant_id, '1234')
         self.assertEqual(role_creds.user_id, '1234')
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_all_cred_cleanup(self, MockRestClient):
-        cfg.CONF.set_default('neutron', False, 'service_available')
         creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
         self._mock_assign_user_role()
         self._mock_list_role()
@@ -242,12 +252,10 @@
         self._mock_user_create('123456', 'fake_admin_user')
         self._mock_list_roles('123456', 'admin')
         creds.get_admin_creds()
-        user_mock = self.patch(
-            'tempest.services.identity.v2.json.users_client.'
-            'UsersClient.delete_user')
-        tenant_mock = self.patch(
-            'tempest.services.identity.v2.json.tenants_client.'
-            'TenantsClient.delete_tenant')
+        user_mock = self.patchobject(self.users_client.UsersClient,
+                                     'delete_user')
+        tenant_mock = self.patchobject(self.tenants_client_class,
+                                       self.delete_tenant)
         creds.clear_creds()
         # Verify user delete calls
         calls = user_mock.mock_calls
@@ -266,9 +274,8 @@
         self.assertIn('12345', args)
         self.assertIn('123456', args)
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_alt_creds(self, MockRestClient):
-        cfg.CONF.set_default('neutron', False, 'service_available')
         creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
         self._mock_assign_user_role()
         self._mock_list_role()
@@ -281,10 +288,12 @@
         self.assertEqual(alt_creds.tenant_id, '1234')
         self.assertEqual(alt_creds.user_id, '1234')
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_no_network_creation_with_config_set(self, MockRestClient):
-        cfg.CONF.set_default('create_isolated_networks', False, group='auth')
-        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        creds = dynamic_creds.DynamicCredentialProvider(
+            neutron_available=True, create_networks=False,
+            project_network_cidr='10.100.0.0/16', project_network_mask_bits=28,
+            **self.fixed_params)
         self._mock_assign_user_role()
         self._mock_list_role()
         self._mock_user_create('1234', 'fake_prim_user')
@@ -295,7 +304,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()
 
@@ -310,9 +319,12 @@
         self.assertIsNone(subnet)
         self.assertIsNone(router)
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_network_creation(self, MockRestClient):
-        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        creds = dynamic_creds.DynamicCredentialProvider(
+            neutron_available=True,
+            project_network_cidr='10.100.0.0/16', project_network_mask_bits=28,
+            **self.fixed_params)
         self._mock_assign_user_role()
         self._mock_list_role()
         self._mock_user_create('1234', 'fake_prim_user')
@@ -321,7 +333,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.lib.services.network.routers_client.RoutersClient.'
             'add_router_interface')
         primary_creds = creds.get_primary_creds()
         router_interface_mock.assert_called_once_with('1234', subnet_id='1234')
@@ -335,7 +347,7 @@
         self.assertEqual(router['id'], '1234')
         self.assertEqual(router['name'], 'fake_router')
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_network_cleanup(self, MockRestClient):
         def side_effect(**args):
             return {"security_groups": [{"tenant_id": args['tenant_id'],
@@ -343,7 +355,10 @@
                                          "description": args['name'],
                                          "security_group_rules": [],
                                          "id": "sg-%s" % args['tenant_id']}]}
-        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        creds = dynamic_creds.DynamicCredentialProvider(
+            neutron_available=True,
+            project_network_cidr='10.100.0.0/16', project_network_mask_bits=28,
+            **self.fixed_params)
         # Create primary tenant and network
         self._mock_assign_user_role()
         self._mock_list_role()
@@ -353,7 +368,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.lib.services.network.routers_client.RoutersClient.'
             'add_router_interface')
         creds.get_primary_creds()
         router_interface_mock.assert_called_once_with('1234', subnet_id='1234')
@@ -376,21 +391,16 @@
         self._mock_router_create('123456', 'fake_admin_router')
         self._mock_list_roles('123456', 'admin')
         creds.get_admin_creds()
-        self.patch('tempest.services.identity.v2.json.users_client.'
-                   'UsersClient.delete_user')
-        self.patch('tempest.services.identity.v2.json.tenants_client.'
-                   'TenantsClient.delete_tenant')
-        net = mock.patch.object(creds.networks_admin_client,
-                                'delete_network')
+        self.patchobject(self.users_client.UsersClient, 'delete_user')
+        self.patchobject(self.tenants_client_class, self.delete_tenant)
+        net = mock.patch.object(creds.networks_admin_client, 'delete_network')
         net_mock = net.start()
-        subnet = mock.patch.object(creds.subnets_admin_client,
-                                   'delete_subnet')
+        subnet = mock.patch.object(creds.subnets_admin_client, 'delete_subnet')
         subnet_mock = subnet.start()
-        router = mock.patch.object(creds.network_admin_client,
-                                   'delete_router')
+        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.lib.services.network.routers_client.RoutersClient.'
             'remove_router_interface')
         return_values = ({'status': 200}, {'ports': []})
         port_list_mock = mock.patch.object(creds.ports_admin_client,
@@ -404,9 +414,9 @@
             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.'
+            'tempest.lib.services.network.security_groups_client.'
             'SecurityGroupsClient.delete', return_value=return_values)
         creds.clear_creds()
         # Verify default security group delete
@@ -450,9 +460,12 @@
         self.assertIn('12345', args)
         self.assertIn('123456', args)
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_network_alt_creation(self, MockRestClient):
-        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        creds = dynamic_creds.DynamicCredentialProvider(
+            neutron_available=True,
+            project_network_cidr='10.100.0.0/16', project_network_mask_bits=28,
+            **self.fixed_params)
         self._mock_assign_user_role()
         self._mock_list_role()
         self._mock_user_create('1234', 'fake_alt_user')
@@ -461,7 +474,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.lib.services.network.routers_client.RoutersClient.'
             'add_router_interface')
         alt_creds = creds.get_alt_creds()
         router_interface_mock.assert_called_once_with('1234', subnet_id='1234')
@@ -475,9 +488,12 @@
         self.assertEqual(router['id'], '1234')
         self.assertEqual(router['name'], 'fake_alt_router')
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_network_admin_creation(self, MockRestClient):
-        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        creds = dynamic_creds.DynamicCredentialProvider(
+            neutron_available=True,
+            project_network_cidr='10.100.0.0/16', project_network_mask_bits=28,
+            **self.fixed_params)
         self._mock_assign_user_role()
         self._mock_user_create('1234', 'fake_admin_user')
         self._mock_tenant_create('1234', 'fake_admin_tenant')
@@ -485,7 +501,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.lib.services.network.routers_client.RoutersClient.'
             'add_router_interface')
         self._mock_list_roles('123456', 'admin')
         admin_creds = creds.get_admin_creds()
@@ -500,7 +516,7 @@
         self.assertEqual(router['id'], '1234')
         self.assertEqual(router['name'], 'fake_admin_router')
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_no_network_resources(self, MockRestClient):
         net_dict = {
             'network': False,
@@ -509,6 +525,8 @@
             'dhcp': False,
         }
         creds = dynamic_creds.DynamicCredentialProvider(
+            neutron_available=True,
+            project_network_cidr='10.100.0.0/16', project_network_mask_bits=28,
             network_resources=net_dict,
             **self.fixed_params)
         self._mock_assign_user_role()
@@ -521,7 +539,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()
 
@@ -536,7 +554,7 @@
         self.assertIsNone(subnet)
         self.assertIsNone(router)
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_router_without_network(self, MockRestClient):
         net_dict = {
             'network': False,
@@ -545,16 +563,18 @@
             'dhcp': False,
         }
         creds = dynamic_creds.DynamicCredentialProvider(
+            neutron_available=True,
+            project_network_cidr='10.100.0.0/16', project_network_mask_bits=28,
             network_resources=net_dict,
             **self.fixed_params)
         self._mock_assign_user_role()
         self._mock_list_role()
         self._mock_user_create('1234', 'fake_prim_user')
         self._mock_tenant_create('1234', 'fake_prim_tenant')
-        self.assertRaises(exceptions.InvalidConfiguration,
+        self.assertRaises(lib_exc.InvalidConfiguration,
                           creds.get_primary_creds)
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_subnet_without_network(self, MockRestClient):
         net_dict = {
             'network': False,
@@ -563,16 +583,18 @@
             'dhcp': False,
         }
         creds = dynamic_creds.DynamicCredentialProvider(
+            neutron_available=True,
+            project_network_cidr='10.100.0.0/16', project_network_mask_bits=28,
             network_resources=net_dict,
             **self.fixed_params)
         self._mock_assign_user_role()
         self._mock_list_role()
         self._mock_user_create('1234', 'fake_prim_user')
         self._mock_tenant_create('1234', 'fake_prim_tenant')
-        self.assertRaises(exceptions.InvalidConfiguration,
+        self.assertRaises(lib_exc.InvalidConfiguration,
                           creds.get_primary_creds)
 
-    @mock.patch('tempest_lib.common.rest_client.RestClient')
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_dhcp_without_subnet(self, MockRestClient):
         net_dict = {
             'network': False,
@@ -581,11 +603,63 @@
             'dhcp': True,
         }
         creds = dynamic_creds.DynamicCredentialProvider(
+            neutron_available=True,
+            project_network_cidr='10.100.0.0/16', project_network_mask_bits=28,
             network_resources=net_dict,
             **self.fixed_params)
         self._mock_assign_user_role()
         self._mock_list_role()
         self._mock_user_create('1234', 'fake_prim_user')
         self._mock_tenant_create('1234', 'fake_prim_tenant')
-        self.assertRaises(exceptions.InvalidConfiguration,
+        self.assertRaises(lib_exc.InvalidConfiguration,
                           creds.get_primary_creds)
+
+
+class TestDynamicCredentialProviderV3(TestDynamicCredentialProvider):
+
+    fixed_params = {'name': 'test class',
+                    'identity_version': 'v3',
+                    'admin_role': 'admin'}
+
+    token_client = v3_token_client
+    iden_client = v3_iden_client
+    roles_client = v3_roles_client
+    tenants_client = v3_projects_client
+    users_client = v3_users_client
+    token_client_class = token_client.V3TokenClient
+    fake_response = fake_identity._fake_v3_response
+    tenants_client_class = tenants_client.ProjectsClient
+    delete_tenant = 'delete_project'
+
+    def setUp(self):
+        super(TestDynamicCredentialProviderV3, self).setUp()
+        self.useFixture(fake_config.ConfigFixture())
+        self.useFixture(mockpatch.PatchObject(
+            domains_client.DomainsClient, 'list_domains',
+            return_value=dict(domains=[dict(id='default',
+                                            name='Default')])))
+        self.patchobject(self.roles_client.RolesClient,
+                         'create_user_role_on_domain')
+
+    def _mock_list_ec2_credentials(self, user_id, tenant_id):
+        pass
+
+    def _mock_tenant_create(self, id, name):
+        project_fix = self.useFixture(mockpatch.PatchObject(
+            self.tenants_client.ProjectsClient,
+            'create_project',
+            return_value=(rest_client.ResponseBody
+                          (200, {'project': {'id': id, 'name': name}}))))
+        return project_fix
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_member_role_creation_with_duplicate(self, rest_client_mock):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        creds.creds_client = mock.MagicMock()
+        creds.creds_client.create_user_role.side_effect = lib_exc.Conflict
+        with mock.patch('tempest.common.dynamic_creds.LOG') as log_mock:
+            creds._create_creds()
+            log_mock.warning.assert_called_once_with(
+                "Member role already exists, ignoring conflict.")
+        creds.creds_client.assign_user_role.assert_called_once_with(
+            mock.ANY, mock.ANY, 'Member')
diff --git a/tempest/tests/common/test_image.py b/tempest/tests/common/test_image.py
new file mode 100644
index 0000000..240df4d
--- /dev/null
+++ b/tempest/tests/common/test_image.py
@@ -0,0 +1,61 @@
+# 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.common import image
+from tempest.lib.common import rest_client
+from tempest.tests import base
+
+
+class TestImage(base.TestCase):
+
+    def test_get_image_meta_from_headers(self):
+        resp = {
+            'x-image-meta-id': 'ea30c926-0629-4400-bb6e-f8a8da6a4e56',
+            'x-image-meta-owner': '8f421f9470e645b1b10f5d2db7804924',
+            'x-image-meta-status': 'queued',
+            'x-image-meta-name': 'New Http Image'
+        }
+        respbody = rest_client.ResponseBody(resp)
+        observed = image.get_image_meta_from_headers(respbody)
+
+        expected = {
+            'properties': {},
+            'id': 'ea30c926-0629-4400-bb6e-f8a8da6a4e56',
+            'owner': '8f421f9470e645b1b10f5d2db7804924',
+            'status': 'queued',
+            'name': 'New Http Image'
+        }
+        self.assertEqual(expected, observed)
+
+    def test_image_meta_to_headers(self):
+        observed = image.image_meta_to_headers(
+            name='test',
+            container_format='wrong',
+            disk_format='vhd',
+            copy_from='http://localhost/images/10',
+            properties={'foo': 'bar'},
+            api={'abc': 'def'},
+            purge_props=True)
+
+        expected = {
+            'x-image-meta-name': 'test',
+            'x-image-meta-container_format': 'wrong',
+            'x-image-meta-disk_format': 'vhd',
+            'x-glance-api-copy-from': 'http://localhost/images/10',
+            'x-image-meta-property-foo': 'bar',
+            'x-glance-api-property-abc': 'def',
+            'x-glance-registry-purge-props': True
+        }
+        self.assertEqual(expected, observed)
diff --git a/tempest/tests/common/test_preprov_creds.py b/tempest/tests/common/test_preprov_creds.py
index fd7df16..1c9982c 100644
--- a/tempest/tests/common/test_preprov_creds.py
+++ b/tempest/tests/common/test_preprov_creds.py
@@ -14,6 +14,7 @@
 
 import hashlib
 import os
+import testtools
 
 import mock
 from oslo_concurrency.fixture import lockutils as lockutils_fixtures
@@ -21,17 +22,15 @@
 from oslotest import mockpatch
 import shutil
 import six
-from tempest_lib import auth
-from tempest_lib import exceptions as lib_exc
-from tempest_lib.services.identity.v2 import token_client
 
-from tempest.common import cred_provider
 from tempest.common import preprov_creds
 from tempest import config
+from tempest.lib import auth
+from tempest.lib.common import cred_provider
+from tempest.lib import exceptions as lib_exc
 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 fake_identity
 
 
 class TestPreProvisionedCredentials(base.TestCase):
@@ -44,40 +43,48 @@
                     'object_storage_operator_role': 'operator',
                     'object_storage_reseller_admin_role': 'reseller'}
 
-    def setUp(self):
-        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())
-        self.test_accounts = [
+    identity_response = fake_identity._fake_v2_response
+    token_client = ('tempest.lib.services.identity.v2.token_client'
+                    '.TokenClient.raw_request')
+
+    @classmethod
+    def _fake_accounts(cls, admin_role):
+        return [
             {'username': 'test_user1', 'tenant_name': 'test_tenant1',
              'password': 'p'},
-            {'username': 'test_user2', 'tenant_name': 'test_tenant2',
+            {'username': 'test_user2', 'project_name': 'test_tenant2',
              'password': 'p'},
             {'username': 'test_user3', 'tenant_name': 'test_tenant3',
              'password': 'p'},
-            {'username': 'test_user4', 'tenant_name': 'test_tenant4',
+            {'username': 'test_user4', 'project_name': 'test_tenant4',
              'password': 'p'},
             {'username': 'test_user5', 'tenant_name': 'test_tenant5',
              'password': 'p'},
-            {'username': 'test_user6', 'tenant_name': 'test_tenant6',
+            {'username': 'test_user6', 'project_name': 'test_tenant6',
              'password': 'p', 'roles': ['role1', 'role2']},
             {'username': 'test_user7', 'tenant_name': 'test_tenant7',
              'password': 'p', 'roles': ['role2', 'role3']},
-            {'username': 'test_user8', 'tenant_name': 'test_tenant8',
+            {'username': 'test_user8', 'project_name': 'test_tenant8',
              'password': 'p', 'roles': ['role4', 'role1']},
             {'username': 'test_user9', 'tenant_name': 'test_tenant9',
              'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
-            {'username': 'test_user10', 'tenant_name': 'test_tenant10',
+            {'username': 'test_user10', 'project_name': 'test_tenant10',
              'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
-            {'username': 'test_user11', 'tenant_name': 'test_tenant11',
-             'password': 'p', 'roles': [cfg.CONF.identity.admin_role]},
-            {'username': 'test_user12', 'tenant_name': 'test_tenant12',
-             'password': 'p', 'roles': [cfg.CONF.identity.admin_role]},
-        ]
+            {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
+             'password': 'p', 'roles': [admin_role]},
+            {'username': 'test_admin2', 'project_name': 'test_tenant12',
+             'password': 'p', 'roles': [admin_role]},
+            {'username': 'test_admin3', 'project_name': 'test_tenant13',
+             'password': 'p', 'types': ['admin']}]
+
+    def setUp(self):
+        super(TestPreProvisionedCredentials, self).setUp()
+        self.useFixture(fake_config.ConfigFixture())
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
+        self.patch(self.token_client, side_effect=self.identity_response)
+        self.useFixture(lockutils_fixtures.ExternalLockFixture())
+        self.test_accounts = self._fake_accounts(cfg.CONF.identity.admin_role)
         self.accounts_mock = self.useFixture(mockpatch.Patch(
             'tempest.common.preprov_creds.read_accounts_yaml',
             return_value=self.test_accounts))
@@ -90,24 +97,33 @@
 
     def _get_hash_list(self, accounts_list):
         hash_list = []
+        hash_fields = (
+            preprov_creds.PreProvisionedCredentialProvider.HASH_CRED_FIELDS)
         for account in accounts_list:
             hash = hashlib.md5()
-            hash.update(six.text_type(account).encode('utf-8'))
+            account_for_hash = dict((k, v) for (k, v) in account.items()
+                                    if k in hash_fields)
+            hash.update(six.text_type(account_for_hash).encode('utf-8'))
             temp_hash = hash.hexdigest()
             hash_list.append(temp_hash)
         return hash_list
 
     def test_get_hash(self):
-        self.stubs.Set(token_client.TokenClient, 'raw_request',
-                       fake_identity._fake_v2_response)
-        test_account_class = preprov_creds.PreProvisionedCredentialProvider(
-            **self.fixed_params)
-        hash_list = self._get_hash_list(self.test_accounts)
-        test_cred_dict = self.test_accounts[3]
-        test_creds = auth.get_credentials(fake_identity.FAKE_AUTH_URL,
-                                          **test_cred_dict)
-        results = test_account_class.get_hash(test_creds)
-        self.assertEqual(hash_list[3], results)
+        # Test with all accounts to make sure we try all combinations
+        # and hide no race conditions
+        hash_index = 0
+        for test_cred_dict in self.test_accounts:
+            test_account_class = (
+                preprov_creds.PreProvisionedCredentialProvider(
+                    **self.fixed_params))
+            hash_list = self._get_hash_list(self.test_accounts)
+            test_creds = auth.get_credentials(
+                fake_identity.FAKE_AUTH_URL,
+                identity_version=self.fixed_params['identity_version'],
+                **test_cred_dict)
+            results = test_account_class.get_hash(test_creds)
+            self.assertEqual(hash_list[hash_index], results)
+            hash_index += 1
 
     def test_get_hash_dict(self):
         test_account_class = preprov_creds.PreProvisionedCredentialProvider(
@@ -190,7 +206,7 @@
                 return False
             return True
 
-        self.stubs.Set(os.path, 'isfile', _fake_is_file)
+        self.patchobject(os.path, 'isfile', _fake_is_file)
         with mock.patch('six.moves.builtins.open', mock.mock_open(),
                         create=True) as open_mock:
             test_account_class._get_free_hash(hash_list)
@@ -249,9 +265,6 @@
         self.assertFalse(test_accounts_class.is_multi_user())
 
     def test__get_creds_by_roles_one_role(self):
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
-            return_value=self.test_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
         hashes = test_accounts_class.hash_dict['roles']['role4']
@@ -267,9 +280,6 @@
             self.assertIn(i, args)
 
     def test__get_creds_by_roles_list_role(self):
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
-            return_value=self.test_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
         hashes = test_accounts_class.hash_dict['roles']['role4']
@@ -287,9 +297,6 @@
             self.assertIn(i, args)
 
     def test__get_creds_by_roles_no_admin(self):
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
-            return_value=self.test_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
         hashes = list(test_accounts_class.hash_dict['creds'].keys())
@@ -319,16 +326,148 @@
             return_value=test_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
-        with mock.patch('tempest_lib.services.compute.networks_client.'
+        with mock.patch('tempest.lib.services.compute.networks_client.'
                         'NetworksClient.list_networks',
                         return_value={'networks': [{'name': 'network-2',
                                                     '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)
         self.assertIn('id', network)
         self.assertEqual('fake-id', network['id'])
         self.assertEqual('network-2', network['name'])
+
+    def test_get_primary_creds(self):
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        primary_creds = test_accounts_class.get_primary_creds()
+        self.assertNotIn('test_admin', primary_creds.username)
+
+    def test_get_primary_creds_none_available(self):
+        admin_accounts = [x for x in self.test_accounts if 'test_admin'
+                          in x['username']]
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.preprov_creds.read_accounts_yaml',
+            return_value=admin_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        with testtools.ExpectedException(lib_exc.InvalidCredentials):
+            # Get one more
+            test_accounts_class.get_primary_creds()
+
+    def test_get_alt_creds(self):
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        alt_creds = test_accounts_class.get_alt_creds()
+        self.assertNotIn('test_admin', alt_creds.username)
+
+    def test_get_alt_creds_none_available(self):
+        admin_accounts = [x for x in self.test_accounts if 'test_admin'
+                          in x['username']]
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.preprov_creds.read_accounts_yaml',
+            return_value=admin_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        with testtools.ExpectedException(lib_exc.InvalidCredentials):
+            # Get one more
+            test_accounts_class.get_alt_creds()
+
+    def test_get_admin_creds(self):
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        admin_creds = test_accounts_class.get_admin_creds()
+        self.assertIn('test_admin', admin_creds.username)
+
+    def test_get_admin_creds_by_type(self):
+        test_accounts = [
+            {'username': 'test_user10', 'project_name': 'test_tenant10',
+             'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
+            {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
+             'password': 'p', 'types': ['admin']}]
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.preprov_creds.read_accounts_yaml',
+            return_value=test_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        admin_creds = test_accounts_class.get_admin_creds()
+        self.assertIn('test_admin', admin_creds.username)
+
+    def test_get_admin_creds_by_role(self):
+        test_accounts = [
+            {'username': 'test_user10', 'project_name': 'test_tenant10',
+             'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
+            {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
+             'password': 'p', 'roles': [cfg.CONF.identity.admin_role]}]
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.preprov_creds.read_accounts_yaml',
+            return_value=test_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        admin_creds = test_accounts_class.get_admin_creds()
+        self.assertIn('test_admin', admin_creds.username)
+
+    def test_get_admin_creds_none_available(self):
+        non_admin_accounts = [x for x in self.test_accounts if 'test_admin'
+                              not in x['username']]
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.preprov_creds.read_accounts_yaml',
+            return_value=non_admin_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        with testtools.ExpectedException(lib_exc.InvalidCredentials):
+            # Get one more
+            test_accounts_class.get_admin_creds()
+
+
+class TestPreProvisionedCredentialsV3(TestPreProvisionedCredentials):
+
+    fixed_params = {'name': 'test class',
+                    'identity_version': 'v3',
+                    'test_accounts_file': 'fake_accounts_file',
+                    'accounts_lock_dir': 'fake_locks_dir_v3',
+                    'admin_role': 'admin',
+                    'object_storage_operator_role': 'operator',
+                    'object_storage_reseller_admin_role': 'reseller'}
+
+    identity_response = fake_identity._fake_v3_response
+    token_client = ('tempest.lib.services.identity.v3.token_client'
+                    '.V3TokenClient.raw_request')
+
+    @classmethod
+    def _fake_accounts(cls, admin_role):
+        return [
+            {'username': 'test_user1', 'project_name': 'test_project1',
+             'domain_name': 'domain', 'password': 'p'},
+            {'username': 'test_user2', 'project_name': 'test_project2',
+             'domain_name': 'domain', 'password': 'p'},
+            {'username': 'test_user3', 'project_name': 'test_project3',
+             'domain_name': 'domain', 'password': 'p'},
+            {'username': 'test_user4', 'project_name': 'test_project4',
+             'domain_name': 'domain', 'password': 'p'},
+            {'username': 'test_user5', 'project_name': 'test_project5',
+             'domain_name': 'domain', 'password': 'p'},
+            {'username': 'test_user6', 'project_name': 'test_project6',
+             'domain_name': 'domain', 'password': 'p',
+             'roles': ['role1', 'role2']},
+            {'username': 'test_user7', 'project_name': 'test_project7',
+             'domain_name': 'domain', 'password': 'p',
+             'roles': ['role2', 'role3']},
+            {'username': 'test_user8', 'project_name': 'test_project8',
+             'domain_name': 'domain', 'password': 'p',
+             'roles': ['role4', 'role1']},
+            {'username': 'test_user9', 'project_name': 'test_project9',
+             'domain_name': 'domain', 'password': 'p',
+             'roles': ['role1', 'role2', 'role3', 'role4']},
+            {'username': 'test_user10', 'project_name': 'test_project10',
+             'domain_name': 'domain', 'password': 'p',
+             'roles': ['role1', 'role2', 'role3', 'role4']},
+            {'username': 'test_admin1', 'project_name': 'test_project11',
+             'domain_name': 'domain', 'password': 'p', 'roles': [admin_role]},
+            {'username': 'test_admin2', 'project_name': 'test_project12',
+             'domain_name': 'domain', 'password': 'p', 'roles': [admin_role]},
+            {'username': 'test_admin3', 'project_name': 'test_tenant13',
+             'domain_name': 'domain', 'password': 'p', 'types': ['admin']}]
diff --git a/tempest/tests/common/test_service_clients.py b/tempest/tests/common/test_service_clients.py
deleted file mode 100644
index 5a00a2b..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..46f9526 100644
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -18,8 +18,10 @@
 
 from tempest.common import waiters
 from tempest import exceptions
-from tempest.services.volume.base import base_volumes_client
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v2 import volumes_client
 from tempest.tests import base
+import tempest.tests.utils as utils
 
 
 class TestImageWaiters(base.TestCase):
@@ -35,11 +37,14 @@
         waiters.wait_for_image_status(self.client, 'fake_image_id', 'active')
         end_time = int(time.time())
         # Ensure waiter returns before build_timeout
-        self.assertTrue((end_time - start_time) < 10)
+        self.assertLess((end_time - start_time), 10)
 
     def test_wait_for_image_status_timeout(self):
+        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,
+        self.assertRaises(lib_exc.TimeoutException,
                           waiters.wait_for_image_status,
                           self.client, 'fake_image_id', 'active')
 
@@ -53,7 +58,7 @@
     def test_wait_for_volume_status_error_restoring(self, mock_sleep):
         # Tests that the wait method raises VolumeRestoreErrorException if
         # the volume status is 'error_restoring'.
-        client = mock.Mock(spec=base_volumes_client.BaseVolumesClient,
+        client = mock.Mock(spec=volumes_client.VolumesClient,
                            build_interval=1)
         volume1 = {'volume': {'status': 'restoring-backup'}}
         volume2 = {'volume': {'status': 'error_restoring'}}
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 9c2b99e..e4f4c04 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -12,6 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import fixtures
 import time
 
 from oslo_config import cfg
@@ -19,15 +20,45 @@
 
 from tempest.common.utils.linux import remote_client
 from tempest import config
+from tempest.lib import exceptions as lib_exc
 from tempest.tests import base
 from tempest.tests import fake_config
 
 
+SERVER = {
+    'id': 'server_uuid',
+    'name': 'fake_server',
+    'status': 'ACTIVE'
+}
+
+BROKEN_SERVER = {
+    'id': 'broken_server_uuid',
+    'name': 'broken_server',
+    'status': 'ERROR'
+}
+
+
+class FakeServersClient(object):
+
+    CONSOLE_OUTPUT = "Console output for %s"
+
+    def get_console_output(self, server_id):
+        status = 'ERROR'
+        for s in SERVER, BROKEN_SERVER:
+            if s['id'] == server_id:
+                status = s['status']
+        if status == 'ERROR':
+            raise lib_exc.BadRequest('Server in ERROR state')
+        else:
+            return dict(output=self.CONSOLE_OUTPUT % server_id)
+
+
 class TestRemoteClient(base.TestCase):
     def setUp(self):
         super(TestRemoteClient, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
-        self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
         cfg.CONF.set_default('ip_version_for_ssh', 4, group='validation')
         cfg.CONF.set_default('network_for_ssh', 'public', group='validation')
         cfg.CONF.set_default('connect_timeout', 1, group='validation')
@@ -36,14 +67,9 @@
         self.ssh_mock = self.useFixture(mockpatch.PatchObject(self.conn,
                                                               'ssh_client'))
 
-    def test_hostname_equals_servername_for_expected_names(self):
+    def test_get_hostname(self):
         self.ssh_mock.mock.exec_command.return_value = 'fake_hostname'
-        self.assertTrue(self.conn.hostname_equals_servername('fake_hostname'))
-
-    def test_hostname_equals_servername_for_unexpected_names(self):
-        self.ssh_mock.mock.exec_command.return_value = 'fake_hostname'
-        self.assertFalse(
-            self.conn.hostname_equals_servername('unexpected_hostname'))
+        self.assertEqual(self.conn.get_hostname(), 'fake_hostname')
 
     def test_get_ram_size(self):
         free_output = "Mem:         48294      45738       2555          0" \
@@ -81,13 +107,20 @@
         self.assertEqual(self.conn.get_number_of_vcpus(), 16)
         self._assert_exec_called_with('grep -c ^processor /proc/cpuinfo')
 
-    def test_get_partitions(self):
-        proc_partitions = """major minor  #blocks  name
+    def test_get_disks(self):
+        output_lsblk = """\
+NAME       MAJ:MIN    RM          SIZE RO TYPE MOUNTPOINT
+sda          8:0       0  128035676160  0 disk
+sdb          8:16      0 1000204886016  0 disk
+sr0         11:0       1    1073741312  0 rom"""
+        result = """\
+NAME       MAJ:MIN    RM          SIZE RO TYPE MOUNTPOINT
+sda          8:0       0  128035676160  0 disk
+sdb          8:16      0 1000204886016  0 disk"""
 
-8        0  1048576 vda"""
-        self.ssh_mock.mock.exec_command.return_value = proc_partitions
-        self.assertEqual(self.conn.get_partitions(), proc_partitions)
-        self._assert_exec_called_with('cat /proc/partitions')
+        self.ssh_mock.mock.exec_command.return_value = output_lsblk
+        self.assertEqual(self.conn.get_disks(), result)
+        self._assert_exec_called_with('lsblk -lb --nodeps')
 
     def test_get_boot_time(self):
         booted_at = 10000
@@ -154,3 +187,78 @@
         self.conn.set_nic_state(nic, "down")
         self._assert_exec_called_with(
             'sudo ip link set %s down' % nic)
+
+
+class TestRemoteClientWithServer(base.TestCase):
+
+    server = SERVER
+
+    def setUp(self):
+        super(TestRemoteClientWithServer, self).setUp()
+        self.useFixture(fake_config.ConfigFixture())
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
+        cfg.CONF.set_default('ip_version_for_ssh', 4, group='validation')
+        cfg.CONF.set_default('network_for_ssh', 'public',
+                             group='validation')
+        cfg.CONF.set_default('connect_timeout', 1, group='validation')
+        cfg.CONF.set_default('console_output', True,
+                             group='compute-feature-enabled')
+
+        self.conn = remote_client.RemoteClient(
+            '127.0.0.1', 'user', 'pass',
+            server=self.server, servers_client=FakeServersClient())
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.ssh.Client._get_ssh_connection',
+            side_effect=lib_exc.SSHTimeout(host='127.0.0.1',
+                                           user='user',
+                                           password='pass')))
+        self.log = self.useFixture(fixtures.FakeLogger(
+            name='tempest.common.utils.linux.remote_client',
+            level='DEBUG'))
+
+    def test_validate_debug_ssh_console(self):
+        self.assertRaises(lib_exc.SSHTimeout,
+                          self.conn.validate_authentication)
+        msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
+            'TestRemoteClientWithServer:test_validate_debug_ssh_console',
+            self.server)
+        self.assertIn(msg, self.log.output)
+        self.assertIn('Console output for', self.log.output)
+
+    def test_exec_command_debug_ssh_console(self):
+        self.assertRaises(lib_exc.SSHTimeout,
+                          self.conn.exec_command, 'fake command')
+        self.assertIn('fake command', self.log.output)
+        msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
+            'TestRemoteClientWithServer:test_exec_command_debug_ssh_console',
+            self.server)
+        self.assertIn(msg, self.log.output)
+        self.assertIn('Console output for', self.log.output)
+
+
+class TestRemoteClientWithBrokenServer(TestRemoteClientWithServer):
+
+    server = BROKEN_SERVER
+
+    def test_validate_debug_ssh_console(self):
+        self.assertRaises(lib_exc.SSHTimeout,
+                          self.conn.validate_authentication)
+        msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
+            'TestRemoteClientWithBrokenServer:test_validate_debug_ssh_console',
+            self.server)
+        self.assertIn(msg, self.log.output)
+        msg = 'Could not get console_log for server %s' % self.server['id']
+        self.assertIn(msg, self.log.output)
+
+    def test_exec_command_debug_ssh_console(self):
+        self.assertRaises(lib_exc.SSHTimeout,
+                          self.conn.exec_command, 'fake command')
+        self.assertIn('fake command', self.log.output)
+        caller = ":".join(['TestRemoteClientWithBrokenServer',
+                           'test_exec_command_debug_ssh_console'])
+        msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
+            caller, self.server)
+        self.assertIn(msg, self.log.output)
+        msg = 'Could not get console_log for server %s' % self.server['id']
+        self.assertIn(msg, self.log.output)
diff --git a/tempest/tests/common/utils/test_file_utils.py b/tempest/tests/common/utils/test_file_utils.py
deleted file mode 100644
index 937aefa..0000000
--- a/tempest/tests/common/utils/test_file_utils.py
+++ /dev/null
@@ -1,32 +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 mock
-
-from tempest.common.utils import file_utils
-from tempest.tests import base
-
-
-class TestFileUtils(base.TestCase):
-
-    def test_have_effective_read_path(self):
-        with mock.patch('six.moves.builtins.open', mock.mock_open(),
-                        create=True):
-            result = file_utils.have_effective_read_access('fake_path')
-        self.assertTrue(result)
-
-    def test_not_effective_read_path(self):
-        result = file_utils.have_effective_read_access('fake_path')
-        self.assertFalse(result)
diff --git a/tempest/tests/common/utils/test_net_utils.py b/tempest/tests/common/utils/test_net_utils.py
new file mode 100644
index 0000000..83c6bcc
--- /dev/null
+++ b/tempest/tests/common/utils/test_net_utils.py
@@ -0,0 +1,33 @@
+#    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.common.utils import net_utils
+from tempest.lib import exceptions as lib_exc
+from tempest.tests import base
+
+
+class TestGetPingPayloadSize(base.TestCase):
+
+    def test_ipv4(self):
+        self.assertEqual(1422, net_utils.get_ping_payload_size(1450, 4))
+
+    def test_ipv6(self):
+        self.assertEqual(1406, net_utils.get_ping_payload_size(1450, 6))
+
+    def test_too_low_mtu(self):
+        self.assertRaises(
+            lib_exc.BadRequest, net_utils.get_ping_payload_size, 10, 4)
+
+    def test_None(self):
+        self.assertIsNone(net_utils.get_ping_payload_size(None, mock.Mock()))
diff --git a/tempest/tests/fake_auth_provider.py b/tempest/tests/fake_auth_provider.py
deleted file mode 100644
index bc68d26..0000000
--- a/tempest/tests/fake_auth_provider.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright 2014 Hewlett-Packard Development Company, L.P.
-# 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.
-
-
-class FakeAuthProvider(object):
-
-    def auth_request(self, method, url, headers=None, body=None, filters=None):
-        return url, headers, body
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index c45f6da..71a4c81 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -46,19 +46,68 @@
             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_':
-                    group = 'auth'
-                else:
-                    group = 'identity'
-                self.conf.set_default(prefix + config_option,
-                                      'fake_' + config_option,
-                                      group=group)
+            self.conf.set_default('admin_' + config_option,
+                                  'fake_' + config_option,
+                                  group='auth')
 
 
 class FakePrivate(config.TempestConfigPrivate):
     def __init__(self, parse_conf=True, config_path=None):
         self._set_attrs()
         self.lock_path = cfg.CONF.oslo_concurrency.lock_path
+
+fake_service1_group = cfg.OptGroup(name='fake-service1', title='Fake service1')
+
+FakeService1Group = [
+    cfg.StrOpt('catalog_type', default='fake-service1'),
+    cfg.StrOpt('endpoint_type', default='faketype'),
+    cfg.StrOpt('region', default='fake_region'),
+    cfg.IntOpt('build_timeout', default=99),
+    cfg.IntOpt('build_interval', default=9)]
+
+fake_service2_group = cfg.OptGroup(name='fake-service2', title='Fake service2')
+
+FakeService2Group = [
+    cfg.StrOpt('catalog_type', default='fake-service2'),
+    cfg.StrOpt('endpoint_type', default='faketype')]
+
+
+class ServiceClientsConfigFixture(conf_fixture.Config):
+
+    def __init__(self):
+        cfg.CONF([], default_config_files=[])
+        config._opts.append((fake_service1_group, FakeService1Group))
+        config._opts.append((fake_service2_group, FakeService2Group))
+        config.register_opts()
+        super(ServiceClientsConfigFixture, self).__init__()
+
+    def setUp(self):
+        super(ServiceClientsConfigFixture, self).setUp()
+        # Debug default values
+        self.conf.set_default('trace_requests', 'fake_module', 'debug')
+        # Identity default values
+        self.conf.set_default('disable_ssl_certificate_validation', True,
+                              group='identity')
+        self.conf.set_default('ca_certificates_file', '/fake/certificates',
+                              group='identity')
+        self.conf.set_default('region', 'fake_region', 'identity')
+        # Identity endpoints
+        self.conf.set_default('v3_endpoint_type', 'fake_v3_uri', 'identity')
+        self.conf.set_default('v2_public_endpoint_type', 'fake_v2_public_uri',
+                              'identity')
+        self.conf.set_default('v2_admin_endpoint_type', 'fake_v2_admin_uri',
+                              'identity')
+        # Compute default values
+        self.conf.set_default('build_interval', 88, group='compute')
+        self.conf.set_default('build_timeout', 8, group='compute')
+
+
+class ServiceClientsFakePrivate(config.TempestConfigPrivate):
+    def __init__(self, parse_conf=True, config_path=None):
+        self._set_attrs()
+        self.fake_service1 = cfg.CONF['fake-service1']
+        self.fake_service2 = cfg.CONF['fake-service2']
+        print('Services registered')
+        self.lock_path = cfg.CONF.oslo_concurrency.lock_path
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/fake_tempest_plugin.py b/tempest/tests/fake_tempest_plugin.py
index f718d0b..56aae1e 100644
--- a/tempest/tests/fake_tempest_plugin.py
+++ b/tempest/tests/fake_tempest_plugin.py
@@ -18,6 +18,7 @@
 
 class FakePlugin(plugins.TempestPlugin):
     expected_load_test = ["my/test/path", "/home/dir"]
+    expected_service_clients = [{'foo': 'bar'}]
 
     def load_tests(self):
         return self.expected_load_test
@@ -28,6 +29,9 @@
     def get_opt_lists(self):
         return []
 
+    def get_service_clients(self):
+        return self.expected_service_clients
+
 
 class FakeStevedoreObj(object):
     obj = FakePlugin()
@@ -38,3 +42,26 @@
 
     def __init__(self, name='Test1'):
         self._name = name
+
+
+class FakePluginNoServiceClients(plugins.TempestPlugin):
+
+    def load_tests(self):
+        return []
+
+    def register_opts(self, conf):
+        return
+
+    def get_opt_lists(self):
+        return []
+
+
+class FakeStevedoreObjNoServiceClients(object):
+    obj = FakePluginNoServiceClients()
+
+    @property
+    def name(self):
+        return self._name
+
+    def __init__(self, name='Test2'):
+        self._name = name
diff --git a/tempest/tests/stress/__init__.py b/tempest/tests/lib/__init__.py
similarity index 100%
rename from tempest/tests/stress/__init__.py
rename to tempest/tests/lib/__init__.py
diff --git a/tempest/tests/negative/__init__.py b/tempest/tests/lib/cli/__init__.py
similarity index 100%
rename from tempest/tests/negative/__init__.py
rename to tempest/tests/lib/cli/__init__.py
diff --git a/tempest/tests/lib/cli/test_command_failed.py b/tempest/tests/lib/cli/test_command_failed.py
new file mode 100644
index 0000000..388028a
--- /dev/null
+++ b/tempest/tests/lib/cli/test_command_failed.py
@@ -0,0 +1,30 @@
+#    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 import exceptions
+from tempest.tests import base
+
+
+class TestOutputParser(base.TestCase):
+
+    def test_command_failed_exception(self):
+        returncode = 1
+        cmd = "foo"
+        stdout = "output"
+        stderr = "error"
+        try:
+            raise exceptions.CommandFailed(returncode, cmd, stdout, stderr)
+        except exceptions.CommandFailed as e:
+            self.assertIn(str(returncode), str(e))
+            self.assertIn(cmd, str(e))
+            self.assertIn(stdout, str(e))
+            self.assertIn(stderr, str(e))
diff --git a/tempest/tests/lib/cli/test_execute.py b/tempest/tests/lib/cli/test_execute.py
new file mode 100644
index 0000000..aaeb6f4
--- /dev/null
+++ b/tempest/tests/lib/cli/test_execute.py
@@ -0,0 +1,92 @@
+#
+#    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 subprocess
+
+from tempest.lib.cli import base as cli_base
+from tempest.lib import exceptions
+from tempest.tests import base
+
+
+class TestExecute(base.TestCase):
+
+    @mock.patch('subprocess.Popen', autospec=True)
+    def test_execute_success(self, mock_popen):
+        mock_popen.return_value.returncode = 0
+        mock_popen.return_value.communicate.return_value = (
+            "__init__.py", "")
+        result = cli_base.execute("/bin/ls", action="tempest",
+                                  flags="-l -a")
+        args, kwargs = mock_popen.call_args
+        # Check merge_stderr == False
+        self.assertEqual(subprocess.PIPE, kwargs['stderr'])
+        # Check action and flags are passed
+        args = args[0]
+        # We just tests that all pieces are passed through, we cannot make
+        # assumptions about the order
+        self.assertIn("/bin/ls", args)
+        self.assertIn("-l", args)
+        self.assertIn("-a", args)
+        self.assertIn("tempest", args)
+        # The result is mocked - checking that the mock was invoked correctly
+        self.assertIsInstance(result, str)
+        self.assertIn("__init__.py", result)
+
+    @mock.patch('subprocess.Popen', autospec=True)
+    def test_execute_failure(self, mock_popen):
+        mock_popen.return_value.returncode = 1
+        mock_popen.return_value.communicate.return_value = (
+            "No such option --foobar", "")
+        result = cli_base.execute("/bin/ls", action="tempest.lib",
+                                  flags="--foobar", merge_stderr=True,
+                                  fail_ok=True)
+        args, kwargs = mock_popen.call_args
+        # Check the merge_stderr
+        self.assertEqual(subprocess.STDOUT, kwargs['stderr'])
+        # Check action and flags are passed
+        args = args[0]
+        # We just tests that all pieces are passed through, we cannot make
+        # assumptions about the order
+        self.assertIn("/bin/ls", args)
+        self.assertIn("--foobar", args)
+        self.assertIn("tempest.lib", args)
+        # The result is mocked - checking that the mock was invoked correctly
+        self.assertIsInstance(result, str)
+        self.assertIn("--foobar", result)
+
+    @mock.patch('subprocess.Popen', autospec=True)
+    def test_execute_failure_raise_exception(self, mock_popen):
+        mock_popen.return_value.returncode = 1
+        mock_popen.return_value.communicate.return_value = (
+            "No such option --foobar", "")
+        self.assertRaises(exceptions.CommandFailed, cli_base.execute,
+                          "/bin/ls", action="tempest", flags="--foobar",
+                          merge_stderr=True)
+
+    def test_execute_with_prefix(self):
+        result = cli_base.execute("env", action="",
+                                  prefix="env NEW_VAR=1")
+        self.assertIsInstance(result, str)
+        self.assertIn("NEW_VAR=1", result)
+
+
+class TestCLIClient(base.TestCase):
+
+    @mock.patch.object(cli_base, 'execute')
+    def test_execute_with_prefix(self, mock_execute):
+        cli = cli_base.CLIClient(prefix='env LAC_ALL=C')
+        cli.glance('action')
+        self.assertEqual(mock_execute.call_count, 1)
+        self.assertEqual(mock_execute.call_args[1],
+                         {'prefix': 'env LAC_ALL=C'})
diff --git a/tempest/tests/lib/cli/test_output_parser.py b/tempest/tests/lib/cli/test_output_parser.py
new file mode 100644
index 0000000..d88dfc3
--- /dev/null
+++ b/tempest/tests/lib/cli/test_output_parser.py
@@ -0,0 +1,177 @@
+# 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.
+
+
+from tempest.lib.cli import output_parser
+from tempest.lib import exceptions
+from tempest.tests import base
+
+
+class TestOutputParser(base.TestCase):
+    OUTPUT_LINES = """
++----+------+---------+
+| ID | Name | Status  |
++----+------+---------+
+| 11 | foo  | BUILD   |
+| 21 | bar  | ERROR   |
+| 31 | bee  | None    |
++----+------+---------+
+"""
+    OUTPUT_LINES2 = """
++----+-------+---------+
+| ID | Name2 | Status2 |
++----+-------+---------+
+| 41 | aaa   | SSSSS   |
+| 51 | bbb   | TTTTT   |
+| 61 | ccc   | AAAAA   |
++----+-------+---------+
+"""
+
+    EXPECTED_TABLE = {'headers': ['ID', 'Name', 'Status'],
+                      'values': [['11', 'foo', 'BUILD'],
+                                 ['21', 'bar', 'ERROR'],
+                                 ['31', 'bee', 'None']]}
+    EXPECTED_TABLE2 = {'headers': ['ID', 'Name2', 'Status2'],
+                       'values': [['41', 'aaa', 'SSSSS'],
+                                  ['51', 'bbb', 'TTTTT'],
+                                  ['61', 'ccc', 'AAAAA']]}
+
+    def test_table_with_normal_values(self):
+        actual = output_parser.table(self.OUTPUT_LINES)
+        self.assertIsInstance(actual, dict)
+        self.assertEqual(self.EXPECTED_TABLE, actual)
+
+    def test_table_with_list(self):
+        output_lines = self.OUTPUT_LINES.split('\n')
+        actual = output_parser.table(output_lines)
+        self.assertIsInstance(actual, dict)
+        self.assertEqual(self.EXPECTED_TABLE, actual)
+
+    def test_table_with_invalid_line(self):
+        output_lines = self.OUTPUT_LINES + "aaaa"
+        actual = output_parser.table(output_lines)
+        self.assertIsInstance(actual, dict)
+        self.assertEqual(self.EXPECTED_TABLE, actual)
+
+    def test_tables_with_normal_values(self):
+        output_lines = ('test' + self.OUTPUT_LINES +
+                        'test2' + self.OUTPUT_LINES2)
+        expected = [{'headers': self.EXPECTED_TABLE['headers'],
+                     'label': 'test',
+                     'values': self.EXPECTED_TABLE['values']},
+                    {'headers': self.EXPECTED_TABLE2['headers'],
+                     'label': 'test2',
+                     'values': self.EXPECTED_TABLE2['values']}]
+        actual = output_parser.tables(output_lines)
+        self.assertIsInstance(actual, list)
+        self.assertEqual(expected, actual)
+
+    def test_tables_with_invalid_values(self):
+        output_lines = ('test' + self.OUTPUT_LINES +
+                        'test2' + self.OUTPUT_LINES2 + '\n')
+        expected = [{'headers': self.EXPECTED_TABLE['headers'],
+                     'label': 'test',
+                     'values': self.EXPECTED_TABLE['values']},
+                    {'headers': self.EXPECTED_TABLE2['headers'],
+                     'label': 'test2',
+                     'values': self.EXPECTED_TABLE2['values']}]
+        actual = output_parser.tables(output_lines)
+        self.assertIsInstance(actual, list)
+        self.assertEqual(expected, actual)
+
+    def test_tables_with_invalid_line(self):
+        output_lines = ('test' + self.OUTPUT_LINES +
+                        'test2' + self.OUTPUT_LINES2 +
+                        '+----+-------+---------+')
+        expected = [{'headers': self.EXPECTED_TABLE['headers'],
+                     'label': 'test',
+                     'values': self.EXPECTED_TABLE['values']},
+                    {'headers': self.EXPECTED_TABLE2['headers'],
+                     'label': 'test2',
+                     'values': self.EXPECTED_TABLE2['values']}]
+
+        actual = output_parser.tables(output_lines)
+        self.assertIsInstance(actual, list)
+        self.assertEqual(expected, actual)
+
+    LISTING_OUTPUT = """
++----+
+| ID |
++----+
+| 11 |
+| 21 |
+| 31 |
++----+
+"""
+
+    def test_listing(self):
+        expected = [{'ID': '11'}, {'ID': '21'}, {'ID': '31'}]
+        actual = output_parser.listing(self.LISTING_OUTPUT)
+        self.assertIsInstance(actual, list)
+        self.assertEqual(expected, actual)
+
+    def test_details_multiple_with_invalid_line(self):
+        self.assertRaises(exceptions.InvalidStructure,
+                          output_parser.details_multiple,
+                          self.OUTPUT_LINES)
+
+    DETAILS_LINES1 = """First Table
++----------+--------+
+| Property | Value  |
++----------+--------+
+| foo      | BUILD  |
+| bar      | ERROR  |
+| bee      | None   |
++----------+--------+
+"""
+    DETAILS_LINES2 = """Second Table
++----------+--------+
+| Property | Value  |
++----------+--------+
+| aaa      | VVVVV  |
+| bbb      | WWWWW  |
+| ccc      | XXXXX  |
++----------+--------+
+"""
+
+    def test_details_with_normal_line_label_false(self):
+        expected = {'foo': 'BUILD', 'bar': 'ERROR', 'bee': 'None'}
+        actual = output_parser.details(self.DETAILS_LINES1)
+        self.assertEqual(expected, actual)
+
+    def test_details_with_normal_line_label_true(self):
+        expected = {'__label': 'First Table',
+                    'foo': 'BUILD', 'bar': 'ERROR', 'bee': 'None'}
+        actual = output_parser.details(self.DETAILS_LINES1, with_label=True)
+        self.assertEqual(expected, actual)
+
+    def test_details_multiple_with_normal_line_label_false(self):
+        expected = [{'foo': 'BUILD', 'bar': 'ERROR', 'bee': 'None'},
+                    {'aaa': 'VVVVV', 'bbb': 'WWWWW', 'ccc': 'XXXXX'}]
+        actual = output_parser.details_multiple(self.DETAILS_LINES1 +
+                                                self.DETAILS_LINES2)
+        self.assertIsInstance(actual, list)
+        self.assertEqual(expected, actual)
+
+    def test_details_multiple_with_normal_line_label_true(self):
+        expected = [{'__label': 'First Table',
+                     'foo': 'BUILD', 'bar': 'ERROR', 'bee': 'None'},
+                    {'__label': 'Second Table',
+                     'aaa': 'VVVVV', 'bbb': 'WWWWW', 'ccc': 'XXXXX'}]
+        actual = output_parser.details_multiple(self.DETAILS_LINES1 +
+                                                self.DETAILS_LINES2,
+                                                with_label=True)
+        self.assertIsInstance(actual, list)
+        self.assertEqual(expected, actual)
diff --git a/tempest/tests/negative/__init__.py b/tempest/tests/lib/common/__init__.py
similarity index 100%
copy from tempest/tests/negative/__init__.py
copy to tempest/tests/lib/common/__init__.py
diff --git a/tempest/tests/lib/common/test_api_version_request.py b/tempest/tests/lib/common/test_api_version_request.py
new file mode 100644
index 0000000..58e7040
--- /dev/null
+++ b/tempest/tests/lib/common/test_api_version_request.py
@@ -0,0 +1,146 @@
+# Copyright 2014 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.
+
+from tempest.lib.common import api_version_request
+from tempest.lib import exceptions
+from tempest.tests import base
+
+
+class APIVersionRequestTests(base.TestCase):
+    def test_valid_version_strings(self):
+        def _test_string(version, exp_major, exp_minor):
+            v = api_version_request.APIVersionRequest(version)
+            self.assertEqual(v.ver_major, exp_major)
+            self.assertEqual(v.ver_minor, exp_minor)
+
+        _test_string("1.1", 1, 1)
+        _test_string("2.10", 2, 10)
+        _test_string("5.234", 5, 234)
+        _test_string("12.5", 12, 5)
+        _test_string("2.0", 2, 0)
+        _test_string("2.200", 2, 200)
+
+    def test_null_version(self):
+        v = api_version_request.APIVersionRequest()
+        self.assertTrue(v.is_null())
+
+    def test_invalid_version_strings(self):
+        self.assertRaises(exceptions.InvalidAPIVersionString,
+                          api_version_request.APIVersionRequest, "2")
+
+        self.assertRaises(exceptions.InvalidAPIVersionString,
+                          api_version_request.APIVersionRequest, "200")
+
+        self.assertRaises(exceptions.InvalidAPIVersionString,
+                          api_version_request.APIVersionRequest, "2.1.4")
+
+        self.assertRaises(exceptions.InvalidAPIVersionString,
+                          api_version_request.APIVersionRequest, "200.23.66.3")
+
+        self.assertRaises(exceptions.InvalidAPIVersionString,
+                          api_version_request.APIVersionRequest, "5 .3")
+
+        self.assertRaises(exceptions.InvalidAPIVersionString,
+                          api_version_request.APIVersionRequest, "5. 3")
+
+        self.assertRaises(exceptions.InvalidAPIVersionString,
+                          api_version_request.APIVersionRequest, "5.03")
+
+        self.assertRaises(exceptions.InvalidAPIVersionString,
+                          api_version_request.APIVersionRequest, "02.1")
+
+        self.assertRaises(exceptions.InvalidAPIVersionString,
+                          api_version_request.APIVersionRequest, "2.001")
+
+        self.assertRaises(exceptions.InvalidAPIVersionString,
+                          api_version_request.APIVersionRequest, "")
+
+        self.assertRaises(exceptions.InvalidAPIVersionString,
+                          api_version_request.APIVersionRequest, " 2.1")
+
+        self.assertRaises(exceptions.InvalidAPIVersionString,
+                          api_version_request.APIVersionRequest, "2.1 ")
+
+    def test_version_comparisons(self):
+        vers2_0 = api_version_request.APIVersionRequest("2.0")
+        vers2_5 = api_version_request.APIVersionRequest("2.5")
+        vers5_23 = api_version_request.APIVersionRequest("5.23")
+        v_null = api_version_request.APIVersionRequest()
+        v_latest = api_version_request.APIVersionRequest('latest')
+
+        self.assertTrue(v_null < vers2_5)
+        self.assertTrue(vers2_0 < vers2_5)
+        self.assertTrue(vers2_0 <= vers2_5)
+        self.assertTrue(vers2_0 <= vers2_0)
+        self.assertTrue(vers2_5 > v_null)
+        self.assertTrue(vers5_23 > vers2_5)
+        self.assertTrue(vers2_0 >= vers2_0)
+        self.assertTrue(vers5_23 >= vers2_5)
+        self.assertTrue(vers2_0 != vers2_5)
+        self.assertTrue(vers2_0 == vers2_0)
+        self.assertTrue(vers2_0 != v_null)
+        self.assertTrue(v_null == v_null)
+        self.assertTrue(vers2_0 <= v_latest)
+        self.assertTrue(vers2_0 != v_latest)
+        self.assertTrue(v_latest == v_latest)
+        self.assertRaises(TypeError, vers2_0.__lt__, "2.1")
+
+    def test_version_matches(self):
+        vers2_0 = api_version_request.APIVersionRequest("2.0")
+        vers2_5 = api_version_request.APIVersionRequest("2.5")
+        vers2_45 = api_version_request.APIVersionRequest("2.45")
+        vers3_3 = api_version_request.APIVersionRequest("3.3")
+        vers3_23 = api_version_request.APIVersionRequest("3.23")
+        vers4_0 = api_version_request.APIVersionRequest("4.0")
+        v_null = api_version_request.APIVersionRequest()
+        v_latest = api_version_request.APIVersionRequest('latest')
+
+        def _check_version_matches(version, version1, version2, check=True):
+            if check:
+                msg = "Version %s does not matches with [%s - %s] range"
+                self.assertTrue(version.matches(version1, version2),
+                                msg % (version.get_string(),
+                                       version1.get_string(),
+                                       version2.get_string()))
+            else:
+                msg = "Version %s matches with [%s - %s] range"
+                self.assertFalse(version.matches(version1, version2),
+                                 msg % (version.get_string(),
+                                        version1.get_string(),
+                                        version2.get_string()))
+
+        _check_version_matches(vers2_5, vers2_0, vers2_45)
+        _check_version_matches(vers2_5, vers2_0, v_null)
+        _check_version_matches(vers2_0, vers2_0, vers2_5)
+        _check_version_matches(vers3_3, vers2_5, vers3_3)
+        _check_version_matches(vers3_3, v_null, vers3_3)
+        _check_version_matches(vers3_3, v_null, vers4_0)
+        _check_version_matches(vers2_0, vers2_5, vers2_45, False)
+        _check_version_matches(vers3_23, vers2_5, vers3_3, False)
+        _check_version_matches(vers2_5, vers2_45, vers2_0, False)
+        _check_version_matches(vers2_5, vers2_0, v_latest)
+        _check_version_matches(v_latest, v_latest, v_latest)
+        _check_version_matches(vers2_5, v_latest, v_latest, False)
+        _check_version_matches(v_latest, vers2_0, vers4_0, False)
+
+        self.assertRaises(ValueError, v_null.matches, vers2_0, vers2_45)
+
+    def test_get_string(self):
+        vers_string = ["3.23", "latest"]
+        for ver in vers_string:
+            ver_obj = api_version_request.APIVersionRequest(ver)
+            self.assertEqual(ver, ver_obj.get_string())
+
+        self.assertIsNotNone(
+            api_version_request.APIVersionRequest().get_string)
diff --git a/tempest/tests/lib/common/test_api_version_utils.py b/tempest/tests/lib/common/test_api_version_utils.py
new file mode 100644
index 0000000..6206379
--- /dev/null
+++ b/tempest/tests/lib/common/test_api_version_utils.py
@@ -0,0 +1,114 @@
+# 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 testtools
+
+from tempest.lib.common import api_version_utils
+from tempest.lib import exceptions
+from tempest.tests import base
+
+
+class TestVersionSkipLogic(base.TestCase):
+
+    def _test_version(self, test_min_version, test_max_version,
+                      cfg_min_version, cfg_max_version, expected_skip=False):
+        try:
+            api_version_utils.check_skip_with_microversion(test_min_version,
+                                                           test_max_version,
+                                                           cfg_min_version,
+                                                           cfg_max_version)
+        except testtools.TestCase.skipException as e:
+            if not expected_skip:
+                raise testtools.TestCase.failureException(e.message)
+
+    def test_version_min_in_range(self):
+        self._test_version('2.2', '2.10', '2.1', '2.7')
+
+    def test_version_max_in_range(self):
+        self._test_version('2.1', '2.3', '2.2', '2.7')
+
+    def test_version_cfg_in_range(self):
+        self._test_version('2.2', '2.9', '2.3', '2.7')
+
+    def test_version_equal(self):
+        self._test_version('2.2', '2.2', '2.2', '2.2')
+
+    def test_version_below_cfg_min(self):
+        self._test_version('2.2', '2.4', '2.5', '2.7', expected_skip=True)
+
+    def test_version_above_cfg_max(self):
+        self._test_version('2.8', '2.9', '2.3', '2.7', expected_skip=True)
+
+    def test_version_min_greater_than_max(self):
+        self.assertRaises(exceptions.InvalidAPIVersionRange,
+                          self._test_version, '2.8', '2.7', '2.3', '2.7')
+
+    def test_cfg_version_min_greater_than_max(self):
+        self.assertRaises(exceptions.InvalidAPIVersionRange,
+                          self._test_version, '2.2', '2.7', '2.9', '2.7')
+
+
+class TestSelectRequestMicroversion(base.TestCase):
+
+    def _test_request_version(self, test_min_version,
+                              cfg_min_version, expected_version):
+        selected_version = api_version_utils.select_request_microversion(
+            test_min_version, cfg_min_version)
+        self.assertEqual(expected_version, selected_version)
+
+    def test_cfg_min_version_greater(self):
+        self._test_request_version('2.1', '2.3', expected_version='2.3')
+
+    def test_class_min_version_greater(self):
+        self._test_request_version('2.5', '2.3', expected_version='2.5')
+
+    def test_cfg_min_version_none(self):
+        self._test_request_version('2.5', None, expected_version='2.5')
+
+    def test_class_min_version_none(self):
+        self._test_request_version(None, '2.3', expected_version='2.3')
+
+    def test_both_min_version_none(self):
+        self._test_request_version(None, None, expected_version=None)
+
+    def test_both_min_version_equal(self):
+        self._test_request_version('2.3', '2.3', expected_version='2.3')
+
+
+class TestMicroversionHeaderMatches(base.TestCase):
+
+    def test_header_matches(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_respose = {microversion_header_name: request_microversion}
+        api_version_utils.assert_version_header_matches_request(
+            microversion_header_name, request_microversion, test_respose)
+
+    def test_header_does_not_match(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_respose = {microversion_header_name: '2.2'}
+        self.assertRaises(
+            exceptions.InvalidHTTPResponseHeader,
+            api_version_utils.assert_version_header_matches_request,
+            microversion_header_name, request_microversion, test_respose)
+
+    def test_header_not_present(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_respose = {}
+        self.assertRaises(
+            exceptions.InvalidHTTPResponseHeader,
+            api_version_utils.assert_version_header_matches_request,
+            microversion_header_name, request_microversion, test_respose)
diff --git a/tempest/tests/lib/common/test_cred_client.py b/tempest/tests/lib/common/test_cred_client.py
new file mode 100644
index 0000000..1cb3103
--- /dev/null
+++ b/tempest/tests/lib/common/test_cred_client.py
@@ -0,0 +1,78 @@
+# Copyright 2016 Hewlett Packard Enterprise Development LP
+# 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.lib.common import cred_client
+from tempest.tests import base
+
+
+class TestCredClientV2(base.TestCase):
+    def setUp(self):
+        super(TestCredClientV2, self).setUp()
+        self.identity_client = mock.MagicMock()
+        self.projects_client = mock.MagicMock()
+        self.users_client = mock.MagicMock()
+        self.roles_client = mock.MagicMock()
+        self.creds_client = cred_client.V2CredsClient(self.identity_client,
+                                                      self.projects_client,
+                                                      self.users_client,
+                                                      self.roles_client)
+
+    def test_create_project(self):
+        self.projects_client.create_tenant.return_value = {
+            'tenant': 'a_tenant'
+        }
+        res = self.creds_client.create_project('fake_name', 'desc')
+        self.assertEqual('a_tenant', res)
+        self.projects_client.create_tenant.assert_called_once_with(
+            name='fake_name', description='desc')
+
+    def test_delete_project(self):
+        self.creds_client.delete_project('fake_id')
+        self.projects_client.delete_tenant.assert_called_once_with(
+            'fake_id')
+
+
+class TestCredClientV3(base.TestCase):
+    def setUp(self):
+        super(TestCredClientV3, self).setUp()
+        self.identity_client = mock.MagicMock()
+        self.projects_client = mock.MagicMock()
+        self.users_client = mock.MagicMock()
+        self.roles_client = mock.MagicMock()
+        self.domains_client = mock.MagicMock()
+        self.domains_client.list_domains.return_value = {
+            'domains': [{'id': 'fake_domain_id'}]
+        }
+        self.creds_client = cred_client.V3CredsClient(self.identity_client,
+                                                      self.projects_client,
+                                                      self.users_client,
+                                                      self.roles_client,
+                                                      self.domains_client,
+                                                      'fake_domain')
+
+    def test_create_project(self):
+        self.projects_client.create_project.return_value = {
+            'project': 'a_tenant'
+        }
+        res = self.creds_client.create_project('fake_name', 'desc')
+        self.assertEqual('a_tenant', res)
+        self.projects_client.create_project.assert_called_once_with(
+            name='fake_name', description='desc', domain_id='fake_domain_id')
+
+    def test_delete_project(self):
+        self.creds_client.delete_project('fake_id')
+        print(self.projects_client.calls)
+        self.projects_client.delete_project.assert_called_once_with(
+            'fake_id')
diff --git a/tempest/tests/negative/__init__.py b/tempest/tests/lib/common/utils/__init__.py
similarity index 100%
copy from tempest/tests/negative/__init__.py
copy to tempest/tests/lib/common/utils/__init__.py
diff --git a/tempest/tests/lib/common/utils/test_data_utils.py b/tempest/tests/lib/common/utils/test_data_utils.py
new file mode 100644
index 0000000..4446e5c
--- /dev/null
+++ b/tempest/tests/lib/common/utils/test_data_utils.py
@@ -0,0 +1,178 @@
+# 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.
+
+import netaddr
+
+from tempest.lib.common.utils import data_utils
+from tempest.tests import base
+
+
+class TestDataUtils(base.TestCase):
+
+    def test_rand_uuid(self):
+        actual = data_utils.rand_uuid()
+        self.assertIsInstance(actual, str)
+        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.assertRegex(actual, "^[0-9a-f]{32}$")
+
+        actual2 = data_utils.rand_uuid_hex()
+        self.assertNotEqual(actual, actual2)
+
+    def test_rand_name(self):
+        actual = data_utils.rand_name()
+        self.assertIsInstance(actual, str)
+        actual2 = data_utils.rand_name()
+        self.assertNotEqual(actual, actual2)
+
+        actual = data_utils.rand_name('foo')
+        self.assertTrue(actual.startswith('foo'))
+        actual2 = data_utils.rand_name('foo')
+        self.assertTrue(actual.startswith('foo'))
+        self.assertNotEqual(actual, actual2)
+
+    def test_rand_name_with_prefix(self):
+        actual = data_utils.rand_name(prefix='prefix-str')
+        self.assertIsInstance(actual, 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.assertRegex(actual, "[A-Za-z0-9~!@#%^&*_=+]{15,}")
+        actual2 = data_utils.rand_password()
+        self.assertNotEqual(actual, actual2)
+
+    def test_rand_password_with_len(self):
+        actual = data_utils.rand_password(8)
+        self.assertIsInstance(actual, str)
+        self.assertEqual(len(actual), 8)
+        self.assertRegex(actual, "[A-Za-z0-9~!@#%^&*_=+]{8}")
+        actual2 = data_utils.rand_password(8)
+        self.assertNotEqual(actual, actual2)
+
+    def test_rand_password_with_len_2(self):
+        actual = data_utils.rand_password(2)
+        self.assertIsInstance(actual, str)
+        self.assertEqual(len(actual), 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.assertRegex(actual, "^https://url-[0-9]*\.com$")
+        actual2 = data_utils.rand_url()
+        self.assertNotEqual(actual, actual2)
+
+    def test_rand_int(self):
+        actual = data_utils.rand_int_id()
+        self.assertIsInstance(actual, int)
+
+        actual2 = data_utils.rand_int_id()
+        self.assertNotEqual(actual, actual2)
+
+    def test_rand_infiniband_guid_address(self):
+        actual = data_utils.rand_infiniband_guid_address()
+        self.assertIsInstance(actual, str)
+        self.assertRegex(actual, "^([0-9a-f][0-9a-f]:){7}"
+                         "[0-9a-f][0-9a-f]$")
+
+        actual2 = data_utils.rand_infiniband_guid_address()
+        self.assertNotEqual(actual, actual2)
+
+    def test_rand_mac_address(self):
+        actual = data_utils.rand_mac_address()
+        self.assertIsInstance(actual, str)
+        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)
+
+    def test_parse_image_id(self):
+        actual = data_utils.parse_image_id("/foo/bar/deadbeaf")
+        self.assertEqual("deadbeaf", actual)
+
+    def test_arbitrary_string(self):
+        actual = data_utils.arbitrary_string()
+        self.assertEqual(actual, "test")
+        actual = data_utils.arbitrary_string(size=30, base_text="abc")
+        self.assertEqual(actual, "abc" * int(30 / len("abc")))
+        actual = data_utils.arbitrary_string(size=5, base_text="deadbeaf")
+        self.assertEqual(actual, "deadb")
+
+    def test_random_bytes(self):
+        actual = data_utils.random_bytes()  # default size=1024
+        self.assertIsInstance(actual, bytes)
+        self.assertEqual(1024, len(actual))
+        actual2 = data_utils.random_bytes()
+        self.assertNotEqual(actual, actual2)
+
+        actual = data_utils.random_bytes(size=2048)
+        self.assertEqual(2048, len(actual))
+
+    def test_get_ipv6_addr_by_EUI64(self):
+        actual = data_utils.get_ipv6_addr_by_EUI64('2001:db8::',
+                                                   '00:16:3e:33:44:55')
+        self.assertIsInstance(actual, netaddr.IPAddress)
+        self.assertEqual(actual,
+                         netaddr.IPAddress('2001:db8::216:3eff:fe33:4455'))
+
+    def test_get_ipv6_addr_by_EUI64_with_IPv4_prefix(self):
+        ipv4_prefix = '10.0.8'
+        mac = '00:16:3e:33:44:55'
+        self.assertRaises(TypeError, data_utils.get_ipv6_addr_by_EUI64,
+                          ipv4_prefix, mac)
+
+    def test_get_ipv6_addr_by_EUI64_bad_cidr_type(self):
+        bad_cidr = 123
+        mac = '00:16:3e:33:44:55'
+        self.assertRaises(TypeError, data_utils.get_ipv6_addr_by_EUI64,
+                          bad_cidr, mac)
+
+    def test_get_ipv6_addr_by_EUI64_bad_cidr_value(self):
+        bad_cidr = 'bb'
+        mac = '00:16:3e:33:44:55'
+        self.assertRaises(TypeError, data_utils.get_ipv6_addr_by_EUI64,
+                          bad_cidr, mac)
+
+    def test_get_ipv6_addr_by_EUI64_bad_mac_value(self):
+        cidr = '2001:db8::'
+        bad_mac = '00:16:3e:33:44:5Z'
+        self.assertRaises(TypeError, data_utils.get_ipv6_addr_by_EUI64,
+                          cidr, bad_mac)
+
+    def test_get_ipv6_addr_by_EUI64_bad_mac_type(self):
+        cidr = '2001:db8::'
+        bad_mac = 99999999999999999999
+        self.assertRaises(TypeError, data_utils.get_ipv6_addr_by_EUI64,
+                          cidr, bad_mac)
+
+    def test_chunkify(self):
+        data = "aaa"
+        chunks = data_utils.chunkify(data, 2)
+        self.assertEqual("aa", next(chunks))
+        self.assertEqual("a", next(chunks))
+        self.assertRaises(StopIteration, next, chunks)
diff --git a/tempest/tests/lib/common/utils/test_misc.py b/tempest/tests/lib/common/utils/test_misc.py
new file mode 100644
index 0000000..47e81d1
--- /dev/null
+++ b/tempest/tests/lib/common/utils/test_misc.py
@@ -0,0 +1,52 @@
+# 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.
+
+
+from tempest.lib.common.utils import misc
+from tempest.tests import base
+
+
+@misc.singleton
+class TestFoo(object):
+
+    count = 0
+
+    def increment(self):
+        self.count += 1
+        return self.count
+
+
+@misc.singleton
+class TestBar(object):
+
+    count = 0
+
+    def increment(self):
+        self.count += 1
+        return self.count
+
+
+class TestMisc(base.TestCase):
+
+    def test_singleton(self):
+        test = TestFoo()
+        self.assertEqual(0, test.count)
+        self.assertEqual(1, test.increment())
+        test2 = TestFoo()
+        self.assertEqual(1, test.count)
+        self.assertEqual(1, test2.count)
+        self.assertEqual(test, test2)
+        test3 = TestBar()
+        self.assertNotEqual(test, test3)
diff --git a/tempest/tests/lib/common/utils/test_test_utils.py b/tempest/tests/lib/common/utils/test_test_utils.py
new file mode 100644
index 0000000..29c5684
--- /dev/null
+++ b/tempest/tests/lib/common/utils/test_test_utils.py
@@ -0,0 +1,103 @@
+# Copyright 2016 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 mock
+
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
+from tempest.tests import base
+from tempest.tests import utils
+
+
+class TestTestUtils(base.TestCase):
+
+    def test_find_test_caller_test_case(self):
+        # Calling it from here should give us the method we're in.
+        self.assertEqual('TestTestUtils:test_find_test_caller_test_case',
+                         test_utils.find_test_caller())
+
+    def test_find_test_caller_setup_self(self):
+        def setUp(self):
+            return test_utils.find_test_caller()
+        self.assertEqual('TestTestUtils:setUp', setUp(self))
+
+    def test_find_test_caller_setup_no_self(self):
+        def setUp():
+            return test_utils.find_test_caller()
+        self.assertEqual(':setUp', setUp())
+
+    def test_find_test_caller_setupclass_cls(self):
+        def setUpClass(cls):  # noqa
+            return test_utils.find_test_caller()
+        self.assertEqual('TestTestUtils:setUpClass',
+                         setUpClass(self.__class__))
+
+    def test_find_test_caller_teardown_self(self):
+        def tearDown(self):
+            return test_utils.find_test_caller()
+        self.assertEqual('TestTestUtils:tearDown', tearDown(self))
+
+    def test_find_test_caller_teardown_no_self(self):
+        def tearDown():
+            return test_utils.find_test_caller()
+        self.assertEqual(':tearDown', tearDown())
+
+    def test_find_test_caller_teardown_class(self):
+        def tearDownClass(cls):  # noqa
+            return test_utils.find_test_caller()
+        self.assertEqual('TestTestUtils:tearDownClass',
+                         tearDownClass(self.__class__))
+
+    def test_call_and_ignore_notfound_exc_when_notfound_raised(self):
+        def raise_not_found():
+            raise exceptions.NotFound()
+        self.assertIsNone(
+            test_utils.call_and_ignore_notfound_exc(raise_not_found))
+
+    def test_call_and_ignore_notfound_exc_when_value_error_raised(self):
+        def raise_value_error():
+            raise ValueError()
+        self.assertRaises(ValueError, test_utils.call_and_ignore_notfound_exc,
+                          raise_value_error)
+
+    def test_call_and_ignore_notfound_exc(self):
+        m = mock.Mock(return_value=42)
+        args, kwargs = (1,), {'1': None}
+        self.assertEqual(
+            42, test_utils.call_and_ignore_notfound_exc(m, *args, **kwargs))
+        m.assert_called_once_with(*args, **kwargs)
+
+    @mock.patch('time.sleep')
+    @mock.patch('time.time')
+    def test_call_until_true_when_f_never_returns_true(self, m_time, m_sleep):
+        timeout = 42  # The value doesn't matter as we mock time.time()
+        sleep = 60  # The value doesn't matter as we mock time.sleep()
+        m_time.side_effect = utils.generate_timeout_series(timeout)
+        self.assertEqual(
+            False, test_utils.call_until_true(lambda: False, timeout, sleep)
+        )
+        m_sleep.call_args_list = [mock.call(sleep)] * 2
+        m_time.call_args_list = [mock.call()] * 2
+
+    @mock.patch('time.sleep')
+    @mock.patch('time.time')
+    def test_call_until_true_when_f_returns_true(self, m_time, m_sleep):
+        timeout = 42  # The value doesn't matter as we mock time.time()
+        sleep = 60  # The value doesn't matter as we mock time.sleep()
+        m_time.return_value = 0
+        self.assertEqual(
+            True, test_utils.call_until_true(lambda: True, timeout, sleep)
+        )
+        self.assertEqual(0, m_sleep.call_count)
+        self.assertEqual(1, m_time.call_count)
diff --git a/tempest/tests/lib/fake_auth_provider.py b/tempest/tests/lib/fake_auth_provider.py
new file mode 100644
index 0000000..e4582f8
--- /dev/null
+++ b/tempest/tests/lib/fake_auth_provider.py
@@ -0,0 +1,38 @@
+# Copyright 2014 Hewlett-Packard Development Company, L.P.
+# 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.
+
+
+class FakeAuthProvider(object):
+
+    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 self.fake_base_url or "https://example.com"
+
+    def get_token(self):
+        return "faketoken"
+
+
+class FakeCredentials(object):
+
+    def __init__(self, creds_dict):
+        for key in creds_dict:
+            setattr(self, key, creds_dict[key])
diff --git a/tempest/tests/lib/fake_credentials.py b/tempest/tests/lib/fake_credentials.py
new file mode 100644
index 0000000..eac4ada
--- /dev/null
+++ b/tempest/tests/lib/fake_credentials.py
@@ -0,0 +1,74 @@
+# Copyright 2014 Hewlett-Packard Development Company, L.P.
+# 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 import auth
+
+
+class FakeCredentials(auth.Credentials):
+
+    def is_valid(self):
+        return True
+
+
+class FakeKeystoneV2Credentials(auth.KeystoneV2Credentials):
+
+    def __init__(self):
+        creds = dict(
+            username='fake_username',
+            password='fake_password',
+            tenant_name='fake_tenant_name'
+        )
+        super(FakeKeystoneV2Credentials, self).__init__(**creds)
+
+
+class FakeKeystoneV3Credentials(auth.KeystoneV3Credentials):
+    """Fake credentials suitable for the Keystone Identity V3 API"""
+
+    def __init__(self):
+        creds = dict(
+            username='fake_username',
+            password='fake_password',
+            user_domain_name='fake_domain_name',
+            project_name='fake_tenant_name',
+            project_domain_name='fake_domain_name'
+        )
+        super(FakeKeystoneV3Credentials, self).__init__(**creds)
+
+
+class FakeKeystoneV3DomainCredentials(auth.KeystoneV3Credentials):
+    """Fake credentials for the Keystone Identity V3 API, with no scope"""
+
+    def __init__(self):
+        creds = dict(
+            username='fake_username',
+            password='fake_password',
+            user_domain_name='fake_domain_name'
+        )
+        super(FakeKeystoneV3DomainCredentials, self).__init__(**creds)
+
+
+class FakeKeystoneV3AllCredentials(auth.KeystoneV3Credentials):
+    """Fake credentials for the Keystone Identity V3 API, with no scope"""
+
+    def __init__(self):
+        creds = dict(
+            username='fake_username',
+            password='fake_password',
+            user_domain_name='fake_domain_name',
+            project_name='fake_tenant_name',
+            project_domain_name='fake_domain_name',
+            domain_name='fake_domain_name'
+        )
+        super(FakeKeystoneV3AllCredentials, self).__init__(**creds)
diff --git a/tempest/tests/lib/fake_http.py b/tempest/tests/lib/fake_http.py
new file mode 100644
index 0000000..cfa4b93
--- /dev/null
+++ b/tempest/tests/lib/fake_http.py
@@ -0,0 +1,77 @@
+# 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
+
+
+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, chunked=False):
+        if not self.return_type:
+            fake_headers = fake_http_response(headers)
+            return_obj = {
+                'uri': uri,
+                'method': method,
+                'body': body,
+                'headers': headers
+            }
+            return (fake_headers, return_obj)
+        elif isinstance(self.return_type, int):
+            body = body or "fake_body"
+            header_info = {
+                'content-type': 'text/plain',
+                'content-length': len(body)
+            }
+            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_http_response(dict):
+    def __init__(self, headers, body=None,
+                 version=1.0, status=200, reason="Ok"):
+        """Initialization of fake HTTP Response
+
+        :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['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()
+
+    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/lib/fake_identity.py b/tempest/tests/lib/fake_identity.py
new file mode 100644
index 0000000..8bae34f
--- /dev/null
+++ b/tempest/tests/lib/fake_identity.py
@@ -0,0 +1,240 @@
+# 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.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.tests.lib import fake_http
+
+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_alt_tenant_id"
+            },
+        },
+        "user": {
+            "id": "fake_alt_user_id",
+            "password_expires_at": None,
+        },
+        "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",
+            "password_expires_at": None,
+        },
+        "serviceCatalog": CATALOG_V2,
+    },
+}
+
+# Fake Identity V3 constants
+COMPUTE_ENDPOINTS_V3 = {
+    "endpoints": [
+        {
+            "id": "first_compute_fake_service",
+            "interface": "public",
+            "region": "NoMatchRegion",
+            "region_id": "NoMatchRegion",
+            "url": "http://fake_url/v3/first_endpoint/api"
+        },
+        {
+            "id": "second_fake_service",
+            "interface": "public",
+            "region": "FakeRegion",
+            "region_id": "FakeRegion",
+            "url": "http://fake_url/v3/second_endpoint/api"
+        },
+        {
+            "id": "third_fake_service",
+            "interface": "admin",
+            "region": "MiddleEarthRegion",
+            "region_id": "MiddleEarthRegion",
+            "url": "http://fake_url/v3/third_endpoint/api"
+        }
+
+    ],
+    "type": "compute",
+    "id": "fake_compute_endpoint",
+    "name": "nova"
+}
+
+CATALOG_V3 = [COMPUTE_ENDPOINTS_V3, ]
+
+IDENTITY_V3_RESPONSE = {
+    "token": {
+        "audit_ids": ["ny5LA5YXToa_mAVO8Hnupw", "9NPTvsRDSkmsW61abP978Q"],
+        "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",
+            "password_expires_at": None,
+        },
+        "issued_at": "2013-05-29T16:55:21.468960Z",
+        "catalog": CATALOG_V3
+    }
+}
+
+IDENTITY_V3_RESPONSE_DOMAIN_SCOPE = {
+    "token": {
+        "audit_ids": ["ny5LA5YXToa_mAVO8Hnupw", "9NPTvsRDSkmsW61abP978Q"],
+        "methods": [
+            "token",
+            "password"
+        ],
+        "expires_at": "2020-01-01T00:00:10.000123Z",
+        "domain": {
+            "id": "fake_domain_id",
+            "name": "domain_name"
+        },
+        "user": {
+            "domain": {
+                "id": "fake_domain_id",
+                "name": "domain_name"
+            },
+            "id": "fake_user_id",
+            "name": "username",
+            "password_expires_at": None,
+        },
+        "issued_at": "2013-05-29T16:55:21.468960Z",
+        "catalog": CATALOG_V3
+    }
+}
+
+IDENTITY_V3_RESPONSE_NO_SCOPE = {
+    "token": {
+        "audit_ids": ["ny5LA5YXToa_mAVO8Hnupw", "9NPTvsRDSkmsW61abP978Q"],
+        "methods": [
+            "token",
+            "password"
+        ],
+        "expires_at": "2020-01-01T00:00:10.000123Z",
+        "user": {
+            "domain": {
+                "id": "fake_domain_id",
+                "name": "domain_name"
+            },
+            "id": "fake_user_id",
+            "name": "username",
+            "password_expires_at": None,
+        },
+        "issued_at": "2013-05-29T16:55:21.468960Z",
+    }
+}
+
+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 = {
+        "x-subject-token": TOKEN
+    }
+    return (fake_http.fake_http_response(fake_headers, status=201),
+            json.dumps(IDENTITY_V3_RESPONSE))
+
+
+def _fake_v3_response_domain_scope(self, uri, method="GET", body=None,
+                                   headers=None, redirections=5,
+                                   connection_type=None):
+    fake_headers = {
+        "status": "201",
+        "x-subject-token": TOKEN
+    }
+    return (fake_http.fake_http_response(fake_headers, status=201),
+            json.dumps(IDENTITY_V3_RESPONSE_DOMAIN_SCOPE))
+
+
+def _fake_v3_response_no_scope(self, uri, method="GET", body=None,
+                               headers=None, redirections=5,
+                               connection_type=None):
+    fake_headers = {
+        "status": "201",
+        "x-subject-token": TOKEN
+    }
+    return (fake_http.fake_http_response(fake_headers, status=201),
+            json.dumps(IDENTITY_V3_RESPONSE_NO_SCOPE))
+
+
+def _fake_v2_response(self, uri, method="GET", body=None, headers=None,
+                      redirections=5, connection_type=None):
+    return (fake_http.fake_http_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 fake_http.fake_http_response({}, status=401), json.dumps(body)
diff --git a/tempest/tests/stress/__init__.py b/tempest/tests/lib/services/__init__.py
similarity index 100%
copy from tempest/tests/stress/__init__.py
copy to tempest/tests/lib/services/__init__.py
diff --git a/tempest/tests/lib/services/base.py b/tempest/tests/lib/services/base.py
new file mode 100644
index 0000000..a244aa2
--- /dev/null
+++ b/tempest/tests/lib/services/base.py
@@ -0,0 +1,42 @@
+# 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.
+
+from oslo_serialization import jsonutils as json
+from oslotest import mockpatch
+
+from tempest.tests import base
+from tempest.tests.lib import fake_http
+
+
+class BaseServiceTest(base.TestCase):
+    def create_response(self, body, to_utf=False, status=200, headers=None):
+        json_body = {}
+        if body:
+            json_body = json.dumps(body)
+            if to_utf:
+                json_body = json_body.encode('utf-8')
+        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,
+                                      headers=None, **kwargs):
+        mocked_response = self.create_response(body, to_utf, status, headers)
+        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/__init__.py b/tempest/tests/lib/services/compute/__init__.py
similarity index 100%
rename from tempest/tests/services/compute/__init__.py
rename to tempest/tests/lib/services/compute/__init__.py
diff --git a/tempest/tests/lib/services/compute/test_agents_client.py b/tempest/tests/lib/services/compute/test_agents_client.py
new file mode 100644
index 0000000..880220e
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_agents_client.py
@@ -0,0 +1,103 @@
+# 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.services.compute import agents_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestAgentsClient(base.BaseServiceTest):
+    FAKE_CREATE_AGENT = {
+        "agent": {
+            "url": "http://foo.com",
+            "hypervisor": "kvm",
+            "md5hash": "md5",
+            "version": "2",
+            "architecture": "x86_64",
+            "os": "linux",
+            "agent_id": 1
+        }
+    }
+
+    FAKE_UPDATE_AGENT = {
+        "agent": {
+            "url": "http://foo.com",
+            "md5hash": "md5",
+            "version": "2",
+            "agent_id": 1
+        }
+    }
+
+    def setUp(self):
+        super(TestAgentsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = agents_client.AgentsClient(fake_auth,
+                                                 'compute', 'regionOne')
+
+    def _test_list_agents(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_agents,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"agents": []},
+            bytes_body)
+        self.check_service_client_function(
+            self.client.list_agents,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"agents": []},
+            bytes_body,
+            hypervisor="kvm")
+
+    def _test_create_agent(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_agent,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_AGENT,
+            bytes_body,
+            url="http://foo.com", hypervisor="kvm", md5hash="md5",
+            version="2", architecture="x86_64", os="linux")
+
+    def _test_delete_agent(self):
+        self.check_service_client_function(
+            self.client.delete_agent,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, agent_id="1")
+
+    def _test_update_agent(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_agent,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_AGENT,
+            bytes_body,
+            agent_id="1", url="http://foo.com", md5hash="md5", version="2")
+
+    def test_list_agents_with_str_body(self):
+        self._test_list_agents()
+
+    def test_list_agents_with_bytes_body(self):
+        self._test_list_agents(bytes_body=True)
+
+    def test_create_agent_with_str_body(self):
+        self._test_create_agent()
+
+    def test_create_agent_with_bytes_body(self):
+        self._test_create_agent(bytes_body=True)
+
+    def test_delete_agent(self):
+        self._test_delete_agent()
+
+    def test_update_agent_with_str_body(self):
+        self._test_update_agent()
+
+    def test_update_agent_with_bytes_body(self):
+        self._test_update_agent(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_aggregates_client.py b/tempest/tests/lib/services/compute/test_aggregates_client.py
new file mode 100644
index 0000000..674d92a
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_aggregates_client.py
@@ -0,0 +1,192 @@
+# 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.services.compute import aggregates_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestAggregatesClient(base.BaseServiceTest):
+    FAKE_SHOW_AGGREGATE = {
+        "aggregate":
+        {
+            "name": "hoge",
+            "availability_zone": None,
+            "deleted": False,
+            "created_at":
+            "2015-07-16T03:07:32.000000",
+            "updated_at": None,
+            "hosts": [],
+            "deleted_at": None,
+            "id": 1,
+            "metadata": {}
+        }
+    }
+
+    FAKE_CREATE_AGGREGATE = {
+        "aggregate":
+        {
+            "name": u'\xf4',
+            "availability_zone": None,
+            "deleted": False,
+            "created_at": "2015-07-21T04:11:18.000000",
+            "updated_at": None,
+            "deleted_at": None,
+            "id": 1
+        }
+    }
+
+    FAKE_UPDATE_AGGREGATE = {
+        "aggregate":
+        {
+            "name": u'\xe9',
+            "availability_zone": None,
+            "deleted": False,
+            "created_at": "2015-07-16T03:07:32.000000",
+            "updated_at": "2015-07-23T05:16:29.000000",
+            "hosts": [],
+            "deleted_at": None,
+            "id": 1,
+            "metadata": {}
+        }
+    }
+
+    FAKE_AGGREGATE = {
+        "availability_zone": "nova",
+        "created_at": "2013-08-18T12:17:56.297823",
+        "deleted": False,
+        "deleted_at": None,
+        "hosts": [
+            "21549b2f665945baaa7101926a00143c"
+        ],
+        "id": 1,
+        "metadata": {
+            "availability_zone": "nova"
+        },
+        "name": u'\xe9',
+        "updated_at": None
+    }
+
+    FAKE_ADD_HOST = {'aggregate': FAKE_AGGREGATE}
+    FAKE_REMOVE_HOST = {'aggregate': FAKE_AGGREGATE}
+    FAKE_SET_METADATA = {'aggregate': FAKE_AGGREGATE}
+
+    def setUp(self):
+        super(TestAggregatesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = aggregates_client.AggregatesClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_list_aggregates(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_aggregates,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"aggregates": []},
+            bytes_body)
+
+    def test_list_aggregates_with_str_body(self):
+        self._test_list_aggregates()
+
+    def test_list_aggregates_with_bytes_body(self):
+        self._test_list_aggregates(bytes_body=True)
+
+    def _test_show_aggregate(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_aggregate,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SHOW_AGGREGATE,
+            bytes_body,
+            aggregate_id=1)
+
+    def test_show_aggregate_with_str_body(self):
+        self._test_show_aggregate()
+
+    def test_show_aggregate_with_bytes_body(self):
+        self._test_show_aggregate(bytes_body=True)
+
+    def _test_create_aggregate(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_aggregate,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_AGGREGATE,
+            bytes_body,
+            name='hoge')
+
+    def test_create_aggregate_with_str_body(self):
+        self._test_create_aggregate()
+
+    def test_create_aggregate_with_bytes_body(self):
+        self._test_create_aggregate(bytes_body=True)
+
+    def test_delete_aggregate(self):
+        self.check_service_client_function(
+            self.client.delete_aggregate,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, aggregate_id="1")
+
+    def _test_update_aggregate(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_aggregate,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_AGGREGATE,
+            bytes_body,
+            aggregate_id=1)
+
+    def test_update_aggregate_with_str_body(self):
+        self._test_update_aggregate()
+
+    def test_update_aggregate_with_bytes_body(self):
+        self._test_update_aggregate(bytes_body=True)
+
+    def _test_add_host(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.add_host,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_ADD_HOST,
+            bytes_body,
+            aggregate_id=1)
+
+    def test_add_host_with_str_body(self):
+        self._test_add_host()
+
+    def test_add_host_with_bytes_body(self):
+        self._test_add_host(bytes_body=True)
+
+    def _test_remove_host(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.remove_host,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_REMOVE_HOST,
+            bytes_body,
+            aggregate_id=1)
+
+    def test_remove_host_with_str_body(self):
+        self._test_remove_host()
+
+    def test_remove_host_with_bytes_body(self):
+        self._test_remove_host(bytes_body=True)
+
+    def _test_set_metadata(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.set_metadata,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_SET_METADATA,
+            bytes_body,
+            aggregate_id=1)
+
+    def test_set_metadata_with_str_body(self):
+        self._test_set_metadata()
+
+    def test_set_metadata_with_bytes_body(self):
+        self._test_set_metadata(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_availability_zone_client.py b/tempest/tests/lib/services/compute/test_availability_zone_client.py
new file mode 100644
index 0000000..6608592
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_availability_zone_client.py
@@ -0,0 +1,51 @@
+# 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.services.compute import availability_zone_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestAvailabilityZoneClient(base.BaseServiceTest):
+
+    FAKE_AVAILABIRITY_ZONE_INFO = {
+        "availabilityZoneInfo":
+        [
+            {
+                "zoneState": {
+                    "available": True
+                },
+                "hosts": None,
+                "zoneName": u'\xf4'
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestAvailabilityZoneClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = availability_zone_client.AvailabilityZoneClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def test_list_availability_zones_with_str_body(self):
+        self.check_service_client_function(
+            self.client.list_availability_zones,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_AVAILABIRITY_ZONE_INFO)
+
+    def test_list_availability_zones_with_bytes_body(self):
+        self.check_service_client_function(
+            self.client.list_availability_zones,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_AVAILABIRITY_ZONE_INFO, to_utf=True)
diff --git a/tempest/tests/lib/services/compute/test_baremetal_nodes_client.py b/tempest/tests/lib/services/compute/test_baremetal_nodes_client.py
new file mode 100644
index 0000000..81c54b5
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_baremetal_nodes_client.py
@@ -0,0 +1,74 @@
+# 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.lib.services.compute import baremetal_nodes_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestBareMetalNodesClient(base.BaseServiceTest):
+
+    FAKE_NODE_INFO = {'cpus': '8',
+                      'disk_gb': '64',
+                      'host': '10.0.2.15',
+                      'id': 'Identifier',
+                      'instance_uuid': "null",
+                      'interfaces': [
+                          {
+                              "address": "20::01",
+                              "datapath_id": "null",
+                              "id": 1,
+                              "port_no": None
+                          }
+                      ],
+                      'memory_mb': '8192',
+                      'task_state': None}
+
+    def setUp(self):
+        super(TestBareMetalNodesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.baremetal_nodes_client = (baremetal_nodes_client.
+                                       BaremetalNodesClient
+                                       (fake_auth, 'compute',
+                                        'regionOne'))
+
+    def _test_bareMetal_nodes(self, operation='list', bytes_body=False):
+        if operation != 'list':
+            expected = {"node": self.FAKE_NODE_INFO}
+            function = self.baremetal_nodes_client.show_baremetal_node
+        else:
+            node_info = copy.deepcopy(self.FAKE_NODE_INFO)
+            del node_info['instance_uuid']
+            expected = {"nodes": [node_info]}
+            function = self.baremetal_nodes_client.list_baremetal_nodes
+
+        self.check_service_client_function(
+            function,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected, bytes_body, 200,
+            baremetal_node_id='Identifier')
+
+    def test_list_bareMetal_nodes_with_str_body(self):
+        self._test_bareMetal_nodes()
+
+    def test_list_bareMetal_nodes_with_bytes_body(self):
+        self._test_bareMetal_nodes(bytes_body=True)
+
+    def test_show_bareMetal_node_with_str_body(self):
+        self._test_bareMetal_nodes('show')
+
+    def test_show_bareMetal_node_with_bytes_body(self):
+        self._test_bareMetal_nodes('show', True)
diff --git a/tempest/tests/lib/services/compute/test_base_compute_client.py b/tempest/tests/lib/services/compute/test_base_compute_client.py
new file mode 100644
index 0000000..69e8542
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_base_compute_client.py
@@ -0,0 +1,206 @@
+# 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
+
+from tempest.lib.common import rest_client
+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 import base
+
+
+class TestMicroversionHeaderCheck(base.BaseServiceTest):
+
+    def setUp(self):
+        super(TestMicroversionHeaderCheck, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = base_compute_client.BaseComputeClient(
+            fake_auth, 'compute', 'regionOne')
+        base_compute_client.COMPUTE_MICROVERSION = '2.2'
+
+    def tearDown(self):
+        super(TestMicroversionHeaderCheck, self).tearDown()
+        base_compute_client.COMPUTE_MICROVERSION = None
+
+    @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')
+
+    @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')
+
+    @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')
+
+
+class DummyServiceClient1(base_compute_client.BaseComputeClient):
+    schema_versions_info = [
+        {'min': None, 'max': '2.1', 'schema': 'schemav21'},
+        {'min': '2.2', 'max': '2.9', 'schema': 'schemav22'},
+        {'min': '2.10', 'max': None, 'schema': 'schemav210'}]
+
+    def return_selected_schema(self):
+        return self.get_schema(self.schema_versions_info)
+
+
+class TestSchemaVersionsNone(base.BaseServiceTest):
+    api_microversion = None
+    expected_schema = 'schemav21'
+
+    def setUp(self):
+        super(TestSchemaVersionsNone, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = DummyServiceClient1(fake_auth, 'compute', 'regionOne')
+        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,
+                         self.client.return_selected_schema())
+
+
+class TestSchemaVersionsV21(TestSchemaVersionsNone):
+    api_microversion = '2.1'
+    expected_schema = 'schemav21'
+
+
+class TestSchemaVersionsV22(TestSchemaVersionsNone):
+    api_microversion = '2.2'
+    expected_schema = 'schemav22'
+
+
+class TestSchemaVersionsV25(TestSchemaVersionsNone):
+    api_microversion = '2.5'
+    expected_schema = 'schemav22'
+
+
+class TestSchemaVersionsV29(TestSchemaVersionsNone):
+    api_microversion = '2.9'
+    expected_schema = 'schemav22'
+
+
+class TestSchemaVersionsV210(TestSchemaVersionsNone):
+    api_microversion = '2.10'
+    expected_schema = 'schemav210'
+
+
+class TestSchemaVersionsLatest(TestSchemaVersionsNone):
+    api_microversion = 'latest'
+    expected_schema = 'schemav210'
+
+
+class DummyServiceClient2(base_compute_client.BaseComputeClient):
+    schema_versions_info = [
+        {'min': None, 'max': '2.1', 'schema': 'schemav21'},
+        {'min': '2.2', 'max': '2.9', 'schema': 'schemav22'}]
+
+    def return_selected_schema(self):
+        return self.get_schema(self.schema_versions_info)
+
+
+class TestSchemaVersionsNotFound(base.BaseServiceTest):
+    api_microversion = '2.10'
+    expected_schema = 'schemav210'
+
+    def setUp(self):
+        super(TestSchemaVersionsNotFound, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = DummyServiceClient2(fake_auth, 'compute', 'regionOne')
+        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,
+                          self.client.return_selected_schema)
+
+
+class TestClientWithoutMicroversionHeader(base.BaseServiceTest):
+
+    def setUp(self):
+        super(TestClientWithoutMicroversionHeader, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = base_compute_client.BaseComputeClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def test_no_microverion_header(self):
+        header = self.client.get_headers()
+        self.assertNotIn('X-OpenStack-Nova-API-Version', header)
+
+    def test_no_microverion_header_in_raw_request(self):
+        def raw_request(*args, **kwargs):
+            self.assertNotIn('X-OpenStack-Nova-API-Version', kwargs['headers'])
+            return (fake_http.fake_http_response({}, status=200), '')
+
+        with mock.patch.object(rest_client.RestClient,
+                               'raw_request') as mock_get:
+            mock_get.side_effect = raw_request
+            self.client.get('fake_url')
+
+
+class TestClientWithMicroversionHeader(base.BaseServiceTest):
+
+    def setUp(self):
+        super(TestClientWithMicroversionHeader, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = base_compute_client.BaseComputeClient(
+            fake_auth, 'compute', 'regionOne')
+        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()
+        self.assertIn('X-OpenStack-Nova-API-Version', header)
+        self.assertEqual('2.2',
+                         header['X-OpenStack-Nova-API-Version'])
+
+    def test_microverion_header_in_raw_request(self):
+        def raw_request(*args, **kwargs):
+            self.assertIn('X-OpenStack-Nova-API-Version', kwargs['headers'])
+            self.assertEqual('2.2',
+                             kwargs['headers']['X-OpenStack-Nova-API-Version'])
+            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:
+            mock_get.side_effect = raw_request
+            self.client.get('fake_url')
diff --git a/tempest/tests/lib/services/compute/test_certificates_client.py b/tempest/tests/lib/services/compute/test_certificates_client.py
new file mode 100644
index 0000000..9faef6f
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_certificates_client.py
@@ -0,0 +1,64 @@
+# 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.lib.services.compute import certificates_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestCertificatesClient(base.BaseServiceTest):
+
+    FAKE_CERTIFICATE = {
+        "certificate": {
+            "data": "-----BEGIN----MIICyzCCAjSgAwI----END CERTIFICATE-----\n",
+            "private_key": None
+        }
+    }
+
+    def setUp(self):
+        super(TestCertificatesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = certificates_client.CertificatesClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_show_certificate(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_certificate,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CERTIFICATE,
+            bytes_body,
+            certificate_id="fake-id")
+
+    def test_show_certificate_with_str_body(self):
+        self._test_show_certificate()
+
+    def test_show_certificate_with_bytes_body(self):
+        self._test_show_certificate(bytes_body=True)
+
+    def _test_create_certificate(self, bytes_body=False):
+        cert = copy.deepcopy(self.FAKE_CERTIFICATE)
+        cert['certificate']['private_key'] = "my_private_key"
+        self.check_service_client_function(
+            self.client.create_certificate,
+            'tempest.lib.common.rest_client.RestClient.post',
+            cert,
+            bytes_body)
+
+    def test_create_certificate_with_str_body(self):
+        self._test_create_certificate()
+
+    def test_create_certificate_with_bytes_body(self):
+        self._test_create_certificate(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_extensions_client.py b/tempest/tests/lib/services/compute/test_extensions_client.py
new file mode 100644
index 0000000..d7e217e
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_extensions_client.py
@@ -0,0 +1,65 @@
+# 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.services.compute import extensions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestExtensionsClient(base.BaseServiceTest):
+
+    FAKE_SHOW_EXTENSION = {
+        "extension": {
+            "updated": "2011-06-09T00:00:00Z",
+            "name": "Multinic",
+            "links": [],
+            "namespace":
+            "http://docs.openstack.org/compute/ext/multinic/api/v1.1",
+            "alias": "NMN",
+            "description": u'\u2740(*\xb4\u25e1`*)\u2740'
+        }
+    }
+
+    def setUp(self):
+        super(TestExtensionsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = extensions_client.ExtensionsClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_list_extensions(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_extensions,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"extensions": []},
+            bytes_body)
+
+    def test_list_extensions_with_str_body(self):
+        self._test_list_extensions()
+
+    def test_list_extensions_with_bytes_body(self):
+        self._test_list_extensions(bytes_body=True)
+
+    def _test_show_extension(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_extension,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SHOW_EXTENSION,
+            bytes_body,
+            extension_alias="NMN")
+
+    def test_show_extension_with_str_body(self):
+        self._test_show_extension()
+
+    def test_show_extension_with_bytes_body(self):
+        self._test_show_extension(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_fixedIPs_client.py b/tempest/tests/lib/services/compute/test_fixedIPs_client.py
new file mode 100644
index 0000000..65bda45
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_fixedIPs_client.py
@@ -0,0 +1,58 @@
+# 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.services.compute import fixed_ips_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestFixedIPsClient(base.BaseServiceTest):
+    FIXED_IP_INFO = {"fixed_ip": {"address": "10.0.0.1",
+                                  "cidr": "10.11.12.0/24",
+                                  "host": "localhost",
+                                  "hostname": "OpenStack"}}
+
+    def setUp(self):
+        super(TestFixedIPsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.fixedIPsClient = (fixed_ips_client.
+                               FixedIPsClient
+                               (fake_auth, 'compute',
+                                'regionOne'))
+
+    def _test_show_fixed_ip(self, bytes_body=False):
+        self.check_service_client_function(
+            self.fixedIPsClient.show_fixed_ip,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FIXED_IP_INFO, bytes_body,
+            status=200, fixed_ip='Identifier')
+
+    def test_show_fixed_ip_with_str_body(self):
+        self._test_show_fixed_ip()
+
+    def test_show_fixed_ip_with_bytes_body(self):
+        self._test_show_fixed_ip(True)
+
+    def _test_reserve_fixed_ip(self, bytes_body=False):
+        self.check_service_client_function(
+            self.fixedIPsClient.reserve_fixed_ip,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {}, bytes_body,
+            status=202, fixed_ip='Identifier')
+
+    def test_reserve_fixed_ip_with_str_body(self):
+        self._test_reserve_fixed_ip()
+
+    def test_reserve_fixed_ip_with_bytes_body(self):
+        self._test_reserve_fixed_ip(True)
diff --git a/tempest/tests/lib/services/compute/test_flavors_client.py b/tempest/tests/lib/services/compute/test_flavors_client.py
new file mode 100644
index 0000000..445ee22
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_flavors_client.py
@@ -0,0 +1,255 @@
+# Copyright 2015 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
+
+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 import base
+
+
+class TestFlavorsClient(base.BaseServiceTest):
+
+    FAKE_FLAVOR = {
+        "disk": 1,
+        "id": "1",
+        "links": [{
+            "href": "http://openstack.example.com/v2/openstack/flavors/1",
+            "rel": "self"}, {
+            "href": "http://openstack.example.com/openstack/flavors/1",
+            "rel": "bookmark"}],
+        "name": "m1.tiny",
+        "ram": 512,
+        "swap": 1,
+        "vcpus": 1
+    }
+
+    EXTRA_SPECS = {"extra_specs": {
+        "key1": "value1",
+        "key2": "value2"}
+    }
+
+    FAKE_FLAVOR_ACCESS = {
+        "flavor_id": "10",
+        "tenant_id": "1a951d988e264818afe520e78697dcbf"
+    }
+
+    def setUp(self):
+        super(TestFlavorsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = flavors_client.FlavorsClient(fake_auth,
+                                                   'compute', 'regionOne')
+
+    def _test_list_flavors(self, bytes_body=False):
+        flavor = copy.deepcopy(TestFlavorsClient.FAKE_FLAVOR)
+        # Remove extra attributes
+        for attribute in ('disk', 'vcpus', 'ram', 'swap'):
+            del flavor[attribute]
+        expected = {'flavors': [flavor]}
+        self.check_service_client_function(
+            self.client.list_flavors,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected,
+            bytes_body)
+
+    def test_list_flavors_str_body(self):
+        self._test_list_flavors(bytes_body=False)
+
+    def test_list_flavors_byte_body(self):
+        self._test_list_flavors(bytes_body=True)
+
+    def _test_show_flavor(self, bytes_body=False):
+        expected = {"flavor": TestFlavorsClient.FAKE_FLAVOR}
+        self.check_service_client_function(
+            self.client.show_flavor,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected,
+            bytes_body,
+            flavor_id='fake-id')
+
+    def test_show_flavor_str_body(self):
+        self._test_show_flavor(bytes_body=False)
+
+    def test_show_flavor_byte_body(self):
+        self._test_show_flavor(bytes_body=True)
+
+    def _test_create_flavor(self, bytes_body=False):
+        expected = {"flavor": TestFlavorsClient.FAKE_FLAVOR}
+        request = copy.deepcopy(TestFlavorsClient.FAKE_FLAVOR)
+        # The 'links' parameter should not be passed in
+        del request['links']
+        self.check_service_client_function(
+            self.client.create_flavor,
+            'tempest.lib.common.rest_client.RestClient.post',
+            expected,
+            bytes_body,
+            **request)
+
+    def test_create_flavor_str_body(self):
+        self._test_create_flavor(bytes_body=False)
+
+    def test_create_flavor__byte_body(self):
+        self._test_create_flavor(bytes_body=True)
+
+    def test_delete_flavor(self):
+        self.check_service_client_function(
+            self.client.delete_flavor,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, status=202, flavor_id='c782b7a9-33cd-45f0-b795-7f87f456408b')
+
+    def _test_is_resource_deleted(self, flavor_id, is_deleted=True,
+                                  bytes_body=False):
+        body = json.dumps({'flavors': [TestFlavorsClient.FAKE_FLAVOR]})
+        if bytes_body:
+            body = body.encode('utf-8')
+        response = fake_http.fake_http_response({}, status=200), body
+        self.useFixture(mockpatch.Patch(
+            'tempest.lib.common.rest_client.RestClient.get',
+            return_value=response))
+        self.assertEqual(is_deleted,
+                         self.client.is_resource_deleted(flavor_id))
+
+    def test_is_resource_deleted_true_str_body(self):
+        self._test_is_resource_deleted('2', bytes_body=False)
+
+    def test_is_resource_deleted_true_byte_body(self):
+        self._test_is_resource_deleted('2', bytes_body=True)
+
+    def test_is_resource_deleted_false_str_body(self):
+        self._test_is_resource_deleted('1', is_deleted=False, bytes_body=False)
+
+    def test_is_resource_deleted_false_byte_body(self):
+        self._test_is_resource_deleted('1', is_deleted=False, bytes_body=True)
+
+    def _test_set_flavor_extra_spec(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.set_flavor_extra_spec,
+            'tempest.lib.common.rest_client.RestClient.post',
+            TestFlavorsClient.EXTRA_SPECS,
+            bytes_body,
+            flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6',
+            **TestFlavorsClient.EXTRA_SPECS)
+
+    def test_set_flavor_extra_spec_str_body(self):
+        self._test_set_flavor_extra_spec(bytes_body=False)
+
+    def test_set_flavor_extra_spec_byte_body(self):
+        self._test_set_flavor_extra_spec(bytes_body=True)
+
+    def _test_list_flavor_extra_specs(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_flavor_extra_specs,
+            'tempest.lib.common.rest_client.RestClient.get',
+            TestFlavorsClient.EXTRA_SPECS,
+            bytes_body,
+            flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6')
+
+    def test_list_flavor_extra_specs_str_body(self):
+        self._test_list_flavor_extra_specs(bytes_body=False)
+
+    def test_list_flavor_extra_specs__byte_body(self):
+        self._test_list_flavor_extra_specs(bytes_body=True)
+
+    def _test_show_flavor_extra_spec(self, bytes_body=False):
+        expected = {"key": "value"}
+        self.check_service_client_function(
+            self.client.show_flavor_extra_spec,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected,
+            bytes_body,
+            flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6',
+            key='key')
+
+    def test_show_flavor_extra_spec_str_body(self):
+        self._test_show_flavor_extra_spec(bytes_body=False)
+
+    def test_show_flavor_extra_spec__byte_body(self):
+        self._test_show_flavor_extra_spec(bytes_body=True)
+
+    def _test_update_flavor_extra_spec(self, bytes_body=False):
+        expected = {"key1": "value"}
+        self.check_service_client_function(
+            self.client.update_flavor_extra_spec,
+            'tempest.lib.common.rest_client.RestClient.put',
+            expected,
+            bytes_body,
+            flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6',
+            key='key1', **expected)
+
+    def test_update_flavor_extra_spec_str_body(self):
+        self._test_update_flavor_extra_spec(bytes_body=False)
+
+    def test_update_flavor_extra_spec_byte_body(self):
+        self._test_update_flavor_extra_spec(bytes_body=True)
+
+    def test_unset_flavor_extra_spec(self):
+        self.check_service_client_function(
+            self.client.unset_flavor_extra_spec,
+            'tempest.lib.common.rest_client.RestClient.delete', {},
+            flavor_id='c782b7a9-33cd-45f0-b795-7f87f456408b', key='key')
+
+    def _test_list_flavor_access(self, bytes_body=False):
+        expected = {'flavor_access': [TestFlavorsClient.FAKE_FLAVOR_ACCESS]}
+        self.check_service_client_function(
+            self.client.list_flavor_access,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected,
+            bytes_body,
+            flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6')
+
+    def test_list_flavor_access_str_body(self):
+        self._test_list_flavor_access(bytes_body=False)
+
+    def test_list_flavor_access_byte_body(self):
+        self._test_list_flavor_access(bytes_body=True)
+
+    def _test_add_flavor_access(self, bytes_body=False):
+        expected = {
+            "flavor_access": [TestFlavorsClient.FAKE_FLAVOR_ACCESS]
+        }
+        self.check_service_client_function(
+            self.client.add_flavor_access,
+            'tempest.lib.common.rest_client.RestClient.post',
+            expected,
+            bytes_body,
+            flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6',
+            tenant_id='1a951d988e264818afe520e78697dcbf')
+
+    def test_add_flavor_access_str_body(self):
+        self._test_add_flavor_access(bytes_body=False)
+
+    def test_add_flavor_access_byte_body(self):
+        self._test_add_flavor_access(bytes_body=True)
+
+    def _test_remove_flavor_access(self, bytes_body=False):
+        expected = {
+            "flavor_access": [TestFlavorsClient.FAKE_FLAVOR_ACCESS]
+        }
+        self.check_service_client_function(
+            self.client.remove_flavor_access,
+            'tempest.lib.common.rest_client.RestClient.post',
+            expected,
+            bytes_body,
+            flavor_id='10',
+            tenant_id='a6edd4d66ad04245b5d2d8716ecc91e3')
+
+    def test_remove_flavor_access_str_body(self):
+        self._test_remove_flavor_access(bytes_body=False)
+
+    def test_remove_flavor_access_byte_body(self):
+        self._test_remove_flavor_access(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_floating_ip_pools_client.py b/tempest/tests/lib/services/compute/test_floating_ip_pools_client.py
new file mode 100644
index 0000000..b0c00f0
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_floating_ip_pools_client.py
@@ -0,0 +1,46 @@
+# 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.services.compute import floating_ip_pools_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestFloatingIPPoolsClient(base.BaseServiceTest):
+
+    FAKE_FLOATING_IP_POOLS = {
+        "floating_ip_pools":
+        [
+            {"name": u'\u3042'},
+            {"name": u'\u3044'}
+        ]
+    }
+
+    def setUp(self):
+        super(TestFloatingIPPoolsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = floating_ip_pools_client.FloatingIPPoolsClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def test_list_floating_ip_pools_with_str_body(self):
+        self.check_service_client_function(
+            self.client.list_floating_ip_pools,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_FLOATING_IP_POOLS)
+
+    def test_list_floating_ip_pools_with_bytes_body(self):
+        self.check_service_client_function(
+            self.client.list_floating_ip_pools,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_FLOATING_IP_POOLS, to_utf=True)
diff --git a/tempest/tests/lib/services/compute/test_floating_ips_bulk_client.py b/tempest/tests/lib/services/compute/test_floating_ips_bulk_client.py
new file mode 100644
index 0000000..ace76f8
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_floating_ips_bulk_client.py
@@ -0,0 +1,88 @@
+# 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.tests.lib import fake_auth_provider
+
+from tempest.lib.services.compute import floating_ips_bulk_client
+from tempest.tests.lib.services import base
+
+
+class TestFloatingIPsBulkClient(base.BaseServiceTest):
+
+    FAKE_FIP_BULK_LIST = {"floating_ip_info": [{
+        "address": "10.10.10.1",
+        "instance_uuid": None,
+        "fixed_ip": None,
+        "interface": "eth0",
+        "pool": "nova",
+        "project_id": None
+        },
+        {
+        "address": "10.10.10.2",
+        "instance_uuid": None,
+        "fixed_ip": None,
+        "interface": "eth0",
+        "pool": "nova",
+        "project_id": None
+        }]}
+
+    def setUp(self):
+        super(TestFloatingIPsBulkClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = floating_ips_bulk_client.FloatingIPsBulkClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_list_floating_ips_bulk(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_floating_ips_bulk,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_FIP_BULK_LIST,
+            to_utf=bytes_body)
+
+    def _test_create_floating_ips_bulk(self, bytes_body=False):
+        fake_fip_create_data = {"floating_ips_bulk_create": {
+            "ip_range": "192.168.1.0/24", "pool": "nova", "interface": "eth0"}}
+        self.check_service_client_function(
+            self.client.create_floating_ips_bulk,
+            'tempest.lib.common.rest_client.RestClient.post',
+            fake_fip_create_data,
+            to_utf=bytes_body,
+            ip_range="192.168.1.0/24", pool="nova", interface="eth0")
+
+    def _test_delete_floating_ips_bulk(self, bytes_body=False):
+        fake_fip_delete_data = {"floating_ips_bulk_delete": "192.168.1.0/24"}
+        self.check_service_client_function(
+            self.client.delete_floating_ips_bulk,
+            'tempest.lib.common.rest_client.RestClient.put',
+            fake_fip_delete_data,
+            to_utf=bytes_body,
+            ip_range="192.168.1.0/24")
+
+    def test_list_floating_ips_bulk_with_str_body(self):
+        self._test_list_floating_ips_bulk()
+
+    def test_list_floating_ips_bulk_with_bytes_body(self):
+        self._test_list_floating_ips_bulk(True)
+
+    def test_create_floating_ips_bulk_with_str_body(self):
+        self._test_create_floating_ips_bulk()
+
+    def test_create_floating_ips_bulk_with_bytes_body(self):
+        self._test_create_floating_ips_bulk(True)
+
+    def test_delete_floating_ips_bulk_with_str_body(self):
+        self._test_delete_floating_ips_bulk()
+
+    def test_delete_floating_ips_bulk_with_bytes_body(self):
+        self._test_delete_floating_ips_bulk(True)
diff --git a/tempest/tests/lib/services/compute/test_floating_ips_client.py b/tempest/tests/lib/services/compute/test_floating_ips_client.py
new file mode 100644
index 0000000..92737f2
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_floating_ips_client.py
@@ -0,0 +1,113 @@
+# Copyright 2015 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.
+
+from oslotest import mockpatch
+
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import floating_ips_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestFloatingIpsClient(base.BaseServiceTest):
+
+    floating_ip = {"fixed_ip": None,
+                   "id": "46d61064-13ba-4bf0-9557-69de824c3d6f",
+                   "instance_id": "a1daa443-a6bb-463e-aea2-104b7d912eb8",
+                   "ip": "10.10.10.1",
+                   "pool": "nova"}
+
+    def setUp(self):
+        super(TestFloatingIpsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = floating_ips_client.FloatingIPsClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_list_floating_ips(self, bytes_body=False):
+        expected = {'floating_ips': [TestFloatingIpsClient.floating_ip]}
+        self.check_service_client_function(
+            self.client.list_floating_ips,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected,
+            bytes_body)
+
+    def test_list_floating_ips_str_body(self):
+        self._test_list_floating_ips(bytes_body=False)
+
+    def test_list_floating_ips_byte_body(self):
+        self._test_list_floating_ips(bytes_body=True)
+
+    def _test_show_floating_ip(self, bytes_body=False):
+        expected = {"floating_ip": TestFloatingIpsClient.floating_ip}
+        self.check_service_client_function(
+            self.client.show_floating_ip,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected,
+            bytes_body,
+            floating_ip_id='a1daa443-a6bb-463e-aea2-104b7d912eb8')
+
+    def test_show_floating_ip_str_body(self):
+        self._test_show_floating_ip(bytes_body=False)
+
+    def test_show_floating_ip_byte_body(self):
+        self._test_show_floating_ip(bytes_body=True)
+
+    def _test_create_floating_ip(self, bytes_body=False):
+        expected = {"floating_ip": TestFloatingIpsClient.floating_ip}
+        self.check_service_client_function(
+            self.client.create_floating_ip,
+            'tempest.lib.common.rest_client.RestClient.post',
+            expected,
+            bytes_body,
+            pool_name='nova')
+
+    def test_create_floating_ip_str_body(self):
+        self._test_create_floating_ip(bytes_body=False)
+
+    def test_create_floating_ip_byte_body(self):
+        self._test_create_floating_ip(bytes_body=True)
+
+    def test_delete_floating_ip(self):
+        self.check_service_client_function(
+            self.client.delete_floating_ip,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, status=202, floating_ip_id='fake-id')
+
+    def test_associate_floating_ip_to_server(self):
+        self.check_service_client_function(
+            self.client.associate_floating_ip_to_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {}, status=202, floating_ip='10.10.10.1',
+            server_id='c782b7a9-33cd-45f0-b795-7f87f456408b')
+
+    def test_disassociate_floating_ip_from_server(self):
+        self.check_service_client_function(
+            self.client.disassociate_floating_ip_from_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {}, status=202, floating_ip='10.10.10.1',
+            server_id='c782b7a9-33cd-45f0-b795-7f87f456408b')
+
+    def test_is_resource_deleted_true(self):
+        self.useFixture(mockpatch.Patch(
+            'tempest.lib.services.compute.floating_ips_client.'
+            'FloatingIPsClient.show_floating_ip',
+            side_effect=lib_exc.NotFound()))
+        self.assertTrue(self.client.is_resource_deleted('fake-id'))
+
+    def test_is_resource_deleted_false(self):
+        self.useFixture(mockpatch.Patch(
+            'tempest.lib.services.compute.floating_ips_client.'
+            'FloatingIPsClient.show_floating_ip',
+            return_value={"floating_ip": TestFloatingIpsClient.floating_ip}))
+        self.assertFalse(self.client.is_resource_deleted('fake-id'))
diff --git a/tempest/tests/lib/services/compute/test_hosts_client.py b/tempest/tests/lib/services/compute/test_hosts_client.py
new file mode 100644
index 0000000..78537c2
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_hosts_client.py
@@ -0,0 +1,147 @@
+# 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.services.compute import hosts_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestHostsClient(base.BaseServiceTest):
+    FAKE_HOST_DATA = {
+        "host": {
+            "resource": {
+                "cpu": 1,
+                "disk_gb": 1028,
+                "host": "c1a7de0ac9d94e4baceae031d05caae3",
+                "memory_mb": 8192,
+                "project": "(total)"
+                }
+        },
+        "hosts": {
+            "host_name": "c1a7de0ac9d94e4baceae031d05caae3",
+            "service": "conductor",
+            "zone": "internal"
+        },
+        "enable_hosts": {
+            "host": "65c5d5b7e3bd44308e67fc50f362aee6",
+            "maintenance_mode": "off_maintenance",
+            "status": "enabled"
+        }
+        }
+
+    FAKE_CONTROL_DATA = {
+        "shutdown": {
+            "host": "c1a7de0ac9d94e4baceae031d05caae3",
+            "power_action": "shutdown"
+        },
+        "startup": {
+            "host": "c1a7de0ac9d94e4baceae031d05caae3",
+            "power_action": "startup"
+        },
+        "reboot": {
+            "host": "c1a7de0ac9d94e4baceae031d05caae3",
+            "power_action": "reboot"
+        }}
+
+    HOST_DATA = {'host': [FAKE_HOST_DATA['host']]}
+    HOSTS_DATA = {'hosts': [FAKE_HOST_DATA['hosts']]}
+    ENABLE_HOST_DATA = FAKE_HOST_DATA['enable_hosts']
+    HOST_ID = "c1a7de0ac9d94e4baceae031d05caae3"
+    TEST_HOST_DATA = {
+        "status": "enable",
+        "maintenance_mode": "disable"
+    }
+
+    def setUp(self):
+        super(TestHostsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = hosts_client.HostsClient(fake_auth, 'compute',
+                                               'regionOne')
+        self.params = {'hostname': self.HOST_ID}
+        self.func2mock = {
+            'get': 'tempest.lib.common.rest_client.RestClient.get',
+            'put': 'tempest.lib.common.rest_client.RestClient.put'}
+
+    def _test_host_data(self, test_type='list', bytes_body=False):
+        expected_resp = self.HOST_DATA
+        if test_type != 'list':
+            function_call = self.client.show_host
+        else:
+            expected_resp = self.HOSTS_DATA
+            function_call = self.client.list_hosts
+            self.params = {'host_name': self.HOST_ID}
+
+        self.check_service_client_function(
+            function_call, self.func2mock['get'],
+            expected_resp, bytes_body,
+            200, **self.params)
+
+    def _test_update_hosts(self, bytes_body=False):
+        expected_resp = self.ENABLE_HOST_DATA
+        self.check_service_client_function(
+            self.client.update_host, self.func2mock['put'],
+            expected_resp, bytes_body,
+            200, **self.params)
+
+    def _test_control_host(self, control_op='reboot', bytes_body=False):
+        if control_op == 'start':
+            expected_resp = self.FAKE_CONTROL_DATA['startup']
+            function_call = self.client.startup_host
+        elif control_op == 'stop':
+            expected_resp = self.FAKE_CONTROL_DATA['shutdown']
+            function_call = self.client.shutdown_host
+        else:
+            expected_resp = self.FAKE_CONTROL_DATA['reboot']
+            function_call = self.client.reboot_host
+
+        self.check_service_client_function(
+            function_call, self.func2mock['get'],
+            expected_resp, bytes_body,
+            200, **self.params)
+
+    def test_show_host_with_str_body(self):
+        self._test_host_data('show')
+
+    def test_show_host_with_bytes_body(self):
+        self._test_host_data('show', True)
+
+    def test_list_host_with_str_body(self):
+        self._test_host_data()
+
+    def test_list_host_with_bytes_body(self):
+        self._test_host_data(bytes_body=True)
+
+    def test_start_host_with_str_body(self):
+        self._test_control_host('start')
+
+    def test_start_host_with_bytes_body(self):
+        self._test_control_host('start', True)
+
+    def test_stop_host_with_str_body(self):
+        self._test_control_host('stop')
+
+    def test_stop_host_with_bytes_body(self):
+        self._test_control_host('stop', True)
+
+    def test_reboot_host_with_str_body(self):
+        self._test_control_host('reboot')
+
+    def test_reboot_host_with_bytes_body(self):
+        self._test_control_host('reboot', True)
+
+    def test_update_host_with_str_body(self):
+        self._test_update_hosts()
+
+    def test_update_host_with_bytes_body(self):
+        self._test_update_hosts(True)
diff --git a/tempest/tests/lib/services/compute/test_hypervisor_client.py b/tempest/tests/lib/services/compute/test_hypervisor_client.py
new file mode 100644
index 0000000..89eb698
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_hypervisor_client.py
@@ -0,0 +1,167 @@
+# Copyright 2015 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.
+
+from tempest.lib.services.compute import hypervisor_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestHypervisorClient(base.BaseServiceTest):
+
+    hypervisor_id = "1"
+    hypervisor_name = "hyper.hostname.com"
+
+    def setUp(self):
+        super(TestHypervisorClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = hypervisor_client.HypervisorClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def test_list_hypervisor_str_body(self):
+        self._test_list_hypervisor(bytes_body=False)
+
+    def test_list_hypervisor_byte_body(self):
+        self._test_list_hypervisor(bytes_body=True)
+
+    def _test_list_hypervisor(self, bytes_body=False):
+        expected = {"hypervisors": [{
+            "id": 1,
+            "hypervisor_hostname": "hypervisor1.hostname.com"},
+            {
+            "id": 2,
+            "hypervisor_hostname": "hypervisor2.hostname.com"}]}
+        self.check_service_client_function(
+            self.client.list_hypervisors,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected, bytes_body)
+
+    def test_show_hypervisor_str_body(self):
+        self._test_show_hypervisor(bytes_body=False)
+
+    def test_show_hypervisor_byte_body(self):
+        self._test_show_hypervisor(bytes_body=True)
+
+    def _test_show_hypervisor(self, bytes_body=False):
+        expected = {"hypervisor": {
+            "cpu_info": "?",
+            "current_workload": 0,
+            "disk_available_least": 1,
+            "host_ip": "10.10.10.10",
+            "free_disk_gb": 1028,
+            "free_ram_mb": 7680,
+            "hypervisor_hostname": "fake-mini",
+            "hypervisor_type": "fake",
+            "hypervisor_version": 1,
+            "id": 1,
+            "local_gb": 1028,
+            "local_gb_used": 0,
+            "memory_mb": 8192,
+            "memory_mb_used": 512,
+            "running_vms": 0,
+            "service": {
+                "host": "fake_host",
+                "id": 2},
+            "vcpus": 1,
+            "vcpus_used": 0}}
+        self.check_service_client_function(
+            self.client.show_hypervisor,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected, bytes_body,
+            hypervisor_id=self.hypervisor_id)
+
+    def test_list_servers_on_hypervisor_str_body(self):
+        self._test_list_servers_on_hypervisor(bytes_body=False)
+
+    def test_list_servers_on_hypervisor_byte_body(self):
+        self._test_list_servers_on_hypervisor(bytes_body=True)
+
+    def _test_list_servers_on_hypervisor(self, bytes_body=False):
+        expected = {"hypervisors": [{
+            "id": 1,
+            "hypervisor_hostname": "hyper.hostname.com",
+            "servers": [{
+                "uuid": "e1ae8fc4-b72d-4c2f-a427-30dd420b6277",
+                "name": "instance-00000001"},
+                {
+                "uuid": "e1ae8fc4-b72d-4c2f-a427-30dd42066666",
+                "name": "instance-00000002"}
+                ]}
+            ]}
+        self.check_service_client_function(
+            self.client.list_servers_on_hypervisor,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected, bytes_body,
+            hypervisor_name=self.hypervisor_name)
+
+    def test_show_hypervisor_statistics_str_body(self):
+        self._test_show_hypervisor_statistics(bytes_body=False)
+
+    def test_show_hypervisor_statistics_byte_body(self):
+        self._test_show_hypervisor_statistics(bytes_body=True)
+
+    def _test_show_hypervisor_statistics(self, bytes_body=False):
+        expected = {
+            "hypervisor_statistics": {
+                "count": 1,
+                "current_workload": 0,
+                "disk_available_least": 0,
+                "free_disk_gb": 1028,
+                "free_ram_mb": 7680,
+                "local_gb": 1028,
+                "local_gb_used": 0,
+                "memory_mb": 8192,
+                "memory_mb_used": 512,
+                "running_vms": 0,
+                "vcpus": 1,
+                "vcpus_used": 0}}
+        self.check_service_client_function(
+            self.client.show_hypervisor_statistics,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected, bytes_body)
+
+    def test_show_hypervisor_uptime_str_body(self):
+        self._test_show_hypervisor_uptime(bytes_body=False)
+
+    def test_show_hypervisor_uptime_byte_body(self):
+        self._test_show_hypervisor_uptime(bytes_body=True)
+
+    def _test_show_hypervisor_uptime(self, bytes_body=False):
+        expected = {
+            "hypervisor": {
+                "hypervisor_hostname": "fake-mini",
+                "id": 1,
+                "uptime": (" 08:32:11 up 93 days, 18:25, 12 users, "
+                           " load average: 0.20, 0.12, 0.14")
+            }}
+        self.check_service_client_function(
+            self.client.show_hypervisor_uptime,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected, bytes_body,
+            hypervisor_id=self.hypervisor_id)
+
+    def test_search_hypervisor_str_body(self):
+        self._test_search_hypervisor(bytes_body=False)
+
+    def test_search_hypervisor_byte_body(self):
+        self._test_search_hypervisor(bytes_body=True)
+
+    def _test_search_hypervisor(self, bytes_body=False):
+        expected = {"hypervisors": [{
+            "id": 2,
+            "hypervisor_hostname": "hyper.hostname.com"}]}
+        self.check_service_client_function(
+            self.client.search_hypervisor,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected, bytes_body,
+            hypervisor_name=self.hypervisor_name)
diff --git a/tempest/tests/lib/services/compute/test_images_client.py b/tempest/tests/lib/services/compute/test_images_client.py
new file mode 100644
index 0000000..a9a570d
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_images_client.py
@@ -0,0 +1,265 @@
+# 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 oslotest import mockpatch
+
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import images_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestImagesClient(base.BaseServiceTest):
+    # Data Dictionaries used for testing #
+    FAKE_IMAGE_METADATA = {
+        "list":
+            {"metadata": {
+             "auto_disk_config": "True",
+             "Label": "Changed"
+             }},
+        "set_item":
+            {"meta": {
+             "auto_disk_config": "True"
+             }},
+        "show_item":
+            {"meta": {
+             "kernel_id": "nokernel",
+             }},
+        "update":
+            {"metadata": {
+             "kernel_id": "False",
+             "Label": "UpdatedImage"
+             }},
+        "set":
+            {"metadata": {
+             "Label": "Changed",
+             "auto_disk_config": "True"
+             }},
+        "delete_item": {}
+        }
+
+    FAKE_IMAGE_DATA = {
+        "list":
+            {"images": [
+             {"id": "70a599e0-31e7-49b7-b260-868f441e862b",
+              "links": [
+                    {"href": "http://openstack.example.com/v2/openstack" +
+                             "/images/70a599e0-31e7-49b7-b260-868f441e862b",
+                     "rel": "self"
+                     }
+              ],
+              "name": "fakeimage7"
+              }]},
+        "show": {"image": {
+            "created": "2011-01-01T01:02:03Z",
+            "id": "70a599e0-31e7-49b7-b260-868f441e862b",
+            "links": [
+                {
+                    "href": "http://openstack.example.com/v2/openstack" +
+                            "/images/70a599e0-31e7-49b7-b260-868f441e862b",
+                    "rel": "self"
+                },
+            ],
+            "metadata": {
+                "architecture": "x86_64",
+                "auto_disk_config": "True",
+                "kernel_id": "nokernel",
+                "ramdisk_id": "nokernel"
+            },
+            "minDisk": 0,
+            "minRam": 0,
+            "name": "fakeimage7",
+            "progress": 100,
+            "status": "ACTIVE",
+            "updated": "2011-01-01T01:02:03Z"}},
+        "create": {},
+        "delete": {}
+        }
+    func2mock = {
+        'get': 'tempest.lib.common.rest_client.RestClient.get',
+        'post': 'tempest.lib.common.rest_client.RestClient.post',
+        'put': 'tempest.lib.common.rest_client.RestClient.put',
+        'delete': 'tempest.lib.common.rest_client.RestClient.delete'}
+    # Variable definition
+    FAKE_IMAGE_ID = FAKE_IMAGE_DATA['show']['image']['id']
+    FAKE_SERVER_ID = "80a599e0-31e7-49b7-b260-868f441e343f"
+    FAKE_CREATE_INFO = {'location': 'None'}
+    FAKE_METADATA = FAKE_IMAGE_METADATA['show_item']['meta']
+
+    def setUp(self):
+        super(TestImagesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = images_client.ImagesClient(fake_auth,
+                                                 "compute", "regionOne")
+
+    def _test_image_operation(self, operation="delete", bytes_body=False):
+        response_code = 200
+        mock_operation = self.func2mock['get']
+        expected_op = self.FAKE_IMAGE_DATA[operation]
+        params = {"image_id": self.FAKE_IMAGE_ID}
+        headers = None
+        if operation == 'list':
+            function = self.client.list_images
+        elif operation == 'show':
+            function = self.client.show_image
+        elif operation == 'create':
+            function = self.client.create_image
+            mock_operation = self.func2mock['post']
+            params = {"server_id": self.FAKE_SERVER_ID}
+            response_code = 202
+            headers = {
+                'connection': 'keep-alive',
+                'content-length': '0',
+                'content-type': 'application/json',
+                'status': '202',
+                'x-compute-request-id': 'req-fake',
+                'vary': 'accept-encoding',
+                'x-openstack-nova-api-version': 'v2.1',
+                'date': '13 Oct 2015 05:55:36 GMT',
+                'location': 'http://fake.com/images/fake'
+            }
+        else:
+            function = self.client.delete_image
+            mock_operation = self.func2mock['delete']
+            response_code = 204
+
+        self.check_service_client_function(
+            function, mock_operation, expected_op,
+            bytes_body, response_code, headers, **params)
+
+    def _test_image_metadata(self, operation="set_item", bytes_body=False):
+        response_code = 200
+        expected_op = self.FAKE_IMAGE_METADATA[operation]
+        if operation == 'list':
+            function = self.client.list_image_metadata
+            mock_operation = self.func2mock['get']
+            params = {"image_id": self.FAKE_IMAGE_ID}
+
+        elif operation == 'set':
+            function = self.client.set_image_metadata
+            mock_operation = self.func2mock['put']
+            params = {"image_id": "_dummy_data",
+                      "meta": self.FAKE_METADATA}
+
+        elif operation == 'update':
+            function = self.client.update_image_metadata
+            mock_operation = self.func2mock['post']
+            params = {"image_id": self.FAKE_IMAGE_ID,
+                      "meta": self.FAKE_METADATA}
+
+        elif operation == 'show_item':
+            mock_operation = self.func2mock['get']
+            function = self.client.show_image_metadata_item
+            params = {"image_id": self.FAKE_IMAGE_ID,
+                      "key": "123"}
+
+        elif operation == 'delete_item':
+            function = self.client.delete_image_metadata_item
+            mock_operation = self.func2mock['delete']
+            response_code = 204
+            params = {"image_id": self.FAKE_IMAGE_ID,
+                      "key": "123"}
+
+        else:
+            function = self.client.set_image_metadata_item
+            mock_operation = self.func2mock['put']
+            params = {"image_id": self.FAKE_IMAGE_ID,
+                      "key": "123",
+                      "meta": self.FAKE_METADATA}
+
+        self.check_service_client_function(
+            function, mock_operation, expected_op,
+            bytes_body, response_code, **params)
+
+    def _test_resource_deleted(self, bytes_body=False):
+        params = {"id": self.FAKE_IMAGE_ID}
+        expected_op = self.FAKE_IMAGE_DATA['show']
+        self.useFixture(mockpatch.Patch('tempest.lib.services.compute'
+                        '.images_client.ImagesClient.show_image',
+                                        side_effect=lib_exc.NotFound))
+        self.assertEqual(True, self.client.is_resource_deleted(**params))
+        tempdata = copy.deepcopy(self.FAKE_IMAGE_DATA['show'])
+        tempdata['image']['id'] = None
+        self.useFixture(mockpatch.Patch('tempest.lib.services.compute'
+                        '.images_client.ImagesClient.show_image',
+                                        return_value=expected_op))
+        self.assertEqual(False, self.client.is_resource_deleted(**params))
+
+    def test_list_images_with_str_body(self):
+        self._test_image_operation('list')
+
+    def test_list_images_with_bytes_body(self):
+        self._test_image_operation('list', True)
+
+    def test_show_image_with_str_body(self):
+        self._test_image_operation('show')
+
+    def test_show_image_with_bytes_body(self):
+        self._test_image_operation('show', True)
+
+    def test_create_image_with_str_body(self):
+        self._test_image_operation('create')
+
+    def test_create_image_with_bytes_body(self):
+        self._test_image_operation('create', True)
+
+    def test_delete_image_with_str_body(self):
+        self._test_image_operation('delete')
+
+    def test_delete_image_with_bytes_body(self):
+        self._test_image_operation('delete', True)
+
+    def test_list_image_metadata_with_str_body(self):
+        self._test_image_metadata('list')
+
+    def test_list_image_metadata_with_bytes_body(self):
+        self._test_image_metadata('list', True)
+
+    def test_set_image_metadata_with_str_body(self):
+        self._test_image_metadata('set')
+
+    def test_set_image_metadata_with_bytes_body(self):
+        self._test_image_metadata('set', True)
+
+    def test_update_image_metadata_with_str_body(self):
+        self._test_image_metadata('update')
+
+    def test_update_image_metadata_with_bytes_body(self):
+        self._test_image_metadata('update', True)
+
+    def test_set_image_metadata_item_with_str_body(self):
+        self._test_image_metadata()
+
+    def test_set_image_metadata_item_with_bytes_body(self):
+        self._test_image_metadata(bytes_body=True)
+
+    def test_show_image_metadata_item_with_str_body(self):
+        self._test_image_metadata('show_item')
+
+    def test_show_image_metadata_item_with_bytes_body(self):
+        self._test_image_metadata('show_item', True)
+
+    def test_delete_image_metadata_item_with_str_body(self):
+        self._test_image_metadata('delete_item')
+
+    def test_delete_image_metadata_item_with_bytes_body(self):
+        self._test_image_metadata('delete_item', True)
+
+    def test_resource_delete_with_str_body(self):
+        self._test_resource_deleted()
+
+    def test_resource_delete_with_bytes_body(self):
+        self._test_resource_deleted(True)
diff --git a/tempest/tests/lib/services/compute/test_instance_usage_audit_log_client.py b/tempest/tests/lib/services/compute/test_instance_usage_audit_log_client.py
new file mode 100644
index 0000000..21764ff
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_instance_usage_audit_log_client.py
@@ -0,0 +1,73 @@
+# 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 datetime
+
+from tempest.lib.services.compute import instance_usage_audit_log_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestInstanceUsagesAuditLogClient(base.BaseServiceTest):
+
+    FAKE_AUDIT_LOG = {
+        "hosts_not_run": [
+            "f4eb7cfd155f4574967f8b55a7faed75"
+        ],
+        "log": {},
+        "num_hosts": 1,
+        "num_hosts_done": 0,
+        "num_hosts_not_run": 1,
+        "num_hosts_running": 0,
+        "overall_status": "0 of 1 hosts done. 0 errors.",
+        "period_beginning": "2012-12-01 00:00:00",
+        "period_ending": "2013-01-01 00:00:00",
+        "total_errors": 0,
+        "total_instances": 0
+    }
+
+    def setUp(self):
+        super(TestInstanceUsagesAuditLogClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = (instance_usage_audit_log_client.
+                       InstanceUsagesAuditLogClient(fake_auth, 'compute',
+                                                    'regionOne'))
+
+    def _test_list_instance_usage_audit_logs(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_instance_usage_audit_logs,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"instance_usage_audit_logs": self.FAKE_AUDIT_LOG},
+            bytes_body)
+
+    def test_list_instance_usage_audit_logs_with_str_body(self):
+        self._test_list_instance_usage_audit_logs()
+
+    def test_list_instance_usage_audit_logs_with_bytes_body(self):
+        self._test_list_instance_usage_audit_logs(bytes_body=True)
+
+    def _test_show_instance_usage_audit_log(self, bytes_body=False):
+        before_time = datetime.datetime(2012, 12, 1, 0, 0)
+        self.check_service_client_function(
+            self.client.show_instance_usage_audit_log,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"instance_usage_audit_log": self.FAKE_AUDIT_LOG},
+            bytes_body,
+            time_before=before_time)
+
+    def test_show_instance_usage_audit_log_with_str_body(self):
+        self._test_show_instance_usage_audit_log()
+
+    def test_show_network_with_bytes_body_with_bytes_body(self):
+        self._test_show_instance_usage_audit_log(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_interfaces_client.py b/tempest/tests/lib/services/compute/test_interfaces_client.py
new file mode 100644
index 0000000..4d78103
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_interfaces_client.py
@@ -0,0 +1,98 @@
+# 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.services.compute import interfaces_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestInterfacesClient(base.BaseServiceTest):
+    # Data Values to be used for testing #
+    FAKE_INTERFACE_DATA = {
+        "fixed_ips": [{
+            "ip_address": "192.168.1.1",
+            "subnet_id": "f8a6e8f8-c2ec-497c-9f23-da9616de54ef"
+            }],
+        "mac_addr": "fa:16:3e:4c:2c:30",
+        "net_id": "3cb9bc59-5699-4588-a4b1-b87f96708bc6",
+        "port_id": "ce531f90-199f-48c0-816c-13e38010b442",
+        "port_state": "ACTIVE"}
+
+    FAKE_SHOW_DATA = {
+        "interfaceAttachment": FAKE_INTERFACE_DATA}
+    FAKE_LIST_DATA = {
+        "interfaceAttachments": [FAKE_INTERFACE_DATA]}
+
+    FAKE_SERVER_ID = "ec14c864-096e-4e27-bb8a-2c2b4dc6f3f5"
+    FAKE_PORT_ID = FAKE_SHOW_DATA['interfaceAttachment']['port_id']
+    func2mock = {
+        'delete': 'tempest.lib.common.rest_client.RestClient.delete',
+        'get': 'tempest.lib.common.rest_client.RestClient.get',
+        'post': 'tempest.lib.common.rest_client.RestClient.post'}
+
+    def setUp(self):
+        super(TestInterfacesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = interfaces_client.InterfacesClient(fake_auth,
+                                                         "compute",
+                                                         "regionOne")
+
+    def _test_interface_operation(self, operation="create", bytes_body=False):
+        response_code = 200
+        expected_op = self.FAKE_SHOW_DATA
+        mock_operation = self.func2mock['get']
+        params = {'server_id': self.FAKE_SERVER_ID,
+                  'port_id': self.FAKE_PORT_ID}
+        if operation == 'list':
+            expected_op = self.FAKE_LIST_DATA
+            function = self.client.list_interfaces
+            params = {'server_id': self.FAKE_SERVER_ID}
+        elif operation == 'show':
+            function = self.client.show_interface
+        elif operation == 'delete':
+            expected_op = {}
+            mock_operation = self.func2mock['delete']
+            function = self.client.delete_interface
+            response_code = 202
+        else:
+            function = self.client.create_interface
+            mock_operation = self.func2mock['post']
+
+        self.check_service_client_function(
+            function, mock_operation, expected_op,
+            bytes_body, response_code, **params)
+
+    def test_list_interfaces_with_str_body(self):
+        self._test_interface_operation('list')
+
+    def test_list_interfaces_with_bytes_body(self):
+        self._test_interface_operation('list', True)
+
+    def test_show_interface_with_str_body(self):
+        self._test_interface_operation('show')
+
+    def test_show_interface_with_bytes_body(self):
+        self._test_interface_operation('show', True)
+
+    def test_delete_interface_with_str_body(self):
+        self._test_interface_operation('delete')
+
+    def test_delete_interface_with_bytes_body(self):
+        self._test_interface_operation('delete', True)
+
+    def test_create_interface_with_str_body(self):
+        self._test_interface_operation()
+
+    def test_create_interface_with_bytes_body(self):
+        self._test_interface_operation(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_keypairs_client.py b/tempest/tests/lib/services/compute/test_keypairs_client.py
new file mode 100644
index 0000000..ed3b9dd
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_keypairs_client.py
@@ -0,0 +1,94 @@
+# 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.lib.services.compute import keypairs_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestKeyPairsClient(base.BaseServiceTest):
+
+    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/lib/services/compute/test_limits_client.py b/tempest/tests/lib/services/compute/test_limits_client.py
new file mode 100644
index 0000000..5f3fa5a
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_limits_client.py
@@ -0,0 +1,66 @@
+# 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.services.compute import limits_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestLimitsClient(base.BaseServiceTest):
+
+    def setUp(self):
+        super(TestLimitsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = limits_client.LimitsClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_show_limits(self, bytes_body=False):
+        expected = {
+            "limits": {
+                "rate": [],
+                "absolute": {
+                    "maxServerMeta": 128,
+                    "maxPersonality": 5,
+                    "totalServerGroupsUsed": 0,
+                    "maxImageMeta": 128,
+                    "maxPersonalitySize": 10240,
+                    "maxServerGroups": 10,
+                    "maxSecurityGroupRules": 20,
+                    "maxTotalKeypairs": 100,
+                    "totalCoresUsed": 0,
+                    "totalRAMUsed": 0,
+                    "totalInstancesUsed": 0,
+                    "maxSecurityGroups": 10,
+                    "totalFloatingIpsUsed": 0,
+                    "maxTotalCores": 20,
+                    "totalSecurityGroupsUsed": 0,
+                    "maxTotalFloatingIps": 10,
+                    "maxTotalInstances": 10,
+                    "maxTotalRAMSize": 51200,
+                    "maxServerGroupMembers": 10
+                    }
+            }
+        }
+
+        self.check_service_client_function(
+            self.client.show_limits,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected,
+            bytes_body)
+
+    def test_show_limits_with_str_body(self):
+        self._test_show_limits()
+
+    def test_show_limits_with_bytes_body(self):
+        self._test_show_limits(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_migrations_client.py b/tempest/tests/lib/services/compute/test_migrations_client.py
new file mode 100644
index 0000000..be62c0c
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_migrations_client.py
@@ -0,0 +1,52 @@
+# 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.services.compute import migrations_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestMigrationsClient(base.BaseServiceTest):
+    FAKE_MIGRATION_INFO = {"migrations": [{
+        "created_at": "2012-10-29T13:42:02",
+        "dest_compute": "compute2",
+        "dest_host": "1.2.3.4",
+        "dest_node": "node2",
+        "id": 1234,
+        "instance_uuid": "e9e4fdd7-f956-44ff-bfeb-d654a96ab3a2",
+        "new_instance_type_id": 2,
+        "old_instance_type_id": 1,
+        "source_compute": "compute1",
+        "source_node": "node1",
+        "status": "finished",
+        "updated_at": "2012-10-29T13:42:02"}]}
+
+    def setUp(self):
+        super(TestMigrationsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.mg_client_obj = migrations_client.MigrationsClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_list_migrations(self, bytes_body=False):
+        self.check_service_client_function(
+            self.mg_client_obj.list_migrations,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_MIGRATION_INFO,
+            bytes_body)
+
+    def test_list_migration_with_str_body(self):
+        self._test_list_migrations()
+
+    def test_list_migration_with_bytes_body(self):
+        self._test_list_migrations(True)
diff --git a/tempest/tests/lib/services/compute/test_networks_client.py b/tempest/tests/lib/services/compute/test_networks_client.py
new file mode 100644
index 0000000..1908b57
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_networks_client.py
@@ -0,0 +1,94 @@
+# 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.services.compute import networks_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestNetworksClient(base.BaseServiceTest):
+
+    FAKE_NETWORK = {
+        "bridge": None,
+        "vpn_public_port": None,
+        "dhcp_start": None,
+        "bridge_interface": None,
+        "share_address": None,
+        "updated_at": None,
+        "id": "34d5ae1e-5659-49cf-af80-73bccd7d7ad3",
+        "cidr_v6": None,
+        "deleted_at": None,
+        "gateway": None,
+        "rxtx_base": None,
+        "label": u'30d7',
+        "priority": None,
+        "project_id": None,
+        "vpn_private_address": None,
+        "deleted": None,
+        "vlan": None,
+        "broadcast": None,
+        "netmask": None,
+        "injected": None,
+        "cidr": None,
+        "vpn_public_address": None,
+        "multi_host": None,
+        "enable_dhcp": None,
+        "dns2": None,
+        "created_at": None,
+        "host": None,
+        "mtu": None,
+        "gateway_v6": None,
+        "netmask_v6": None,
+        "dhcp_server": None,
+        "dns1": None
+        }
+
+    network_id = "34d5ae1e-5659-49cf-af80-73bccd7d7ad3"
+
+    FAKE_NETWORKS = [FAKE_NETWORK]
+
+    def setUp(self):
+        super(TestNetworksClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = networks_client.NetworksClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_list_networks(self, bytes_body=False):
+        fake_list = {"networks": self.FAKE_NETWORKS}
+        self.check_service_client_function(
+            self.client.list_networks,
+            'tempest.lib.common.rest_client.RestClient.get',
+            fake_list,
+            bytes_body)
+
+    def test_list_networks_with_str_body(self):
+        self._test_list_networks()
+
+    def test_list_networks_with_bytes_body(self):
+        self._test_list_networks(bytes_body=True)
+
+    def _test_show_network(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_network,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"network": self.FAKE_NETWORK},
+            bytes_body,
+            network_id=self.network_id
+            )
+
+    def test_show_network_with_str_body(self):
+        self._test_show_network()
+
+    def test_show_network_with_bytes_body(self):
+        self._test_show_network(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_quota_classes_client.py b/tempest/tests/lib/services/compute/test_quota_classes_client.py
new file mode 100644
index 0000000..22d8b91
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_quota_classes_client.py
@@ -0,0 +1,71 @@
+# 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.lib.services.compute import quota_classes_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQuotaClassesClient(base.BaseServiceTest):
+
+    FAKE_QUOTA_CLASS_SET = {
+        "injected_file_content_bytes": 10240,
+        "metadata_items": 128,
+        "server_group_members": 10,
+        "server_groups": 10,
+        "ram": 51200,
+        "floating_ips": 10,
+        "key_pairs": 100,
+        "id": u'\u2740(*\xb4\u25e1`*)\u2740',
+        "instances": 10,
+        "security_group_rules": 20,
+        "security_groups": 10,
+        "injected_files": 5,
+        "cores": 20,
+        "fixed_ips": -1,
+        "injected_file_path_bytes": 255,
+        }
+
+    def setUp(self):
+        super(TestQuotaClassesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = quota_classes_client.QuotaClassesClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_show_quota_class_set(self, bytes_body=False):
+        fake_body = {'quota_class_set': self.FAKE_QUOTA_CLASS_SET}
+        self.check_service_client_function(
+            self.client.show_quota_class_set,
+            'tempest.lib.common.rest_client.RestClient.get',
+            fake_body,
+            bytes_body,
+            quota_class_id="test")
+
+    def test_show_quota_class_set_with_str_body(self):
+        self._test_show_quota_class_set()
+
+    def test_show_quota_class_set_with_bytes_body(self):
+        self._test_show_quota_class_set(bytes_body=True)
+
+    def test_update_quota_class_set(self):
+        fake_quota_class_set = copy.deepcopy(self.FAKE_QUOTA_CLASS_SET)
+        fake_quota_class_set.pop("id")
+        fake_body = {'quota_class_set': fake_quota_class_set}
+        self.check_service_client_function(
+            self.client.update_quota_class_set,
+            'tempest.lib.common.rest_client.RestClient.put',
+            fake_body,
+            quota_class_id="test")
diff --git a/tempest/tests/lib/services/compute/test_quotas_client.py b/tempest/tests/lib/services/compute/test_quotas_client.py
new file mode 100644
index 0000000..4c49e8d
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_quotas_client.py
@@ -0,0 +1,130 @@
+# 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.lib.services.compute import quotas_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQuotasClient(base.BaseServiceTest):
+
+    FAKE_QUOTA_SET = {
+        "quota_set": {
+            "injected_file_content_bytes": 10240,
+            "metadata_items": 128,
+            "server_group_members": 10,
+            "server_groups": 10,
+            "ram": 51200,
+            "floating_ips": 10,
+            "key_pairs": 100,
+            "id": "8421f7be61064f50b680465c07f334af",
+            "instances": 10,
+            "security_group_rules": 20,
+            "injected_files": 5,
+            "cores": 20,
+            "fixed_ips": -1,
+            "injected_file_path_bytes": 255,
+            "security_groups": 10}
+        }
+
+    project_id = "8421f7be61064f50b680465c07f334af"
+    fake_user_id = "65f09168cbb04eb593f3138b63b67b67"
+
+    def setUp(self):
+        super(TestQuotasClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = quotas_client.QuotasClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_show_quota_set(self, bytes_body=False, user_id=None):
+        if user_id:
+            self.check_service_client_function(
+                self.client.show_quota_set,
+                'tempest.lib.common.rest_client.RestClient.get',
+                self.FAKE_QUOTA_SET,
+                to_utf=bytes_body,
+                tenant_id=self.project_id,
+                user_id=user_id)
+        else:
+            self.check_service_client_function(
+                self.client.show_quota_set,
+                'tempest.lib.common.rest_client.RestClient.get',
+                self.FAKE_QUOTA_SET,
+                to_utf=bytes_body,
+                tenant_id=self.project_id)
+
+    def test_show_quota_set_with_str_body(self):
+        self._test_show_quota_set()
+
+    def test_show_quota_set_with_bytes_body(self):
+        self._test_show_quota_set(bytes_body=True)
+
+    def test_show_quota_set_for_user_with_str_body(self):
+        self._test_show_quota_set(user_id=self.fake_user_id)
+
+    def test_show_quota_set_for_user_with_bytes_body(self):
+        self._test_show_quota_set(bytes_body=True, user_id=self.fake_user_id)
+
+    def _test_show_default_quota_set(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_default_quota_set,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_QUOTA_SET,
+            to_utf=bytes_body,
+            tenant_id=self.project_id)
+
+    def test_show_default_quota_set_with_str_body(self):
+        self._test_show_default_quota_set()
+
+    def test_show_default_quota_set_with_bytes_body(self):
+        self._test_show_default_quota_set(bytes_body=True)
+
+    def _test_update_quota_set(self, bytes_body=False, user_id=None):
+        fake_quota_set = copy.deepcopy(self.FAKE_QUOTA_SET)
+        fake_quota_set['quota_set'].pop("id")
+        if user_id:
+            self.check_service_client_function(
+                self.client.update_quota_set,
+                'tempest.lib.common.rest_client.RestClient.put',
+                fake_quota_set,
+                to_utf=bytes_body,
+                tenant_id=self.project_id,
+                user_id=user_id)
+        else:
+            self.check_service_client_function(
+                self.client.update_quota_set,
+                'tempest.lib.common.rest_client.RestClient.put',
+                fake_quota_set,
+                to_utf=bytes_body,
+                tenant_id=self.project_id)
+
+    def test_update_quota_set_with_str_body(self):
+        self._test_update_quota_set()
+
+    def test_update_quota_set_with_bytes_body(self):
+        self._test_update_quota_set(bytes_body=True)
+
+    def test_update_quota_set_for_user_with_str_body(self):
+        self._test_update_quota_set(user_id=self.fake_user_id)
+
+    def test_update_quota_set_for_user_with_bytes_body(self):
+        self._test_update_quota_set(bytes_body=True, user_id=self.fake_user_id)
+
+    def test_delete_quota_set(self):
+        self.check_service_client_function(
+            self.client.delete_quota_set,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, status=202, tenant_id=self.project_id)
diff --git a/tempest/tests/lib/services/compute/test_security_group_default_rules_client.py b/tempest/tests/lib/services/compute/test_security_group_default_rules_client.py
new file mode 100644
index 0000000..86eee02
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_security_group_default_rules_client.py
@@ -0,0 +1,88 @@
+# 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.services.compute import security_group_default_rules_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSecurityGroupDefaultRulesClient(base.BaseServiceTest):
+    FAKE_RULE = {
+        "from_port": 80,
+        "id": 1,
+        "ip_protocol": "TCP",
+        "ip_range": {
+            "cidr": "10.10.10.0/24"
+        },
+        "to_port": 80
+    }
+
+    def setUp(self):
+        super(TestSecurityGroupDefaultRulesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = (security_group_default_rules_client.
+                       SecurityGroupDefaultRulesClient(fake_auth, 'compute',
+                                                       'regionOne'))
+
+    def _test_list_security_group_default_rules(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_security_group_default_rules,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"security_group_default_rules": [self.FAKE_RULE]},
+            to_utf=bytes_body)
+
+    def test_list_security_group_default_rules_with_str_body(self):
+        self._test_list_security_group_default_rules()
+
+    def test_list_security_group_default_rules_with_bytes_body(self):
+        self._test_list_security_group_default_rules(bytes_body=True)
+
+    def _test_show_security_group_default_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_security_group_default_rule,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"security_group_default_rule": self.FAKE_RULE},
+            to_utf=bytes_body,
+            security_group_default_rule_id=1)
+
+    def test_show_security_group_default_rule_with_str_body(self):
+        self._test_show_security_group_default_rule()
+
+    def test_show_security_group_default_rule_with_bytes_body(self):
+        self._test_show_security_group_default_rule(bytes_body=True)
+
+    def _test_create_security_default_group_rule(self, bytes_body=False):
+        request_body = {
+            "to_port": 80,
+            "from_port": 80,
+            "ip_protocol": "TCP",
+            "cidr": "10.10.10.0/24"
+        }
+        self.check_service_client_function(
+            self.client.create_security_default_group_rule,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {"security_group_default_rule": self.FAKE_RULE},
+            to_utf=bytes_body, **request_body)
+
+    def test_create_security_default_group_rule_with_str_body(self):
+        self._test_create_security_default_group_rule()
+
+    def test_create_security_default_group_rule_with_bytes_body(self):
+        self._test_create_security_default_group_rule(bytes_body=True)
+
+    def test_delete_security_group_default_rule(self):
+        self.check_service_client_function(
+            self.client.delete_security_group_default_rule,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, status=204, security_group_default_rule_id=1)
diff --git a/tempest/tests/lib/services/compute/test_security_group_rules_client.py b/tempest/tests/lib/services/compute/test_security_group_rules_client.py
new file mode 100644
index 0000000..2b0a94d
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_security_group_rules_client.py
@@ -0,0 +1,66 @@
+# 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.services.compute import security_group_rules_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSecurityGroupRulesClient(base.BaseServiceTest):
+
+    FAKE_SECURITY_GROUP_RULE = {
+        "security_group_rule": {
+            "id": "2d021cf1-ce4b-4292-994f-7a785d62a144",
+            "ip_range": {
+                "cidr": "0.0.0.0/0"
+            },
+            "parent_group_id": "48700ff3-30b8-4e63-845f-a79c9633e9fb",
+            "to_port": 443,
+            "ip_protocol": "tcp",
+            "group": {},
+            "from_port": 443
+        }
+    }
+
+    def setUp(self):
+        super(TestSecurityGroupRulesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = security_group_rules_client.SecurityGroupRulesClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_create_security_group_rule(self, bytes_body=False):
+        req_body = {
+            "from_port": "443",
+            "ip_protocol": "tcp",
+            "to_port": "443",
+            "cidr": "0.0.0.0/0",
+            "parent_group_id": "48700ff3-30b8-4e63-845f-a79c9633e9fb"
+        }
+        self.check_service_client_function(
+            self.client.create_security_group_rule,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_SECURITY_GROUP_RULE,
+            to_utf=bytes_body, **req_body)
+
+    def test_create_security_group_rule_with_str_body(self):
+        self._test_create_security_group_rule()
+
+    def test_create_security_group_rule_with_bytes_body(self):
+        self._test_create_security_group_rule(bytes_body=True)
+
+    def test_delete_security_group_rule(self):
+        self.check_service_client_function(
+            self.client.delete_security_group_rule,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, status=202, group_rule_id='group-id')
diff --git a/tempest/tests/lib/services/compute/test_security_groups_client.py b/tempest/tests/lib/services/compute/test_security_groups_client.py
new file mode 100644
index 0000000..d293a08
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_security_groups_client.py
@@ -0,0 +1,113 @@
+# 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 oslotest import mockpatch
+
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import security_groups_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSecurityGroupsClient(base.BaseServiceTest):
+
+    FAKE_SECURITY_GROUP_INFO = [{
+        "description": "default",
+        "id": "3fb26eb3-581b-4420-9963-b0879a026506",
+        "name": "default",
+        "rules": [],
+        "tenant_id": "openstack"
+    }]
+
+    def setUp(self):
+        super(TestSecurityGroupsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = security_groups_client.SecurityGroupsClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_list_security_groups(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_security_groups,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"security_groups": self.FAKE_SECURITY_GROUP_INFO},
+            to_utf=bytes_body)
+
+    def test_list_security_groups_with_str_body(self):
+        self._test_list_security_groups()
+
+    def test_list_security_groups_with_bytes_body(self):
+        self._test_list_security_groups(bytes_body=True)
+
+    def _test_show_security_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_security_group,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"security_group": self.FAKE_SECURITY_GROUP_INFO[0]},
+            to_utf=bytes_body,
+            security_group_id='fake-id')
+
+    def test_show_security_group_with_str_body(self):
+        self._test_show_security_group()
+
+    def test_show_security_group_with_bytes_body(self):
+        self._test_show_security_group(bytes_body=True)
+
+    def _test_create_security_group(self, bytes_body=False):
+        post_body = {"name": "test", "description": "test_group"}
+        self.check_service_client_function(
+            self.client.create_security_group,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {"security_group": self.FAKE_SECURITY_GROUP_INFO[0]},
+            to_utf=bytes_body,
+            kwargs=post_body)
+
+    def test_create_security_group_with_str_body(self):
+        self._test_create_security_group()
+
+    def test_create_security_group_with_bytes_body(self):
+        self._test_create_security_group(bytes_body=True)
+
+    def _test_update_security_group(self, bytes_body=False):
+        req_body = {"name": "test", "description": "test_group"}
+        self.check_service_client_function(
+            self.client.update_security_group,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {"security_group": self.FAKE_SECURITY_GROUP_INFO[0]},
+            to_utf=bytes_body,
+            security_group_id='fake-id',
+            kwargs=req_body)
+
+    def test_update_security_group_with_str_body(self):
+        self._test_update_security_group()
+
+    def test_update_security_group_with_bytes_body(self):
+        self._test_update_security_group(bytes_body=True)
+
+    def test_delete_security_group(self):
+        self.check_service_client_function(
+            self.client.delete_security_group,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, status=202, security_group_id='fake-id')
+
+    def test_is_resource_deleted_true(self):
+        mod = ('tempest.lib.services.compute.security_groups_client.'
+               'SecurityGroupsClient.show_security_group')
+        self.useFixture(mockpatch.Patch(mod, side_effect=lib_exc.NotFound))
+        self.assertTrue(self.client.is_resource_deleted('fake-id'))
+
+    def test_is_resource_deleted_false(self):
+        mod = ('tempest.lib.services.compute.security_groups_client.'
+               'SecurityGroupsClient.show_security_group')
+        self.useFixture(mockpatch.Patch(mod, return_value='success'))
+        self.assertFalse(self.client.is_resource_deleted('fake-id'))
diff --git a/tempest/tests/lib/services/compute/test_server_groups_client.py b/tempest/tests/lib/services/compute/test_server_groups_client.py
new file mode 100644
index 0000000..bf03b84
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_server_groups_client.py
@@ -0,0 +1,83 @@
+# Copyright 2015 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.
+
+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 import base
+
+
+class TestServerGroupsClient(base.BaseServiceTest):
+
+    server_group = {
+        "id": "5bbcc3c4-1da2-4437-a48a-66f15b1b13f9",
+        "name": "test",
+        "policies": ["anti-affinity"],
+        "members": [],
+        "metadata": {}}
+
+    def setUp(self):
+        super(TestServerGroupsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = server_groups_client.ServerGroupsClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_create_server_group(self, bytes_body=False):
+        expected = {"server_group": TestServerGroupsClient.server_group}
+        self.check_service_client_function(
+            self.client.create_server_group,
+            'tempest.lib.common.rest_client.RestClient.post', expected,
+            bytes_body, name='fake-group', policies=['affinity'])
+
+    def test_create_server_group_str_body(self):
+        self._test_create_server_group(bytes_body=False)
+
+    def test_create_server_group_byte_body(self):
+        self._test_create_server_group(bytes_body=True)
+
+    def test_delete_server_group(self):
+        response = fake_http.fake_http_response({}, status=204), ''
+        self.useFixture(mockpatch.Patch(
+            'tempest.lib.common.rest_client.RestClient.delete',
+            return_value=response))
+        self.client.delete_server_group('fake-group')
+
+    def _test_list_server_groups(self, bytes_body=False):
+        expected = {"server_groups": [TestServerGroupsClient.server_group]}
+        self.check_service_client_function(
+            self.client.list_server_groups,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected, bytes_body)
+
+    def test_list_server_groups_str_body(self):
+        self._test_list_server_groups(bytes_body=False)
+
+    def test_list_server_groups_byte_body(self):
+        self._test_list_server_groups(bytes_body=True)
+
+    def _test_show_server_group(self, bytes_body=False):
+        expected = {"server_group": TestServerGroupsClient.server_group}
+        self.check_service_client_function(
+            self.client.show_server_group,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected, bytes_body,
+            server_group_id='5bbcc3c4-1da2-4437-a48a-66f15b1b13f9')
+
+    def test_show_server_group_str_body(self):
+        self._test_show_server_group(bytes_body=False)
+
+    def test_show_server_group_byte_body(self):
+        self._test_show_server_group(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_servers_client.py b/tempest/tests/lib/services/compute/test_servers_client.py
new file mode 100644
index 0000000..93550fd
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_servers_client.py
@@ -0,0 +1,1011 @@
+# Copyright 2015 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
+
+from tempest.lib.services.compute import servers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestServersClient(base.BaseServiceTest):
+
+    FAKE_SERVERS = {'servers': [{
+        "id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
+        "links": [
+            {
+                "href": "http://os.co/v2/616fb98f-46ca-475e-917e-2563e5a8cd19",
+                "rel": "self"
+            },
+            {
+                "href": "http://os.co/616fb98f-46ca-475e-917e-2563e5a8cd19",
+                "rel": "bookmark"
+            }
+        ],
+        "name": u"new\u1234-server-test"}]
+    }
+
+    FAKE_SERVER_DIAGNOSTICS = {
+        "cpu0_time": 17300000000,
+        "memory": 524288,
+        "vda_errors": -1,
+        "vda_read": 262144,
+        "vda_read_req": 112,
+        "vda_write": 5778432,
+        "vda_write_req": 488,
+        "vnet1_rx": 2070139,
+        "vnet1_rx_drop": 0,
+        "vnet1_rx_errors": 0,
+        "vnet1_rx_packets": 26701,
+        "vnet1_tx": 140208,
+        "vnet1_tx_drop": 0,
+        "vnet1_tx_errors": 0,
+        "vnet1_tx_packets": 662
+    }
+
+    FAKE_SERVER_GET = {'server': {
+        "accessIPv4": "",
+        "accessIPv6": "",
+        "addresses": {
+            "private": [
+                {
+                    "addr": "192.168.0.3",
+                    "version": 4
+                }
+            ]
+        },
+        "created": "2012-08-20T21:11:09Z",
+        "flavor": {
+            "id": "1",
+            "links": [
+                {
+                    "href": "http://os.com/openstack/flavors/1",
+                    "rel": "bookmark"
+                }
+            ]
+        },
+        "hostId": "65201c14a29663e06d0748e561207d998b343e1d164bfa0aafa9c45d",
+        "id": "893c7791-f1df-4c3d-8383-3caae9656c62",
+        "image": {
+            "id": "70a599e0-31e7-49b7-b260-868f441e862b",
+            "links": [
+                {
+                    "href": "http://imgs/70a599e0-31e7-49b7-b260-868f441e862b",
+                    "rel": "bookmark"
+                }
+            ]
+        },
+        "links": [
+            {
+                "href": "http://v2/srvs/893c7791-f1df-4c3d-8383-3caae9656c62",
+                "rel": "self"
+            },
+            {
+                "href": "http://srvs/893c7791-f1df-4c3d-8383-3caae9656c62",
+                "rel": "bookmark"
+            }
+        ],
+        "metadata": {
+            u"My Server N\u1234me": u"Apa\u1234che1"
+        },
+        "name": u"new\u1234-server-test",
+        "progress": 0,
+        "status": "ACTIVE",
+        "tenant_id": "openstack",
+        "updated": "2012-08-20T21:11:09Z",
+        "user_id": "fake"}
+    }
+
+    FAKE_SERVER_POST = {"server": {
+        "id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
+        "adminPass": "fake-admin-pass",
+        "security_groups": [
+            'fake-security-group-1',
+            'fake-security-group-2'
+        ],
+        "links": [
+            {
+                "href": "http://os.co/v2/616fb98f-46ca-475e-917e-2563e5a8cd19",
+                "rel": "self"
+            },
+            {
+                "href": "http://os.co/616fb98f-46ca-475e-917e-2563e5a8cd19",
+                "rel": "bookmark"
+            }
+        ],
+        "OS-DCF:diskConfig": "fake-disk-config"}
+    }
+
+    FAKE_ADDRESS = {"addresses": {
+        "private": [
+            {
+                "addr": "192.168.0.3",
+                "version": 4
+            }
+        ]}
+    }
+
+    FAKE_COMMON_VOLUME = {
+        "id": "a6b0875b-6b5d-4a5a-81eb-0c3aa62e5fdb",
+        "device": "fake-device",
+        "volumeId": "a6b0875b-46ca-475e-917e-0c3aa62e5fdb",
+        "serverId": "616fb98f-46ca-475e-917e-2563e5a8cd19"
+    }
+
+    FAKE_VIRTUAL_INTERFACES = {
+        "id": "a6b0875b-46ca-475e-917e-0c3aa62e5fdb",
+        "mac_address": "00:25:90:5b:f8:c3",
+        "OS-EXT-VIF-NET:net_id": "fake-os-net-id"
+    }
+
+    FAKE_INSTANCE_ACTIONS = {
+        "action": "fake-action",
+        "request_id": "16fb98f-46ca-475e-917e-2563e5a8cd19",
+        "user_id": "16fb98f-46ca-475e-917e-2563e5a8cd12",
+        "project_id": "16fb98f-46ca-475e-917e-2563e5a8cd34",
+        "start_time": "09MAR2015 11:15",
+        "message": "fake-msg",
+        "instance_uuid": "16fb98f-46ca-475e-917e-2563e5a8cd12"
+    }
+
+    FAKE_VNC_CONSOLE = {
+        "type": "fake-type",
+        "url": "http://os.co/v2/616fb98f-46ca-475e-917e-2563e5a8cd19"
+    }
+
+    FAKE_INSTANCE_ACTION_EVENTS = {
+        "event": "fake-event",
+        "start_time": "09MAR2015 11:15",
+        "finish_time": "09MAR2015 11:15",
+        "result": "fake-result",
+        "traceback": "fake-trace-back"
+    }
+
+    FAKE_INSTANCE_WITH_EVENTS = copy.deepcopy(FAKE_INSTANCE_ACTIONS)
+    FAKE_INSTANCE_WITH_EVENTS['events'] = [FAKE_INSTANCE_ACTION_EVENTS]
+
+    FAKE_REBUILD_SERVER = copy.deepcopy(FAKE_SERVER_GET)
+    FAKE_REBUILD_SERVER['server']['adminPass'] = 'fake-admin-pass'
+
+    server_id = FAKE_SERVER_GET['server']['id']
+    network_id = 'a6b0875b-6b5d-4a5a-81eb-0c3aa62e5fdb'
+
+    def setUp(self):
+        super(TestServersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = servers_client.ServersClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def test_list_servers_with_str_body(self):
+        self._test_list_servers()
+
+    def test_list_servers_with_bytes_body(self):
+        self._test_list_servers(bytes_body=True)
+
+    def _test_list_servers(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_servers,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SERVERS,
+            bytes_body)
+
+    def test_show_server_with_str_body(self):
+        self._test_show_server()
+
+    def test_show_server_with_bytes_body(self):
+        self._test_show_server(bytes_body=True)
+
+    def _test_show_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_server,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SERVER_GET,
+            bytes_body,
+            server_id=self.server_id
+            )
+
+    def test_delete_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.delete_server,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            server_id=self.server_id
+            )
+
+    def test_create_server_with_str_body(self):
+        self._test_create_server()
+
+    def test_create_server_with_bytes_body(self):
+        self._test_create_server(True)
+
+    def _test_create_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_SERVER_POST,
+            bytes_body,
+            status=202,
+            name='fake-name',
+            imageRef='fake-image-ref',
+            flavorRef='fake-flavor-ref'
+            )
+
+    def test_list_addresses_with_str_body(self):
+        self._test_list_addresses()
+
+    def test_list_addresses_with_bytes_body(self):
+        self._test_list_addresses(True)
+
+    def _test_list_addresses(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_addresses,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ADDRESS,
+            bytes_body,
+            server_id=self.server_id
+            )
+
+    def test_list_addresses_by_network_with_str_body(self):
+        self._test_list_addresses_by_network()
+
+    def test_list_addresses_by_network_with_bytes_body(self):
+        self._test_list_addresses_by_network(True)
+
+    def _test_list_addresses_by_network(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_addresses_by_network,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ADDRESS['addresses'],
+            server_id=self.server_id,
+            network_id=self.network_id
+            )
+
+    def test_action_with_str_body(self):
+        self._test_action()
+
+    def test_action_with_bytes_body(self):
+        self._test_action(True)
+
+    def _test_action(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.action,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            server_id=self.server_id,
+            action_name='fake-action-name',
+            schema={'status_code': 200}
+            )
+
+    def test_create_backup_with_str_body(self):
+        self._test_create_backup()
+
+    def test_create_backup_with_bytes_body(self):
+        self._test_create_backup(True)
+
+    def _test_create_backup(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_backup,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id,
+            backup_type='fake-backup',
+            rotation='fake-rotation',
+            name='fake-name'
+            )
+
+    def test_change_password_with_str_body(self):
+        self._test_change_password()
+
+    def test_change_password_with_bytes_body(self):
+        self._test_change_password(True)
+
+    def _test_change_password(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.change_password,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id,
+            adminPass='fake-admin-pass'
+            )
+
+    def test_show_password_with_str_body(self):
+        self._test_show_password()
+
+    def test_show_password_with_bytes_body(self):
+        self._test_show_password(True)
+
+    def _test_show_password(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_password,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {'password': 'fake-password'},
+            server_id=self.server_id
+            )
+
+    def test_delete_password_with_str_body(self):
+        self._test_delete_password()
+
+    def test_delete_password_with_bytes_body(self):
+        self._test_delete_password(True)
+
+    def _test_delete_password(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.delete_password,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            server_id=self.server_id
+            )
+
+    def test_reboot_server_with_str_body(self):
+        self._test_reboot_server()
+
+    def test_reboot_server_with_bytes_body(self):
+        self._test_reboot_server(True)
+
+    def _test_reboot_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.reboot_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id,
+            type='fake-reboot-type'
+            )
+
+    def test_rebuild_server_with_str_body(self):
+        self._test_rebuild_server()
+
+    def test_rebuild_server_with_bytes_body(self):
+        self._test_rebuild_server(True)
+
+    def _test_rebuild_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.rebuild_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_REBUILD_SERVER,
+            status=202,
+            server_id=self.server_id,
+            image_ref='fake-image-ref'
+            )
+
+    def test_resize_server_with_str_body(self):
+        self._test_resize_server()
+
+    def test_resize_server_with_bytes_body(self):
+        self._test_resize_server(True)
+
+    def _test_resize_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.resize_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id,
+            flavor_ref='fake-flavor-ref'
+            )
+
+    def test_confirm_resize_server_with_str_body(self):
+        self._test_confirm_resize_server()
+
+    def test_confirm_resize_server_with_bytes_body(self):
+        self._test_confirm_resize_server(True)
+
+    def _test_confirm_resize_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.confirm_resize_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=204,
+            server_id=self.server_id
+            )
+
+    def test_revert_resize_server_with_str_body(self):
+        self._test_revert_resize()
+
+    def test_revert_resize_server_with_bytes_body(self):
+        self._test_revert_resize(True)
+
+    def _test_revert_resize(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.revert_resize_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_list_server_metadata_with_str_body(self):
+        self._test_list_server_metadata()
+
+    def test_list_server_metadata_with_bytes_body(self):
+        self._test_list_server_metadata()
+
+    def _test_list_server_metadata(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_server_metadata,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {'metadata': {'fake-key': 'fake-meta-data'}},
+            server_id=self.server_id
+            )
+
+    def test_set_server_metadata_with_str_body(self):
+        self._test_set_server_metadata()
+
+    def test_set_server_metadata_with_bytes_body(self):
+        self._test_set_server_metadata(True)
+
+    def _test_set_server_metadata(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.set_server_metadata,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {'metadata': {'fake-key': 'fake-meta-data'}},
+            server_id=self.server_id,
+            meta='fake-meta'
+            )
+
+    def test_update_server_metadata_with_str_body(self):
+        self._test_update_server_metadata()
+
+    def test_update_server_metadata_with_bytes_body(self):
+        self._test_update_server_metadata(True)
+
+    def _test_update_server_metadata(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_server_metadata,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {'metadata': {'fake-key': 'fake-meta-data'}},
+            server_id=self.server_id,
+            meta='fake-meta'
+            )
+
+    def test_show_server_metadata_item_with_str_body(self):
+        self._test_show_server_metadata()
+
+    def test_show_server_metadata_item_with_bytes_body(self):
+        self._test_show_server_metadata(True)
+
+    def _test_show_server_metadata(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_server_metadata_item,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {'meta': {'fake-key': 'fake-meta-data'}},
+            server_id=self.server_id,
+            key='fake-key'
+            )
+
+    def test_set_server_metadata_item_with_str_body(self):
+        self._test_set_server_metadata_item()
+
+    def test_set_server_metadata_item_with_bytes_body(self):
+        self._test_set_server_metadata_item(True)
+
+    def _test_set_server_metadata_item(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.set_server_metadata_item,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {'meta': {'fake-key': 'fake-meta-data'}},
+            server_id=self.server_id,
+            key='fake-key',
+            meta='fake-meta'
+            )
+
+    def test_delete_server_metadata_item_with_str_body(self):
+        self._test_delete_server_metadata()
+
+    def test_delete_server_metadata_item_with_bytes_body(self):
+        self._test_delete_server_metadata(True)
+
+    def _test_delete_server_metadata(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.delete_server_metadata_item,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            server_id=self.server_id,
+            key='fake-key'
+            )
+
+    def test_stop_server_with_str_body(self):
+        self._test_stop_server()
+
+    def test_stop_server_with_bytes_body(self):
+        self._test_stop_server(True)
+
+    def _test_stop_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.stop_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_start_server_with_str_body(self):
+        self._test_start_server()
+
+    def test_start_server_with_bytes_body(self):
+        self._test_start_server(True)
+
+    def _test_start_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.start_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_attach_volume_with_str_body(self):
+        self._test_attach_volume_server()
+
+    def test_attach_volume_with_bytes_body(self):
+        self._test_attach_volume_server(True)
+
+    def _test_attach_volume_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.attach_volume,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {'volumeAttachment': self.FAKE_COMMON_VOLUME},
+            server_id=self.server_id
+            )
+
+    def test_update_attached_volume(self):
+        self.check_service_client_function(
+            self.client.update_attached_volume,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            status=202,
+            server_id=self.server_id,
+            attachment_id='fake-attachment-id',
+            volumeId='fake-volume-id'
+            )
+
+    def test_detach_volume_with_str_body(self):
+        self._test_detach_volume_server()
+
+    def test_detach_volume_with_bytes_body(self):
+        self._test_detach_volume_server(True)
+
+    def _test_detach_volume_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.detach_volume,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=202,
+            server_id=self.server_id,
+            volume_id=self.FAKE_COMMON_VOLUME['volumeId']
+            )
+
+    def test_show_volume_attachment_with_str_body(self):
+        self._test_show_volume_attachment()
+
+    def test_show_volume_attachment_with_bytes_body(self):
+        self._test_show_volume_attachment(True)
+
+    def _test_show_volume_attachment(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_volume_attachment,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {'volumeAttachment': self.FAKE_COMMON_VOLUME},
+            server_id=self.server_id,
+            volume_id=self.FAKE_COMMON_VOLUME['volumeId']
+            )
+
+    def test_list_volume_attachments_with_str_body(self):
+        self._test_list_volume_attachments()
+
+    def test_list_volume_attachments_with_bytes_body(self):
+        self._test_list_volume_attachments(True)
+
+    def _test_list_volume_attachments(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_volume_attachments,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {'volumeAttachments': [self.FAKE_COMMON_VOLUME]},
+            server_id=self.server_id
+            )
+
+    def test_add_security_group_with_str_body(self):
+        self._test_add_security_group()
+
+    def test_add_security_group_with_bytes_body(self):
+        self._test_add_security_group(True)
+
+    def _test_add_security_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.add_security_group,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id,
+            name='fake-name'
+            )
+
+    def test_remove_security_group_with_str_body(self):
+        self._test_remove_security_group()
+
+    def test_remove_security_group_with_bytes_body(self):
+        self._test_remove_security_group(True)
+
+    def _test_remove_security_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.remove_security_group,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id,
+            name='fake-name'
+            )
+
+    def test_live_migrate_server_with_str_body(self):
+        self._test_live_migrate_server()
+
+    def test_live_migrate_server_with_bytes_body(self):
+        self._test_live_migrate_server(True)
+
+    def _test_live_migrate_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.live_migrate_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_migrate_server_with_str_body(self):
+        self._test_migrate_server()
+
+    def test_migrate_server_with_bytes_body(self):
+        self._test_migrate_server(True)
+
+    def _test_migrate_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.migrate_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_lock_server_with_str_body(self):
+        self._test_lock_server()
+
+    def test_lock_server_with_bytes_body(self):
+        self._test_lock_server(True)
+
+    def _test_lock_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.lock_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_unlock_server_with_str_body(self):
+        self._test_unlock_server()
+
+    def test_unlock_server_with_bytes_body(self):
+        self._test_unlock_server(True)
+
+    def _test_unlock_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.unlock_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_suspend_server_with_str_body(self):
+        self._test_suspend_server()
+
+    def test_suspend_server_with_bytes_body(self):
+        self._test_suspend_server(True)
+
+    def _test_suspend_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.suspend_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_resume_server_with_str_body(self):
+        self._test_resume_server()
+
+    def test_resume_server_with_bytes_body(self):
+        self._test_resume_server(True)
+
+    def _test_resume_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.resume_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_pause_server_with_str_body(self):
+        self._test_pause_server()
+
+    def test_pause_server_with_bytes_body(self):
+        self._test_pause_server(True)
+
+    def _test_pause_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.pause_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_unpause_server_with_str_body(self):
+        self._test_unpause_server()
+
+    def test_unpause_server_with_bytes_body(self):
+        self._test_unpause_server(True)
+
+    def _test_unpause_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.unpause_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_reset_state_with_str_body(self):
+        self._test_reset_state()
+
+    def test_reset_state_with_bytes_body(self):
+        self._test_reset_state(True)
+
+    def _test_reset_state(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.reset_state,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id,
+            state='fake-state'
+            )
+
+    def test_shelve_server_with_str_body(self):
+        self._test_shelve_server()
+
+    def test_shelve_server_with_bytes_body(self):
+        self._test_shelve_server(True)
+
+    def _test_shelve_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.shelve_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_unshelve_server_with_str_body(self):
+        self._test_unshelve_server()
+
+    def test_unshelve_server_with_bytes_body(self):
+        self._test_unshelve_server(True)
+
+    def _test_unshelve_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.unshelve_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_shelve_offload_server_with_str_body(self):
+        self._test_shelve_offload_server()
+
+    def test_shelve_offload_server_with_bytes_body(self):
+        self._test_shelve_offload_server(True)
+
+    def _test_shelve_offload_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.shelve_offload_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_get_console_output_with_str_body(self):
+        self._test_get_console_output()
+
+    def test_get_console_output_with_bytes_body(self):
+        self._test_get_console_output(True)
+
+    def _test_get_console_output(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.get_console_output,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {'output': 'fake-output'},
+            server_id=self.server_id,
+            length='fake-length'
+            )
+
+    def test_list_virtual_interfaces_with_str_body(self):
+        self._test_list_virtual_interfaces()
+
+    def test_list_virtual_interfaces_with_bytes_body(self):
+        self._test_list_virtual_interfaces(True)
+
+    def _test_list_virtual_interfaces(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_virtual_interfaces,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {'virtual_interfaces': [self.FAKE_VIRTUAL_INTERFACES]},
+            server_id=self.server_id
+            )
+
+    def test_rescue_server_with_str_body(self):
+        self._test_rescue_server()
+
+    def test_rescue_server_with_bytes_body(self):
+        self._test_rescue_server(True)
+
+    def _test_rescue_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.rescue_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {'adminPass': 'fake-admin-pass'},
+            server_id=self.server_id
+            )
+
+    def test_unrescue_server_with_str_body(self):
+        self._test_unrescue_server()
+
+    def test_unrescue_server_with_bytes_body(self):
+        self._test_unrescue_server(True)
+
+    def _test_unrescue_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.unrescue_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_show_server_diagnostics_with_str_body(self):
+        self._test_show_server_diagnostics()
+
+    def test_show_server_diagnostics_with_bytes_body(self):
+        self._test_show_server_diagnostics(True)
+
+    def _test_show_server_diagnostics(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_server_diagnostics,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SERVER_DIAGNOSTICS,
+            status=200,
+            server_id=self.server_id
+            )
+
+    def test_list_instance_actions_with_str_body(self):
+        self._test_list_instance_actions()
+
+    def test_list_instance_actions_with_bytes_body(self):
+        self._test_list_instance_actions(True)
+
+    def _test_list_instance_actions(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_instance_actions,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {'instanceActions': [self.FAKE_INSTANCE_ACTIONS]},
+            server_id=self.server_id
+            )
+
+    def test_show_instance_action_with_str_body(self):
+        self._test_show_instance_action()
+
+    def test_show_instance_action_with_bytes_body(self):
+        self._test_show_instance_action(True)
+
+    def _test_show_instance_action(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_instance_action,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {'instanceAction': self.FAKE_INSTANCE_WITH_EVENTS},
+            server_id=self.server_id,
+            request_id='fake-request-id'
+            )
+
+    def test_force_delete_server_with_str_body(self):
+        self._test_force_delete_server()
+
+    def test_force_delete_server_with_bytes_body(self):
+        self._test_force_delete_server(True)
+
+    def _test_force_delete_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.force_delete_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_restore_soft_deleted_server_with_str_body(self):
+        self._test_restore_soft_deleted_server()
+
+    def test_restore_soft_deleted_server_with_bytes_body(self):
+        self._test_restore_soft_deleted_server(True)
+
+    def _test_restore_soft_deleted_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.restore_soft_deleted_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_reset_network_with_str_body(self):
+        self._test_reset_network()
+
+    def test_reset_network_with_bytes_body(self):
+        self._test_reset_network(True)
+
+    def _test_reset_network(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.reset_network,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_inject_network_info_with_str_body(self):
+        self._test_inject_network_info()
+
+    def test_inject_network_info_with_bytes_body(self):
+        self._test_inject_network_info(True)
+
+    def _test_inject_network_info(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.inject_network_info,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            server_id=self.server_id
+            )
+
+    def test_get_vnc_console_with_str_body(self):
+        self._test_get_vnc_console()
+
+    def test_get_vnc_console_with_bytes_body(self):
+        self._test_get_vnc_console(True)
+
+    def _test_get_vnc_console(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.get_vnc_console,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {'console': self.FAKE_VNC_CONSOLE},
+            server_id=self.server_id,
+            type='fake-console-type'
+            )
diff --git a/tempest/tests/lib/services/compute/test_services_client.py b/tempest/tests/lib/services/compute/test_services_client.py
new file mode 100644
index 0000000..41da39c
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_services_client.py
@@ -0,0 +1,94 @@
+# 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.lib.services.compute import services_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestServicesClient(base.BaseServiceTest):
+
+    FAKE_SERVICES = {
+        "services":
+        [{
+            "status": "enabled",
+            "binary": "nova-conductor",
+            "zone": "internal",
+            "state": "up",
+            "updated_at": "2015-08-19T06:50:55.000000",
+            "host": "controller",
+            "disabled_reason": None,
+            "id": 1
+        }]
+    }
+
+    FAKE_SERVICE = {
+        "service":
+        {
+            "status": "enabled",
+            "binary": "nova-conductor",
+            "host": "controller"
+        }
+    }
+
+    def setUp(self):
+        super(TestServicesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = services_client.ServicesClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def test_list_services_with_str_body(self):
+        self.check_service_client_function(
+            self.client.list_services,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SERVICES)
+
+    def test_list_services_with_bytes_body(self):
+        self.check_service_client_function(
+            self.client.list_services,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SERVICES, to_utf=True)
+
+    def _test_enable_service(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.enable_service,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_SERVICE,
+            bytes_body,
+            host_name="nova-conductor", binary="controller")
+
+    def test_enable_service_with_str_body(self):
+        self._test_enable_service()
+
+    def test_enable_service_with_bytes_body(self):
+        self._test_enable_service(bytes_body=True)
+
+    def _test_disable_service(self, bytes_body=False):
+        fake_service = copy.deepcopy(self.FAKE_SERVICE)
+        fake_service["service"]["status"] = "disable"
+
+        self.check_service_client_function(
+            self.client.disable_service,
+            'tempest.lib.common.rest_client.RestClient.put',
+            fake_service,
+            bytes_body,
+            host_name="nova-conductor", binary="controller")
+
+    def test_disable_service_with_str_body(self):
+        self._test_disable_service()
+
+    def test_disable_service_with_bytes_body(self):
+        self._test_disable_service(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_snapshots_client.py b/tempest/tests/lib/services/compute/test_snapshots_client.py
new file mode 100644
index 0000000..1629943
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_snapshots_client.py
@@ -0,0 +1,103 @@
+# 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 oslotest import mockpatch
+
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import snapshots_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSnapshotsClient(base.BaseServiceTest):
+
+    FAKE_SNAPSHOT = {
+        "createdAt": "2015-10-02T16:27:54.724209",
+        "displayDescription": u"Another \u1234.",
+        "displayName": u"v\u1234-001",
+        "id": "100",
+        "size": 100,
+        "status": "available",
+        "volumeId": "12"
+    }
+
+    FAKE_SNAPSHOTS = {"snapshots": [FAKE_SNAPSHOT]}
+
+    def setUp(self):
+        super(TestSnapshotsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = snapshots_client.SnapshotsClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_create_snapshot(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_snapshot,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {"snapshot": self.FAKE_SNAPSHOT},
+            to_utf=bytes_body, status=200,
+            volume_id=self.FAKE_SNAPSHOT["volumeId"])
+
+    def test_create_snapshot_with_str_body(self):
+        self._test_create_snapshot()
+
+    def test_create_shapshot_with_bytes_body(self):
+        self._test_create_snapshot(bytes_body=True)
+
+    def _test_show_snapshot(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_snapshot,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"snapshot": self.FAKE_SNAPSHOT},
+            to_utf=bytes_body, snapshot_id=self.FAKE_SNAPSHOT["id"])
+
+    def test_show_snapshot_with_str_body(self):
+        self._test_show_snapshot()
+
+    def test_show_snapshot_with_bytes_body(self):
+        self._test_show_snapshot(bytes_body=True)
+
+    def _test_list_snapshots(self, bytes_body=False, **params):
+        self.check_service_client_function(
+            self.client.list_snapshots,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SNAPSHOTS, to_utf=bytes_body, **params)
+
+    def test_list_snapshots_with_str_body(self):
+        self._test_list_snapshots()
+
+    def test_list_snapshots_with_byte_body(self):
+        self._test_list_snapshots(bytes_body=True)
+
+    def test_list_snapshots_with_params(self):
+        self._test_list_snapshots('fake')
+
+    def test_delete_snapshot(self):
+        self.check_service_client_function(
+            self.client.delete_snapshot,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, status=202, snapshot_id=self.FAKE_SNAPSHOT['id'])
+
+    def test_is_resource_deleted_true(self):
+        module = ('tempest.lib.services.compute.snapshots_client.'
+                  'SnapshotsClient.show_snapshot')
+        self.useFixture(mockpatch.Patch(
+            module, side_effect=lib_exc.NotFound))
+        self.assertTrue(self.client.is_resource_deleted('fake-id'))
+
+    def test_is_resource_deleted_false(self):
+        module = ('tempest.lib.services.compute.snapshots_client.'
+                  'SnapshotsClient.show_snapshot')
+        self.useFixture(mockpatch.Patch(
+            module, return_value={}))
+        self.assertFalse(self.client.is_resource_deleted('fake-id'))
diff --git a/tempest/tests/lib/services/compute/test_tenant_networks_client.py b/tempest/tests/lib/services/compute/test_tenant_networks_client.py
new file mode 100644
index 0000000..f71aad9
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_tenant_networks_client.py
@@ -0,0 +1,63 @@
+# 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.services.compute import tenant_networks_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestTenantNetworksClient(base.BaseServiceTest):
+
+    FAKE_NETWORK = {
+        "cidr": "None",
+        "id": "c2329eb4-cc8e-4439-ac4c-932369309e36",
+        "label": u'\u30d7'
+        }
+
+    FAKE_NETWORKS = [FAKE_NETWORK]
+
+    NETWORK_ID = FAKE_NETWORK['id']
+
+    def setUp(self):
+        super(TestTenantNetworksClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = tenant_networks_client.TenantNetworksClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_list_tenant_networks(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_tenant_networks,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"networks": self.FAKE_NETWORKS},
+            bytes_body)
+
+    def test_list_tenant_networks_with_str_body(self):
+        self._test_list_tenant_networks()
+
+    def test_list_tenant_networks_with_bytes_body(self):
+        self._test_list_tenant_networks(bytes_body=True)
+
+    def _test_show_tenant_network(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_tenant_network,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"network": self.FAKE_NETWORK},
+            bytes_body,
+            network_id=self.NETWORK_ID)
+
+    def test_show_tenant_network_with_str_body(self):
+        self._test_show_tenant_network()
+
+    def test_show_tenant_network_with_bytes_body(self):
+        self._test_show_tenant_network(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_tenant_usages_client.py b/tempest/tests/lib/services/compute/test_tenant_usages_client.py
new file mode 100644
index 0000000..49eae08
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_tenant_usages_client.py
@@ -0,0 +1,79 @@
+# 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.services.compute import tenant_usages_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestTenantUsagesClient(base.BaseServiceTest):
+
+    FAKE_SERVER_USAGES = [{
+        "ended_at": None,
+        "flavor": "m1.tiny",
+        "hours": 1.0,
+        "instance_id": "1f1deceb-17b5-4c04-84c7-e0d4499c8fe0",
+        "local_gb": 1,
+        "memory_mb": 512,
+        "name": "new-server-test",
+        "started_at": "2012-10-08T20:10:44.541277",
+        "state": "active",
+        "tenant_id": "openstack",
+        "uptime": 3600,
+        "vcpus": 1
+        }]
+
+    FAKE_TENANT_USAGES = [{
+        "server_usages": FAKE_SERVER_USAGES,
+        "start": "2012-10-08T21:10:44.587336",
+        "stop": "2012-10-08T22:10:44.587336",
+        "tenant_id": "openstack",
+        "total_hours": 1,
+        "total_local_gb_usage": 1,
+        "total_memory_mb_usage": 512,
+        "total_vcpus_usage": 1
+        }]
+
+    def setUp(self):
+        super(TestTenantUsagesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = tenant_usages_client.TenantUsagesClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_list_tenant_usages(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_tenant_usages,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"tenant_usages": self.FAKE_TENANT_USAGES},
+            to_utf=bytes_body)
+
+    def test_list_tenant_usages_with_str_body(self):
+        self._test_list_tenant_usages()
+
+    def test_list_tenant_usages_with_bytes_body(self):
+        self._test_list_tenant_usages(bytes_body=True)
+
+    def _test_show_tenant_usage(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_tenant_usage,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"tenant_usage": self.FAKE_TENANT_USAGES[0]},
+            to_utf=bytes_body,
+            tenant_id='openstack')
+
+    def test_show_tenant_usage_with_str_body(self):
+        self._test_show_tenant_usage()
+
+    def test_show_tenant_usage_with_bytes_body(self):
+        self._test_show_tenant_usage(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_versions_client.py b/tempest/tests/lib/services/compute/test_versions_client.py
new file mode 100644
index 0000000..06ecdc3
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_versions_client.py
@@ -0,0 +1,135 @@
+# 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 oslotest import mockpatch
+
+from tempest.lib.services.compute import versions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVersionsClient(base.BaseServiceTest):
+
+    FAKE_INIT_VERSION = {
+        "version": {
+            "id": "v2.1",
+            "links": [
+                {
+                    "href": "http://openstack.example.com/v2.1/",
+                    "rel": "self"
+                },
+                {
+                    "href": "http://docs.openstack.org/",
+                    "rel": "describedby",
+                    "type": "text/html"
+                }
+            ],
+            "status": "CURRENT",
+            "updated": "2013-07-23T11:33:21Z",
+            "version": "2.1",
+            "min_version": "2.1"
+            }
+        }
+
+    FAKE_VERSIONS_INFO = {
+        "versions": [FAKE_INIT_VERSION["version"]]
+        }
+
+    FAKE_VERSION_INFO = copy.deepcopy(FAKE_INIT_VERSION)
+
+    FAKE_VERSION_INFO["version"]["media-types"] = [
+        {
+            "base": "application/json",
+            "type": "application/vnd.openstack.compute+json;version=2.1"
+        }
+        ]
+
+    def setUp(self):
+        super(TestVersionsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.versions_client = (
+            versions_client.VersionsClient
+            (fake_auth, 'compute', 'regionOne'))
+
+    def _test_versions_client(self, bytes_body=False):
+        self.check_service_client_function(
+            self.versions_client.list_versions,
+            'tempest.lib.common.rest_client.RestClient.raw_request',
+            self.FAKE_VERSIONS_INFO,
+            bytes_body,
+            200)
+
+    def _test_get_version_by_url(self, bytes_body=False):
+        self.useFixture(mockpatch.Patch(
+            "tempest.lib.common.rest_client.RestClient.token",
+            return_value="Dummy Token"))
+        params = {"version_url": self.versions_client._get_base_version_url()}
+        self.check_service_client_function(
+            self.versions_client.get_version_by_url,
+            'tempest.lib.common.rest_client.RestClient.raw_request',
+            self.FAKE_VERSION_INFO,
+            bytes_body,
+            200, **params)
+
+    def test_list_versions_client_with_str_body(self):
+        self._test_versions_client()
+
+    def test_list_versions_client_with_bytes_body(self):
+        self._test_versions_client(bytes_body=True)
+
+    def test_get_version_by_url_with_str_body(self):
+        self._test_get_version_by_url()
+
+    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/compute/test_volumes_client.py b/tempest/tests/lib/services/compute/test_volumes_client.py
new file mode 100644
index 0000000..b81cdbb
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_volumes_client.py
@@ -0,0 +1,114 @@
+# 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 oslotest import mockpatch
+
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import volumes_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVolumesClient(base.BaseServiceTest):
+
+    FAKE_VOLUME = {
+        "id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+        "displayName": u"v\u12345ol-001",
+        "displayDescription": u"Another \u1234volume.",
+        "size": 30,
+        "status": "Active",
+        "volumeType": "289da7f8-6440-407c-9fb4-7db01ec49164",
+        "metadata": {
+            "contents": "junk"
+        },
+        "availabilityZone": "us-east1",
+        "snapshotId": None,
+        "attachments": [],
+        "createdAt": "2012-02-14T20:53:07Z"
+    }
+
+    FAKE_VOLUMES = {"volumes": [FAKE_VOLUME]}
+
+    def setUp(self):
+        super(TestVolumesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = volumes_client.VolumesClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_list_volumes(self, bytes_body=False, **params):
+        self.check_service_client_function(
+            self.client.list_volumes,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_VOLUMES, to_utf=bytes_body, **params)
+
+    def test_list_volumes_with_str_body(self):
+        self._test_list_volumes()
+
+    def test_list_volumes_with_byte_body(self):
+        self._test_list_volumes(bytes_body=True)
+
+    def test_list_volumes_with_params(self):
+        self._test_list_volumes(name='fake')
+
+    def _test_show_volume(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_volume,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"volume": self.FAKE_VOLUME},
+            to_utf=bytes_body, volume_id=self.FAKE_VOLUME['id'])
+
+    def test_show_volume_with_str_body(self):
+        self._test_show_volume()
+
+    def test_show_volume_with_bytes_body(self):
+        self._test_show_volume(bytes_body=True)
+
+    def _test_create_volume(self, bytes_body=False):
+        post_body = copy.deepcopy(self.FAKE_VOLUME)
+        del post_body['id']
+        del post_body['createdAt']
+        del post_body['status']
+        self.check_service_client_function(
+            self.client.create_volume,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {"volume": self.FAKE_VOLUME},
+            to_utf=bytes_body, status=200, **post_body)
+
+    def test_create_volume_with_str_body(self):
+        self._test_create_volume()
+
+    def test_create_volume_with_bytes_body(self):
+        self._test_create_volume(bytes_body=True)
+
+    def test_delete_volume(self):
+        self.check_service_client_function(
+            self.client.delete_volume,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, status=202, volume_id=self.FAKE_VOLUME['id'])
+
+    def test_is_resource_deleted_true(self):
+        module = ('tempest.lib.services.compute.volumes_client.'
+                  'VolumesClient.show_volume')
+        self.useFixture(mockpatch.Patch(
+            module, side_effect=lib_exc.NotFound))
+        self.assertTrue(self.client.is_resource_deleted('fake-id'))
+
+    def test_is_resource_deleted_false(self):
+        module = ('tempest.lib.services.compute.volumes_client.'
+                  'VolumesClient.show_volume')
+        self.useFixture(mockpatch.Patch(
+            module, return_value={}))
+        self.assertFalse(self.client.is_resource_deleted('fake-id'))
diff --git a/tempest/services/identity/__init__.py b/tempest/tests/lib/services/identity/__init__.py
similarity index 100%
rename from tempest/services/identity/__init__.py
rename to tempest/tests/lib/services/identity/__init__.py
diff --git a/tempest/services/identity/v2/__init__.py b/tempest/tests/lib/services/identity/v2/__init__.py
similarity index 100%
rename from tempest/services/identity/v2/__init__.py
rename to tempest/tests/lib/services/identity/v2/__init__.py
diff --git a/tempest/tests/lib/services/identity/v2/test_endpoints_client.py b/tempest/tests/lib/services/identity/v2/test_endpoints_client.py
new file mode 100644
index 0000000..7d2cac2
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v2/test_endpoints_client.py
@@ -0,0 +1,99 @@
+# 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.lib.services.identity.v2 import endpoints_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestEndpointsClient(base.BaseServiceTest):
+    FAKE_CREATE_ENDPOINT = {
+        "endpoint": {
+            "id": 1,
+            "tenantId": 1,
+            "region": "North",
+            "type": "compute",
+            "publicURL": "https://compute.north.public.com/v1",
+            "internalURL": "https://compute.north.internal.com/v1",
+            "adminURL": "https://compute.north.internal.com/v1"
+        }
+    }
+
+    FAKE_LIST_ENDPOINTS = {
+        "endpoints": [
+            {
+                "id": 1,
+                "tenantId": "1",
+                "region": "North",
+                "type": "compute",
+                "publicURL": "https://compute.north.public.com/v1",
+                "internalURL": "https://compute.north.internal.com/v1",
+                "adminURL": "https://compute.north.internal.com/v1"
+            },
+            {
+                "id": 2,
+                "tenantId": "1",
+                "region": "South",
+                "type": "compute",
+                "publicURL": "https://compute.north.public.com/v1",
+                "internalURL": "https://compute.north.internal.com/v1",
+                "adminURL": "https://compute.north.internal.com/v1"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestEndpointsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = endpoints_client.EndpointsClient(fake_auth,
+                                                       'identity', 'regionOne')
+
+    def _test_create_endpoint(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_endpoint,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_ENDPOINT,
+            bytes_body,
+            service_id="b344506af7644f6794d9cb316600b020",
+            region="region-demo",
+            publicurl="https://compute.north.public.com/v1",
+            adminurl="https://compute.north.internal.com/v1",
+            internalurl="https://compute.north.internal.com/v1")
+
+    def _test_list_endpoints(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_endpoints,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ENDPOINTS,
+            bytes_body)
+
+    def test_create_endpoint_with_str_body(self):
+        self._test_create_endpoint()
+
+    def test_create_endpoint_with_bytes_body(self):
+        self._test_create_endpoint(bytes_body=True)
+
+    def test_list_endpoints_with_str_body(self):
+        self._test_list_endpoints()
+
+    def test_list_endpoints_with_bytes_body(self):
+        self._test_list_endpoints(bytes_body=True)
+
+    def test_delete_endpoint(self):
+        self.check_service_client_function(
+            self.client.delete_endpoint,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            endpoint_id="b344506af7644f6794d9cb316600b020",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v2/test_identity_client.py b/tempest/tests/lib/services/identity/v2/test_identity_client.py
new file mode 100644
index 0000000..96d50d7
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v2/test_identity_client.py
@@ -0,0 +1,175 @@
+# 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.lib.services.identity.v2 import identity_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestIdentityClient(base.BaseServiceTest):
+    FAKE_TOKEN = {
+        "tokens": {
+            "id": "cbc36478b0bd8e67e89",
+            "name": "FakeToken",
+            "type": "token",
+        }
+    }
+
+    FAKE_API_INFO = {
+        "name": "API_info",
+        "type": "API",
+        "description": "test_description"
+    }
+
+    FAKE_LIST_EXTENSIONS = {
+        "extensions": {
+            "values": [
+                {
+                    "updated": "2013-07-07T12:00:0-00:00",
+                    "name": "OpenStack S3 API",
+                    "links": [
+                        {
+                            "href": "https://github.com/openstack/" +
+                                    "identity-api",
+                            "type": "text/html",
+                            "rel": "describedby"
+                        }
+                    ],
+                    "namespace": "http://docs.openstack.org/identity/" +
+                                 "api/ext/s3tokens/v1.0",
+                    "alias": "s3tokens",
+                    "description": "OpenStack S3 API."
+                },
+                {
+                    "updated": "2013-12-17T12:00:0-00:00",
+                    "name": "OpenStack Federation APIs",
+                    "links": [
+                        {
+                            "href": "https://github.com/openstack/" +
+                                    "identity-api",
+                            "type": "text/html",
+                            "rel": "describedby"
+                        }
+                    ],
+                    "namespace": "http://docs.openstack.org/identity/" +
+                                 "api/ext/OS-FEDERATION/v1.0",
+                    "alias": "OS-FEDERATION",
+                    "description": "OpenStack Identity Providers Mechanism."
+                },
+                {
+                    "updated": "2014-01-20T12:00:0-00:00",
+                    "name": "OpenStack Simple Certificate API",
+                    "links": [
+                        {
+                            "href": "https://github.com/openstack/" +
+                                    "identity-api",
+                            "type": "text/html",
+                            "rel": "describedby"
+                        }
+                    ],
+                    "namespace": "http://docs.openstack.org/identity/api/" +
+                                 "ext/OS-SIMPLE-CERT/v1.0",
+                    "alias": "OS-SIMPLE-CERT",
+                    "description": "OpenStack simple certificate extension"
+                },
+                {
+                    "updated": "2013-07-07T12:00:0-00:00",
+                    "name": "OpenStack OAUTH1 API",
+                    "links": [
+                        {
+                            "href": "https://github.com/openstack/" +
+                                    "identity-api",
+                            "type": "text/html",
+                            "rel": "describedby"
+                        }
+                    ],
+                    "namespace": "http://docs.openstack.org/identity/" +
+                                 "api/ext/OS-OAUTH1/v1.0",
+                    "alias": "OS-OAUTH1",
+                    "description": "OpenStack OAuth Delegated Auth Mechanism."
+                },
+                {
+                    "updated": "2013-07-07T12:00:0-00:00",
+                    "name": "OpenStack EC2 API",
+                    "links": [
+                        {
+                            "href": "https://github.com/openstack/" +
+                                    "identity-api",
+                            "type": "text/html",
+                            "rel": "describedby"
+                        }
+                    ],
+                    "namespace": "http://docs.openstack.org/identity/api/" +
+                                 "ext/OS-EC2/v1.0",
+                    "alias": "OS-EC2",
+                    "description": "OpenStack EC2 Credentials backend."
+                }
+            ]
+        }
+    }
+
+    def setUp(self):
+        super(TestIdentityClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = identity_client.IdentityClient(fake_auth,
+                                                     'identity',
+                                                     'regionOne')
+
+    def _test_show_api_description(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_api_description,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_API_INFO,
+            bytes_body)
+
+    def _test_list_extensions(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_extensions,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_EXTENSIONS,
+            bytes_body)
+
+    def _test_show_token(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_token,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_TOKEN,
+            bytes_body,
+            token_id="cbc36478b0bd8e67e89")
+
+    def test_show_api_description_with_str_body(self):
+        self._test_show_api_description()
+
+    def test_show_api_description_with_bytes_body(self):
+        self._test_show_api_description(bytes_body=True)
+
+    def test_show_list_extensions_with_str_body(self):
+        self._test_list_extensions()
+
+    def test_show_list_extensions_with_bytes_body(self):
+        self._test_list_extensions(bytes_body=True)
+
+    def test_show_token_with_str_body(self):
+        self._test_show_token()
+
+    def test_show_token_with_bytes_body(self):
+        self._test_show_token(bytes_body=True)
+
+    def test_delete_token(self):
+        self.check_service_client_function(
+            self.client.delete_token,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            token_id="cbc36478b0bd8e67e89",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v2/test_roles_client.py b/tempest/tests/lib/services/identity/v2/test_roles_client.py
new file mode 100644
index 0000000..464a715
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v2/test_roles_client.py
@@ -0,0 +1,141 @@
+# 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.lib.services.identity.v2 import roles_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestRolesClient(base.BaseServiceTest):
+    FAKE_ROLE_INFO = {
+        "role": {
+            "id": "1",
+            "name": "test",
+            "description": "test_description"
+        }
+    }
+
+    FAKE_LIST_ROLES = {
+        "roles": [
+            {
+                "id": "1",
+                "name": "test",
+                "description": "test_description"
+            },
+            {
+                "id": "2",
+                "name": "test2",
+                "description": "test2_description"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestRolesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = roles_client.RolesClient(fake_auth,
+                                               'identity', 'regionOne')
+
+    def _test_create_role(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_role,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_ROLE_INFO,
+            bytes_body,
+            id="1",
+            name="test",
+            description="test_description")
+
+    def _test_show_role(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_role,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ROLE_INFO,
+            bytes_body,
+            role_id_or_name="1")
+
+    def _test_list_roles(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_roles,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ROLES,
+            bytes_body)
+
+    def _test_create_user_role_on_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_user_role_on_project,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_ROLE_INFO,
+            bytes_body,
+            tenant_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=200)
+
+    def _test_list_user_roles_on_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_user_roles_on_project,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ROLES,
+            bytes_body,
+            tenant_id="b344506af7644f6794d9cb316600b020",
+            user_id="123")
+
+    def test_create_role_with_str_body(self):
+        self._test_create_role()
+
+    def test_create_role_with_bytes_body(self):
+        self._test_create_role(bytes_body=True)
+
+    def test_show_role_with_str_body(self):
+        self._test_show_role()
+
+    def test_show_role_with_bytes_body(self):
+        self._test_show_role(bytes_body=True)
+
+    def test_list_roles_with_str_body(self):
+        self._test_list_roles()
+
+    def test_list_roles_with_bytes_body(self):
+        self._test_list_roles(bytes_body=True)
+
+    def test_delete_role(self):
+        self.check_service_client_function(
+            self.client.delete_role,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            role_id="1",
+            status=204)
+
+    def test_create_user_role_on_project_with_str_body(self):
+        self._test_create_user_role_on_project()
+
+    def test_create_user_role_on_project_with_bytes_body(self):
+        self._test_create_user_role_on_project(bytes_body=True)
+
+    def test_list_user_roles_on_project_with_str_body(self):
+        self._test_list_user_roles_on_project()
+
+    def test_list_user_roles_on_project_with_bytes_body(self):
+        self._test_list_user_roles_on_project(bytes_body=True)
+
+    def test_delete_role_from_user_on_project(self):
+        self.check_service_client_function(
+            self.client.delete_role_from_user_on_project,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            tenant_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v2/test_services_client.py b/tempest/tests/lib/services/identity/v2/test_services_client.py
new file mode 100644
index 0000000..bafb6b1
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v2/test_services_client.py
@@ -0,0 +1,97 @@
+# 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.lib.services.identity.v2 import services_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestServicesClient(base.BaseServiceTest):
+    FAKE_SERVICE_INFO = {
+        "OS-KSADM:service": {
+            "id": "1",
+            "name": "test",
+            "type": "compute",
+            "description": "test_description"
+        }
+    }
+
+    FAKE_LIST_SERVICES = {
+        "OS-KSADM:services": [
+            {
+                "id": "1",
+                "name": "test",
+                "type": "compute",
+                "description": "test_description"
+            },
+            {
+                "id": "2",
+                "name": "test2",
+                "type": "compute",
+                "description": "test2_description"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestServicesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = services_client.ServicesClient(fake_auth,
+                                                     'identity', 'regionOne')
+
+    def _test_create_service(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_service,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_SERVICE_INFO,
+            bytes_body,
+            id="1",
+            name="test",
+            type="compute",
+            description="test_description")
+
+    def _test_show_service(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_service,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SERVICE_INFO,
+            bytes_body,
+            service_id="1")
+
+    def _test_list_services(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_services,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_SERVICES,
+            bytes_body)
+
+    def test_create_service_with_str_body(self):
+        self._test_create_service()
+
+    def test_create_service_with_bytes_body(self):
+        self._test_create_service(bytes_body=True)
+
+    def test_show_service_with_str_body(self):
+        self._test_show_service()
+
+    def test_show_service_with_bytes_body(self):
+        self._test_show_service(bytes_body=True)
+
+    def test_delete_service(self):
+        self.check_service_client_function(
+            self.client.delete_service,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            service_id="1",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v2/test_tenants_client.py b/tempest/tests/lib/services/identity/v2/test_tenants_client.py
new file mode 100644
index 0000000..ae3d13a
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v2/test_tenants_client.py
@@ -0,0 +1,131 @@
+# 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.lib.services.identity.v2 import tenants_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestTenantsClient(base.BaseServiceTest):
+    FAKE_TENANT_INFO = {
+        "tenant": {
+            "id": "1",
+            "name": "test",
+            "description": "test_description",
+            "enabled": True
+        }
+    }
+
+    FAKE_LIST_TENANTS = {
+        "tenants": [
+            {
+                "id": "1",
+                "name": "test",
+                "description": "test_description",
+                "enabled": True
+            },
+            {
+                "id": "2",
+                "name": "test2",
+                "description": "test2_description",
+                "enabled": True
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestTenantsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = tenants_client.TenantsClient(fake_auth,
+                                                   'identity', 'regionOne')
+
+    def _test_create_tenant(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_tenant,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_TENANT_INFO,
+            bytes_body,
+            name="test",
+            description="test_description")
+
+    def _test_show_tenant(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_tenant,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_TENANT_INFO,
+            bytes_body,
+            tenant_id="1")
+
+    def _test_update_tenant(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_tenant,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_TENANT_INFO,
+            bytes_body,
+            tenant_id="1",
+            name="test",
+            description="test_description")
+
+    def _test_list_tenants(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_tenants,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_TENANTS,
+            bytes_body)
+
+    def _test_list_tenant_users(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_tenant_users,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_TENANTS,
+            bytes_body,
+            tenant_id="1")
+
+    def test_create_tenant_with_str_body(self):
+        self._test_create_tenant()
+
+    def test_create_tenant_with_bytes_body(self):
+        self._test_create_tenant(bytes_body=True)
+
+    def test_show_tenant_with_str_body(self):
+        self._test_show_tenant()
+
+    def test_show_tenant_with_bytes_body(self):
+        self._test_show_tenant(bytes_body=True)
+
+    def test_update_tenant_with_str_body(self):
+        self._test_update_tenant()
+
+    def test_update_tenant_with_bytes_body(self):
+        self._test_update_tenant(bytes_body=True)
+
+    def test_list_tenants_with_str_body(self):
+        self._test_list_tenants()
+
+    def test_list_tenants_with_bytes_body(self):
+        self._test_list_tenants(bytes_body=True)
+
+    def test_delete_tenant(self):
+        self.check_service_client_function(
+            self.client.delete_tenant,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            tenant_id="1",
+            status=204)
+
+    def test_list_tenant_users_with_str_body(self):
+        self._test_list_tenant_users()
+
+    def test_list_tenant_users_with_bytes_body(self):
+        self._test_list_tenant_users(bytes_body=True)
diff --git a/tempest/tests/lib/services/identity/v2/test_token_client.py b/tempest/tests/lib/services/identity/v2/test_token_client.py
new file mode 100644
index 0000000..dfce9b3
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v2/test_token_client.py
@@ -0,0 +1,104 @@
+# Copyright 2015 Hewlett-Packard 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.
+
+import json
+
+import mock
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions
+from tempest.lib.services.identity.v2 import token_client
+from tempest.tests import base
+from tempest.tests.lib import fake_http
+
+
+class TestTokenClientV2(base.TestCase):
+
+    def test_init_without_authurl(self):
+        self.assertRaises(exceptions.IdentityError,
+                          token_client.TokenClient, None)
+
+    def test_auth(self):
+        token_client_v2 = token_client.TokenClient('fake_url')
+        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': {
+                'passwordCredentials': {
+                    'username': 'fake_user',
+                    'password': 'fake_pass',
+                },
+            }
+        }, sort_keys=True)
+        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')
+        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': {
+                'tenantName': 'fake_tenant',
+                'passwordCredentials': {
+                    'username': 'fake_user',
+                    'password': 'fake_pass',
+                },
+            }
+        }, sort_keys=True)
+        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')
+        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')
+
+        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/v2/test_users_client.py b/tempest/tests/lib/services/identity/v2/test_users_client.py
new file mode 100644
index 0000000..9534e44
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v2/test_users_client.py
@@ -0,0 +1,243 @@
+# 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.lib.services.identity.v2 import users_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestUsersClient(base.BaseServiceTest):
+    FAKE_USER_INFO = {
+        "user": {
+            "id": "1",
+            "name": "test",
+            "email": "john.smith@example.org",
+            "enabled": True
+        }
+    }
+
+    FAKE_LIST_USERS = {
+        "users": [
+            {
+                "id": "1",
+                "name": "test",
+                "email": "john.smith@example.org",
+                "enabled": True
+            },
+            {
+                "id": "2",
+                "name": "test2",
+                "email": "john.smith@example.org",
+                "enabled": True
+            }
+        ]
+    }
+
+    FAKE_USER_EC2_CREDENTIAL_INFO = {
+        "credential": {
+            'user_id': '9beb0e12f3e5416db8d7cccfc785db3b',
+            'access': '79abf59acc77492a86170cbe2f1feafa',
+            'secret': 'c4e7d3a691fd4563873d381a40320f46',
+            'trust_id': None,
+            'tenant_id': '596557269d7b4dd78631a602eb9f151d'
+        }
+    }
+
+    FAKE_LIST_USER_EC2_CREDENTIALS = {
+        "credentials": [
+            {
+                'user_id': '9beb0e12f3e5416db8d7cccfc785db3b',
+                'access': '79abf59acc77492a86170cbe2f1feafa',
+                'secret': 'c4e7d3a691fd4563873d381a40320f46',
+                'trust_id': None,
+                'tenant_id': '596557269d7b4dd78631a602eb9f151d'
+            },
+            {
+                'user_id': '3beb0e12f3e5416db8d7cccfc785de4r',
+                'access': '45abf59acc77492a86170cbe2f1fesde',
+                'secret': 'g4e7d3a691fd4563873d381a40320e45',
+                'trust_id': None,
+                'tenant_id': '123557269d7b4dd78631a602eb9f112f'
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestUsersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = users_client.UsersClient(fake_auth,
+                                               'identity', 'regionOne')
+
+    def _test_create_user(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_user,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            name="test",
+            email="john.smith@example.org")
+
+    def _test_update_user(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_user,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            user_id="1",
+            name="test")
+
+    def _test_show_user(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_user,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            user_id="1")
+
+    def _test_list_users(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_users,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_USERS,
+            bytes_body)
+
+    def _test_update_user_enabled(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_user_enabled,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            user_id="1",
+            enabled=True)
+
+    def _test_update_user_password(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_user_password,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            user_id="1",
+            password="pass")
+
+    def _test_update_user_own_password(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_user_own_password,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            user_id="1",
+            password="pass")
+
+    def _test_create_user_ec2_credential(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_user_ec2_credential,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_USER_EC2_CREDENTIAL_INFO,
+            bytes_body,
+            user_id="1",
+            tenant_id="123")
+
+    def _test_show_user_ec2_credential(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_user_ec2_credential,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_USER_EC2_CREDENTIAL_INFO,
+            bytes_body,
+            user_id="1",
+            access="123")
+
+    def _test_list_user_ec2_credentials(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_user_ec2_credentials,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_USER_EC2_CREDENTIALS,
+            bytes_body,
+            user_id="1")
+
+    def test_create_user_with_str_body(self):
+        self._test_create_user()
+
+    def test_create_user_with_bytes_body(self):
+        self._test_create_user(bytes_body=True)
+
+    def test_update_user_with_str_body(self):
+        self._test_update_user()
+
+    def test_update_user_with_bytes_body(self):
+        self._test_update_user(bytes_body=True)
+
+    def test_show_user_with_str_body(self):
+        self._test_show_user()
+
+    def test_show_user_with_bytes_body(self):
+        self._test_show_user(bytes_body=True)
+
+    def test_list_users_with_str_body(self):
+        self._test_list_users()
+
+    def test_list_users_with_bytes_body(self):
+        self._test_list_users(bytes_body=True)
+
+    def test_delete_user(self):
+        self.check_service_client_function(
+            self.client.delete_user,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            user_id="1",
+            status=204)
+
+    def test_update_user_enabled_with_str_body(self):
+        self._test_update_user_enabled()
+
+    def test_update_user_enabled_with_bytes_body(self):
+        self._test_update_user_enabled(bytes_body=True)
+
+    def test_update_user_password_with_str_body(self):
+        self._test_update_user_password()
+
+    def test_update_user_password_with_bytes_body(self):
+        self._test_update_user_password(bytes_body=True)
+
+    def test_update_user_own_password_with_str_body(self):
+        self._test_update_user_own_password()
+
+    def test_update_user_own_password_with_bytes_body(self):
+        self._test_update_user_own_password(bytes_body=True)
+
+    def test_create_user_ec2_credential_with_str_body(self):
+        self._test_create_user_ec2_credential()
+
+    def test_create_user_ec2_credential_with_bytes_body(self):
+        self._test_create_user_ec2_credential(bytes_body=True)
+
+    def test_show_user_ec2_credential_with_str_body(self):
+        self._test_show_user_ec2_credential()
+
+    def test_show_user_ec2_credential_with_bytes_body(self):
+        self._test_show_user_ec2_credential(bytes_body=True)
+
+    def test_list_user_ec2_credentials_with_str_body(self):
+        self._test_list_user_ec2_credentials()
+
+    def test_list_user_ec2_credentials_with_bytes_body(self):
+        self._test_list_user_ec2_credentials(bytes_body=True)
+
+    def test_delete_user_ec2_credential(self):
+        self.check_service_client_function(
+            self.client.delete_user_ec2_credential,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            user_id="123",
+            access="1234",
+            status=204)
diff --git a/tempest/services/identity/v3/__init__.py b/tempest/tests/lib/services/identity/v3/__init__.py
similarity index 100%
rename from tempest/services/identity/v3/__init__.py
rename to tempest/tests/lib/services/identity/v3/__init__.py
diff --git a/tempest/tests/lib/services/identity/v3/test_credentials_client.py b/tempest/tests/lib/services/identity/v3/test_credentials_client.py
new file mode 100644
index 0000000..29d7496
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_credentials_client.py
@@ -0,0 +1,179 @@
+# 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.lib.services.identity.v3 import credentials_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestCredentialsClient(base.BaseServiceTest):
+    FAKE_CREATE_CREDENTIAL = {
+        "credential": {
+            "blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}",
+            "project_id": "731fc6f265cd486d900f16e84c5cb594",
+            "type": "ec2",
+            "user_id": "bb5476fd12884539b41d5a88f838d773"
+        }
+    }
+
+    FAKE_INFO_CREDENTIAL = {
+        "credential": {
+            "user_id": "bb5476fd12884539b41d5a88f838d773",
+            "links": {
+                "self": "http://example.com/identity/v3/credentials/" +
+                        "207e9b76935efc03804d3dd6ab52d22e9b22a0711e4" +
+                        "ada4ff8b76165a07311d7"
+            },
+            "blob": "{\"access\": \"a42a27755ce6442596b049bd7dd8a563\"," +
+                    " \"secret\": \"71faf1d40bb24c82b479b1c6fbbd9f0c\"}",
+            "project_id": "6e01855f345f4c59812999b5e459137d",
+            "type": "ec2",
+            "id": "207e9b76935efc03804d3dd6ab52d22e9b22a0711e4ada4f"
+        }
+    }
+
+    FAKE_LIST_CREDENTIALS = {
+        "credentials": [
+            {
+                "user_id": "bb5476fd12884539b41d5a88f838d773",
+                "links": {
+                    "self": "http://example.com/identity/v3/credentials/" +
+                            "207e9b76935efc03804d3dd6ab52d22e9b22a0711e4" +
+                            "ada4ff8b76165a07311d7"
+                },
+                "blob": "{\"access\": \"a42a27755ce6442596b049bd7dd8a563\"," +
+                        " \"secret\": \"71faf1d40bb24c82b479b1c6fbbd9f0c\"," +
+                        " \"trust_id\": null}",
+                "project_id": "6e01855f345f4c59812999b5e459137d",
+                "type": "ec2",
+                "id": "207e9b76935efc03804d3dd6ab52d22e9b22a0711e4ada4f"
+            },
+            {
+                "user_id": "6f556708d04b4ea6bc72d7df2296b71a",
+                "links": {
+                    "self": "http://example.com/identity/v3/credentials/" +
+                            "2441494e52ab6d594a34d74586075cb299489bdd1e9" +
+                            "389e3ab06467a4f460609"
+                },
+                "blob": "{\"access\": \"7da79ff0aa364e1396f067e352b9b79a\"," +
+                        " \"secret\": \"7a18d68ba8834b799d396f3ff6f1e98c\"," +
+                        " \"trust_id\": null}",
+                "project_id": "1a1d14690f3c4ec5bf5f321c5fde3c16",
+                "type": "ec2",
+                "id": "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3"
+            },
+            {
+                "user_id": "c14107e65d5c4a7f8894fc4b3fc209ff",
+                "links": {
+                    "self": "http://example.com/identity/v3/credentials/" +
+                            "3397b204b5f04c495bcdc8f34c8a39996f280f91726" +
+                            "58241873e15f070ec79d7"
+                },
+                "blob": "{\"access\": \"db9c58a558534a10a070110de4f9f20c\"," +
+                        " \"secret\": \"973e790b88db447ba6f93bca02bc745b\"," +
+                        " \"trust_id\": null}",
+                "project_id": "7396e43183db40dcbf40dd727637b548",
+                "type": "ec2",
+                "id": "3397b204b5f04c495bcdc8f34c8a39996f280f9172658241"
+            },
+            {
+                "user_id": "bb5476fd12884539b41d5a88f838d773",
+                "links": {
+                    "self": "http://example.com/identity/v3/credentials/" +
+                            "7ef4faa904ae7b8b4ddc7bad15b05ee359dad7d7a9b" +
+                            "82861d4ad92fdbbb2eb4e"
+                },
+                "blob": "{\"access\": \"7d7559359b57419eb5f5f5dcd65ab57d\"," +
+                        " \"secret\": \"570652bcf8c2483c86eb29e9734eed3c\"," +
+                        " \"trust_id\": null}",
+                "project_id": "731fc6f265cd486d900f16e84c5cb594",
+                "type": "ec2",
+                "id": "7ef4faa904ae7b8b4ddc7bad15b05ee359dad7d7a9b82861"
+            },
+        ],
+        "links": {
+            "self": "http://example.com/identity/v3/credentials",
+            "previous": None,
+            "next": None
+        }
+    }
+
+    def setUp(self):
+        super(TestCredentialsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = credentials_client.CredentialsClient(fake_auth,
+                                                           'identity',
+                                                           'regionOne')
+
+    def _test_create_credential(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_credential,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_CREDENTIAL,
+            bytes_body, status=201)
+
+    def _test_show_credential(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_credential,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_INFO_CREDENTIAL,
+            bytes_body,
+            credential_id="207e9b76935efc03804d3dd6ab52d22e9b22a0711e4ada4f")
+
+    def _test_update_credential(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_credential,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_INFO_CREDENTIAL,
+            bytes_body,
+            credential_id="207e9b76935efc03804d3dd6ab52d22e9b22a0711e4ada4f")
+
+    def _test_list_credentials(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_credentials,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_CREDENTIALS,
+            bytes_body)
+
+    def test_create_credential_with_str_body(self):
+        self._test_create_credential()
+
+    def test_create_credential_with_bytes_body(self):
+        self._test_create_credential(bytes_body=True)
+
+    def test_show_credential_with_str_body(self):
+        self._test_show_credential()
+
+    def test_show_credential_with_bytes_body(self):
+        self._test_show_credential(bytes_body=True)
+
+    def test_update_credential_with_str_body(self):
+        self._test_update_credential()
+
+    def test_update_credential_with_bytes_body(self):
+        self._test_update_credential(bytes_body=True)
+
+    def test_list_credentials_with_str_body(self):
+        self._test_list_credentials()
+
+    def test_list_credentials_with_bytes_body(self):
+        self._test_list_credentials(bytes_body=True)
+
+    def test_delete_credential(self):
+        self.check_service_client_function(
+            self.client.delete_credential,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            credential_id="207e9b76935efc03804d3dd6ab52d22e9b22a0711e4ada4f",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_domains_client.py b/tempest/tests/lib/services/identity/v3/test_domains_client.py
new file mode 100644
index 0000000..f89ced7
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_domains_client.py
@@ -0,0 +1,138 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.identity.v3 import domains_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestDomainsClient(base.BaseServiceTest):
+    FAKE_CREATE_DOMAIN = {
+        "domain": {
+            "description": "Domain description",
+            "enabled": True,
+            "name": "myDomain"
+        }
+    }
+
+    FAKE_DOMAIN_INFO = {
+        "domain": {
+            "description": "Used for swift functional testing",
+            "enabled": True,
+            "id": "5a75994a3",
+            "links": {
+                "self": "http://example.com/identity/v3/domains/5a75994a3"
+            },
+            "name": "swift_test"
+        }
+    }
+
+    FAKE_LIST_DOMAINS = {
+        "domains": [
+            {
+                "description": "Used for swift functional testing",
+                "enabled": True,
+                "id": "5a75994a3",
+                "links": {
+                    "self": "http://example.com/identity/v3/domains/5a75994a3"
+                },
+                "name": "swift_test"
+            },
+            {
+                "description": "Owns users and tenants available on " +
+                               "Identity API",
+                "enabled": True,
+                "id": "default",
+                "links": {
+                    "self": "http://example.com/identity/v3/domains/default"
+                },
+                "name": "Default"
+            }
+        ],
+        "links": {
+            "next": None,
+            "previous": None,
+            "self": "http://example.com/identity/v3/domains"
+        }
+    }
+
+    def setUp(self):
+        super(TestDomainsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = domains_client.DomainsClient(fake_auth,
+                                                   'identity',
+                                                   'regionOne')
+
+    def _test_create_domain(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_domain,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_DOMAIN,
+            bytes_body,
+            status=201)
+
+    def _test_show_domain(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_domain,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_DOMAIN_INFO,
+            bytes_body,
+            domain_id="5a75994a3")
+
+    def _test_list_domains(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_domains,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_DOMAINS,
+            bytes_body)
+
+    def _test_update_domain(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_domain,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_DOMAIN_INFO,
+            bytes_body,
+            domain_id="5a75994a3")
+
+    def test_create_domain_with_str_body(self):
+        self._test_create_domain()
+
+    def test_create_domain_with_bytes_body(self):
+        self._test_create_domain(bytes_body=True)
+
+    def test_show_domain_with_str_body(self):
+        self._test_show_domain()
+
+    def test_show_domain_with_bytes_body(self):
+        self._test_show_domain(bytes_body=True)
+
+    def test_list_domain_with_str_body(self):
+        self._test_list_domains()
+
+    def test_list_domain_with_bytes_body(self):
+        self._test_list_domains(bytes_body=True)
+
+    def test_update_domain_with_str_body(self):
+        self._test_update_domain()
+
+    def test_update_domain_with_bytes_body(self):
+        self._test_update_domain(bytes_body=True)
+
+    def test_delete_domain(self):
+        self.check_service_client_function(
+            self.client.delete_domain,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            domain_id="5a75994a3",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_endpoints_client.py b/tempest/tests/lib/services/identity/v3/test_endpoints_client.py
new file mode 100644
index 0000000..f8c553f
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_endpoints_client.py
@@ -0,0 +1,100 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.identity.v3 import endpoints_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestEndpointsClient(base.BaseServiceTest):
+    FAKE_CREATE_ENDPOINT = {
+        "endpoint": {
+            "id": 1,
+            "tenantId": 1,
+            "region": "North",
+            "type": "compute",
+            "publicURL": "https://compute.north.public.com/v1",
+            "internalURL": "https://compute.north.internal.com/v1",
+            "adminURL": "https://compute.north.internal.com/v1"
+        }
+    }
+
+    FAKE_LIST_ENDPOINTS = {
+        "endpoints": [
+            {
+                "id": 1,
+                "tenantId": "1",
+                "region": "North",
+                "type": "compute",
+                "publicURL": "https://compute.north.public.com/v1",
+                "internalURL": "https://compute.north.internal.com/v1",
+                "adminURL": "https://compute.north.internal.com/v1"
+            },
+            {
+                "id": 2,
+                "tenantId": "1",
+                "region": "South",
+                "type": "compute",
+                "publicURL": "https://compute.north.public.com/v1",
+                "internalURL": "https://compute.north.internal.com/v1",
+                "adminURL": "https://compute.north.internal.com/v1"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestEndpointsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = endpoints_client.EndPointsClient(fake_auth,
+                                                       'identity', 'regionOne')
+
+    def _test_create_endpoint(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_endpoint,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_ENDPOINT,
+            bytes_body,
+            status=201,
+            service_id="b344506af7644f6794d9cb316600b020",
+            region="region-demo",
+            publicurl="https://compute.north.public.com/v1",
+            adminurl="https://compute.north.internal.com/v1",
+            internalurl="https://compute.north.internal.com/v1")
+
+    def _test_list_endpoints(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_endpoints,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ENDPOINTS,
+            bytes_body)
+
+    def test_create_endpoint_with_str_body(self):
+        self._test_create_endpoint()
+
+    def test_create_endpoint_with_bytes_body(self):
+        self._test_create_endpoint(bytes_body=True)
+
+    def test_list_endpoints_with_str_body(self):
+        self._test_list_endpoints()
+
+    def test_list_endpoints_with_bytes_body(self):
+        self._test_list_endpoints(bytes_body=True)
+
+    def test_delete_endpoint(self):
+        self.check_service_client_function(
+            self.client.delete_endpoint,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            endpoint_id="b344506af7644f6794d9cb316600b020",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_groups_client.py b/tempest/tests/lib/services/identity/v3/test_groups_client.py
new file mode 100644
index 0000000..38cf3ae
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_groups_client.py
@@ -0,0 +1,213 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.identity.v3 import groups_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestGroupsClient(base.BaseServiceTest):
+    FAKE_CREATE_GROUP = {
+        'group': {
+            'description': 'Tempest Group Description',
+            'domain_id': 'TempestDomain',
+            'name': 'Tempest Group',
+        }
+    }
+
+    FAKE_GROUP_INFO = {
+        'group': {
+            'description': 'Tempest Group Description',
+            'domain_id': 'TempestDomain',
+            'id': '6e13e2068cf9466e98950595baf6bb35',
+            'links': {
+                'self': 'http://example.com/identity/v3/groups/' +
+                        '6e13e2068cf9466e98950595baf6bb35'
+            },
+            'name': 'Tempest Group',
+        }
+    }
+
+    FAKE_GROUP_LIST = {
+        'links': {
+            'self': 'http://example.com/identity/v3/groups',
+            'previous': None,
+            'next': None,
+        },
+        'groups': [
+            {
+                'description': 'Tempest Group One Description',
+                'domain_id': 'TempestDomain',
+                'id': '1c92f3453ed34291a074b87493455b8f',
+                'links': {
+                    'self': 'http://example.com/identity/v3/groups/' +
+                            '1c92f3453ed34291a074b87493455b8f'
+                },
+                'name': 'Tempest Group One',
+            },
+            {
+                'description': 'Tempest Group Two Description',
+                'domain_id': 'TempestDomain',
+                'id': 'ce9e7dafed3b4877a7d4466ed730a9ee',
+                'links': {
+                    'self': 'http://example.com/identity/v3/groups/' +
+                            'ce9e7dafed3b4877a7d4466ed730a9ee'
+                },
+                'name': 'Tempest Group Two',
+            },
+        ]
+    }
+
+    FAKE_USER_LIST = {
+        'links': {
+            'self': 'http://example.com/identity/v3/groups/' +
+                    '6e13e2068cf9466e98950595baf6bb35/users',
+            'previous': None,
+            'next': None,
+        },
+        'users': [
+            {
+                'domain_id': 'TempestDomain',
+                'description': 'Tempest Test User One Description',
+                'enabled': True,
+                'id': '642688fa65a84217b86cef3c063de2b9',
+                'name': 'TempestUserOne',
+                'links': {
+                    'self': 'http://example.com/identity/v3/users/' +
+                            '642688fa65a84217b86cef3c063de2b9'
+                }
+            },
+            {
+                'domain_id': 'TempestDomain',
+                'description': 'Tempest Test User Two Description',
+                'enabled': True,
+                'id': '1048ead6f8ef4a859b44ffbce3ac0b52',
+                'name': 'TempestUserTwo',
+                'links': {
+                    'self': 'http://example.com/identity/v3/users/' +
+                            '1048ead6f8ef4a859b44ffbce3ac0b52'
+                }
+            },
+        ]
+    }
+
+    def setUp(self):
+        super(TestGroupsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = groups_client.GroupsClient(fake_auth, 'identity',
+                                                 'regionOne')
+
+    def _test_create_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_group,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_GROUP,
+            bytes_body,
+            status=201,
+        )
+
+    def _test_show_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_group,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_GROUP_INFO,
+            bytes_body,
+            group_id='6e13e2068cf9466e98950595baf6bb35',
+        )
+
+    def _test_list_groups(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_groups,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_GROUP_LIST,
+            bytes_body,
+        )
+
+    def _test_update_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_group,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_GROUP_INFO,
+            bytes_body,
+            group_id='6e13e2068cf9466e98950595baf6bb35',
+            name='NewName',
+        )
+
+    def _test_list_users_in_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_group_users,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_USER_LIST,
+            bytes_body,
+            group_id='6e13e2068cf9466e98950595baf6bb35',
+        )
+
+    def test_create_group_with_string_body(self):
+        self._test_create_group()
+
+    def test_create_group_with_bytes_body(self):
+        self._test_create_group(bytes_body=True)
+
+    def test_show_group_with_string_body(self):
+        self._test_show_group()
+
+    def test_show_group_with_bytes_body(self):
+        self._test_show_group(bytes_body=True)
+
+    def test_list_groups_with_string_body(self):
+        self._test_list_groups()
+
+    def test_list_groups_with_bytes_body(self):
+        self._test_list_groups(bytes_body=True)
+
+    def test_update_group_with_string_body(self):
+        self._test_update_group()
+
+    def test_update_group_with_bytes_body(self):
+        self._test_update_group(bytes_body=True)
+
+    def test_list_users_in_group_with_string_body(self):
+        self._test_list_users_in_group()
+
+    def test_list_users_in_group_with_bytes_body(self):
+        self._test_list_users_in_group(bytes_body=True)
+
+    def test_delete_group(self):
+        self.check_service_client_function(
+            self.client.delete_group,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            group_id='6e13e2068cf9466e98950595baf6bb35',
+            status=204,
+        )
+
+    def test_add_user_to_group(self):
+        self.check_service_client_function(
+            self.client.add_group_user,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            status=204,
+            group_id='6e13e2068cf9466e98950595baf6bb35',
+            user_id='642688fa65a84217b86cef3c063de2b9',
+        )
+
+    def test_check_user_in_group(self):
+        self.check_service_client_function(
+            self.client.check_group_user_existence,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            status=204,
+            group_id='6e13e2068cf9466e98950595baf6bb35',
+            user_id='642688fa65a84217b86cef3c063de2b9',
+        )
diff --git a/tempest/tests/lib/services/identity/v3/test_identity_client.py b/tempest/tests/lib/services/identity/v3/test_identity_client.py
new file mode 100644
index 0000000..9eaaaaf
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_identity_client.py
@@ -0,0 +1,75 @@
+# 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.lib.services.identity.v3 import identity_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestIdentityClient(base.BaseServiceTest):
+    FAKE_TOKEN = {
+        "tokens": {
+            "id": "cbc36478b0bd8e67e89",
+            "name": "FakeToken",
+            "type": "token",
+        }
+    }
+
+    FAKE_API_INFO = {
+        "name": "API_info",
+        "type": "API",
+        "description": "test_description"
+    }
+
+    def setUp(self):
+        super(TestIdentityClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = identity_client.IdentityClient(fake_auth,
+                                                     'identity',
+                                                     'regionOne')
+
+    def _test_show_api_description(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_api_description,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_API_INFO,
+            bytes_body)
+
+    def _test_show_token(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_token,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_TOKEN,
+            bytes_body,
+            resp_token="cbc36478b0bd8e67e89")
+
+    def test_show_api_description_with_str_body(self):
+        self._test_show_api_description()
+
+    def test_show_api_description_with_bytes_body(self):
+        self._test_show_api_description(bytes_body=True)
+
+    def test_show_token_with_str_body(self):
+        self._test_show_token()
+
+    def test_show_token_with_bytes_body(self):
+        self._test_show_token(bytes_body=True)
+
+    def test_delete_token(self):
+        self.check_service_client_function(
+            self.client.delete_token,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            resp_token="cbc36478b0bd8e67e89",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_inherited_roles_client.py b/tempest/tests/lib/services/identity/v3/test_inherited_roles_client.py
new file mode 100644
index 0000000..9da3cce
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_inherited_roles_client.py
@@ -0,0 +1,220 @@
+# 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.lib.services.identity.v3 import inherited_roles_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestInheritedRolesClient(base.BaseServiceTest):
+    FAKE_LIST_INHERITED_ROLES = {
+        "roles": [
+            {
+                "id": "1",
+                "name": "test",
+                "links": "example.com"
+            },
+            {
+                "id": "2",
+                "name": "test2",
+                "links": "example.com"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestInheritedRolesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = inherited_roles_client.InheritedRolesClient(
+            fake_auth, 'identity', 'regionOne')
+
+    def _test_create_inherited_role_on_domains_user(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_inherited_role_on_domains_user,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            domain_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
+
+    def _test_list_inherited_project_role_for_user_on_domain(
+        self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_inherited_project_role_for_user_on_domain,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_INHERITED_ROLES,
+            bytes_body,
+            domain_id="b344506af7644f6794d9cb316600b020",
+            user_id="123")
+
+    def _test_create_inherited_role_on_domains_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_inherited_role_on_domains_group,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            domain_id="b344506af7644f6794d9cb316600b020",
+            group_id="123",
+            role_id="1234",
+            status=204)
+
+    def _test_list_inherited_project_role_for_group_on_domain(
+        self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_inherited_project_role_for_group_on_domain,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_INHERITED_ROLES,
+            bytes_body,
+            domain_id="b344506af7644f6794d9cb316600b020",
+            group_id="123")
+
+    def _test_create_inherited_role_on_projects_user(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_inherited_role_on_projects_user,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            project_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
+
+    def _test_create_inherited_role_on_projects_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_inherited_role_on_projects_group,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            project_id="b344506af7644f6794d9cb316600b020",
+            group_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_create_inherited_role_on_domains_user_with_str_body(self):
+        self._test_create_inherited_role_on_domains_user()
+
+    def test_create_inherited_role_on_domains_user_with_bytes_body(self):
+        self._test_create_inherited_role_on_domains_user(bytes_body=True)
+
+    def test_create_inherited_role_on_domains_group_with_str_body(self):
+        self._test_create_inherited_role_on_domains_group()
+
+    def test_create_inherited_role_on_domains_group_with_bytes_body(self):
+        self._test_create_inherited_role_on_domains_group(bytes_body=True)
+
+    def test_create_inherited_role_on_projects_user_with_str_body(self):
+        self._test_create_inherited_role_on_projects_user()
+
+    def test_create_inherited_role_on_projects_group_with_bytes_body(self):
+        self._test_create_inherited_role_on_projects_group(bytes_body=True)
+
+    def test_list_inherited_project_role_for_user_on_domain_with_str_body(
+        self):
+        self._test_list_inherited_project_role_for_user_on_domain()
+
+    def test_list_inherited_project_role_for_user_on_domain_with_bytes_body(
+        self):
+        self._test_list_inherited_project_role_for_user_on_domain(
+            bytes_body=True)
+
+    def test_list_inherited_project_role_for_group_on_domain_with_str_body(
+        self):
+        self._test_list_inherited_project_role_for_group_on_domain()
+
+    def test_list_inherited_project_role_for_group_on_domain_with_bytes_body(
+        self):
+        self._test_list_inherited_project_role_for_group_on_domain(
+            bytes_body=True)
+
+    def test_delete_inherited_role_from_user_on_domain(self):
+        self.check_service_client_function(
+            self.client.delete_inherited_role_from_user_on_domain,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            domain_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_check_user_inherited_project_role_on_domain(self):
+        self.check_service_client_function(
+            self.client.check_user_inherited_project_role_on_domain,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            domain_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_delete_inherited_role_from_group_on_domain(self):
+        self.check_service_client_function(
+            self.client.delete_inherited_role_from_group_on_domain,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            domain_id="b344506af7644f6794d9cb316600b020",
+            group_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_check_group_inherited_project_role_on_domain(self):
+        self.check_service_client_function(
+            self.client.check_group_inherited_project_role_on_domain,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            domain_id="b344506af7644f6794d9cb316600b020",
+            group_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_delete_inherited_role_from_user_on_project(self):
+        self.check_service_client_function(
+            self.client.delete_inherited_role_from_user_on_project,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            project_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_check_user_has_flag_on_inherited_to_project(self):
+        self.check_service_client_function(
+            self.client.check_user_has_flag_on_inherited_to_project,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            project_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_delete_inherited_role_from_group_on_project(self):
+        self.check_service_client_function(
+            self.client.delete_inherited_role_from_group_on_project,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            project_id="b344506af7644f6794d9cb316600b020",
+            group_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_check_group_has_flag_on_inherited_to_project(self):
+        self.check_service_client_function(
+            self.client.check_group_has_flag_on_inherited_to_project,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            project_id="b344506af7644f6794d9cb316600b020",
+            group_id="123",
+            role_id="1234",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_policies_client.py b/tempest/tests/lib/services/identity/v3/test_policies_client.py
new file mode 100644
index 0000000..66c3d65
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_policies_client.py
@@ -0,0 +1,152 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.identity.v3 import policies_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestPoliciesClient(base.BaseServiceTest):
+    FAKE_CREATE_POLICY = {
+        "policy": {
+            "blob": "{'foobar_user': 'role:compute-user'}",
+            "project_id": "0426ac1e48f642ef9544c2251e07e261",
+            "type": "application/json",
+            "user_id": "0ffd248c55b443eaac5253b4e9cbf9b5"
+            }
+        }
+
+    FAKE_POLICY_INFO = {
+        "policy": {
+            "blob": {
+                "foobar_user": [
+                    "role:compute-user"
+                    ]
+                },
+            "id": "717273",
+            "links": {
+                "self": "http://example.com/identity/v3/policies/717273"
+                },
+            "project_id": "456789",
+            "type": "application/json",
+            "user_id": "616263"
+            }
+        }
+
+    FAKE_LIST_POLICIES = {
+        "links": {
+            "next": None,
+            "previous": None,
+            "self": "http://example.com/identity/v3/policies"
+            },
+        "policies": [
+            {
+                "blob": {
+                    "foobar_user": [
+                        "role:compute-user"
+                        ]
+                    },
+                "id": "717273",
+                "links": {
+                    "self": "http://example.com/identity/v3/policies/717273"
+                    },
+                "project_id": "456789",
+                "type": "application/json",
+                "user_id": "616263"
+                },
+            {
+                "blob": {
+                    "foobar_user": [
+                        "role:compute-user"
+                        ]
+                    },
+                "id": "717274",
+                "links": {
+                    "self": "http://example.com/identity/v3/policies/717274"
+                    },
+                "project_id": "456789",
+                "type": "application/json",
+                "user_id": "616263"
+                }
+            ]
+    }
+
+    def setUp(self):
+        super(TestPoliciesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = policies_client.PoliciesClient(fake_auth,
+                                                     'identity', 'regionOne')
+
+    def _test_create_policy(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_policy,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_POLICY,
+            bytes_body,
+            status=201)
+
+    def _test_show_policy(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_policy,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_POLICY_INFO,
+            bytes_body,
+            policy_id="717273")
+
+    def _test_list_policies(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_policies,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_POLICIES,
+            bytes_body)
+
+    def _test_update_policy(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_policy,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_POLICY_INFO,
+            bytes_body,
+            policy_id="717273")
+
+    def test_create_policy_with_str_body(self):
+        self._test_create_policy()
+
+    def test_create_policy_with_bytes_body(self):
+        self._test_create_policy(bytes_body=True)
+
+    def test_show_policy_with_str_body(self):
+        self._test_show_policy()
+
+    def test_show_policy_with_bytes_body(self):
+        self._test_show_policy(bytes_body=True)
+
+    def test_list_policies_with_str_body(self):
+        self._test_list_policies()
+
+    def test_list_policies_with_bytes_body(self):
+        self._test_list_policies(bytes_body=True)
+
+    def test_update_policy_with_str_body(self):
+        self._test_update_policy()
+
+    def test_update_policy_with_bytes_body(self):
+        self._test_update_policy(bytes_body=True)
+
+    def test_delete_policy(self):
+        self.check_service_client_function(
+            self.client.delete_policy,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            policy_id="717273",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_projects_client.py b/tempest/tests/lib/services/identity/v3/test_projects_client.py
new file mode 100644
index 0000000..6ffbcde
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_projects_client.py
@@ -0,0 +1,178 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.identity.v3 import projects_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestProjectsClient(base.BaseServiceTest):
+    FAKE_CREATE_PROJECT = {
+        "project": {
+            "description": "My new project",
+            "domain_id": "default",
+            "enabled": True,
+            "is_domain": False,
+            "name": "myNewProject"
+        }
+    }
+
+    FAKE_PROJECT_INFO = {
+        "project": {
+            "is_domain": False,
+            "description": None,
+            "domain_id": "default",
+            "enabled": True,
+            "id": "0c4e939acacf4376bdcd1129f1a054ad",
+            "links": {
+                "self": "http://example.com/identity/v3/projects/0c4e" +
+                        "939acacf4376bdcd1129f1a054ad"
+            },
+            "name": "admin",
+            "parent_id": "default"
+        }
+    }
+
+    FAKE_LIST_PROJECTS = {
+        "links": {
+            "next": None,
+            "previous": None,
+            "self": "http://example.com/identity/v3/projects"
+        },
+        "projects": [
+            {
+                "is_domain": False,
+                "description": None,
+                "domain_id": "default",
+                "enabled": True,
+                "id": "0c4e939acacf4376bdcd1129f1a054ad",
+                "links": {
+                    "self": "http://example.com/identity/v3/projects" +
+                            "/0c4e939acacf4376bdcd1129f1a054ad"
+                },
+                "name": "admin",
+                "parent_id": None
+            },
+            {
+                "is_domain": False,
+                "description": None,
+                "domain_id": "default",
+                "enabled": True,
+                "id": "0cbd49cbf76d405d9c86562e1d579bd3",
+                "links": {
+                    "self": "http://example.com/identity/v3/projects" +
+                            "/0cbd49cbf76d405d9c86562e1d579bd3"
+                },
+                "name": "demo",
+                "parent_id": None
+            },
+            {
+                "is_domain": False,
+                "description": None,
+                "domain_id": "default",
+                "enabled": True,
+                "id": "2db68fed84324f29bb73130c6c2094fb",
+                "links": {
+                    "self": "http://example.com/identity/v3/projects" +
+                            "/2db68fed84324f29bb73130c6c2094fb"
+                },
+                "name": "swifttenanttest2",
+                "parent_id": None
+            },
+            {
+                "is_domain": False,
+                "description": None,
+                "domain_id": "default",
+                "enabled": True,
+                "id": "3d594eb0f04741069dbbb521635b21c7",
+                "links": {
+                    "self": "http://example.com/identity/v3/projects" +
+                            "/3d594eb0f04741069dbbb521635b21c7"
+                },
+                "name": "service",
+                "parent_id": None
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestProjectsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = projects_client.ProjectsClient(fake_auth,
+                                                     'identity',
+                                                     'regionOne')
+
+    def _test_create_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_project,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_PROJECT,
+            bytes_body,
+            name=self.FAKE_CREATE_PROJECT["project"]["name"],
+            status=201)
+
+    def _test_show_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_project,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_PROJECT_INFO,
+            bytes_body,
+            project_id="0c4e939acacf4376bdcd1129f1a054ad")
+
+    def _test_list_projects(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_projects,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_PROJECTS,
+            bytes_body)
+
+    def _test_update_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_project,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_PROJECT_INFO,
+            bytes_body,
+            project_id="0c4e939acacf4376bdcd1129f1a054ad")
+
+    def test_create_project_with_str_body(self):
+        self._test_create_project()
+
+    def test_create_project_with_bytes_body(self):
+        self._test_create_project(bytes_body=True)
+
+    def test_show_project_with_str_body(self):
+        self._test_show_project()
+
+    def test_show_project_with_bytes_body(self):
+        self._test_show_project(bytes_body=True)
+
+    def test_list_projects_with_str_body(self):
+        self._test_list_projects()
+
+    def test_list_projects_with_bytes_body(self):
+        self._test_list_projects(bytes_body=True)
+
+    def test_update_project_with_str_body(self):
+        self._test_update_project()
+
+    def test_update_project_with_bytes_body(self):
+        self._test_update_project(bytes_body=True)
+
+    def test_delete_project(self):
+        self.check_service_client_function(
+            self.client.delete_project,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            project_id="0c4e939acacf4376bdcd1129f1a054ad",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_regions_client.py b/tempest/tests/lib/services/identity/v3/test_regions_client.py
new file mode 100644
index 0000000..a2cb86f
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_regions_client.py
@@ -0,0 +1,125 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.identity.v3 import regions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestRegionsClient(base.BaseServiceTest):
+    FAKE_CREATE_REGION = {
+        "region": {
+            "description": "My subregion",
+            "id": "RegionOneSubRegion",
+            "parent_region_id": "RegionOne"
+            }
+        }
+
+    FAKE_REGION_INFO = {
+        "region": {
+            "description": "My subregion 3",
+            "id": "RegionThree",
+            "links": {
+                "self": "http://example.com/identity/v3/regions/RegionThree"
+                },
+            "parent_region_id": "RegionOne"
+            }
+        }
+
+    FAKE_LIST_REGIONS = {
+        "links": {
+            "next": None,
+            "previous": None,
+            "self": "http://example.com/identity/v3/regions"
+            },
+        "regions": [
+            {
+                "description": "",
+                "id": "RegionOne",
+                "links": {
+                    "self": "http://example.com/identity/v3/regions/RegionOne"
+                    },
+                "parent_region_id": None
+                }
+            ]
+        }
+
+    def setUp(self):
+        super(TestRegionsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = regions_client.RegionsClient(fake_auth, 'identity',
+                                                   'regionOne')
+
+    def _test_create_region(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_region,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_REGION,
+            bytes_body,
+            status=201)
+
+    def _test_show_region(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_region,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_REGION_INFO,
+            bytes_body,
+            region_id="RegionThree")
+
+    def _test_list_regions(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_regions,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_REGIONS,
+            bytes_body)
+
+    def _test_update_region(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_region,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_REGION_INFO,
+            bytes_body,
+            region_id="RegionThree")
+
+    def test_create_region_with_str_body(self):
+        self._test_create_region()
+
+    def test_create_region_with_bytes_body(self):
+        self._test_create_region(bytes_body=True)
+
+    def test_show_region_with_str_body(self):
+        self._test_show_region()
+
+    def test_show_region_with_bytes_body(self):
+        self._test_show_region(bytes_body=True)
+
+    def test_list_regions_with_str_body(self):
+        self._test_list_regions()
+
+    def test_list_regions_with_bytes_body(self):
+        self._test_list_regions(bytes_body=True)
+
+    def test_update_region_with_str_body(self):
+        self._test_update_region()
+
+    def test_update_region_with_bytes_body(self):
+        self._test_update_region(bytes_body=True)
+
+    def test_delete_region(self):
+        self.check_service_client_function(
+            self.client.delete_region,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            region_id="RegionThree",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_role_assignments_client.py b/tempest/tests/lib/services/identity/v3/test_role_assignments_client.py
new file mode 100644
index 0000000..7d304c1
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_role_assignments_client.py
@@ -0,0 +1,206 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.identity.v3 import role_assignments_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestRoleAssignmentsClient(base.BaseServiceTest):
+
+    FAKE_USER_ID = "313234"
+    FAKE_GROUP_ID = "101112"
+
+    FAKE_ROLE1_ID = "123456"
+    FAKE_ROLE2_ID = "123457"
+
+    FAKE_PROJECT_ID = "456789"
+    FAKE_DOMAIN_ID = "102030"
+
+    FAKE_USER_PROJECT_ASSIGNMENT = {
+        "links": {
+            "assignment": "http://example.com/identity/v3/projects/"
+                          "%s/users/%s/roles/%s" % (FAKE_PROJECT_ID,
+                                                    FAKE_USER_ID,
+                                                    FAKE_ROLE2_ID)
+        },
+        "role": {
+            "id": FAKE_ROLE2_ID
+        },
+        "scope": {
+            "project": {
+                "id": FAKE_PROJECT_ID
+            }
+        },
+        "user": {
+            "id": FAKE_USER_ID
+        }
+    }
+
+    FAKE_GROUP_PROJECT_ASSIGNMENT = {
+        "links": {
+            "assignment": "http://example.com/identity/v3/projects/"
+                          "%s/groups/%s/roles/%s" % (FAKE_PROJECT_ID,
+                                                     FAKE_GROUP_ID,
+                                                     FAKE_ROLE1_ID)
+        },
+        "role": {
+            "id": FAKE_ROLE1_ID
+        },
+        "scope": {
+            "project": {
+                "id": FAKE_PROJECT_ID
+            }
+        },
+        "group": {
+            "id": FAKE_GROUP_ID
+        }
+    }
+
+    FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENT = {
+        "links": {
+            "assignment": "http://example.com/identity/v3/projects/"
+                          "%s/groups/%s/roles/%s" % (FAKE_PROJECT_ID,
+                                                     FAKE_GROUP_ID,
+                                                     FAKE_ROLE1_ID),
+            "membership": "http://example.com/identity/v3/groups/"
+                          "%s/users/%s" % (FAKE_GROUP_ID, FAKE_USER_ID)
+        },
+        "role": {
+            "id": FAKE_ROLE1_ID
+        },
+        "scope": {
+            "project": {
+                "id": FAKE_PROJECT_ID
+            }
+        },
+        "user": {
+            "id": FAKE_USER_ID
+        }
+    }
+
+    FAKE_USER_DOMAIN_ASSIGNMENT = {
+        "links": {
+            "assignment": "http://example.com/identity/v3/domains/"
+                          "%s/users/%s/roles/%s" % (FAKE_DOMAIN_ID,
+                                                    FAKE_USER_ID,
+                                                    FAKE_ROLE1_ID)
+        },
+        "role": {
+            "id": FAKE_ROLE1_ID
+        },
+        "scope": {
+            "domain": {
+                "id": FAKE_DOMAIN_ID
+            }
+        },
+        "user": {
+            "id": FAKE_USER_ID
+        }
+    }
+
+    FAKE_GROUP_PROJECT_ASSIGNMENTS = {
+        "role_assignments": [
+            FAKE_GROUP_PROJECT_ASSIGNMENT
+        ],
+        "links": {
+            "self": "http://example.com/identity/v3/role_assignments?"
+                    "scope.project.id=%s&group.id=%s&effective" % (
+                        FAKE_PROJECT_ID, FAKE_GROUP_ID),
+            "previous": None,
+            "next": None
+        }
+    }
+
+    FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENTS = {
+        "role_assignments": [
+            FAKE_USER_PROJECT_ASSIGNMENT,
+            FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENT
+        ],
+        "links": {
+            "self": "http://example.com/identity/v3/role_assignments?"
+                    "scope.project.id=%s&user.id=%s&effective" % (
+                        FAKE_PROJECT_ID, FAKE_USER_ID),
+            "previous": None,
+            "next": None
+        }
+    }
+
+    FAKE_USER_DOMAIN_ASSIGNMENTS = {
+        "role_assignments": [
+            FAKE_USER_DOMAIN_ASSIGNMENT
+        ],
+        "links": {
+            "self": "http://example.com/identity/v3/role_assignments?"
+                    "scope.domain.id=%s&user.id=%s&effective" % (
+                        FAKE_DOMAIN_ID, FAKE_USER_ID),
+            "previous": None,
+            "next": None
+        }
+    }
+
+    def setUp(self):
+        super(TestRoleAssignmentsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = role_assignments_client.RoleAssignmentsClient(
+            fake_auth, 'identity', 'regionOne')
+
+    def _test_list_user_project_effective_assignments(self, bytes_body=False):
+        params = {'scope.project.id': self.FAKE_PROJECT_ID,
+                  'user.id': self.FAKE_USER_ID}
+        self.check_service_client_function(
+            self.client.list_role_assignments,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENTS,
+            bytes_body,
+            effective=True,
+            **params)
+
+    def test_list_user_project_effective_assignments_with_str_body(self):
+        self._test_list_user_project_effective_assignments()
+
+    def test_list_user_project_effective_assignments_with_bytes_body(self):
+        self._test_list_user_project_effective_assignments(bytes_body=True)
+
+    def _test_list_group_project_assignments(self, bytes_body=False):
+        params = {'scope.project.id': self.FAKE_PROJECT_ID,
+                  'group.id': self.FAKE_GROUP_ID}
+        self.check_service_client_function(
+            self.client.list_role_assignments,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_GROUP_PROJECT_ASSIGNMENTS,
+            bytes_body,
+            **params)
+
+    def test_list_group_project_assignments_with_str_body(self):
+        self._test_list_group_project_assignments()
+
+    def test_list_group_project_assignments_with_bytes_body(self):
+        self._test_list_group_project_assignments(bytes_body=True)
+
+    def _test_list_user_domain_assignments(self, bytes_body=False):
+        params = {'scope.domain.id': self.FAKE_DOMAIN_ID,
+                  'user.id': self.FAKE_USER_ID}
+        self.check_service_client_function(
+            self.client.list_role_assignments,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_GROUP_PROJECT_ASSIGNMENTS,
+            bytes_body,
+            **params)
+
+    def test_list_user_domain_assignments_with_str_body(self):
+        self._test_list_user_domain_assignments()
+
+    def test_list_user_domain_assignments_with_bytes_body(self):
+        self._test_list_user_domain_assignments(bytes_body=True)
diff --git a/tempest/tests/lib/services/identity/v3/test_roles_client.py b/tempest/tests/lib/services/identity/v3/test_roles_client.py
new file mode 100644
index 0000000..bad1ef9
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_roles_client.py
@@ -0,0 +1,313 @@
+# 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.lib.services.identity.v3 import roles_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestRolesClient(base.BaseServiceTest):
+    FAKE_ROLE_INFO = {
+        "role": {
+            "domain_id": "1",
+            "id": "1",
+            "name": "test",
+            "links": "example.com"
+        }
+    }
+
+    FAKE_LIST_ROLES = {
+        "roles": [
+            {
+                "domain_id": "1",
+                "id": "1",
+                "name": "test",
+                "links": "example.com"
+            },
+            {
+                "domain_id": "2",
+                "id": "2",
+                "name": "test2",
+                "links": "example.com"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestRolesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = roles_client.RolesClient(fake_auth,
+                                               'identity', 'regionOne')
+
+    def _test_create_role(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_role,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_ROLE_INFO,
+            bytes_body,
+            domain_id="1",
+            name="test",
+            status=201)
+
+    def _test_show_role(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_role,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ROLE_INFO,
+            bytes_body,
+            role_id="1")
+
+    def _test_list_roles(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_roles,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ROLES,
+            bytes_body)
+
+    def _test_update_role(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_role,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_ROLE_INFO,
+            bytes_body,
+            role_id="1",
+            name="test")
+
+    def _test_create_user_role_on_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_user_role_on_project,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            project_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
+
+    def _test_create_user_role_on_domain(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_user_role_on_domain,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            domain_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
+
+    def _test_list_user_roles_on_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_user_roles_on_project,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ROLES,
+            bytes_body,
+            project_id="b344506af7644f6794d9cb316600b020",
+            user_id="123")
+
+    def _test_list_user_roles_on_domain(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_user_roles_on_domain,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ROLES,
+            bytes_body,
+            domain_id="b344506af7644f6794d9cb316600b020",
+            user_id="123")
+
+    def _test_create_group_role_on_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_group_role_on_project,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            project_id="b344506af7644f6794d9cb316600b020",
+            group_id="123",
+            role_id="1234",
+            status=204)
+
+    def _test_create_group_role_on_domain(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_group_role_on_domain,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            domain_id="b344506af7644f6794d9cb316600b020",
+            group_id="123",
+            role_id="1234",
+            status=204)
+
+    def _test_list_group_roles_on_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_group_roles_on_project,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ROLES,
+            bytes_body,
+            project_id="b344506af7644f6794d9cb316600b020",
+            group_id="123")
+
+    def _test_list_group_roles_on_domain(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_group_roles_on_domain,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ROLES,
+            bytes_body,
+            domain_id="b344506af7644f6794d9cb316600b020",
+            group_id="123")
+
+    def test_create_role_with_str_body(self):
+        self._test_create_role()
+
+    def test_create_role_with_bytes_body(self):
+        self._test_create_role(bytes_body=True)
+
+    def test_show_role_with_str_body(self):
+        self._test_show_role()
+
+    def test_show_role_with_bytes_body(self):
+        self._test_show_role(bytes_body=True)
+
+    def test_list_roles_with_str_body(self):
+        self._test_list_roles()
+
+    def test_list_roles_with_bytes_body(self):
+        self._test_list_roles(bytes_body=True)
+
+    def test_update_role_with_str_body(self):
+        self._test_update_role()
+
+    def test_update_role_with_bytes_body(self):
+        self._test_update_role(bytes_body=True)
+
+    def test_delete_role(self):
+        self.check_service_client_function(
+            self.client.delete_role,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            role_id="1",
+            status=204)
+
+    def test_create_user_role_on_project_with_str_body(self):
+        self._test_create_user_role_on_project()
+
+    def test_create_user_role_on_project_with_bytes_body(self):
+        self._test_create_user_role_on_project(bytes_body=True)
+
+    def test_create_user_role_on_domain_with_str_body(self):
+        self._test_create_user_role_on_domain()
+
+    def test_create_user_role_on_domain_with_bytes_body(self):
+        self._test_create_user_role_on_domain(bytes_body=True)
+
+    def test_create_group_role_on_domain_with_str_body(self):
+        self._test_create_group_role_on_domain()
+
+    def test_create_group_role_on_domain_with_bytes_body(self):
+        self._test_create_group_role_on_domain(bytes_body=True)
+
+    def test_list_user_roles_on_project_with_str_body(self):
+        self._test_list_user_roles_on_project()
+
+    def test_list_user_roles_on_project_with_bytes_body(self):
+        self._test_list_user_roles_on_project(bytes_body=True)
+
+    def test_list_user_roles_on_domain_with_str_body(self):
+        self._test_list_user_roles_on_domain()
+
+    def test_list_user_roles_on_domain_with_bytes_body(self):
+        self._test_list_user_roles_on_domain(bytes_body=True)
+
+    def test_list_group_roles_on_domain_with_str_body(self):
+        self._test_list_group_roles_on_domain()
+
+    def test_list_group_roles_on_domain_with_bytes_body(self):
+        self._test_list_group_roles_on_domain(bytes_body=True)
+
+    def test_delete_role_from_user_on_project(self):
+        self.check_service_client_function(
+            self.client.delete_role_from_user_on_project,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            project_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_delete_role_from_user_on_domain(self):
+        self.check_service_client_function(
+            self.client.delete_role_from_user_on_domain,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            domain_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_delete_role_from_group_on_project(self):
+        self.check_service_client_function(
+            self.client.delete_role_from_group_on_project,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            project_id="b344506af7644f6794d9cb316600b020",
+            group_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_delete_role_from_group_on_domain(self):
+        self.check_service_client_function(
+            self.client.delete_role_from_group_on_domain,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            domain_id="b344506af7644f6794d9cb316600b020",
+            group_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_check_user_role_existence_on_project(self):
+        self.check_service_client_function(
+            self.client.check_user_role_existence_on_project,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            project_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_check_user_role_existence_on_domain(self):
+        self.check_service_client_function(
+            self.client.check_user_role_existence_on_domain,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            domain_id="b344506af7644f6794d9cb316600b020",
+            user_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_check_role_from_group_on_project_existence(self):
+        self.check_service_client_function(
+            self.client.check_role_from_group_on_project_existence,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            project_id="b344506af7644f6794d9cb316600b020",
+            group_id="123",
+            role_id="1234",
+            status=204)
+
+    def test_check_role_from_group_on_domain_existence(self):
+        self.check_service_client_function(
+            self.client.check_role_from_group_on_domain_existence,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            domain_id="b344506af7644f6794d9cb316600b020",
+            group_id="123",
+            role_id="1234",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_services_client.py b/tempest/tests/lib/services/identity/v3/test_services_client.py
new file mode 100644
index 0000000..f87fcce
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_services_client.py
@@ -0,0 +1,149 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.identity.v3 import services_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestServicesClient(base.BaseServiceTest):
+    FAKE_CREATE_SERVICE = {
+        "service": {
+            "type": "compute",
+            "name": "compute2",
+            "description": "Compute service 2"
+            }
+        }
+
+    FAKE_SERVICE_INFO = {
+        "service": {
+            "description": "Keystone Identity Service",
+            "enabled": True,
+            "id": "686766",
+            "links": {
+                "self": "http://example.com/identity/v3/services/686766"
+                },
+            "name": "keystone",
+            "type": "identity"
+            }
+        }
+
+    FAKE_LIST_SERVICES = {
+        "links": {
+            "next": None,
+            "previous": None,
+            "self": "http://example.com/identity/v3/services"
+            },
+        "services": [
+            {
+                "description": "Nova Compute Service",
+                "enabled": True,
+                "id": "1999c3",
+                "links": {
+                    "self": "http://example.com/identity/v3/services/1999c3"
+                    },
+                "name": "nova",
+                "type": "compute"
+                },
+            {
+                "description": "Cinder Volume Service V2",
+                "enabled": True,
+                "id": "392166",
+                "links": {
+                    "self": "http://example.com/identity/v3/services/392166"
+                    },
+                "name": "cinderv2",
+                "type": "volumev2"
+                },
+            {
+                "description": "Neutron Service",
+                "enabled": True,
+                "id": "4fe41a",
+                "links": {
+                    "self": "http://example.com/identity/v3/services/4fe41a"
+                    },
+                "name": "neutron",
+                "type": "network"
+                }
+        ]
+    }
+
+    def setUp(self):
+        super(TestServicesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = services_client.ServicesClient(fake_auth, 'identity',
+                                                     'regionOne')
+
+    def _test_create_service(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_service,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_SERVICE,
+            bytes_body,
+            status=201)
+
+    def _test_show_service(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_service,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SERVICE_INFO,
+            bytes_body,
+            service_id="686766")
+
+    def _test_list_services(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_services,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_SERVICES,
+            bytes_body)
+
+    def _test_update_service(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_service,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_SERVICE_INFO,
+            bytes_body,
+            service_id="686766")
+
+    def test_create_service_with_str_body(self):
+        self._test_create_service()
+
+    def test_create_service_with_bytes_body(self):
+        self._test_create_service(bytes_body=True)
+
+    def test_show_service_with_str_body(self):
+        self._test_show_service()
+
+    def test_show_service_with_bytes_body(self):
+        self._test_show_service(bytes_body=True)
+
+    def test_list_services_with_str_body(self):
+        self._test_list_services()
+
+    def test_list_services_with_bytes_body(self):
+        self._test_list_services(bytes_body=True)
+
+    def test_update_service_with_str_body(self):
+        self._test_update_service()
+
+    def test_update_service_with_bytes_body(self):
+        self._test_update_service(bytes_body=True)
+
+    def test_delete_service(self):
+        self.check_service_client_function(
+            self.client.delete_service,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            service_id="686766",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_token_client.py b/tempest/tests/lib/services/identity/v3/test_token_client.py
new file mode 100644
index 0000000..38e8c4a
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_token_client.py
@@ -0,0 +1,153 @@
+# Copyright 2015 Hewlett-Packard 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.
+
+import json
+
+import mock
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions
+from tempest.lib.services.identity.v3 import token_client
+from tempest.tests import base
+from tempest.tests.lib import fake_identity
+
+
+class TestTokenClientV3(base.TestCase):
+
+    def test_init_without_authurl(self):
+        self.assertRaises(exceptions.IdentityError,
+                          token_client.V3TokenClient, None)
+
+    def test_auth(self):
+        token_client_v3 = token_client.V3TokenClient('fake_url')
+        response, body_text = fake_identity._fake_v3_response(None, None)
+        body = json.loads(body_text)
+
+        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': {
+                'identity': {
+                    'methods': ['password'],
+                    'password': {
+                        'user': {
+                            'name': 'fake_user',
+                            'password': 'fake_pass',
+                        }
+                    }
+                },
+            }
+        }, sort_keys=True)
+        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')
+        response, body_text = fake_identity._fake_v3_response(None, None)
+        body = json.loads(body_text)
+
+        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': {
+                'identity': {
+                    'methods': ['password'],
+                    'password': {
+                        'user': {
+                            'name': 'fake_user',
+                            'password': 'fake_pass',
+                            'domain': {
+                                'id': '14f4a9a99973404d8c20ba1d2af163ff'
+                            }
+                        }
+                    }
+                },
+                'scope': {
+                    'project': {
+                        'id': 'fcac2a055a294e4c82d0a9c21c620eb4',
+                        'domain': {
+                            'id': '291f63ae9ac54ee292ca09e5f72d9676'
+                        }
+                    }
+                }
+            }
+        }, sort_keys=True)
+        post_mock.assert_called_once_with('fake_url/auth/tokens',
+                                          body=req_dict)
+
+    def test_auth_with_tenant(self):
+        token_client_v3 = token_client.V3TokenClient('fake_url')
+        response, body_text = fake_identity._fake_v3_response(None, None)
+        body = json.loads(body_text)
+
+        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': {
+                'identity': {
+                    'methods': ['password'],
+                    'password': {
+                        'user': {
+                            'name': 'fake_user',
+                            'password': 'fake_pass',
+                        }
+                    }},
+                'scope': {
+                    'project': {
+                        'name': 'fake_tenant'
+                    }
+                },
+            }
+        }, sort_keys=True)
+
+        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')
+
+        with mock.patch.object(token_client_v3, 'raw_request') as mock_raw_r:
+            mock_raw_r.return_value = (
+                fake_identity._fake_v3_response(None, None))
+            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')
+
+        response, body_text = fake_identity._fake_v3_response(None, None)
+        body = body_text.encode('utf-8')
+
+        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/services/identity/v3/test_trusts_client.py b/tempest/tests/lib/services/identity/v3/test_trusts_client.py
new file mode 100644
index 0000000..a1ca020
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_trusts_client.py
@@ -0,0 +1,150 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.identity.v3 import trusts_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestTrustsClient(base.BaseServiceTest):
+    FAKE_CREATE_TRUST = {
+        "trust": {
+            "expires_at": "2013-02-27T18:30:59.999999Z",
+            "impersonation": True,
+            "allow_redelegation": True,
+            "project_id": "ddef321",
+            "roles": [
+                {
+                    "name": "member"
+                    }
+                ],
+            "trustee_user_id": "86c0d5",
+            "trustor_user_id": "a0fdfd"
+            }
+        }
+
+    FAKE_LIST_TRUSTS = {
+        "trusts": [
+            {
+                "id": "1ff900",
+                "expires_at":
+                "2013-02-27T18:30:59.999999Z",
+                "impersonation": True,
+                "links": {
+                    "self":
+                    "http://example.com/identity/v3/OS-TRUST/trusts/1ff900"
+                    },
+                "project_id": "0f1233",
+                "trustee_user_id": "86c0d5",
+                "trustor_user_id": "a0fdfd"
+                },
+            {
+                "id": "f4513a",
+                "impersonation": False,
+                "links": {
+                    "self":
+                    "http://example.com/identity/v3/OS-TRUST/trusts/f45513a"
+                    },
+                "project_id": "0f1233",
+                "trustee_user_id": "86c0d5",
+                "trustor_user_id": "3cd2ce"
+                }
+            ]
+        }
+
+    FAKE_TRUST_INFO = {
+        "trust": {
+            "id": "987fe8",
+            "expires_at": "2013-02-27T18:30:59.999999Z",
+            "impersonation": True,
+            "links": {
+                "self":
+                "http://example.com/identity/v3/OS-TRUST/trusts/987fe8"
+                },
+            "roles": [
+                {
+                    "id": "ed7b78",
+                    "links": {
+                        "self":
+                        "http://example.com/identity/v3/roles/ed7b78"
+                        },
+                    "name": "member"
+                    }
+                ],
+            "roles_links": {
+                "next": None,
+                "previous": None,
+                "self":
+                "http://example.com/identity/v3/OS-TRUST/trusts/1ff900/roles"
+                },
+            "project_id": "0f1233",
+            "trustee_user_id": "be34d1",
+            "trustor_user_id": "56ae32"
+            }
+        }
+
+    def setUp(self):
+        super(TestTrustsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = trusts_client.TrustsClient(fake_auth, 'identity',
+                                                 'regionOne')
+
+    def _test_create_trust(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_trust,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_TRUST,
+            bytes_body,
+            status=201)
+
+    def _test_show_trust(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_trust,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_TRUST_INFO,
+            bytes_body,
+            trust_id="1ff900")
+
+    def _test_list_trusts(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_trusts,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_TRUSTS,
+            bytes_body)
+
+    def test_create_trust_with_str_body(self):
+        self._test_create_trust()
+
+    def test_create_trust_with_bytes_body(self):
+        self._test_create_trust(bytes_body=True)
+
+    def test_show_trust_with_str_body(self):
+        self._test_show_trust()
+
+    def test_show_trust_with_bytes_body(self):
+        self._test_show_trust(bytes_body=True)
+
+    def test_list_trusts_with_str_body(self):
+        self._test_list_trusts()
+
+    def test_list_trusts_with_bytes_body(self):
+        self._test_list_trusts(bytes_body=True)
+
+    def test_delete_trust(self):
+        self.check_service_client_function(
+            self.client.delete_trust,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            trust_id="1ff900",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_users_client.py b/tempest/tests/lib/services/identity/v3/test_users_client.py
new file mode 100644
index 0000000..5b572f5
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_users_client.py
@@ -0,0 +1,205 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.identity.v3 import users_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestUsersClient(base.BaseServiceTest):
+    FAKE_CREATE_USER = {
+        'user': {
+            'default_project_id': '95f8c3f8e7b54409a418fc30717f9ae0',
+            'domain_id': '8347b31afc3545c4b311cb4cce788a08',
+            'enabled': True,
+            'name': 'Tempest User',
+            'password': 'TempestPassword',
+        }
+    }
+
+    FAKE_USER_INFO = {
+        'user': {
+            'default_project_id': '95f8c3f8e7b54409a418fc30717f9ae0',
+            'domain_id': '8347b31afc3545c4b311cb4cce788a08',
+            'enabled': True,
+            'id': '817fb3c23fd7465ba6d7fe1b1320121d',
+            'links': {
+                'self': 'http://example.com/identity',
+            },
+            'name': 'Tempest User',
+            'password_expires_at': '2016-11-06T15:32:17.000000',
+        }
+    }
+
+    FAKE_USER_LIST = {
+        'links': {
+            'next': None,
+            'previous': None,
+            'self': 'http://example.com/identity/v3/users',
+        },
+        'users': [
+            {
+                'domain_id': 'TempestDomain',
+                'enabled': True,
+                'id': '817fb3c23fd7465ba6d7fe1b1320121d',
+                'links': {
+                    'self': 'http://example.com/identity/v3/users/' +
+                            '817fb3c23fd7465ba6d7fe1b1320121d',
+                },
+                'name': 'Tempest User',
+                'password_expires_at': '2016-11-06T15:32:17.000000',
+            },
+            {
+                'domain_id': 'TempestDomain',
+                'enabled': True,
+                'id': 'bdbfb1e2f1344be197e90a778379cca1',
+                'links': {
+                    'self': 'http://example.com/identity/v3/users/' +
+                            'bdbfb1e2f1344be197e90a778379cca1',
+                },
+                'name': 'Tempest User',
+                'password_expires_at': None,
+            },
+        ]
+    }
+
+    FAKE_GROUP_LIST = {
+        'links': {
+            'self': 'http://example.com/identity/v3/groups',
+            'previous': None,
+            'next': None,
+        },
+        'groups': [
+            {
+                'description': 'Tempest Group One Description',
+                'domain_id': 'TempestDomain',
+                'id': '1c92f3453ed34291a074b87493455b8f',
+                'links': {
+                    'self': 'http://example.com/identity/v3/groups/' +
+                            '1c92f3453ed34291a074b87493455b8f'
+                },
+                'name': 'Tempest Group One',
+            },
+            {
+                'description': 'Tempest Group Two Description',
+                'domain_id': 'TempestDomain',
+                'id': 'ce9e7dafed3b4877a7d4466ed730a9ee',
+                'links': {
+                    'self': 'http://example.com/identity/v3/groups/' +
+                            'ce9e7dafed3b4877a7d4466ed730a9ee'
+                },
+                'name': 'Tempest Group Two',
+            },
+        ]
+    }
+
+    def setUp(self):
+        super(TestUsersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = users_client.UsersClient(fake_auth, 'identity',
+                                               'regionOne')
+
+    def _test_create_user(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_user,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_USER,
+            bytes_body,
+            status=201,
+        )
+
+    def _test_show_user(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_user,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            user_id='817fb3c23fd7465ba6d7fe1b1320121d',
+        )
+
+    def _test_list_users(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_users,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_USER_LIST,
+            bytes_body,
+        )
+
+    def _test_update_user(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_user,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            user_id='817fb3c23fd7465ba6d7fe1b1320121d',
+            name='NewName',
+        )
+
+    def _test_list_user_groups(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_user_groups,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_GROUP_LIST,
+            bytes_body,
+            user_id='817fb3c23fd7465ba6d7fe1b1320121d',
+        )
+
+    def test_create_user_with_string_body(self):
+        self._test_create_user()
+
+    def test_create_user_with_bytes_body(self):
+        self._test_create_user(bytes_body=True)
+
+    def test_show_user_with_string_body(self):
+        self._test_show_user()
+
+    def test_show_user_with_bytes_body(self):
+        self._test_show_user(bytes_body=True)
+
+    def test_list_users_with_string_body(self):
+        self._test_list_users()
+
+    def test_list_users_with_bytes_body(self):
+        self._test_list_users(bytes_body=True)
+
+    def test_update_user_with_string_body(self):
+        self._test_update_user()
+
+    def test_update_user_with_bytes_body(self):
+        self._test_update_user(bytes_body=True)
+
+    def test_list_user_groups_with_string_body(self):
+        self._test_list_user_groups()
+
+    def test_list_user_groups_with_bytes_body(self):
+        self._test_list_user_groups(bytes_body=True)
+
+    def test_delete_user(self):
+        self.check_service_client_function(
+            self.client.delete_user,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            user_id='817fb3c23fd7465ba6d7fe1b1320121d',
+            status=204,
+        )
+
+    def test_change_user_password(self):
+        self.check_service_client_function(
+            self.client.update_user_password,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=204,
+            user_id='817fb3c23fd7465ba6d7fe1b1320121d',
+            password='NewTempestPassword',
+            original_password='OldTempestPassword')
diff --git a/tempest/services/image/__init__.py b/tempest/tests/lib/services/image/__init__.py
similarity index 100%
rename from tempest/services/image/__init__.py
rename to tempest/tests/lib/services/image/__init__.py
diff --git a/tempest/services/image/v1/__init__.py b/tempest/tests/lib/services/image/v1/__init__.py
similarity index 100%
rename from tempest/services/image/v1/__init__.py
rename to tempest/tests/lib/services/image/v1/__init__.py
diff --git a/tempest/tests/lib/services/image/v1/test_image_members_client.py b/tempest/tests/lib/services/image/v1/test_image_members_client.py
new file mode 100644
index 0000000..a5a6128
--- /dev/null
+++ b/tempest/tests/lib/services/image/v1/test_image_members_client.py
@@ -0,0 +1,84 @@
+# 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.lib.services.image.v1 import image_members_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestImageMembersClient(base.BaseServiceTest):
+    FAKE_LIST_IMAGE_MEMBERS = {
+        "members": [
+            {
+                "created_at": "2013-10-07T17:58:03Z",
+                "image_id": "dbc999e3-c52f-4200-bedd-3b18fe7f87fe",
+                "member_id": "123456789",
+                "status": "pending",
+                "updated_at": "2013-10-07T17:58:03Z"
+            },
+            {
+                "created_at": "2013-10-07T17:58:55Z",
+                "image_id": "dbc999e3-c52f-4200-bedd-3b18fe7f87fe",
+                "member_id": "987654321",
+                "status": "accepted",
+                "updated_at": "2013-10-08T12:08:55Z"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestImageMembersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = image_members_client.ImageMembersClient(fake_auth,
+                                                              'image',
+                                                              'regionOne')
+
+    def _test_list_image_members(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_image_members,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_IMAGE_MEMBERS,
+            bytes_body,
+            image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e")
+
+    def _test_create_image_member(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_image_member,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+            member_id="8989447062e04a818baf9e073fd04fa7",
+            status=204)
+
+    def test_list_image_members_with_str_body(self):
+        self._test_list_image_members()
+
+    def test_list_image_members_with_bytes_body(self):
+        self._test_list_image_members(bytes_body=True)
+
+    def test_create_image_member_with_str_body(self):
+        self._test_create_image_member()
+
+    def test_create_image_member_with_bytes_body(self):
+        self._test_create_image_member(bytes_body=True)
+
+    def test_delete_image_member(self):
+        self.check_service_client_function(
+            self.client.delete_image_member,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+            member_id="8989447062e04a818baf9e073fd04fa7",
+            status=204)
diff --git a/tempest/services/image/v2/__init__.py b/tempest/tests/lib/services/image/v2/__init__.py
similarity index 100%
rename from tempest/services/image/v2/__init__.py
rename to tempest/tests/lib/services/image/v2/__init__.py
diff --git a/tempest/tests/lib/services/image/v2/test_image_members_client.py b/tempest/tests/lib/services/image/v2/test_image_members_client.py
new file mode 100644
index 0000000..703b6e1
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_image_members_client.py
@@ -0,0 +1,90 @@
+# 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.lib.services.image.v2 import image_members_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestImageMembersClient(base.BaseServiceTest):
+    FAKE_CREATE_SHOW_UPDATE_IMAGE_MEMBER = {
+        "status": "pending",
+        "created_at": "2013-11-26T07:21:21Z",
+        "updated_at": "2013-11-26T07:21:21Z",
+        "image_id": "0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+        "member_id": "8989447062e04a818baf9e073fd04fa7",
+        "schema": "/v2/schemas/member"
+    }
+
+    def setUp(self):
+        super(TestImageMembersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = image_members_client.ImageMembersClient(fake_auth,
+                                                              'image',
+                                                              'regionOne')
+
+    def _test_show_image_member(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_image_member,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CREATE_SHOW_UPDATE_IMAGE_MEMBER,
+            bytes_body,
+            image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+            member_id="8989447062e04a818baf9e073fd04fa7")
+
+    def _test_create_image_member(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_image_member,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_SHOW_UPDATE_IMAGE_MEMBER,
+            bytes_body,
+            image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+            member_id="8989447062e04a818baf9e073fd04fa7")
+
+    def _test_update_image_member(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_image_member,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_CREATE_SHOW_UPDATE_IMAGE_MEMBER,
+            bytes_body,
+            image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+            member_id="8989447062e04a818baf9e073fd04fa7",
+            schema="/v2/schemas/member2")
+
+    def test_show_image_member_with_str_body(self):
+        self._test_show_image_member()
+
+    def test_show_image_member_with_bytes_body(self):
+        self._test_show_image_member(bytes_body=True)
+
+    def test_create_image_member_with_str_body(self):
+        self._test_create_image_member()
+
+    def test_create_image_member_with_bytes_body(self):
+        self._test_create_image_member(bytes_body=True)
+
+    def test_delete_image_member(self):
+        self.check_service_client_function(
+            self.client.delete_image_member,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+            member_id="8989447062e04a818baf9e073fd04fa7",
+            status=204)
+
+    def test_update_image_member_with_str_body(self):
+        self._test_update_image_member()
+
+    def test_update_image_member_with_bytes_body(self):
+        self._test_update_image_member(bytes_body=True)
diff --git a/tempest/tests/lib/services/image/v2/test_images_client.py b/tempest/tests/lib/services/image/v2/test_images_client.py
new file mode 100644
index 0000000..9648985
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_images_client.py
@@ -0,0 +1,111 @@
+# 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.lib.services.image.v2 import images_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestImagesClient(base.BaseServiceTest):
+    FAKE_CREATE_UPDATE_SHOW_IMAGE = {
+        "id": "e485aab9-0907-4973-921c-bb6da8a8fcf8",
+        "name": u"\u2740(*\xb4\u25e2`*)\u2740",
+        "status": "active",
+        "visibility": "public",
+        "size": 2254249,
+        "checksum": "2cec138d7dae2aa59038ef8c9aec2390",
+        "tags": [
+            "fedora",
+            "beefy"
+        ],
+        "created_at": "2012-08-10T19:23:50Z",
+        "updated_at": "2012-08-12T11:11:33Z",
+        "self": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea",
+        "file": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file",
+        "schema": "/v2/schemas/image",
+        "owner": None,
+        "min_ram": None,
+        "min_disk": None,
+        "disk_format": None,
+        "virtual_size": None,
+        "container_format": None
+    }
+
+    def setUp(self):
+        super(TestImagesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = images_client.ImagesClient(fake_auth,
+                                                 'image', 'regionOne')
+
+    def _test_update_image(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_image,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_CREATE_UPDATE_SHOW_IMAGE,
+            bytes_body,
+            image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8",
+            patch=[{"op": "add", "path": "/a/b/c", "value": ["foo", "bar"]}])
+
+    def _test_create_image(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_image,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_UPDATE_SHOW_IMAGE,
+            bytes_body,
+            name="virtual machine image",
+            status=201)
+
+    def _test_show_image(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_image,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CREATE_UPDATE_SHOW_IMAGE,
+            bytes_body,
+            image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8")
+
+    def test_create_image_with_str_body(self):
+        self._test_create_image()
+
+    def test_create_image_with_bytes_body(self):
+        self._test_create_image(bytes_body=True)
+
+    def test_update_image_with_str_body(self):
+        self._test_update_image()
+
+    def test_update_image_with_bytes_body(self):
+        self._test_update_image(bytes_body=True)
+
+    def test_deactivate_image(self):
+        self.check_service_client_function(
+            self.client.deactivate_image,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {}, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8", status=204)
+
+    def test_reactivate_image(self):
+        self.check_service_client_function(
+            self.client.reactivate_image,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {}, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8", status=204)
+
+    def test_delete_image(self):
+        self.check_service_client_function(
+            self.client.delete_image,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8", status=204)
+
+    def test_show_image_with_str_body(self):
+        self._test_show_image()
+
+    def test_show_image_with_bytes_body(self):
+        self._test_show_image(bytes_body=True)
diff --git a/tempest/tests/lib/services/image/v2/test_namespace_object_client.py b/tempest/tests/lib/services/image/v2/test_namespace_object_client.py
new file mode 100644
index 0000000..8d29660
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_namespace_object_client.py
@@ -0,0 +1,210 @@
+# Copyright 2016 EasyStack. 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.services.image.v2 import namespace_objects_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestNamespaceObjectClient(base.BaseServiceTest):
+    FAKE_CREATE_SHOW_OBJECTS = {
+        "created_at": "2016-09-19T18:20:56Z",
+        "description": "You can configure the CPU limits.",
+        "name": "CPU Limits",
+        "properties": {
+            "quota:cpu_period": {
+                "description": "Specifies the enforcement interval",
+                "maximum": 1000000,
+                "minimum": 1000,
+                "title": "Quota: CPU Period",
+                "type": "integer"
+            },
+            "quota:cpu_quota": {
+                "description": "Specifies the maximum allowed bandwidth ",
+                "title": "Quota: CPU Quota",
+                "type": "integer"
+            },
+            "quota:cpu_shares": {
+                "description": "Specifies the proportional weighted share.",
+                "title": "Quota: CPU Shares",
+                "type": "integer"
+            }
+        },
+        "required": [],
+        "schema": "/v2/schemas/metadefs/object",
+        "self": "/v2/metadefs/namespaces/OS::Compute::Quota/objects/CPU",
+        "updated_at": "2016-09-19T18:20:56Z"
+    }
+
+    FAKE_LIST_OBJECTS = {
+        "objects": [
+            {
+                "created_at": "2016-09-18T18:16:35Z",
+                "description": "You can configure the CPU limits.",
+                "name": "CPU Limits",
+                "properties": {
+                    "quota:cpu_period": {
+                        "description": "Specifies the enforcement interval ",
+                        "maximum": 1000000,
+                        "minimum": 1000,
+                        "title": "Quota: CPU Period",
+                        "type": "integer"
+                    },
+                    "quota:cpu_quota": {
+                        "description": "Specifies the maximum.",
+                        "title": "Quota: CPU Quota",
+                        "type": "integer"
+                    },
+                    "quota:cpu_shares": {
+                        "description": " Desc.",
+                        "title": "Quota: CPU Shares",
+                        "type": "integer"
+                    }
+                },
+                "required": [],
+                "schema": "/v2/schemas/metadefs/object",
+                "self":
+                    "/v2/metadefs/namespaces/OS::Compute::Quota/objects/CPU"
+            },
+            {
+                "created_at": "2016-09-18T18:16:35Z",
+                "description": "Using disk I/O quotas.",
+                "name": "Disk QoS",
+                "properties": {
+                    "quota:disk_read_bytes_sec": {
+                        "description": "Sets disk I/O quota.",
+                        "title": "Quota: Disk read bytes / sec",
+                        "type": "integer"
+                    },
+                    "quota:disk_read_iops_sec": {
+                        "description": "Sets disk I/O quota",
+                        "title": "Quota: Disk read IOPS / sec",
+                        "type": "integer"
+                    },
+                    "quota:disk_total_bytes_sec": {
+                        "description": "Sets disk I/O quota.",
+                        "title": "Quota: Disk Total Bytes / sec",
+                        "type": "integer"
+                    },
+                    "quota:disk_total_iops_sec": {
+                        "description": "Sets disk I/O quota.",
+                        "title": "Quota: Disk Total IOPS / sec",
+                        "type": "integer"
+                    },
+                    "quota:disk_write_bytes_sec": {
+                        "description": "Sets disk I/O quota.",
+                        "title": "Quota: Disk Write Bytes / sec",
+                        "type": "integer"
+                    },
+                    "quota:disk_write_iops_sec": {
+                        "description": "Sets disk I/O quota.",
+                        "title": "Quota: Disk Write IOPS / sec",
+                        "type": "integer"
+                    }
+                },
+                "required": [],
+                "schema": "/v2/schemas/metadefs/object",
+                "self":
+                "/v2/metadefs/namespaces/OS::Compute::Quota/objects/Disk QoS"
+            },
+        ],
+        "schema": "v2/schemas/metadefs/objects"
+    }
+
+    FAKE_UPDATE_OBJECTS = {
+        "description": "You can configure the CPU limits.",
+        "name": "CPU",
+        "properties": {
+            "quota:cpu_shares": {
+                "description": "Specify.",
+                "title": "Quota: CPU Shares",
+                "type": "integer"
+            }
+        },
+        "required": []
+    }
+
+    def setUp(self):
+        super(TestNamespaceObjectClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = namespace_objects_client.NamespaceObjectsClient(
+            fake_auth, 'image', 'regionOne')
+
+    def _test_create_namespace_objects(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_namespace_object,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_SHOW_OBJECTS,
+            bytes_body, status=201,
+            namespace="OS::Compute::Hypervisor",
+            object_name="OS::Glance::Image")
+
+    def _test_list_namespace_objects(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_namespace_objects,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_OBJECTS,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor")
+
+    def _test_show_namespace_objects(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_namespace_object,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CREATE_SHOW_OBJECTS,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor",
+            object_name="OS::Glance::Image")
+
+    def _test_update_namespace_objects(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_namespace_object,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_OBJECTS,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor",
+            object_name="OS::Glance::Image",
+            name="CPU")
+
+    def test_create_namespace_object_with_str_body(self):
+        self._test_create_namespace_objects()
+
+    def test_create_namespace_object_with_bytes_body(self):
+        self._test_create_namespace_objects(bytes_body=True)
+
+    def test_list_namespace_object_with_str_body(self):
+        self._test_list_namespace_objects()
+
+    def test_list_namespace_object_with_bytes_body(self):
+        self._test_list_namespace_objects(bytes_body=True)
+
+    def test_show_namespace_object_with_str_body(self):
+        self._test_show_namespace_objects()
+
+    def test_show_namespace_object_with_bytes_body(self):
+        self._test_show_namespace_objects(bytes_body=True)
+
+    def test_update_namespace_object_with_str_body(self):
+        self._test_update_namespace_objects()
+
+    def test_update_namespace_object_with_bytes_body(self):
+        self._test_update_namespace_objects(bytes_body=True)
+
+    def test_delete_namespace_objects(self):
+        self.check_service_client_function(
+            self.client.delete_namespace_object,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, namespace="OS::Compute::Hypervisor",
+            object_name="OS::Glance::Image",
+            status=204)
diff --git a/tempest/tests/lib/services/image/v2/test_namespace_properties_client.py b/tempest/tests/lib/services/image/v2/test_namespace_properties_client.py
new file mode 100644
index 0000000..1d56db6
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_namespace_properties_client.py
@@ -0,0 +1,191 @@
+# Copyright 2016 EasyStack.  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.services.image.v2 import namespace_properties_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestNamespacePropertiesClient(base.BaseServiceTest):
+    FAKE_CREATE_SHOW_NAMESPACE_PROPERTY = {
+        "description": "property",
+        "enum": ["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"],
+        "name": "OS::Glance::Image",
+        "title": "Hypervisor Type",
+        "type": "string"
+    }
+
+    FAKE_LIST_NAMESPACE_PROPERTY = {
+        "properties": {
+            "hw_disk_bus": {
+                "description": "property.",
+                "enum": ["scsi", "virtio", "uml", "xen", "ide", "usb"],
+                "title": "Disk Bus",
+                "type": "string"
+            },
+            "hw_machine_type": {
+                "description": "desc.",
+                "title": "Machine Type",
+                "type": "string"
+            },
+            "hw_qemu_guest_agent": {
+                "description": "desc.",
+                "enum": [
+                    "yes",
+                    "no"
+                ],
+                "title": "QEMU Guest Agent",
+                "type": "string"
+            },
+            "hw_rng_model": {
+                "default": "virtio",
+                "description": "desc",
+                "title": "Random Number Generator Device",
+                "type": "string"
+            },
+            "hw_scsi_model": {
+                "default": "virtio-scsi",
+                "description": "desc.",
+                "title": "SCSI Model",
+                "type": "string"
+            },
+            "hw_video_model": {
+                "description": "The video image driver used.",
+                "enum": [
+                    "vga",
+                    "cirrus",
+                    "vmvga",
+                    "xen",
+                    "qxl"
+                ],
+                "title": "Video Model",
+                "type": "string"
+            },
+            "hw_video_ram": {
+                "description": "desc.",
+                "title": "Max Video Ram",
+                "type": "integer"
+            },
+            "hw_vif_model": {
+                "description": "desc.",
+                "enum": ["e1000",
+                         "ne2k_pci",
+                         "pcnet",
+                         "rtl8139",
+                         "virtio",
+                         "e1000",
+                         "e1000e",
+                         "VirtualE1000",
+                         "VirtualE1000e",
+                         "VirtualPCNet32",
+                         "VirtualSriovEthernetCard",
+                         "VirtualVmxnet",
+                         "netfront",
+                         "ne2k_pci"
+                         ],
+                "title": "Virtual Network Interface",
+                "type": "string"
+            },
+            "os_command_line": {
+                "description": "desc.",
+                "title": "Kernel Command Line",
+                "type": "string"
+            }
+        }
+    }
+
+    FAKE_UPDATE_NAMESPACE_PROPERTY = {
+        "description": "property",
+        "enum": ["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"],
+        "name": "OS::Glance::Image",
+        "title": "update Hypervisor Type",
+        "type": "string"
+    }
+
+    def setUp(self):
+        super(TestNamespacePropertiesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = namespace_properties_client.NamespacePropertiesClient(
+            fake_auth, 'image', 'regionOne')
+
+    def _test_create_namespace_property(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_namespace_property,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_SHOW_NAMESPACE_PROPERTY,
+            bytes_body, status=201,
+            namespace="OS::Compute::Hypervisor",
+            title="Hypervisor Type", name="OS::Glance::Image",
+            type="string",
+            enum=["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"])
+
+    def _test_list_namespace_property(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_namespace_properties,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_NAMESPACE_PROPERTY,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor")
+
+    def _test_show_namespace_property(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_namespace_properties,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CREATE_SHOW_NAMESPACE_PROPERTY,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor",
+            property_name="OS::Glance::Image")
+
+    def _test_update_namespace_property(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_namespace_properties,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_NAMESPACE_PROPERTY,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor",
+            property_name="OS::Glance::Image",
+            title="update Hypervisor Type", type="string",
+            enum=["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"],
+            name="OS::Glance::Image")
+
+    def test_create_namespace_property_with_str_body(self):
+        self._test_create_namespace_property()
+
+    def test_create_namespace_property_with_bytes_body(self):
+        self._test_create_namespace_property(bytes_body=True)
+
+    def test_list_namespace_property_with_str_body(self):
+        self._test_list_namespace_property()
+
+    def test_list_namespace_property_with_bytes_body(self):
+        self._test_list_namespace_property(bytes_body=True)
+
+    def test_show_namespace_property_with_str_body(self):
+        self._test_show_namespace_property()
+
+    def test_show_namespace_property_with_bytes_body(self):
+        self._test_show_namespace_property(bytes_body=True)
+
+    def test_update_namespace_property_with_str_body(self):
+        self._test_update_namespace_property()
+
+    def test_update_namespace_property_with_bytes_body(self):
+        self._test_update_namespace_property(bytes_body=True)
+
+    def test_delete_namespace(self):
+        self.check_service_client_function(
+            self.client.delete_namespace_property,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, namespace="OS::Compute::Hypervisor",
+            property_name="OS::Glance::Image", status=204)
diff --git a/tempest/tests/lib/services/image/v2/test_namespace_tags_client.py b/tempest/tests/lib/services/image/v2/test_namespace_tags_client.py
new file mode 100644
index 0000000..2faa5be
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_namespace_tags_client.py
@@ -0,0 +1,126 @@
+# Copyright 2016 EasyStack. 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.services.image.v2 import namespace_tags_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestNamespaceTagsClient(base.BaseServiceTest):
+    FAKE_CREATE_SHOW_TAGS = {
+        "created_at": "2015-05-09T01:12:31Z",
+        "name": "added-sample-tag",
+        "updated_at": "2015-05-09T01:12:31Z"
+    }
+
+    FAKE_LIST_TAGS = {
+        "tags": [
+            {
+                "name": "sample-tag1"
+            },
+            {
+                "name": "sample-tag2"
+            },
+            {
+                "name": "sample-tag3"
+            }
+        ]
+    }
+
+    FAKE_UPDATE_TAGS = {"name": "new-tag-name"}
+
+    def setUp(self):
+        super(TestNamespaceTagsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = namespace_tags_client.NamespaceTagsClient(
+            fake_auth, 'image', 'regionOne')
+
+    def _test_create_namespace_tags(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_namespace_tags,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_SHOW_TAGS,
+            bytes_body, status=201,
+            namespace="OS::Compute::Hypervisor",
+            tags=[{"name": "sample-tag1"},
+                  {"name": "sample-tag2"},
+                  {"name": "sample-tag3"}])
+
+    def _test_list_namespace_tags(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_namespace_tags,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_TAGS,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor")
+
+    def _test_create_namespace_tag_definition(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_namespace_tag,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_SHOW_TAGS,
+            bytes_body,
+            status=201,
+            namespace="OS::Compute::Hypervisor",
+            tag_name="added-sample-tag")
+
+    def _test_show_namespace_tag_definition(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_namespace_tag,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CREATE_SHOW_TAGS,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor",
+            tag_name="added-sample-tag")
+
+    def _test_update_namespace_tag_definition(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_namespace_tag,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_OBJECTS,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor",
+            tag_name="added-sample-tag",
+            name="new-tag-name")
+
+    def test_create_namespace_tags_with_str_body(self):
+        self._test_create_namespace_tags()
+
+    def test_create_namespace_tags_with_bytes_body(self):
+        self._test_create_namespace_tags(bytes_body=True)
+
+    def test_list_namespace_tags_with_str_body(self):
+        self._test_list_namespace_tags()
+
+    def test_list_namespace_tags_with_bytes_body(self):
+        self._test_list_namespace_tags(bytes_body=True)
+
+    def test_create_namespace_tag_with_str_body(self):
+        self._test_create_namespace_tag_definition()
+
+    def test_create_namespace_tag_with_bytes_body(self):
+        self._test_create_namespace_tag_definition(bytes_body=True)
+
+    def test_show_namespace_tag_with_str_body(self):
+        self._test_show_namespace_tag_definition()
+
+    def test_show_namespace_tag_with_bytes_body(self):
+        self._test_show_namespace_tag_definition(bytes_body=True)
+
+    def test_delete_all_namespace_tags(self):
+        self.check_service_client_function(
+            self.client.delete_namespace_tags,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, status=200,
+            namespace="OS::Compute::Hypervisor")
diff --git a/tempest/tests/lib/services/image/v2/test_namespaces_client.py b/tempest/tests/lib/services/image/v2/test_namespaces_client.py
new file mode 100644
index 0000000..4cb9d01
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_namespaces_client.py
@@ -0,0 +1,93 @@
+# 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.lib.services.image.v2 import namespaces_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestNamespacesClient(base.BaseServiceTest):
+    FAKE_CREATE_SHOW_NAMESPACE = {
+        "namespace": "OS::Compute::Hypervisor",
+        "visibility": "public",
+        "description": "Tempest",
+        "display_name": u"\u2740(*\xb4\u25e1`*)\u2740",
+        "protected": True
+    }
+
+    FAKE_UPDATE_NAMESPACE = {
+        "namespace": "OS::Compute::Hypervisor",
+        "visibility": "public",
+        "description": "Tempest",
+        "display_name": u"\u2740(*\xb4\u25e2`*)\u2740",
+        "protected": True
+    }
+
+    def setUp(self):
+        super(TestNamespacesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = namespaces_client.NamespacesClient(fake_auth,
+                                                         'image', 'regionOne')
+
+    def _test_show_namespace(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_namespace,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CREATE_SHOW_NAMESPACE,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor")
+
+    def _test_create_namespace(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_namespace,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_SHOW_NAMESPACE,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor",
+            visibility="public", description="Tempest",
+            display_name=u"\u2740(*\xb4\u25e1`*)\u2740", protected=True,
+            status=201)
+
+    def _test_update_namespace(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_namespace,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_NAMESPACE,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor",
+            display_name=u"\u2740(*\xb4\u25e2`*)\u2740", protected=True)
+
+    def test_show_namespace_with_str_body(self):
+        self._test_show_namespace()
+
+    def test_show_namespace_with_bytes_body(self):
+        self._test_show_namespace(bytes_body=True)
+
+    def test_create_namespace_with_str_body(self):
+        self._test_create_namespace()
+
+    def test_create_namespace_with_bytes_body(self):
+        self._test_create_namespace(bytes_body=True)
+
+    def test_delete_namespace(self):
+        self.check_service_client_function(
+            self.client.delete_namespace,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, namespace="OS::Compute::Hypervisor", status=204)
+
+    def test_update_namespace_with_str_body(self):
+        self._test_update_namespace()
+
+    def test_update_namespace_with_bytes_body(self):
+        self._test_update_namespace(bytes_body=True)
diff --git a/tempest/tests/lib/services/image/v2/test_resource_types_client.py b/tempest/tests/lib/services/image/v2/test_resource_types_client.py
new file mode 100644
index 0000000..2e3b117
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_resource_types_client.py
@@ -0,0 +1,69 @@
+# 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.lib.services.image.v2 import resource_types_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestResouceTypesClient(base.BaseServiceTest):
+    FAKE_LIST_RESOURCETYPES = {
+        "resource_types": [
+            {
+                "created_at": "2014-08-28T18:13:04Z",
+                "name": "OS::Glance::Image",
+                "updated_at": "2014-08-28T18:13:04Z"
+            },
+            {
+                "created_at": "2014-08-28T18:13:04Z",
+                "name": "OS::Cinder::Volume",
+                "updated_at": "2014-08-28T18:13:04Z"
+            },
+            {
+                "created_at": "2014-08-28T18:13:04Z",
+                "name": "OS::Nova::Flavor",
+                "updated_at": "2014-08-28T18:13:04Z"
+            },
+            {
+                "created_at": "2014-08-28T18:13:04Z",
+                "name": "OS::Nova::Aggregate",
+                "updated_at": "2014-08-28T18:13:04Z"
+            },
+            {
+                "created_at": "2014-08-28T18:13:04Z",
+                "name": u"\u2740(*\xb4\u25e1`*)\u2740",
+                "updated_at": "2014-08-28T18:13:04Z"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestResouceTypesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = resource_types_client.ResourceTypesClient(fake_auth,
+                                                                'image',
+                                                                'regionOne')
+
+    def _test_list_resouce_types(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_resource_types,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_RESOURCETYPES,
+            bytes_body)
+
+    def test_list_resouce_types_with_str_body(self):
+        self._test_list_resouce_types()
+
+    def test_list_resouce_types_with_bytes_body(self):
+        self._test_list_resouce_types(bytes_body=True)
diff --git a/tempest/tests/lib/services/image/v2/test_schemas_client.py b/tempest/tests/lib/services/image/v2/test_schemas_client.py
new file mode 100644
index 0000000..4c4b86a
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_schemas_client.py
@@ -0,0 +1,96 @@
+# 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.lib.services.image.v2 import schemas_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSchemasClient(base.BaseServiceTest):
+    FAKE_SHOW_SCHEMA = {
+        "links": [
+            {
+                "href": "{schema}",
+                "rel": "describedby"
+            }
+        ],
+        "name": "members",
+        "properties": {
+            "members": {
+                "items": {
+                    "name": "member",
+                    "properties": {
+                        "created_at": {
+                            "description": ("Date and time of image member"
+                                            " creation"),
+                            "type": "string"
+                        },
+                        "image_id": {
+                            "description": "An identifier for the image",
+                            "pattern": ("^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}"
+                                        "-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}"
+                                        "-([0-9a-fA-F]){12}$"),
+                            "type": "string"
+                        },
+                        "member_id": {
+                            "description": ("An identifier for the image"
+                                            " member (tenantId)"),
+                            "type": "string"
+                        },
+                        "schema": {
+                            "type": "string"
+                        },
+                        "status": {
+                            "description": "The status of this image member",
+                            "enum": [
+                                "pending",
+                                "accepted",
+                                "rejected"
+                            ],
+                            "type": "string"
+                        },
+                        "updated_at": {
+                            "description": ("Date and time of last"
+                                            " modification of image member"),
+                            "type": "string"
+                        }
+                    }
+                },
+                "type": "array"
+            },
+            "schema": {
+                "type": "string"
+            }
+        }
+    }
+
+    def setUp(self):
+        super(TestSchemasClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = schemas_client.SchemasClient(fake_auth,
+                                                   'image', 'regionOne')
+
+    def _test_show_schema(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_schema,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SHOW_SCHEMA,
+            bytes_body,
+            schema="member")
+
+    def test_show_schema_with_str_body(self):
+        self._test_show_schema()
+
+    def test_show_schema_with_bytes_body(self):
+        self._test_show_schema(bytes_body=True)
diff --git a/tempest/services/network/__init__.py b/tempest/tests/lib/services/network/__init__.py
similarity index 100%
rename from tempest/services/network/__init__.py
rename to tempest/tests/lib/services/network/__init__.py
diff --git a/tempest/tests/lib/services/network/test_routers_client.py b/tempest/tests/lib/services/network/test_routers_client.py
new file mode 100644
index 0000000..2fa5993
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_routers_client.py
@@ -0,0 +1,109 @@
+# 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.lib.services.network import routers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestRoutersClient(base.BaseServiceTest):
+    FAKE_CREATE_ROUTER = {
+        "router": {
+            "name": u'\u2740(*\xb4\u25e1`*)\u2740',
+            "external_gateway_info": {
+                "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b",
+                "enable_snat": True,
+                "external_fixed_ips": [
+                    {
+                        "subnet_id": "255.255.255.0",
+                        "ip": "192.168.10.1"
+                    }
+                ]
+            },
+            "admin_state_up": True,
+            "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e"
+        }
+    }
+
+    FAKE_UPDATE_ROUTER = {
+        "router": {
+            "name": u'\u2740(*\xb4\u25e1`*)\u2740',
+            "external_gateway_info": {
+                "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b",
+                "enable_snat": True,
+                "external_fixed_ips": [
+                    {
+                        "subnet_id": "255.255.255.0",
+                        "ip": "192.168.10.1"
+                    }
+                ]
+            },
+            "admin_state_up": False,
+            "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e"
+        }
+    }
+
+    def setUp(self):
+        super(TestRoutersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = routers_client.RoutersClient(fake_auth,
+                                                   'network', 'regionOne')
+
+    def _test_list_routers(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_routers,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"routers": []},
+            bytes_body)
+
+    def _test_create_router(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_router,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_ROUTER,
+            bytes_body,
+            name="another_router", admin_state_up="true", status=201)
+
+    def _test_update_router(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_router,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_ROUTER,
+            bytes_body,
+            router_id="8604a0de-7f6b-409a-a47c-a1cc7bc77b2e",
+            admin_state_up=False)
+
+    def test_list_routers_with_str_body(self):
+        self._test_list_routers()
+
+    def test_list_routers_with_bytes_body(self):
+        self._test_list_routers(bytes_body=True)
+
+    def test_create_router_with_str_body(self):
+        self._test_create_router()
+
+    def test_create_router_with_bytes_body(self):
+        self._test_create_router(bytes_body=True)
+
+    def test_delete_router(self):
+        self.check_service_client_function(
+            self.client.delete_router,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, router_id="1", status=204)
+
+    def test_update_router_with_str_body(self):
+        self._test_update_router()
+
+    def test_update_router_with_bytes_body(self):
+        self._test_update_router(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_service_providers_client.py b/tempest/tests/lib/services/network/test_service_providers_client.py
new file mode 100644
index 0000000..ae11ef0
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_service_providers_client.py
@@ -0,0 +1,37 @@
+#    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 service_providers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestServiceProvidersClient(base.BaseServiceTest):
+    def setUp(self):
+        super(TestServiceProvidersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = service_providers_client.ServiceProvidersClient(
+            fake_auth, 'network', 'regionOne')
+
+    def _test_list_service_providers(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_service_providers,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"service_providers": []},
+            bytes_body)
+
+    def test_list_service_providers_with_str_body(self):
+        self._test_list_service_providers()
+
+    def test_list_service_providers_with_bytes_body(self):
+        self._test_list_service_providers(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_versions_client.py b/tempest/tests/lib/services/network/test_versions_client.py
new file mode 100644
index 0000000..026dc6d
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_versions_client.py
@@ -0,0 +1,74 @@
+# Copyright 2016 VMware, Inc.  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.lib.services.network import versions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestNetworkVersionsClient(base.BaseServiceTest):
+
+    FAKE_INIT_VERSION = {
+        "version": {
+            "id": "v2.0",
+            "links": [
+                {
+                    "href": "http://openstack.example.com/v2.0/",
+                    "rel": "self"
+                },
+                {
+                    "href": "http://docs.openstack.org/",
+                    "rel": "describedby",
+                    "type": "text/html"
+                }
+            ],
+            "status": "CURRENT"
+            }
+        }
+
+    FAKE_VERSIONS_INFO = {
+        "versions": [FAKE_INIT_VERSION["version"]]
+        }
+
+    FAKE_VERSION_INFO = copy.deepcopy(FAKE_INIT_VERSION)
+
+    FAKE_VERSION_INFO["version"]["media-types"] = [
+        {
+            "base": "application/json",
+            "type": "application/vnd.openstack.network+json;version=2.0"
+        }
+        ]
+
+    def setUp(self):
+        super(TestNetworkVersionsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.versions_client = (
+            versions_client.NetworkVersionsClient
+            (fake_auth, 'compute', 'regionOne'))
+
+    def _test_versions_client(self, bytes_body=False):
+        self.check_service_client_function(
+            self.versions_client.list_versions,
+            'tempest.lib.common.rest_client.RestClient.raw_request',
+            self.FAKE_VERSIONS_INFO,
+            bytes_body,
+            200)
+
+    def test_list_versions_client_with_str_body(self):
+        self._test_versions_client()
+
+    def test_list_versions_client_with_bytes_body(self):
+        self._test_versions_client(bytes_body=True)
diff --git a/tempest/tests/lib/services/test_clients.py b/tempest/tests/lib/services/test_clients.py
new file mode 100644
index 0000000..5db932c
--- /dev/null
+++ b/tempest/tests/lib/services/test_clients.py
@@ -0,0 +1,370 @@
+# Copyright (c) 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.
+
+import fixtures
+import mock
+import testtools
+import types
+
+from tempest.lib import auth
+from tempest.lib import exceptions
+from tempest.lib.services import clients
+from tempest.tests import base
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib import fake_credentials
+
+
+has_attribute = testtools.matchers.MatchesPredicateWithParams(
+    lambda x, y: hasattr(x, y), '{0} does not have an attribute {1}')
+
+
+class TestClientsFactory(base.TestCase):
+
+    def setUp(self):
+        super(TestClientsFactory, self).setUp()
+        self.classes = []
+
+    def _setup_fake_module(self, class_names=None, extra_dict=None):
+        class_names = class_names or []
+        fake_module = types.ModuleType('fake_service_client')
+        _dict = {}
+        # Add fake classes to the fake module
+        for name in class_names:
+            _dict[name] = type(name, (object,), {})
+            # Store it for assertions
+            self.classes.append(_dict[name])
+        if extra_dict:
+            _dict[extra_dict] = extra_dict
+        fake_module.__dict__.update(_dict)
+        fixture_importlib = self.useFixture(fixtures.MockPatch(
+            'importlib.import_module', return_value=fake_module))
+        return fixture_importlib.mock
+
+    def test___init___one_class(self):
+        fake_partial = 'fake_partial'
+        partial_mock = self.useFixture(fixtures.MockPatch(
+            'tempest.lib.services.clients.ClientsFactory._get_partial_class',
+            return_value=fake_partial)).mock
+        class_names = ['FakeServiceClient1']
+        mock_importlib = self._setup_fake_module(class_names=class_names)
+        auth_provider = fake_auth_provider.FakeAuthProvider()
+        params = {'k1': 'v1', 'k2': 'v2'}
+        factory = clients.ClientsFactory('fake_path', class_names,
+                                         auth_provider, **params)
+        # Assert module has been imported
+        mock_importlib.assert_called_once_with('fake_path')
+        # All attributes have been created
+        for client in class_names:
+            self.assertThat(factory, has_attribute(client))
+        # Partial have been invoked correctly
+        partial_mock.assert_called_once_with(
+            self.classes[0], auth_provider, params)
+        # Get the clients
+        for name in class_names:
+            self.assertEqual(fake_partial, getattr(factory, name))
+
+    def test___init___two_classes(self):
+        fake_partial = 'fake_partial'
+        partial_mock = self.useFixture(fixtures.MockPatch(
+            'tempest.lib.services.clients.ClientsFactory._get_partial_class',
+            return_value=fake_partial)).mock
+        class_names = ['FakeServiceClient1', 'FakeServiceClient2']
+        mock_importlib = self._setup_fake_module(class_names=class_names)
+        auth_provider = fake_auth_provider.FakeAuthProvider()
+        params = {'k1': 'v1', 'k2': 'v2'}
+        factory = clients.ClientsFactory('fake_path', class_names,
+                                         auth_provider, **params)
+        # Assert module has been imported
+        mock_importlib.assert_called_once_with('fake_path')
+        # All attributes have been created
+        for client in class_names:
+            self.assertThat(factory, has_attribute(client))
+        # Partial have been invoked the right number of times
+        partial_mock.call_count = len(class_names)
+        # Get the clients
+        for name in class_names:
+            self.assertEqual(fake_partial, getattr(factory, name))
+
+    def test___init___no_module(self):
+        auth_provider = fake_auth_provider.FakeAuthProvider()
+        class_names = ['FakeServiceClient1', 'FakeServiceClient2']
+        with testtools.ExpectedException(ImportError, '.*fake_module.*'):
+            clients.ClientsFactory('fake_module', class_names,
+                                   auth_provider)
+
+    def test___init___not_a_class(self):
+        class_names = ['FakeServiceClient1', 'FakeServiceClient2']
+        extended_class_names = class_names + ['not_really_a_class']
+        self._setup_fake_module(
+            class_names=class_names, extra_dict='not_really_a_class')
+        auth_provider = fake_auth_provider.FakeAuthProvider()
+        expected_msg = '.*not_really_a_class.*str.*'
+        with testtools.ExpectedException(TypeError, expected_msg):
+            clients.ClientsFactory('fake_module', extended_class_names,
+                                   auth_provider)
+
+    def test___init___class_not_found(self):
+        class_names = ['FakeServiceClient1', 'FakeServiceClient2']
+        extended_class_names = class_names + ['not_really_a_class']
+        self._setup_fake_module(class_names=class_names)
+        auth_provider = fake_auth_provider.FakeAuthProvider()
+        expected_msg = '.*not_really_a_class.*fake_service_client.*'
+        with testtools.ExpectedException(AttributeError, expected_msg):
+            clients.ClientsFactory('fake_module', extended_class_names,
+                                   auth_provider)
+
+    def test__get_partial_class_no_later_kwargs(self):
+        expected_fake_client = 'not_really_a_client'
+        self._setup_fake_module(class_names=[])
+        auth_provider = fake_auth_provider.FakeAuthProvider()
+        params = {'k1': 'v1', 'k2': 'v2'}
+        factory = clients.ClientsFactory(
+            'fake_path', [], auth_provider, **params)
+        klass_mock = mock.Mock(return_value=expected_fake_client)
+        partial = factory._get_partial_class(klass_mock, auth_provider, params)
+        # Class has not be initialised yet
+        klass_mock.assert_not_called()
+        # Use partial and assert on parameters
+        client = partial()
+        self.assertEqual(expected_fake_client, client)
+        klass_mock.assert_called_once_with(auth_provider=auth_provider,
+                                           **params)
+
+    def test__get_partial_class_later_kwargs(self):
+        expected_fake_client = 'not_really_a_client'
+        self._setup_fake_module(class_names=[])
+        auth_provider = fake_auth_provider.FakeAuthProvider()
+        params = {'k1': 'v1', 'k2': 'v2'}
+        later_params = {'k2': 'v4', 'k3': 'v3'}
+        factory = clients.ClientsFactory(
+            'fake_path', [], auth_provider, **params)
+        klass_mock = mock.Mock(return_value=expected_fake_client)
+        partial = factory._get_partial_class(klass_mock, auth_provider, params)
+        # Class has not be initialised yet
+        klass_mock.assert_not_called()
+        # Use partial and assert on parameters
+        client = partial(**later_params)
+        params.update(later_params)
+        self.assertEqual(expected_fake_client, client)
+        klass_mock.assert_called_once_with(auth_provider=auth_provider,
+                                           **params)
+
+    def test__get_partial_class_with_alias(self):
+        expected_fake_client = 'not_really_a_client'
+        client_alias = 'fake_client'
+        self._setup_fake_module(class_names=[])
+        auth_provider = fake_auth_provider.FakeAuthProvider()
+        params = {'k1': 'v1', 'k2': 'v2'}
+        later_params = {'k2': 'v4', 'k3': 'v3'}
+        factory = clients.ClientsFactory(
+            'fake_path', [], auth_provider, **params)
+        klass_mock = mock.Mock(return_value=expected_fake_client)
+        partial = factory._get_partial_class(klass_mock, auth_provider, params)
+        # Class has not be initialised yet
+        klass_mock.assert_not_called()
+        # Use partial and assert on parameters
+        client = partial(alias=client_alias, **later_params)
+        params.update(later_params)
+        self.assertEqual(expected_fake_client, client)
+        klass_mock.assert_called_once_with(auth_provider=auth_provider,
+                                           **params)
+        self.assertThat(factory, has_attribute(client_alias))
+        self.assertEqual(expected_fake_client, getattr(factory, client_alias))
+
+
+class TestServiceClients(base.TestCase):
+
+    def setUp(self):
+        super(TestServiceClients, self).setUp()
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.services.clients.tempest_modules', return_value={}))
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.services.clients._tempest_internal_modules',
+            return_value=set(['fake_service1'])))
+
+    def test___init___creds_v2_uri(self):
+        # Verify that no API request is made, since no mock
+        # is required to run the test successfully
+        creds = fake_credentials.FakeKeystoneV2Credentials()
+        uri = 'fake_uri'
+        _manager = clients.ServiceClients(creds, identity_uri=uri)
+        self.assertIsInstance(_manager.auth_provider,
+                              auth.KeystoneV2AuthProvider)
+
+    def test___init___creds_v3_uri(self):
+        # Verify that no API request is made, since no mock
+        # is required to run the test successfully
+        creds = fake_credentials.FakeKeystoneV3Credentials()
+        uri = 'fake_uri'
+        _manager = clients.ServiceClients(creds, identity_uri=uri)
+        self.assertIsInstance(_manager.auth_provider,
+                              auth.KeystoneV3AuthProvider)
+
+    def test___init___base_creds_uri(self):
+        creds = fake_credentials.FakeCredentials()
+        uri = 'fake_uri'
+        with testtools.ExpectedException(exceptions.InvalidCredentials):
+            clients.ServiceClients(creds, identity_uri=uri)
+
+    def test___init___invalid_creds_uri(self):
+        creds = fake_credentials.FakeKeystoneV2Credentials()
+        delattr(creds, 'username')
+        uri = 'fake_uri'
+        with testtools.ExpectedException(exceptions.InvalidCredentials):
+            clients.ServiceClients(creds, identity_uri=uri)
+
+    def test___init___creds_uri_none(self):
+        creds = fake_credentials.FakeKeystoneV2Credentials()
+        msg = ("Invalid Credentials\nDetails: ServiceClients requires a "
+               "non-empty")
+        with testtools.ExpectedException(exceptions.InvalidCredentials,
+                                         value_re=msg):
+            clients.ServiceClients(creds, None)
+
+    def test___init___creds_uri_params(self):
+        creds = fake_credentials.FakeKeystoneV2Credentials()
+        expeted_params = {'fake_param1': 'fake_value1',
+                          'fake_param2': 'fake_value2'}
+        params = {'fake_service1': expeted_params}
+        uri = 'fake_uri'
+        _manager = clients.ServiceClients(creds, identity_uri=uri,
+                                          client_parameters=params)
+        self.assertIn('fake_service1', _manager.parameters)
+        for _key in expeted_params:
+            self.assertIn(_key, _manager.parameters['fake_service1'].keys())
+            self.assertEqual(expeted_params[_key],
+                             _manager.parameters['fake_service1'].get(_key))
+
+    def test___init___creds_uri_params_unknown_services(self):
+        creds = fake_credentials.FakeKeystoneV2Credentials()
+        fake_params = {'fake_param1': 'fake_value1'}
+        params = {'unknown_service1': fake_params,
+                  'unknown_service2': fake_params}
+        uri = 'fake_uri'
+        msg = "(?=.*{0})(?=.*{1})".format(*list(params.keys()))
+        with testtools.ExpectedException(
+                exceptions.UnknownServiceClient, value_re=msg):
+            clients.ServiceClients(creds, identity_uri=uri,
+                                   client_parameters=params)
+
+    def _get_manager(self, init_region='fake_region'):
+        # Get a manager to invoke _setup_parameters on
+        creds = fake_credentials.FakeKeystoneV2Credentials()
+        return clients.ServiceClients(creds, identity_uri='fake_uri',
+                                      region=init_region)
+
+    def test__setup_parameters_none_no_region(self):
+        kwargs = {}
+        _manager = self._get_manager(init_region=None)
+        _params = _manager._setup_parameters(kwargs)
+        self.assertNotIn('region', _params)
+
+    def test__setup_parameters_none(self):
+        kwargs = {}
+        _manager = self._get_manager()
+        _params = _manager._setup_parameters(kwargs)
+        self.assertIn('region', _params)
+        self.assertEqual('fake_region', _params['region'])
+
+    def test__setup_parameters_all(self):
+        expected_params = {'region': 'fake_region1',
+                           'catalog_type': 'fake_service2_mod',
+                           'fake_param1': 'fake_value1',
+                           'fake_param2': 'fake_value2'}
+        _manager = self._get_manager()
+        _params = _manager._setup_parameters(expected_params)
+        for _key in _params.keys():
+            self.assertEqual(expected_params[_key],
+                             _params[_key])
+
+    def test_register_service_client_module(self):
+        expected_params = {'fake_param1': 'fake_value1',
+                           'fake_param2': 'fake_value2'}
+        _manager = self._get_manager(init_region='fake_region_default')
+        # Mock after the _manager is setup to preserve the call count
+        factory_mock = self.useFixture(fixtures.MockPatch(
+            'tempest.lib.services.clients.ClientsFactory')).mock
+        _manager.register_service_client_module(
+            name='fake_module',
+            service_version='fake_service',
+            module_path='fake.path.to.module',
+            client_names=[],
+            **expected_params)
+        self.assertThat(_manager, has_attribute('fake_module'))
+        # Assert called once, without check for exact parameters
+        self.assertTrue(factory_mock.called)
+        self.assertEqual(1, factory_mock.call_count)
+        # Assert expected params are in with their values
+        actual_kwargs = factory_mock.call_args[1]
+        self.assertIn('region', actual_kwargs)
+        self.assertEqual('fake_region_default', actual_kwargs['region'])
+        for param in expected_params:
+            self.assertIn(param, actual_kwargs)
+            self.assertEqual(expected_params[param], actual_kwargs[param])
+        # Assert the new service is registered
+        self.assertIn('fake_service', _manager._registered_services)
+
+    def test_register_service_client_module_override_default(self):
+        new_region = 'new_region'
+        expected_params = {'fake_param1': 'fake_value1',
+                           'fake_param2': 'fake_value2',
+                           'region': new_region}
+        _manager = self._get_manager(init_region='fake_region_default')
+        # Mock after the _manager is setup to preserve the call count
+        factory_mock = self.useFixture(fixtures.MockPatch(
+            'tempest.lib.services.clients.ClientsFactory')).mock
+        _manager.register_service_client_module(
+            name='fake_module',
+            service_version='fake_service',
+            module_path='fake.path.to.module',
+            client_names=[],
+            **expected_params)
+        self.assertThat(_manager, has_attribute('fake_module'))
+        # Assert called once, without check for exact parameters
+        self.assertTrue(factory_mock.called)
+        self.assertEqual(1, factory_mock.call_count)
+        # Assert expected params are in with their values
+        actual_kwargs = factory_mock.call_args[1]
+        self.assertIn('region', actual_kwargs)
+        self.assertEqual(new_region, actual_kwargs['region'])
+        for param in expected_params:
+            self.assertIn(param, actual_kwargs)
+            self.assertEqual(expected_params[param], actual_kwargs[param])
+        # Assert the new service is registered
+        self.assertIn('fake_service', _manager._registered_services)
+
+    def test_register_service_client_module_duplicate_name(self):
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.services.clients.ClientsFactory')).mock
+        _manager = self._get_manager()
+        name_owner = 'this_is_a_string'
+        setattr(_manager, 'fake_module', name_owner)
+        expected_error = '.*' + name_owner
+        with testtools.ExpectedException(
+                exceptions.ServiceClientRegistrationException, expected_error):
+            _manager.register_service_client_module(
+                name='fake_module', module_path='fake.path.to.module',
+                service_version='fake_service', client_names=[])
+
+    def test_register_service_client_module_duplicate_service(self):
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.services.clients.ClientsFactory')).mock
+        _manager = self._get_manager()
+        duplicate_service = 'fake_service1'
+        expected_error = '.*' + duplicate_service
+        with testtools.ExpectedException(
+                exceptions.ServiceClientRegistrationException, expected_error):
+            _manager.register_service_client_module(
+                name='fake_module', module_path='fake.path.to.module',
+                service_version=duplicate_service, client_names=[])
diff --git a/tempest/services/volume/__init__.py b/tempest/tests/lib/services/volume/__init__.py
similarity index 100%
rename from tempest/services/volume/__init__.py
rename to tempest/tests/lib/services/volume/__init__.py
diff --git a/tempest/services/volume/v1/__init__.py b/tempest/tests/lib/services/volume/v1/__init__.py
similarity index 100%
rename from tempest/services/volume/v1/__init__.py
rename to tempest/tests/lib/services/volume/v1/__init__.py
diff --git a/tempest/tests/lib/services/volume/v1/test_encryption_types_client.py b/tempest/tests/lib/services/volume/v1/test_encryption_types_client.py
new file mode 100644
index 0000000..585904e
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v1/test_encryption_types_client.py
@@ -0,0 +1,86 @@
+# 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.lib.services.volume.v1 import encryption_types_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestEncryptionTypesClient(base.BaseServiceTest):
+    FAKE_CREATE_ENCRYPTION_TYPE = {
+        "encryption": {
+            "id": "cbc36478b0bd8e67e89",
+            "name": "FakeEncryptionType",
+            "type": "fakeType",
+            "provider": "LuksEncryptor",
+            "cipher": "aes-xts-plain64",
+            "key_size": "512",
+            "control_location": "front-end"
+        }
+    }
+
+    FAKE_INFO_ENCRYPTION_TYPE = {
+        "encryption": {
+            "name": "FakeEncryptionType",
+            "type": "fakeType",
+            "description": "test_description",
+            "volume_type": "fakeType",
+            "provider": "LuksEncryptor",
+            "cipher": "aes-xts-plain64",
+            "key_size": "512",
+            "control_location": "front-end"
+        }
+    }
+
+    def setUp(self):
+        super(TestEncryptionTypesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = encryption_types_client.EncryptionTypesClient(fake_auth,
+                                                                    'volume',
+                                                                    'regionOne'
+                                                                    )
+
+    def _test_create_encryption(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_encryption_type,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_ENCRYPTION_TYPE,
+            bytes_body, volume_type_id="cbc36478b0bd8e67e89")
+
+    def _test_show_encryption_type(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_encryption_type,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_INFO_ENCRYPTION_TYPE,
+            bytes_body, volume_type_id="cbc36478b0bd8e67e89")
+
+    def test_create_encryption_type_with_str_body(self):
+        self._test_create_encryption()
+
+    def test_create_encryption_type_with_bytes_body(self):
+        self._test_create_encryption(bytes_body=True)
+
+    def test_show_encryption_type_with_str_body(self):
+        self._test_show_encryption_type()
+
+    def test_show_encryption_type_with_bytes_body(self):
+        self._test_show_encryption_type(bytes_body=True)
+
+    def test_delete_encryption_type(self):
+        self.check_service_client_function(
+            self.client.delete_encryption_type,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            volume_type_id="cbc36478b0bd8e67e89",
+            status=202)
diff --git a/tempest/tests/lib/services/volume/v1/test_quotas_client.py b/tempest/tests/lib/services/volume/v1/test_quotas_client.py
new file mode 100644
index 0000000..f9e76af
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v1/test_quotas_client.py
@@ -0,0 +1,96 @@
+# 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.lib.services.volume.v1 import quotas_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQuotasClient(base.BaseServiceTest):
+    FAKE_QUOTAS = {
+        "quota_set": {
+            "cores": 20,
+            "fixed_ips": -1,
+            "floating_ips": 10,
+            "id": "fake_tenant",
+            "injected_file_content_bytes": 10240,
+            "injected_file_path_bytes": 255,
+            "injected_files": 5,
+            "instances": 10,
+            "key_pairs": 100,
+            "metadata_items": 128,
+            "ram": 51200,
+            "security_group_rules": 20,
+            "security_groups": 10
+        }
+    }
+
+    FAKE_UPDATE_QUOTAS_REQUEST = {
+        "quota_set": {
+            "security_groups": 45
+        }
+    }
+
+    def setUp(self):
+        super(TestQuotasClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = quotas_client.QuotasClient(fake_auth,
+                                                 'volume',
+                                                 'regionOne')
+
+    def _test_show_default_quota_set(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_default_quota_set,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_QUOTAS,
+            bytes_body, tenant_id="fake_tenant")
+
+    def _test_show_quota_set(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_quota_set,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_QUOTAS,
+            bytes_body, tenant_id="fake_tenant")
+
+    def _test_update_quota_set(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_quota_set,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_QUOTAS_REQUEST,
+            bytes_body, tenant_id="fake_tenant")
+
+    def test_show_default_quota_set_with_str_body(self):
+        self._test_show_default_quota_set()
+
+    def test_show_default_quota_set_with_bytes_body(self):
+        self._test_show_default_quota_set(bytes_body=True)
+
+    def test_show_quota_set_with_str_body(self):
+        self._test_show_quota_set()
+
+    def test_show_quota_set_with_bytes_body(self):
+        self._test_show_quota_set(bytes_body=True)
+
+    def test_update_quota_set_with_str_body(self):
+        self._test_update_quota_set()
+
+    def test_update_quota_set_with_bytes_body(self):
+        self._test_update_quota_set(bytes_body=True)
+
+    def test_delete_quota_set(self):
+        self.check_service_client_function(
+            self.client.delete_quota_set,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            tenant_id="fake_tenant")
diff --git a/tempest/tests/lib/services/volume/v1/test_snapshots_client.py b/tempest/tests/lib/services/volume/v1/test_snapshots_client.py
new file mode 100644
index 0000000..49191e3
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v1/test_snapshots_client.py
@@ -0,0 +1,200 @@
+# 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.lib.services.volume.v1 import snapshots_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSnapshotsClient(base.BaseServiceTest):
+    FAKE_CREATE_SNAPSHOT = {
+        "snapshot": {
+            "display_name": "snap-001",
+            "display_description": "Daily backup",
+            "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+            "force": True
+        }
+    }
+
+    FAKE_UPDATE_SNAPSHOT_REQUEST = {
+        "metadata": {
+            "key": "v1"
+        }
+    }
+
+    FAKE_INFO_SNAPSHOT = {
+        "snapshot": {
+            "id": "3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+            "display_name": "snap-001",
+            "display_description": "Daily backup",
+            "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+            "status": "available",
+            "size": 30,
+            "created_at": "2012-02-29T03:50:07Z"
+        }
+    }
+
+    FAKE_LIST_SNAPSHOTS = {
+        "snapshots": [
+            {
+                "id": "3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+                "display_name": "snap-001",
+                "display_description": "Daily backup",
+                "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+                "status": "available",
+                "size": 30,
+                "created_at": "2012-02-29T03:50:07Z",
+                "metadata": {
+                    "contents": "junk"
+                }
+            },
+            {
+                "id": "e479997c-650b-40a4-9dfe-77655818b0d2",
+                "display_name": "snap-002",
+                "display_description": "Weekly backup",
+                "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358",
+                "status": "available",
+                "size": 25,
+                "created_at": "2012-03-19T01:52:47Z",
+                "metadata": {}
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestSnapshotsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = snapshots_client.SnapshotsClient(fake_auth,
+                                                       'volume',
+                                                       'regionOne')
+
+    def _test_create_snapshot(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_snapshot,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_SNAPSHOT,
+            bytes_body)
+
+    def _test_show_snapshot(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_snapshot,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_INFO_SNAPSHOT,
+            bytes_body,
+            snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+    def _test_list_snapshots(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_snapshots,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_SNAPSHOTS,
+            bytes_body,
+            detail=True)
+
+    def _test_create_snapshot_metadata(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_snapshot_metadata,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_INFO_SNAPSHOT,
+            bytes_body,
+            snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+            metadata={"key": "v1"})
+
+    def _test_update_snapshot(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_snapshot,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_SNAPSHOT_REQUEST,
+            bytes_body,
+            snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+    def _test_show_snapshot_metadata(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_snapshot_metadata,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_UPDATE_SNAPSHOT_REQUEST,
+            bytes_body,
+            snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+    def _test_update_snapshot_metadata(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_snapshot_metadata,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_SNAPSHOT_REQUEST,
+            bytes_body, snapshot_id="cbc36478b0bd8e67e89")
+
+    def _test_update_snapshot_metadata_item(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_snapshot_metadata_item,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_INFO_SNAPSHOT,
+            bytes_body, volume_type_id="cbc36478b0bd8e67e89")
+
+    def test_create_snapshot_with_str_body(self):
+        self._test_create_snapshot()
+
+    def test_create_snapshot_with_bytes_body(self):
+        self._test_create_snapshot(bytes_body=True)
+
+    def test_show_snapshot_with_str_body(self):
+        self._test_show_snapshot()
+
+    def test_show_snapshot_with_bytes_body(self):
+        self._test_show_snapshot(bytes_body=True)
+
+    def test_list_snapshots_with_str_body(self):
+        self._test_list_snapshots()
+
+    def test_list_snapshots_with_bytes_body(self):
+        self._test_list_snapshots(bytes_body=True)
+
+    def test_create_snapshot_metadata_with_str_body(self):
+        self._test_create_snapshot_metadata()
+
+    def test_create_snapshot_metadata_with_bytes_body(self):
+        self._test_create_snapshot_metadata(bytes_body=True)
+
+    def test_update_snapshot_with_str_body(self):
+        self._test_update_snapshot()
+
+    def test_update_snapshot_with_bytes_body(self):
+        self._test_update_snapshot(bytes_body=True)
+
+    def test_show_snapshot_metadata_with_str_body(self):
+        self._test_show_snapshot_metadata()
+
+    def test_show_snapshot_metadata_with_bytes_body(self):
+        self._test_show_snapshot_metadata(bytes_body=True)
+
+    def test_update_snapshot_metadata_with_str_body(self):
+        self._test_update_snapshot_metadata()
+
+    def test_update_snapshot_metadata_with_bytes_body(self):
+        self._test_update_snapshot_metadata(bytes_body=True)
+
+    def test_force_delete_snapshot(self):
+        self.check_service_client_function(
+            self.client.force_delete_snapshot,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            snapshot_id="521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+            status=202)
+
+    def test_delete_snapshot(self):
+        self.check_service_client_function(
+            self.client.delete_snapshot,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            snapshot_id="521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+            status=202)
diff --git a/tempest/services/volume/v2/__init__.py b/tempest/tests/lib/services/volume/v2/__init__.py
similarity index 100%
rename from tempest/services/volume/v2/__init__.py
rename to tempest/tests/lib/services/volume/v2/__init__.py
diff --git a/tempest/tests/lib/services/volume/v2/test_encryption_types_client.py b/tempest/tests/lib/services/volume/v2/test_encryption_types_client.py
new file mode 100644
index 0000000..d029091
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_encryption_types_client.py
@@ -0,0 +1,86 @@
+# 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.lib.services.volume.v2 import encryption_types_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestEncryptionTypesClient(base.BaseServiceTest):
+    FAKE_CREATE_ENCRYPTION_TYPE = {
+        "encryption": {
+            "id": "cbc36478b0bd8e67e89",
+            "name": "FakeEncryptionType",
+            "type": "fakeType",
+            "provider": "LuksEncryptor",
+            "cipher": "aes-xts-plain64",
+            "key_size": "512",
+            "control_location": "front-end"
+        }
+    }
+
+    FAKE_INFO_ENCRYPTION_TYPE = {
+        "encryption": {
+            "name": "FakeEncryptionType",
+            "type": "fakeType",
+            "description": "test_description",
+            "volume_type": "fakeType",
+            "provider": "LuksEncryptor",
+            "cipher": "aes-xts-plain64",
+            "key_size": "512",
+            "control_location": "front-end"
+        }
+    }
+
+    def setUp(self):
+        super(TestEncryptionTypesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = encryption_types_client.EncryptionTypesClient(fake_auth,
+                                                                    'volume',
+                                                                    'regionOne'
+                                                                    )
+
+    def _test_create_encryption(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_encryption_type,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_ENCRYPTION_TYPE,
+            bytes_body, volume_type_id="cbc36478b0bd8e67e89")
+
+    def _test_show_encryption_type(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_encryption_type,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_INFO_ENCRYPTION_TYPE,
+            bytes_body, volume_type_id="cbc36478b0bd8e67e89")
+
+    def test_create_encryption_type_with_str_body(self):
+        self._test_create_encryption()
+
+    def test_create_encryption_type_with_bytes_body(self):
+        self._test_create_encryption(bytes_body=True)
+
+    def test_show_encryption_type_with_str_body(self):
+        self._test_show_encryption_type()
+
+    def test_show_encryption_type_with_bytes_body(self):
+        self._test_show_encryption_type(bytes_body=True)
+
+    def test_delete_encryption_type(self):
+        self.check_service_client_function(
+            self.client.delete_encryption_type,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            volume_type_id="cbc36478b0bd8e67e89",
+            status=202)
diff --git a/tempest/tests/lib/services/volume/v2/test_quotas_client.py b/tempest/tests/lib/services/volume/v2/test_quotas_client.py
new file mode 100644
index 0000000..6384350
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_quotas_client.py
@@ -0,0 +1,86 @@
+# 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.lib.services.volume.v2 import quotas_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQuotasClient(base.BaseServiceTest):
+    FAKE_QUOTAS = {
+        "quota_set": {
+            "gigabytes": 5,
+            "snapshots": 10,
+            "volumes": 20
+        }
+    }
+
+    FAKE_UPDATE_QUOTAS_REQUEST = {
+        "quota_set": {
+            "security_groups": 45
+        }
+    }
+
+    def setUp(self):
+        super(TestQuotasClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = quotas_client.QuotasClient(fake_auth,
+                                                 'volume',
+                                                 'regionOne')
+
+    def _test_show_default_quota_set(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_default_quota_set,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_QUOTAS,
+            bytes_body, tenant_id="fake_tenant")
+
+    def _test_show_quota_set(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_quota_set,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_QUOTAS,
+            bytes_body, tenant_id="fake_tenant")
+
+    def _test_update_quota_set(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_quota_set,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_QUOTAS_REQUEST,
+            bytes_body, tenant_id="fake_tenant")
+
+    def test_show_default_quota_set_with_str_body(self):
+        self._test_show_default_quota_set()
+
+    def test_show_default_quota_set_with_bytes_body(self):
+        self._test_show_default_quota_set(bytes_body=True)
+
+    def test_show_quota_set_with_str_body(self):
+        self._test_show_quota_set()
+
+    def test_show_quota_set_with_bytes_body(self):
+        self._test_show_quota_set(bytes_body=True)
+
+    def test_update_quota_set_with_str_body(self):
+        self._test_update_quota_set()
+
+    def test_update_quota_set_with_bytes_body(self):
+        self._test_update_quota_set(bytes_body=True)
+
+    def test_delete_quota_set(self):
+        self.check_service_client_function(
+            self.client.delete_quota_set,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            tenant_id="fake_tenant")
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshots_client.py b/tempest/tests/lib/services/volume/v2/test_snapshots_client.py
new file mode 100644
index 0000000..7d656f1
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_snapshots_client.py
@@ -0,0 +1,201 @@
+# 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.lib.services.volume.v2 import snapshots_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSnapshotsClient(base.BaseServiceTest):
+    FAKE_CREATE_SNAPSHOT = {
+        "snapshot": {
+            "display_name": "snap-001",
+            "display_description": "Daily backup",
+            "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+            "force": True
+        }
+    }
+
+    FAKE_UPDATE_SNAPSHOT_REQUEST = {
+        "metadata": {
+            "key": "v1"
+        }
+    }
+
+    FAKE_INFO_SNAPSHOT = {
+        "snapshot": {
+            "id": "3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+            "display_name": "snap-001",
+            "display_description": "Daily backup",
+            "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+            "status": "available",
+            "size": 30,
+            "created_at": "2012-02-29T03:50:07Z"
+        }
+    }
+
+    FAKE_LIST_SNAPSHOTS = {
+        "snapshots": [
+            {
+                "id": "3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+                "display_name": "snap-001",
+                "display_description": "Daily backup",
+                "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+                "status": "available",
+                "size": 30,
+                "created_at": "2012-02-29T03:50:07Z",
+                "metadata": {
+                    "contents": "junk"
+                }
+            },
+            {
+                "id": "e479997c-650b-40a4-9dfe-77655818b0d2",
+                "display_name": "snap-002",
+                "display_description": "Weekly backup",
+                "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358",
+                "status": "available",
+                "size": 25,
+                "created_at": "2012-03-19T01:52:47Z",
+                "metadata": {}
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestSnapshotsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = snapshots_client.SnapshotsClient(fake_auth,
+                                                       'volume',
+                                                       'regionOne')
+
+    def _test_create_snapshot(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_snapshot,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_SNAPSHOT,
+            bytes_body,
+            status=202)
+
+    def _test_show_snapshot(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_snapshot,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_INFO_SNAPSHOT,
+            bytes_body,
+            snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+    def _test_list_snapshots(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_snapshots,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_SNAPSHOTS,
+            bytes_body,
+            detail=True)
+
+    def _test_create_snapshot_metadata(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_snapshot_metadata,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_INFO_SNAPSHOT,
+            bytes_body,
+            snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+            metadata={"key": "v1"})
+
+    def _test_update_snapshot(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_snapshot,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_SNAPSHOT_REQUEST,
+            bytes_body,
+            snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+    def _test_show_snapshot_metadata(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_snapshot_metadata,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_UPDATE_SNAPSHOT_REQUEST,
+            bytes_body,
+            snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+    def _test_update_snapshot_metadata(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_snapshot_metadata,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_SNAPSHOT_REQUEST,
+            bytes_body, snapshot_id="cbc36478b0bd8e67e89")
+
+    def _test_update_snapshot_metadata_item(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_snapshot_metadata_item,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_INFO_SNAPSHOT,
+            bytes_body, volume_type_id="cbc36478b0bd8e67e89")
+
+    def test_create_snapshot_with_str_body(self):
+        self._test_create_snapshot()
+
+    def test_create_snapshot_with_bytes_body(self):
+        self._test_create_snapshot(bytes_body=True)
+
+    def test_show_snapshot_with_str_body(self):
+        self._test_show_snapshot()
+
+    def test_show_snapshot_with_bytes_body(self):
+        self._test_show_snapshot(bytes_body=True)
+
+    def test_list_snapshots_with_str_body(self):
+        self._test_list_snapshots()
+
+    def test_list_snapshots_with_bytes_body(self):
+        self._test_list_snapshots(bytes_body=True)
+
+    def test_create_snapshot_metadata_with_str_body(self):
+        self._test_create_snapshot_metadata()
+
+    def test_create_snapshot_metadata_with_bytes_body(self):
+        self._test_create_snapshot_metadata(bytes_body=True)
+
+    def test_update_snapshot_with_str_body(self):
+        self._test_update_snapshot()
+
+    def test_update_snapshot_with_bytes_body(self):
+        self._test_update_snapshot(bytes_body=True)
+
+    def test_show_snapshot_metadata_with_str_body(self):
+        self._test_show_snapshot_metadata()
+
+    def test_show_snapshot_metadata_with_bytes_body(self):
+        self._test_show_snapshot_metadata(bytes_body=True)
+
+    def test_update_snapshot_metadata_with_str_body(self):
+        self._test_update_snapshot_metadata()
+
+    def test_update_snapshot_metadata_with_bytes_body(self):
+        self._test_update_snapshot_metadata(bytes_body=True)
+
+    def test_force_delete_snapshot(self):
+        self.check_service_client_function(
+            self.client.force_delete_snapshot,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            snapshot_id="521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+            status=202)
+
+    def test_delete_snapshot(self):
+        self.check_service_client_function(
+            self.client.delete_snapshot,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            snapshot_id="521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+            status=202)
diff --git a/tempest/tests/negative/__init__.py b/tempest/tests/lib/services/volume/v3/__init__.py
similarity index 100%
copy from tempest/tests/negative/__init__.py
copy to tempest/tests/lib/services/volume/v3/__init__.py
diff --git a/tempest/tests/lib/services/volume/v3/test_user_messages_client.py b/tempest/tests/lib/services/volume/v3/test_user_messages_client.py
new file mode 100644
index 0000000..4aeed5f
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_user_messages_client.py
@@ -0,0 +1,92 @@
+# Copyright 2016 Red Hat.  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.services.volume.v3 import messages_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestUserMessagesClient(base.BaseServiceTest):
+    USER_MESSAGE_INFO = {
+        "created_at": "2016-11-21T06:16:34.000000",
+        "guaranteed_until": "2016-12-21T06:16:34.000000",
+        "user_message": "No storage could be allocated for this volume "
+                        "request. You may be able to try another size or"
+                        " volume type.",
+        "resource_uuid": "c570b406-bf0b-4067-9398-f0bb09a7d9d7",
+        "request_id": "req-8f68681e-9b6b-4009-b94c-ac0811595451",
+        "message_level": "ERROR",
+        "id": "9a7dafbd-a156-4540-8996-50e71b5dcadf",
+        "resource_type": "VOLUME",
+        "links": [
+            {"href": "http://192.168.100.230:8776/v3/"
+                     "a678cb65f701462ea2257245cd640829/messages/"
+                     "9a7dafbd-a156-4540-8996-50e71b5dcadf",
+             "rel": "self"},
+            {"href": "http://192.168.100.230:8776/"
+                     "a678cb65f701462ea2257245cd640829/messages/"
+                     "9a7dafbd-a156-4540-8996-50e71b5dcadf",
+             "rel": "bookmark"}]
+        }
+    FAKE_SHOW_USER_MESSAGE = {
+        "message": dict(event_id="000002", **USER_MESSAGE_INFO)}
+
+    FAKE_LIST_USER_MESSAGES = {
+        "messages": [
+            dict(event_id="000003", **USER_MESSAGE_INFO),
+            dict(event_id="000004", **USER_MESSAGE_INFO)
+        ]
+    }
+
+    def setUp(self):
+        super(TestUserMessagesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = messages_client.MessagesClient(fake_auth,
+                                                     'volume',
+                                                     'regionOne')
+
+    def _test_show_user_message(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_message,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SHOW_USER_MESSAGE,
+            bytes_body,
+            message_id="9a7dafbd-a156-4540-8996-50e71b5dcadf")
+
+    def _test_list_user_message(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_messages,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_USER_MESSAGES,
+            bytes_body)
+
+    def test_list_user_message_with_str_body(self):
+        self._test_list_user_message()
+
+    def test_list_user_message_with_bytes_body(self):
+        self._test_list_user_message(bytes_body=True)
+
+    def test_show_user_message_with_str_body(self):
+        self._test_show_user_message()
+
+    def test_show_user_message_with_bytes_body(self):
+        self._test_show_user_message(bytes_body=True)
+
+    def test_delete_user_message(self):
+        self.check_service_client_function(
+            self.client.delete_message,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            message_id="9a7dafbd-a156-4540-8996-50e71b5dcadf",
+            status=204)
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
new file mode 100644
index 0000000..2f975d2
--- /dev/null
+++ b/tempest/tests/lib/test_auth.py
@@ -0,0 +1,915 @@
+# 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 copy
+import datetime
+import testtools
+
+from oslotest import mockpatch
+
+from tempest.lib import auth
+from tempest.lib import exceptions
+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.lib import fake_credentials
+from tempest.tests.lib import fake_identity
+
+
+def fake_get_credentials(fill_in=True, identity_version='v2', **kwargs):
+    return fake_credentials.FakeCredentials()
+
+
+class BaseAuthTestsSetUp(base.TestCase):
+    _auth_provider_class = None
+    credentials = fake_credentials.FakeCredentials()
+
+    def _auth(self, credentials, auth_url, **params):
+        """returns auth method according to keystone"""
+        return self._auth_provider_class(credentials, auth_url, **params)
+
+    def setUp(self):
+        super(BaseAuthTestsSetUp, self).setUp()
+        self.patchobject(auth, 'get_credentials', fake_get_credentials)
+        self.auth_provider = self._auth(self.credentials,
+                                        fake_identity.FAKE_AUTH_URL)
+
+
+class TestBaseAuthProvider(BaseAuthTestsSetUp):
+    """Tests for base AuthProvider
+
+    This tests auth.AuthProvider class which is base for the other so we
+    obviously don't test not implemented method or the ones which strongly
+    depends on them.
+    """
+
+    class FakeAuthProviderImpl(auth.AuthProvider):
+        def _decorate_request(self):
+            pass
+
+        def _fill_credentials(self):
+            pass
+
+        def _get_auth(self):
+            pass
+
+        def base_url(self):
+            pass
+
+        def is_expired(self):
+            pass
+
+    _auth_provider_class = FakeAuthProviderImpl
+
+    def _auth(self, credentials, auth_url, **params):
+        """returns auth method according to keystone"""
+        return self._auth_provider_class(credentials, **params)
+
+    def test_check_credentials_bad_type(self):
+        self.assertFalse(self.auth_provider.check_credentials([]))
+
+    def test_auth_data_property_when_cache_exists(self):
+        self.auth_provider.cache = 'foo'
+        self.useFixture(mockpatch.PatchObject(self.auth_provider,
+                                              'is_expired',
+                                              return_value=False))
+        self.assertEqual('foo', getattr(self.auth_provider, 'auth_data'))
+
+    def test_delete_auth_data_property_through_deleter(self):
+        self.auth_provider.cache = 'foo'
+        del self.auth_provider.auth_data
+        self.assertIsNone(self.auth_provider.cache)
+
+    def test_delete_auth_data_property_through_clear_auth(self):
+        self.auth_provider.cache = 'foo'
+        self.auth_provider.clear_auth()
+        self.assertIsNone(self.auth_provider.cache)
+
+    def test_set_and_reset_alt_auth_data(self):
+        self.auth_provider.set_alt_auth_data('foo', 'bar')
+        self.assertEqual(self.auth_provider.alt_part, 'foo')
+        self.assertEqual(self.auth_provider.alt_auth_data, 'bar')
+
+        self.auth_provider.reset_alt_auth_data()
+        self.assertIsNone(self.auth_provider.alt_part)
+        self.assertIsNone(self.auth_provider.alt_auth_data)
+
+    def test_auth_class(self):
+        self.assertRaises(TypeError,
+                          auth.AuthProvider,
+                          fake_credentials.FakeCredentials)
+
+
+class TestKeystoneV2AuthProvider(BaseAuthTestsSetUp):
+    _endpoints = fake_identity.IDENTITY_V2_RESPONSE['access']['serviceCatalog']
+    _auth_provider_class = auth.KeystoneV2AuthProvider
+    credentials = fake_credentials.FakeKeystoneV2Credentials()
+
+    def setUp(self):
+        super(TestKeystoneV2AuthProvider, self).setUp()
+        self.patchobject(v2_client.TokenClient, 'raw_request',
+                         fake_identity._fake_v2_response)
+        self.target_url = 'test_api'
+
+    def _get_fake_identity(self):
+        return fake_identity.IDENTITY_V2_RESPONSE['access']
+
+    def _get_fake_alt_identity(self):
+        return fake_identity.ALT_IDENTITY_V2_RESPONSE['access']
+
+    def _get_result_url_from_endpoint(self, ep, endpoint_type='publicURL',
+                                      replacement=None):
+        if replacement:
+            return ep[endpoint_type].replace('v2', replacement)
+        return ep[endpoint_type]
+
+    def _get_token_from_fake_identity(self):
+        return fake_identity.TOKEN
+
+    def _get_from_fake_identity(self, attr):
+        access = fake_identity.IDENTITY_V2_RESPONSE['access']
+        if attr == 'user_id':
+            return access['user']['id']
+        elif attr == 'tenant_id':
+            return access['token']['tenant']['id']
+
+    def _test_request_helper(self, filters, expected):
+        url, headers, body = self.auth_provider.auth_request('GET',
+                                                             self.target_url,
+                                                             filters=filters)
+
+        self.assertEqual(expected['url'], url)
+        self.assertEqual(expected['token'], headers['X-Auth-Token'])
+        self.assertEqual(expected['body'], body)
+
+    def _auth_data_with_expiry(self, date_as_string):
+        token, access = self.auth_provider.auth_data
+        access['token']['expires'] = date_as_string
+        return token, access
+
+    def test_request(self):
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion'
+        }
+
+        url = self._get_result_url_from_endpoint(
+            self._endpoints[0]['endpoints'][1]) + '/' + self.target_url
+
+        expected = {
+            'body': None,
+            'url': url,
+            'token': self._get_token_from_fake_identity(),
+        }
+        self._test_request_helper(filters, expected)
+
+    def test_request_with_alt_auth_cleans_alt(self):
+        """Test alternate auth data for headers
+
+        Assert that when the alt data is provided for headers, after an
+        auth_request the data alt_data is cleaned-up.
+        """
+        self.auth_provider.set_alt_auth_data(
+            'headers',
+            (fake_identity.ALT_TOKEN, self._get_fake_alt_identity()))
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'fakeRegion'
+        }
+        self.auth_provider.auth_request('GET', self.target_url,
+                                        filters=filters)
+
+        # Assert alt auth data is clear after it
+        self.assertIsNone(self.auth_provider.alt_part)
+        self.assertIsNone(self.auth_provider.alt_auth_data)
+
+    def _test_request_with_identical_alt_auth(self, part):
+        """Test alternate but identical auth data for headers
+
+        Assert that when the alt data is provided, but it's actually
+        identical, an exception is raised.
+        """
+        self.auth_provider.set_alt_auth_data(
+            part,
+            (fake_identity.TOKEN, self._get_fake_identity()))
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'fakeRegion'
+        }
+
+        self.assertRaises(exceptions.BadAltAuth,
+                          self.auth_provider.auth_request,
+                          'GET', self.target_url, filters=filters)
+
+    def test_request_with_identical_alt_auth_headers(self):
+        self._test_request_with_identical_alt_auth('headers')
+
+    def test_request_with_identical_alt_auth_url(self):
+        self._test_request_with_identical_alt_auth('url')
+
+    def test_request_with_identical_alt_auth_body(self):
+        self._test_request_with_identical_alt_auth('body')
+
+    def test_request_with_alt_part_without_alt_data(self):
+        """Test empty alternate auth data
+
+        Assert that when alt_part is defined, the corresponding original
+        request element is kept the same.
+        """
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'fakeRegion'
+        }
+        self.auth_provider.set_alt_auth_data('headers', None)
+
+        url, headers, body = self.auth_provider.auth_request('GET',
+                                                             self.target_url,
+                                                             filters=filters)
+        # The original headers where empty
+        self.assertNotEqual(url, self.target_url)
+        self.assertIsNone(headers)
+        self.assertIsNone(body)
+
+    def _test_request_with_alt_part_without_alt_data_no_change(self, body):
+        """Test empty alternate auth data with no effect
+
+        Assert that when alt_part is defined, no auth_data is provided,
+        and the corresponding original request element was not going to
+        be changed anyways, and exception is raised
+        """
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'fakeRegion'
+        }
+        self.auth_provider.set_alt_auth_data('body', None)
+
+        self.assertRaises(exceptions.BadAltAuth,
+                          self.auth_provider.auth_request,
+                          'GET', self.target_url, filters=filters)
+
+    def test_request_with_alt_part_without_alt_data_no_change_headers(self):
+        self._test_request_with_alt_part_without_alt_data_no_change('headers')
+
+    def test_request_with_alt_part_without_alt_data_no_change_url(self):
+        self._test_request_with_alt_part_without_alt_data_no_change('url')
+
+    def test_request_with_alt_part_without_alt_data_no_change_body(self):
+        self._test_request_with_alt_part_without_alt_data_no_change('body')
+
+    def test_request_with_bad_service(self):
+        filters = {
+            'service': 'BAD_SERVICE',
+            'endpoint_type': 'publicURL',
+            'region': 'fakeRegion'
+        }
+        self.assertRaises(exceptions.EndpointNotFound,
+                          self.auth_provider.auth_request, 'GET',
+                          self.target_url, filters=filters)
+
+    def test_request_without_service(self):
+        filters = {
+            'service': None,
+            'endpoint_type': 'publicURL',
+            'region': 'fakeRegion'
+        }
+        self.assertRaises(exceptions.EndpointNotFound,
+                          self.auth_provider.auth_request, 'GET',
+                          self.target_url, filters=filters)
+
+    def test_check_credentials_missing_attribute(self):
+        for attr in ['username', 'password']:
+            cred = copy.copy(self.credentials)
+            del cred[attr]
+            self.assertFalse(self.auth_provider.check_credentials(cred))
+
+    def test_fill_credentials(self):
+        self.auth_provider.fill_credentials()
+        creds = self.auth_provider.credentials
+        for attr in ['user_id', 'tenant_id']:
+            self.assertEqual(self._get_from_fake_identity(attr),
+                             getattr(creds, attr))
+
+    def _test_base_url_helper(self, expected_url, filters,
+                              auth_data=None):
+
+        url = self.auth_provider.base_url(filters, auth_data)
+        self.assertEqual(url, expected_url)
+
+    def test_base_url(self):
+        self.filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion'
+        }
+        expected = self._get_result_url_from_endpoint(
+            self._endpoints[0]['endpoints'][1])
+        self._test_base_url_helper(expected, self.filters)
+
+    def test_base_url_to_get_admin_endpoint(self):
+        self.filters = {
+            'service': 'compute',
+            'endpoint_type': 'adminURL',
+            'region': 'FakeRegion'
+        }
+        expected = self._get_result_url_from_endpoint(
+            self._endpoints[0]['endpoints'][1], endpoint_type='adminURL')
+        self._test_base_url_helper(expected, self.filters)
+
+    def test_base_url_unknown_region(self):
+        """If the region is unknown, the first endpoint is returned."""
+        self.filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'AintNoBodyKnowThisRegion'
+        }
+        expected = self._get_result_url_from_endpoint(
+            self._endpoints[0]['endpoints'][0])
+        self._test_base_url_helper(expected, self.filters)
+
+    def test_base_url_with_non_existent_service(self):
+        self.filters = {
+            'service': 'BAD_SERVICE',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion'
+        }
+        self.assertRaises(exceptions.EndpointNotFound,
+                          self._test_base_url_helper, None, self.filters)
+
+    def test_base_url_without_service(self):
+        self.filters = {
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion'
+        }
+        self.assertRaises(exceptions.EndpointNotFound,
+                          self._test_base_url_helper, None, self.filters)
+
+    def test_base_url_with_known_name(self):
+        """If name and service is known, return the endpoint."""
+        self.filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'name': 'nova'
+        }
+        expected = self._get_result_url_from_endpoint(
+            self._endpoints[0]['endpoints'][1])
+        self._test_base_url_helper(expected, self.filters)
+
+    def test_base_url_with_known_name_and_unknown_servce(self):
+        """Test with Known Name and Unknown service
+
+        If the name is known but the service is unknown, raise an exception.
+        """
+        self.filters = {
+            'service': 'AintNoBodyKnowThatService',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'name': 'AintNoBodyKnowThatName'
+        }
+        self.assertRaises(exceptions.EndpointNotFound,
+                          self._test_base_url_helper, None, self.filters)
+
+    def test_base_url_with_unknown_name_and_known_service(self):
+        """Test with Unknown Name and Known Service
+
+        If the name is unknown, raise an exception.  Note that filtering by
+        name is only successful service exists.
+        """
+
+        self.filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'name': 'AintNoBodyKnowThatName'
+        }
+        self.assertRaises(exceptions.EndpointNotFound,
+                          self._test_base_url_helper, None, self.filters)
+
+    def test_base_url_without_name(self):
+        self.filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+        }
+        expected = self._get_result_url_from_endpoint(
+            self._endpoints[0]['endpoints'][1])
+        self._test_base_url_helper(expected, self.filters)
+
+    def test_base_url_with_api_version_filter(self):
+        self.filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'api_version': 'v12'
+        }
+        expected = self._get_result_url_from_endpoint(
+            self._endpoints[0]['endpoints'][1], replacement='v12')
+        self._test_base_url_helper(expected, self.filters)
+
+    def test_base_url_with_skip_path_filter(self):
+        self.filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'skip_path': True
+        }
+        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_base_url_with_extra_path_endpoint(self):
+        auth_data = {
+            'serviceCatalog': [
+                {
+                    'type': 'compute',
+                    'endpoints': [
+                        {
+                            'region': 'FakeRegion',
+                            'publicURL': 'http://fake_url/some_path/v2.0'
+                        }
+                    ]
+                }
+            ]
+        }
+
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'api_version': 'v2.0'
+        }
+
+        expected = 'http://fake_url/some_path/v2.0'
+        self._test_base_url_helper(expected, filters, ('token', auth_data))
+
+    def test_base_url_with_unversioned_extra_path_endpoint(self):
+        auth_data = {
+            'serviceCatalog': [
+                {
+                    'type': 'compute',
+                    'endpoints': [
+                        {
+                            'region': 'FakeRegion',
+                            'publicURL': 'http://fake_url/some_path'
+                        }
+                    ]
+                }
+            ]
+        }
+
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'api_version': 'v2.0'
+        }
+
+        expected = 'http://fake_url/some_path/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)
+
+    def test_token_expired(self):
+        expiry_data = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
+        self._verify_expiry(expiry_data=expiry_data, should_be_expired=True)
+
+    def test_token_not_expired_to_be_renewed(self):
+        expiry_data = (datetime.datetime.utcnow() +
+                       self.auth_provider.token_expiry_threshold / 2)
+        self._verify_expiry(expiry_data=expiry_data, should_be_expired=True)
+
+    def _verify_expiry(self, expiry_data, should_be_expired):
+        for expiry_format in self.auth_provider.EXPIRY_DATE_FORMATS:
+            auth_data = self._auth_data_with_expiry(
+                expiry_data.strftime(expiry_format))
+            self.assertEqual(self.auth_provider.is_expired(auth_data),
+                             should_be_expired)
+
+    def test_set_scope_all_valid(self):
+        for scope in self.auth_provider.SCOPES:
+            self.auth_provider.scope = scope
+            self.assertEqual(scope, self.auth_provider.scope)
+
+    def test_set_scope_invalid(self):
+        with testtools.ExpectedException(exceptions.InvalidScope,
+                                         '.* invalid_scope .*'):
+            self.auth_provider.scope = 'invalid_scope'
+
+
+class TestKeystoneV3AuthProvider(TestKeystoneV2AuthProvider):
+    _endpoints = fake_identity.IDENTITY_V3_RESPONSE['token']['catalog']
+    _auth_provider_class = auth.KeystoneV3AuthProvider
+    credentials = fake_credentials.FakeKeystoneV3Credentials()
+
+    def setUp(self):
+        super(TestKeystoneV3AuthProvider, self).setUp()
+        self.patchobject(v3_client.V3TokenClient, 'raw_request',
+                         fake_identity._fake_v3_response)
+
+    def _get_fake_identity(self):
+        return fake_identity.IDENTITY_V3_RESPONSE['token']
+
+    def _get_fake_alt_identity(self):
+        return fake_identity.ALT_IDENTITY_V3['token']
+
+    def _get_result_url_from_endpoint(self, ep, replacement=None):
+        if replacement:
+            return ep['url'].replace('v3', replacement)
+        return ep['url']
+
+    def _auth_data_with_expiry(self, date_as_string):
+        token, access = self.auth_provider.auth_data
+        access['expires_at'] = date_as_string
+        return token, access
+
+    def _get_from_fake_identity(self, attr):
+        token = fake_identity.IDENTITY_V3_RESPONSE['token']
+        if attr == 'user_id':
+            return token['user']['id']
+        elif attr == 'project_id':
+            return token['project']['id']
+        elif attr == 'user_domain_id':
+            return token['user']['domain']['id']
+        elif attr == 'project_domain_id':
+            return token['project']['domain']['id']
+
+    def test_check_credentials_missing_attribute(self):
+        # reset credentials to fresh ones
+        self.credentials.reset()
+        for attr in ['username', 'password', 'user_domain_name',
+                     'project_domain_name']:
+            cred = copy.copy(self.credentials)
+            del cred[attr]
+            self.assertFalse(self.auth_provider.check_credentials(cred),
+                             "Credentials should be invalid without %s" % attr)
+
+    def test_check_domain_credentials_missing_attribute(self):
+        # reset credentials to fresh ones
+        self.credentials.reset()
+        domain_creds = fake_credentials.FakeKeystoneV3DomainCredentials()
+        for attr in ['username', 'password', 'user_domain_name']:
+            cred = copy.copy(domain_creds)
+            del cred[attr]
+            self.assertFalse(self.auth_provider.check_credentials(cred),
+                             "Credentials should be invalid without %s" % attr)
+
+    def test_fill_credentials(self):
+        self.auth_provider.fill_credentials()
+        creds = self.auth_provider.credentials
+        for attr in ['user_id', 'project_id', 'user_domain_id',
+                     'project_domain_id']:
+            self.assertEqual(self._get_from_fake_identity(attr),
+                             getattr(creds, attr))
+
+    # Overwrites v2 test
+    def test_base_url_to_get_admin_endpoint(self):
+        self.filters = {
+            'service': 'compute',
+            'endpoint_type': 'admin',
+            'region': 'MiddleEarthRegion'
+        }
+        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))
+
+    def test_base_url_with_extra_path_endpoint(self):
+        auth_data = {
+            'catalog': [
+                {
+                    'type': 'compute',
+                    'endpoints': [
+                        {
+                            'region': 'FakeRegion',
+                            'url': 'http://fake_url/some_path/v2.0',
+                            'interface': 'public'
+                        }
+                    ]
+                }
+            ]
+        }
+
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'api_version': 'v2.0'
+        }
+
+        expected = 'http://fake_url/some_path/v2.0'
+        self._test_base_url_helper(expected, filters, ('token', auth_data))
+
+    def test_base_url_with_unversioned_extra_path_endpoint(self):
+        auth_data = {
+            'catalog': [
+                {
+                    'type': 'compute',
+                    'endpoints': [
+                        {
+                            'region': 'FakeRegion',
+                            'url': 'http://fake_url/some_path',
+                            'interface': 'public'
+                        }
+                    ]
+                }
+            ]
+        }
+
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'api_version': 'v2.0'
+        }
+
+        expected = 'http://fake_url/some_path/v2.0'
+        self._test_base_url_helper(expected, filters, ('token', auth_data))
+
+    # Base URL test with scope only for V3
+    def test_base_url_scope_project(self):
+        self.auth_provider.scope = 'project'
+        self.filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion'
+        }
+        expected = self._get_result_url_from_endpoint(
+            self._endpoints[0]['endpoints'][1])
+        self._test_base_url_helper(expected, self.filters)
+
+    # Base URL test with scope only for V3
+    def test_base_url_unscoped_identity(self):
+        self.auth_provider.scope = 'unscoped'
+        self.patchobject(v3_client.V3TokenClient, 'raw_request',
+                         fake_identity._fake_v3_response_no_scope)
+        self.filters = {
+            'service': 'identity',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion'
+        }
+        expected = fake_identity.FAKE_AUTH_URL
+        self._test_base_url_helper(expected, self.filters)
+
+    # Base URL test with scope only for V3
+    def test_base_url_unscoped_other(self):
+        self.auth_provider.scope = 'unscoped'
+        self.patchobject(v3_client.V3TokenClient, 'raw_request',
+                         fake_identity._fake_v3_response_no_scope)
+        self.filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion'
+        }
+        self.assertRaises(exceptions.EndpointNotFound,
+                          self.auth_provider.base_url,
+                          auth_data=self.auth_provider.auth_data,
+                          filters=self.filters)
+
+    def test_auth_parameters_with_scope_unset(self):
+        # No scope defaults to 'project'
+        all_creds = fake_credentials.FakeKeystoneV3AllCredentials()
+        self.auth_provider.credentials = all_creds
+        auth_params = self.auth_provider._auth_params()
+        self.assertNotIn('scope', auth_params.keys())
+        for attr in all_creds.get_init_attributes():
+            if attr.startswith('domain_'):
+                self.assertNotIn(attr, auth_params.keys())
+            else:
+                self.assertIn(attr, auth_params.keys())
+                self.assertEqual(getattr(all_creds, attr), auth_params[attr])
+
+    def test_auth_parameters_with_project_scope(self):
+        all_creds = fake_credentials.FakeKeystoneV3AllCredentials()
+        self.auth_provider.credentials = all_creds
+        self.auth_provider.scope = 'project'
+        auth_params = self.auth_provider._auth_params()
+        self.assertNotIn('scope', auth_params.keys())
+        for attr in all_creds.get_init_attributes():
+            if attr.startswith('domain_'):
+                self.assertNotIn(attr, auth_params.keys())
+            else:
+                self.assertIn(attr, auth_params.keys())
+                self.assertEqual(getattr(all_creds, attr), auth_params[attr])
+
+    def test_auth_parameters_with_domain_scope(self):
+        all_creds = fake_credentials.FakeKeystoneV3AllCredentials()
+        self.auth_provider.credentials = all_creds
+        self.auth_provider.scope = 'domain'
+        auth_params = self.auth_provider._auth_params()
+        self.assertNotIn('scope', auth_params.keys())
+        for attr in all_creds.get_init_attributes():
+            if attr.startswith('project_'):
+                self.assertNotIn(attr, auth_params.keys())
+            else:
+                self.assertIn(attr, auth_params.keys())
+                self.assertEqual(getattr(all_creds, attr), auth_params[attr])
+
+    def test_auth_parameters_unscoped(self):
+        all_creds = fake_credentials.FakeKeystoneV3AllCredentials()
+        self.auth_provider.credentials = all_creds
+        self.auth_provider.scope = 'unscoped'
+        auth_params = self.auth_provider._auth_params()
+        self.assertNotIn('scope', auth_params.keys())
+        for attr in all_creds.get_init_attributes():
+            if attr.startswith('project_') or attr.startswith('domain_'):
+                self.assertNotIn(attr, auth_params.keys())
+            else:
+                self.assertIn(attr, auth_params.keys())
+                self.assertEqual(getattr(all_creds, attr), auth_params[attr])
+
+
+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'))
+
+
+class TestKeystoneV3AuthProvider_DomainScope(BaseAuthTestsSetUp):
+    _endpoints = fake_identity.IDENTITY_V3_RESPONSE['token']['catalog']
+    _auth_provider_class = auth.KeystoneV3AuthProvider
+    credentials = fake_credentials.FakeKeystoneV3Credentials()
+
+    def setUp(self):
+        super(TestKeystoneV3AuthProvider_DomainScope, self).setUp()
+        self.patchobject(v3_client.V3TokenClient, 'raw_request',
+                         fake_identity._fake_v3_response_domain_scope)
+
+    def test_get_auth_with_domain_scope(self):
+        self.auth_provider.scope = 'domain'
+        _, auth_data = self.auth_provider.get_auth()
+        self.assertIn('domain', auth_data)
+        self.assertNotIn('project', auth_data)
+
+
+class TestGetCredentials(base.TestCase):
+
+    def test_invalid_identity_version(self):
+        with testtools.ExpectedException(exceptions.InvalidIdentityVersion,
+                                         '.* v1 .*'):
+            auth.get_credentials('http://localhost/identity/v3',
+                                 identity_version='v1')
diff --git a/tempest/tests/lib/test_base.py b/tempest/tests/lib/test_base.py
new file mode 100644
index 0000000..27cda1a
--- /dev/null
+++ b/tempest/tests/lib/test_base.py
@@ -0,0 +1,64 @@
+# Copyright 2014 Mirantis Inc.
+# 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 testtools
+
+from tempest.lib import base
+from tempest.lib import exceptions
+
+
+class TestAttr(base.BaseTestCase):
+
+    def test_has_no_attrs(self):
+        self.assertEqual(
+            'tempest.tests.lib.test_base.TestAttr.test_has_no_attrs',
+            self.id()
+        )
+
+    @testtools.testcase.attr('foo')
+    def test_has_one_attr(self):
+        self.assertEqual(
+            'tempest.tests.lib.test_base.TestAttr.test_has_one_attr[foo]',
+            self.id()
+        )
+
+    @testtools.testcase.attr('foo')
+    @testtools.testcase.attr('bar')
+    def test_has_two_attrs(self):
+        self.assertEqual(
+            'tempest.tests.lib.test_base.TestAttr.test_has_two_attrs[bar,foo]',
+            self.id(),
+        )
+
+
+class TestSetUpClass(base.BaseTestCase):
+
+    @classmethod
+    def setUpClass(cls):  # noqa
+        """Simulate absence of super() call."""
+
+    def setUp(self):
+        try:
+            # We expect here RuntimeError exception because 'setUpClass'
+            # has not called 'super'.
+            super(TestSetUpClass, self).setUp()
+        except RuntimeError:
+            pass
+        else:
+            raise exceptions.TempestException(
+                "If you see this, then expected exception was not raised.")
+
+    def test_setup_class_raises_runtime_error(self):
+        """No-op test just to call setUp."""
diff --git a/tempest/tests/lib/test_credentials.py b/tempest/tests/lib/test_credentials.py
new file mode 100644
index 0000000..c910d6d
--- /dev/null
+++ b/tempest/tests/lib/test_credentials.py
@@ -0,0 +1,182 @@
+# Copyright 2014 Hewlett-Packard Development Company, L.P.
+# 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.lib import auth
+from tempest.lib import exceptions
+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.lib import fake_identity
+
+
+class CredentialsTests(base.TestCase):
+    attributes = {}
+    credentials_class = auth.Credentials
+
+    def _get_credentials(self, attributes=None):
+        if attributes is None:
+            attributes = self.attributes
+        return self.credentials_class(**attributes)
+
+    def _check(self, credentials, credentials_class, filled):
+        # Check the right version of credentials has been returned
+        self.assertIsInstance(credentials, credentials_class)
+        # Check the id attributes are filled in
+        # NOTE(andreaf) project_* attributes are accepted as input but
+        # never set on the credentials object
+        attributes = [x for x in credentials.ATTRIBUTES if (
+            '_id' in x and x != 'domain_id' and x != 'project_id')]
+        for attr in attributes:
+            if filled:
+                self.assertIsNotNone(getattr(credentials, attr))
+            else:
+                self.assertIsNone(getattr(credentials, attr))
+
+    def test_create(self):
+        creds = self._get_credentials()
+        self.assertEqual(self.attributes, creds._initial)
+
+    def test_create_invalid_attr(self):
+        self.assertRaises(exceptions.InvalidCredentials,
+                          self._get_credentials,
+                          attributes=dict(invalid='fake'))
+
+    def test_is_valid(self):
+        creds = self._get_credentials()
+        self.assertRaises(NotImplementedError, creds.is_valid)
+
+
+class KeystoneV2CredentialsTests(CredentialsTests):
+    attributes = {
+        'username': 'fake_username',
+        'password': 'fake_password',
+        'tenant_name': 'fake_tenant_name'
+    }
+
+    identity_response = fake_identity._fake_v2_response
+    credentials_class = auth.KeystoneV2Credentials
+    tokenclient_class = v2_client.TokenClient
+    identity_version = 'v2'
+
+    def setUp(self):
+        super(KeystoneV2CredentialsTests, self).setUp()
+        self.patchobject(self.tokenclient_class, 'raw_request',
+                         self.identity_response)
+
+    def _verify_credentials(self, credentials_class, creds_dict, filled=True):
+        creds = auth.get_credentials(fake_identity.FAKE_AUTH_URL,
+                                     fill_in=filled,
+                                     identity_version=self.identity_version,
+                                     **creds_dict)
+        self._check(creds, credentials_class, filled)
+
+    def test_get_credentials(self):
+        self._verify_credentials(credentials_class=self.credentials_class,
+                                 creds_dict=self.attributes)
+
+    def test_get_credentials_not_filled(self):
+        self._verify_credentials(credentials_class=self.credentials_class,
+                                 creds_dict=self.attributes,
+                                 filled=False)
+
+    def test_is_valid(self):
+        creds = self._get_credentials()
+        self.assertTrue(creds.is_valid())
+
+    def _test_is_not_valid(self, ignore_key):
+        creds = self._get_credentials()
+        for attr in self.attributes:
+            if attr == ignore_key:
+                continue
+            temp_attr = getattr(creds, attr)
+            delattr(creds, attr)
+            self.assertFalse(creds.is_valid(),
+                             "Credentials should be invalid without %s" % attr)
+            setattr(creds, attr, temp_attr)
+
+    def test_is_not_valid(self):
+        # NOTE(mtreinish): A KeystoneV2 credential object is valid without
+        # a tenant_name. So skip that check. See tempest.auth for the valid
+        # credential requirements
+        self._test_is_not_valid('tenant_name')
+
+    def test_reset_all_attributes(self):
+        creds = self._get_credentials()
+        initial_creds = copy.deepcopy(creds)
+        set_attr = creds.__dict__.keys()
+        missing_attr = set(creds.ATTRIBUTES).difference(set_attr)
+        # Set all unset attributes, then reset
+        for attr in missing_attr:
+            setattr(creds, attr, 'fake' + attr)
+        creds.reset()
+        # Check reset credentials are same as initial ones
+        self.assertEqual(creds, initial_creds)
+
+    def test_reset_single_attribute(self):
+        creds = self._get_credentials()
+        initial_creds = copy.deepcopy(creds)
+        set_attr = creds.__dict__.keys()
+        missing_attr = set(creds.ATTRIBUTES).difference(set_attr)
+        # Set one unset attributes, then reset
+        for attr in missing_attr:
+            setattr(creds, attr, 'fake' + attr)
+            creds.reset()
+            # Check reset credentials are same as initial ones
+            self.assertEqual(creds, initial_creds)
+
+
+class KeystoneV3CredentialsTests(KeystoneV2CredentialsTests):
+    attributes = {
+        'username': 'fake_username',
+        'password': 'fake_password',
+        'project_name': 'fake_project_name',
+        'user_domain_name': 'fake_domain_name'
+    }
+
+    credentials_class = auth.KeystoneV3Credentials
+    identity_response = fake_identity._fake_v3_response
+    tokenclient_class = v3_client.V3TokenClient
+    identity_version = 'v3'
+
+    def test_is_not_valid(self):
+        # NOTE(mtreinish) For a Keystone V3 credential object a project name
+        # is not required to be valid, so we skip that check. See tempest.auth
+        # for the valid credential requirements
+        self._test_is_not_valid('project_name')
+
+    def test_synced_attributes(self):
+        attributes = self.attributes
+        # Create V3 credentials with tenant instead of project, and user_domain
+        for attr in ['project_id', 'user_domain_id']:
+            attributes[attr] = 'fake_' + attr
+        creds = self._get_credentials(attributes)
+        self.assertEqual(creds.project_name, creds.tenant_name)
+        self.assertEqual(creds.project_id, creds.tenant_id)
+        self.assertEqual(creds.user_domain_name, creds.project_domain_name)
+        self.assertEqual(creds.user_domain_id, creds.project_domain_id)
+        # Replace user_domain with project_domain
+        del attributes['user_domain_name']
+        del attributes['user_domain_id']
+        del attributes['project_name']
+        del attributes['project_id']
+        for attr in ['project_domain_name', 'project_domain_id',
+                     'tenant_name', 'tenant_id']:
+            attributes[attr] = 'fake_' + attr
+        self.assertEqual(creds.tenant_name, creds.project_name)
+        self.assertEqual(creds.tenant_id, creds.project_id)
+        self.assertEqual(creds.project_domain_name, creds.user_domain_name)
+        self.assertEqual(creds.project_domain_id, creds.user_domain_id)
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
new file mode 100644
index 0000000..f3a4e9c
--- /dev/null
+++ b/tempest/tests/lib/test_decorators.py
@@ -0,0 +1,125 @@
+# Copyright 2013 IBM Corp
+# Copyright 2015 Hewlett-Packard 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.
+
+import testtools
+
+from tempest.lib import base as test
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.tests import base
+
+
+class TestSkipBecauseDecorator(base.TestCase):
+    def _test_skip_because_helper(self, expected_to_skip=True,
+                                  **decorator_args):
+        class TestFoo(test.BaseTestCase):
+            _interface = 'json'
+
+            @decorators.skip_because(**decorator_args)
+            def test_bar(self):
+                return 0
+
+        t = TestFoo('test_bar')
+        if expected_to_skip:
+            self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+        else:
+            # assert that test_bar returned 0
+            self.assertEqual(TestFoo('test_bar').test_bar(), 0)
+
+    def test_skip_because_bug(self):
+        self._test_skip_because_helper(bug='12345')
+
+    def test_skip_because_bug_and_condition_true(self):
+        self._test_skip_because_helper(bug='12348', condition=True)
+
+    def test_skip_because_bug_and_condition_false(self):
+        self._test_skip_because_helper(expected_to_skip=False,
+                                       bug='12349', condition=False)
+
+    def test_skip_because_bug_without_bug_never_skips(self):
+        """Never skip without a bug parameter."""
+        self._test_skip_because_helper(expected_to_skip=False,
+                                       condition=True)
+        self._test_skip_because_helper(expected_to_skip=False)
+
+    def test_skip_because_invalid_bug_number(self):
+        """Raise ValueError if with an invalid bug number"""
+        self.assertRaises(ValueError, self._test_skip_because_helper,
+                          bug='critical_bug')
+
+
+class TestIdempotentIdDecorator(base.TestCase):
+    def _test_helper(self, _id, **decorator_args):
+        @decorators.idempotent_id(_id)
+        def foo():
+            """Docstring"""
+            pass
+
+        return foo
+
+    def _test_helper_without_doc(self, _id, **decorator_args):
+        @decorators.idempotent_id(_id)
+        def foo():
+            pass
+
+        return foo
+
+    def test_positive(self):
+        _id = data_utils.rand_uuid()
+        foo = self._test_helper(_id)
+        self.assertIn('id-%s' % _id, getattr(foo, '__testtools_attrs'))
+        self.assertTrue(foo.__doc__.startswith('Test idempotent id: %s' % _id))
+
+    def test_positive_without_doc(self):
+        _id = data_utils.rand_uuid()
+        foo = self._test_helper_without_doc(_id)
+        self.assertTrue(foo.__doc__.startswith('Test idempotent id: %s' % _id))
+
+    def test_idempotent_id_not_str(self):
+        _id = 42
+        self.assertRaises(TypeError, self._test_helper, _id)
+
+    def test_idempotent_id_not_valid_uuid(self):
+        _id = '42'
+        self.assertRaises(ValueError, self._test_helper, _id)
+
+
+class TestSkipUnlessAttrDecorator(base.TestCase):
+    def _test_skip_unless_attr(self, attr, expected_to_skip=True):
+        class TestFoo(test.BaseTestCase):
+            expected_attr = not expected_to_skip
+
+            @decorators.skip_unless_attr(attr)
+            def test_foo(self):
+                pass
+
+        t = TestFoo('test_foo')
+        if expected_to_skip:
+            self.assertRaises(testtools.TestCase.skipException,
+                              t.test_foo)
+        else:
+            try:
+                t.test_foo()
+            except Exception:
+                raise testtools.TestCase.failureException()
+
+    def test_skip_attr_does_not_exist(self):
+        self._test_skip_unless_attr('unexpected_attr')
+
+    def test_skip_attr_false(self):
+        self._test_skip_unless_attr('expected_attr')
+
+    def test_no_skip_for_attr_exist_and_true(self):
+        self._test_skip_unless_attr('expected_attr', expected_to_skip=False)
diff --git a/tempest/tests/lib/test_rest_client.py b/tempest/tests/lib/test_rest_client.py
new file mode 100644
index 0000000..e6cf047
--- /dev/null
+++ b/tempest/tests/lib/test_rest_client.py
@@ -0,0 +1,1152 @@
+# 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 json
+
+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 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):
+
+    url = 'fake_endpoint'
+
+    def setUp(self):
+        super(BaseRestClientTestClass, self).setUp()
+        self.fake_auth_provider = fake_auth_provider.FakeAuthProvider()
+        self.rest_client = rest_client.RestClient(
+            self.fake_auth_provider, None, None)
+        self.patchobject(http.ClosingHttp, 'request', self.fake_http.request)
+        self.useFixture(mockpatch.PatchObject(self.rest_client,
+                                              '_log_request'))
+
+
+class TestRestClientHTTPMethods(BaseRestClientTestClass):
+    def setUp(self):
+        self.fake_http = fake_http.fake_httplib2()
+        super(TestRestClientHTTPMethods, self).setUp()
+        self.useFixture(mockpatch.PatchObject(self.rest_client,
+                                              '_error_checker'))
+
+    def test_post(self):
+        __, return_dict = self.rest_client.post(self.url, {}, {})
+        self.assertEqual('POST', return_dict['method'])
+
+    def test_get(self):
+        __, return_dict = self.rest_client.get(self.url)
+        self.assertEqual('GET', return_dict['method'])
+
+    def test_delete(self):
+        __, return_dict = self.rest_client.delete(self.url)
+        self.assertEqual('DELETE', return_dict['method'])
+
+    def test_patch(self):
+        __, return_dict = self.rest_client.patch(self.url, {}, {})
+        self.assertEqual('PATCH', return_dict['method'])
+
+    def test_put(self):
+        __, return_dict = self.rest_client.put(self.url, {}, {})
+        self.assertEqual('PUT', return_dict['method'])
+
+    def test_head(self):
+        self.useFixture(mockpatch.PatchObject(self.rest_client,
+                                              'response_checker'))
+        __, return_dict = self.rest_client.head(self.url)
+        self.assertEqual('HEAD', return_dict['method'])
+
+    def test_copy(self):
+        __, return_dict = self.rest_client.copy(self.url)
+        self.assertEqual('COPY', return_dict['method'])
+
+
+class TestRestClientNotFoundHandling(BaseRestClientTestClass):
+    def setUp(self):
+        self.fake_http = fake_http.fake_httplib2(404)
+        super(TestRestClientNotFoundHandling, self).setUp()
+
+    def test_post(self):
+        self.assertRaises(exceptions.NotFound, self.rest_client.post,
+                          self.url, {}, {})
+
+
+class TestRestClientHeadersJSON(TestRestClientHTTPMethods):
+    TYPE = "json"
+
+    def _verify_headers(self, resp):
+        self.assertEqual(self.rest_client._get_type(), self.TYPE)
+        resp = dict((k.lower(), v) for k, v in six.iteritems(resp))
+        self.assertEqual(self.header_value, resp['accept'])
+        self.assertEqual(self.header_value, resp['content-type'])
+
+    def setUp(self):
+        super(TestRestClientHeadersJSON, self).setUp()
+        self.rest_client.TYPE = self.TYPE
+        self.header_value = 'application/%s' % self.rest_client._get_type()
+
+    def test_post(self):
+        resp, __ = self.rest_client.post(self.url, {})
+        self._verify_headers(resp)
+
+    def test_get(self):
+        resp, __ = self.rest_client.get(self.url)
+        self._verify_headers(resp)
+
+    def test_delete(self):
+        resp, __ = self.rest_client.delete(self.url)
+        self._verify_headers(resp)
+
+    def test_patch(self):
+        resp, __ = self.rest_client.patch(self.url, {})
+        self._verify_headers(resp)
+
+    def test_put(self):
+        resp, __ = self.rest_client.put(self.url, {})
+        self._verify_headers(resp)
+
+    def test_head(self):
+        self.useFixture(mockpatch.PatchObject(self.rest_client,
+                                              'response_checker'))
+        resp, __ = self.rest_client.head(self.url)
+        self._verify_headers(resp)
+
+    def test_copy(self):
+        resp, __ = self.rest_client.copy(self.url)
+        self._verify_headers(resp)
+
+
+class TestRestClientUpdateHeaders(BaseRestClientTestClass):
+    def setUp(self):
+        self.fake_http = fake_http.fake_httplib2()
+        super(TestRestClientUpdateHeaders, self).setUp()
+        self.useFixture(mockpatch.PatchObject(self.rest_client,
+                                              '_error_checker'))
+        self.headers = {'X-Configuration-Session': 'session_id'}
+
+    def test_post_update_headers(self):
+        __, return_dict = self.rest_client.post(self.url, {},
+                                                extra_headers=True,
+                                                headers=self.headers)
+
+        self.assertDictContainsSubset(
+            {'X-Configuration-Session': 'session_id',
+             'Content-Type': 'application/json',
+             'Accept': 'application/json'},
+            return_dict['headers']
+        )
+
+    def test_get_update_headers(self):
+        __, return_dict = self.rest_client.get(self.url,
+                                               extra_headers=True,
+                                               headers=self.headers)
+
+        self.assertDictContainsSubset(
+            {'X-Configuration-Session': 'session_id',
+             'Content-Type': 'application/json',
+             'Accept': 'application/json'},
+            return_dict['headers']
+        )
+
+    def test_delete_update_headers(self):
+        __, return_dict = self.rest_client.delete(self.url,
+                                                  extra_headers=True,
+                                                  headers=self.headers)
+
+        self.assertDictContainsSubset(
+            {'X-Configuration-Session': 'session_id',
+             'Content-Type': 'application/json',
+             'Accept': 'application/json'},
+            return_dict['headers']
+        )
+
+    def test_patch_update_headers(self):
+        __, return_dict = self.rest_client.patch(self.url, {},
+                                                 extra_headers=True,
+                                                 headers=self.headers)
+
+        self.assertDictContainsSubset(
+            {'X-Configuration-Session': 'session_id',
+             'Content-Type': 'application/json',
+             'Accept': 'application/json'},
+            return_dict['headers']
+        )
+
+    def test_put_update_headers(self):
+        __, return_dict = self.rest_client.put(self.url, {},
+                                               extra_headers=True,
+                                               headers=self.headers)
+
+        self.assertDictContainsSubset(
+            {'X-Configuration-Session': 'session_id',
+             'Content-Type': 'application/json',
+             'Accept': 'application/json'},
+            return_dict['headers']
+        )
+
+    def test_head_update_headers(self):
+        self.useFixture(mockpatch.PatchObject(self.rest_client,
+                                              'response_checker'))
+
+        __, return_dict = self.rest_client.head(self.url,
+                                                extra_headers=True,
+                                                headers=self.headers)
+
+        self.assertDictContainsSubset(
+            {'X-Configuration-Session': 'session_id',
+             'Content-Type': 'application/json',
+             'Accept': 'application/json'},
+            return_dict['headers']
+        )
+
+    def test_copy_update_headers(self):
+        __, return_dict = self.rest_client.copy(self.url,
+                                                extra_headers=True,
+                                                headers=self.headers)
+
+        self.assertDictContainsSubset(
+            {'X-Configuration-Session': 'session_id',
+             'Content-Type': 'application/json',
+             'Accept': 'application/json'},
+            return_dict['headers']
+        )
+
+
+class TestRestClientParseRespJSON(BaseRestClientTestClass):
+    TYPE = "json"
+
+    keys = ["fake_key1", "fake_key2"]
+    values = ["fake_value1", "fake_value2"]
+    item_expected = dict((key, value) for (key, value) in zip(keys, values))
+    list_expected = {"body_list": [
+        {keys[0]: values[0]},
+        {keys[1]: values[1]},
+    ]}
+    dict_expected = {"body_dict": {
+        keys[0]: values[0],
+        keys[1]: values[1],
+    }}
+    null_dict = {}
+
+    def setUp(self):
+        self.fake_http = fake_http.fake_httplib2()
+        super(TestRestClientParseRespJSON, self).setUp()
+        self.rest_client.TYPE = self.TYPE
+
+    def test_parse_resp_body_item(self):
+        body = self.rest_client._parse_resp(json.dumps(self.item_expected))
+        self.assertEqual(self.item_expected, body)
+
+    def test_parse_resp_body_list(self):
+        body = self.rest_client._parse_resp(json.dumps(self.list_expected))
+        self.assertEqual(self.list_expected["body_list"], body)
+
+    def test_parse_resp_body_dict(self):
+        body = self.rest_client._parse_resp(json.dumps(self.dict_expected))
+        self.assertEqual(self.dict_expected["body_dict"], body)
+
+    def test_parse_resp_two_top_keys(self):
+        dict_two_keys = self.dict_expected.copy()
+        dict_two_keys.update({"second_key": ""})
+        body = self.rest_client._parse_resp(json.dumps(dict_two_keys))
+        self.assertEqual(dict_two_keys, body)
+
+    def test_parse_resp_one_top_key_without_list_or_dict(self):
+        data = {"one_top_key": "not_list_or_dict_value"}
+        body = self.rest_client._parse_resp(json.dumps(data))
+        self.assertEqual(data, body)
+
+    def test_parse_nullable_dict(self):
+        body = self.rest_client._parse_resp(json.dumps(self.null_dict))
+        self.assertEqual(self.null_dict, body)
+
+
+class TestRestClientErrorCheckerJSON(base.TestCase):
+    c_type = "application/json"
+
+    def set_data(self, r_code, enc=None, r_body=None, absolute_limit=True):
+        if enc is None:
+            enc = self.c_type
+        resp_dict = {'status': r_code, 'content-type': enc}
+        resp_body = {'resp_body': 'fake_resp_body'}
+
+        if absolute_limit is False:
+            resp_dict.update({'retry-after': 120})
+            resp_body.update({'overLimit': {'message': 'fake_message'}})
+        resp = fake_http.fake_http_response(headers=resp_dict,
+                                            status=int(r_code),
+                                            body=json.dumps(resp_body))
+        data = {
+            "resp": resp,
+            "resp_body": json.dumps(resp_body)
+        }
+        if r_body is not None:
+            data.update({"resp_body": r_body})
+        return data
+
+    def setUp(self):
+        super(TestRestClientErrorCheckerJSON, self).setUp()
+        self.rest_client = rest_client.RestClient(
+            fake_auth_provider.FakeAuthProvider(), None, None)
+
+    def test_response_less_than_400(self):
+        self.rest_client._error_checker(**self.set_data("399"))
+
+    def _test_error_checker(self, exception_type, data):
+        e = self.assertRaises(exception_type,
+                              self.rest_client._error_checker,
+                              **data)
+        self.assertEqual(e.resp, data['resp'])
+        self.assertTrue(hasattr(e, 'resp_body'))
+        return e
+
+    def test_response_400(self):
+        self._test_error_checker(exceptions.BadRequest, self.set_data("400"))
+
+    def test_response_401(self):
+        self._test_error_checker(exceptions.Unauthorized, self.set_data("401"))
+
+    def test_response_403(self):
+        self._test_error_checker(exceptions.Forbidden, self.set_data("403"))
+
+    def test_response_404(self):
+        self._test_error_checker(exceptions.NotFound, self.set_data("404"))
+
+    def test_response_409(self):
+        self._test_error_checker(exceptions.Conflict, self.set_data("409"))
+
+    def test_response_410(self):
+        self._test_error_checker(exceptions.Gone, self.set_data("410"))
+
+    def test_response_413(self):
+        self._test_error_checker(exceptions.OverLimit, self.set_data("413"))
+
+    def test_response_413_without_absolute_limit(self):
+        self._test_error_checker(exceptions.RateLimitExceeded,
+                                 self.set_data("413", absolute_limit=False))
+
+    def test_response_415(self):
+        self._test_error_checker(exceptions.InvalidContentType,
+                                 self.set_data("415"))
+
+    def test_response_422(self):
+        self._test_error_checker(exceptions.UnprocessableEntity,
+                                 self.set_data("422"))
+
+    def test_response_500_with_text(self):
+        # _parse_resp is expected to return 'str'
+        self._test_error_checker(exceptions.ServerFault, self.set_data("500"))
+
+    def test_response_501_with_text(self):
+        self._test_error_checker(exceptions.NotImplemented,
+                                 self.set_data("501"))
+
+    def test_response_400_with_dict(self):
+        r_body = '{"resp_body": {"err": "fake_resp_body"}}'
+        e = self._test_error_checker(exceptions.BadRequest,
+                                     self.set_data("400", r_body=r_body))
+
+        if self.c_type == 'application/json':
+            expected = {"err": "fake_resp_body"}
+        else:
+            expected = r_body
+        self.assertEqual(expected, e.resp_body)
+
+    def test_response_401_with_dict(self):
+        r_body = '{"resp_body": {"err": "fake_resp_body"}}'
+        e = self._test_error_checker(exceptions.Unauthorized,
+                                     self.set_data("401", r_body=r_body))
+
+        if self.c_type == 'application/json':
+            expected = {"err": "fake_resp_body"}
+        else:
+            expected = r_body
+        self.assertEqual(expected, e.resp_body)
+
+    def test_response_403_with_dict(self):
+        r_body = '{"resp_body": {"err": "fake_resp_body"}}'
+        e = self._test_error_checker(exceptions.Forbidden,
+                                     self.set_data("403", r_body=r_body))
+
+        if self.c_type == 'application/json':
+            expected = {"err": "fake_resp_body"}
+        else:
+            expected = r_body
+        self.assertEqual(expected, e.resp_body)
+
+    def test_response_404_with_dict(self):
+        r_body = '{"resp_body": {"err": "fake_resp_body"}}'
+        e = self._test_error_checker(exceptions.NotFound,
+                                     self.set_data("404", r_body=r_body))
+
+        if self.c_type == 'application/json':
+            expected = {"err": "fake_resp_body"}
+        else:
+            expected = r_body
+        self.assertEqual(expected, e.resp_body)
+
+    def test_response_404_with_invalid_dict(self):
+        r_body = '{"foo": "bar"]'
+        e = self._test_error_checker(exceptions.NotFound,
+                                     self.set_data("404", r_body=r_body))
+
+        expected = r_body
+        self.assertEqual(expected, e.resp_body)
+
+    def test_response_410_with_dict(self):
+        r_body = '{"resp_body": {"err": "fake_resp_body"}}'
+        e = self._test_error_checker(exceptions.Gone,
+                                     self.set_data("410", r_body=r_body))
+
+        if self.c_type == 'application/json':
+            expected = {"err": "fake_resp_body"}
+        else:
+            expected = r_body
+        self.assertEqual(expected, e.resp_body)
+
+    def test_response_410_with_invalid_dict(self):
+        r_body = '{"foo": "bar"]'
+        e = self._test_error_checker(exceptions.Gone,
+                                     self.set_data("410", r_body=r_body))
+
+        expected = r_body
+        self.assertEqual(expected, e.resp_body)
+
+    def test_response_409_with_dict(self):
+        r_body = '{"resp_body": {"err": "fake_resp_body"}}'
+        e = self._test_error_checker(exceptions.Conflict,
+                                     self.set_data("409", r_body=r_body))
+
+        if self.c_type == 'application/json':
+            expected = {"err": "fake_resp_body"}
+        else:
+            expected = r_body
+        self.assertEqual(expected, e.resp_body)
+
+    def test_response_500_with_dict(self):
+        r_body = '{"resp_body": {"err": "fake_resp_body"}}'
+        e = self._test_error_checker(exceptions.ServerFault,
+                                     self.set_data("500", r_body=r_body))
+
+        if self.c_type == 'application/json':
+            expected = {"err": "fake_resp_body"}
+        else:
+            expected = r_body
+        self.assertEqual(expected, e.resp_body)
+
+    def test_response_501_with_dict(self):
+        r_body = '{"resp_body": {"err": "fake_resp_body"}}'
+        self._test_error_checker(exceptions.NotImplemented,
+                                 self.set_data("501", r_body=r_body))
+
+    def test_response_bigger_than_400(self):
+        # Any response code, that bigger than 400, and not in
+        # (401, 403, 404, 409, 413, 422, 500, 501)
+        self._test_error_checker(exceptions.UnexpectedResponseCode,
+                                 self.set_data("402"))
+
+
+class TestRestClientErrorCheckerTEXT(TestRestClientErrorCheckerJSON):
+    c_type = "text/plain"
+
+    def test_fake_content_type(self):
+        # This test is required only in one exemplar
+        # Any response code, that bigger than 400, and not in
+        # (401, 403, 404, 409, 413, 422, 500, 501)
+        self._test_error_checker(exceptions.UnexpectedContentType,
+                                 self.set_data("405", enc="fake_enc"))
+
+    def test_response_413_without_absolute_limit(self):
+        # Skip this test because rest_client cannot get overLimit message
+        # from text body.
+        pass
+
+
+class TestRestClientUtils(BaseRestClientTestClass):
+
+    def _is_resource_deleted(self, resource_id):
+        if not isinstance(self.retry_pass, int):
+            return False
+        if self.retry_count >= self.retry_pass:
+            return True
+        self.retry_count = self.retry_count + 1
+        return False
+
+    def setUp(self):
+        self.fake_http = fake_http.fake_httplib2()
+        super(TestRestClientUtils, self).setUp()
+        self.retry_count = 0
+        self.retry_pass = None
+        self.original_deleted_method = self.rest_client.is_resource_deleted
+        self.rest_client.is_resource_deleted = self._is_resource_deleted
+
+    def test_wait_for_resource_deletion(self):
+        self.retry_pass = 2
+        # Ensure timeout long enough for loop execution to hit retry count
+        self.rest_client.build_timeout = 500
+        sleep_mock = self.patch('time.sleep')
+        self.rest_client.wait_for_resource_deletion('1234')
+        self.assertEqual(len(sleep_mock.mock_calls), 2)
+
+    def test_wait_for_resource_deletion_not_deleted(self):
+        self.patch('time.sleep')
+        # Set timeout to be very quick to force exception faster
+        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,
+                          self.rest_client.wait_for_resource_deletion,
+                          '1234')
+
+    def test_get_versions(self):
+        self.rest_client._parse_resp = lambda x: [{'id': 'v1'}, {'id': 'v2'}]
+        actual_resp, actual_versions = self.rest_client.get_versions()
+        self.assertEqual(['v1', 'v2'], list(actual_versions))
+
+    def test__str__(self):
+        def get_token():
+            return "deadbeef"
+
+        self.fake_auth_provider.get_token = get_token
+        self.assertIsNotNone(str(self.rest_client))
+
+
+class TestRateLimiting(BaseRestClientTestClass):
+
+    def setUp(self):
+        self.fake_http = fake_http.fake_httplib2()
+        super(TestRateLimiting, self).setUp()
+
+    def test__get_retry_after_delay_with_integer(self):
+        resp = {'retry-after': '123'}
+        self.assertEqual(123, self.rest_client._get_retry_after_delay(resp))
+
+    def test__get_retry_after_delay_with_http_date(self):
+        resp = {
+            'date': 'Mon, 4 Apr 2016 21:56:23 GMT',
+            'retry-after': 'Mon, 4 Apr 2016 21:58:26 GMT',
+        }
+        self.assertEqual(123, self.rest_client._get_retry_after_delay(resp))
+
+    def test__get_retry_after_delay_of_zero_with_integer(self):
+        resp = {'retry-after': '0'}
+        self.assertEqual(1, self.rest_client._get_retry_after_delay(resp))
+
+    def test__get_retry_after_delay_of_zero_with_http_date(self):
+        resp = {
+            'date': 'Mon, 4 Apr 2016 21:56:23 GMT',
+            'retry-after': 'Mon, 4 Apr 2016 21:56:23 GMT',
+        }
+        self.assertEqual(1, self.rest_client._get_retry_after_delay(resp))
+
+    def test__get_retry_after_delay_with_missing_date_header(self):
+        resp = {
+            'retry-after': 'Mon, 4 Apr 2016 21:58:26 GMT',
+        }
+        self.assertRaises(ValueError, self.rest_client._get_retry_after_delay,
+                          resp)
+
+    def test__get_retry_after_delay_with_invalid_http_date(self):
+        resp = {
+            'retry-after': 'Mon, 4 AAA 2016 21:58:26 GMT',
+            'date': 'Mon, 4 Apr 2016 21:56:23 GMT',
+        }
+        self.assertRaises(ValueError, self.rest_client._get_retry_after_delay,
+                          resp)
+
+    def test__get_retry_after_delay_with_missing_retry_after_header(self):
+        self.assertRaises(ValueError, self.rest_client._get_retry_after_delay,
+                          {})
+
+    def test_is_absolute_limit_gives_false_with_retry_after(self):
+        resp = {'retry-after': 123}
+
+        # is_absolute_limit() requires the overLimit body to be unwrapped
+        resp_body = self.rest_client._parse_resp("""{
+            "overLimit": {
+                "message": ""
+            }
+        }""")
+        self.assertFalse(self.rest_client.is_absolute_limit(resp, resp_body))
+
+
+class TestProperties(BaseRestClientTestClass):
+
+    def setUp(self):
+        self.fake_http = fake_http.fake_httplib2()
+        super(TestProperties, self).setUp()
+        creds_dict = {
+            'username': 'test-user',
+            'user_id': 'test-user_id',
+            'tenant_name': 'test-tenant_name',
+            'tenant_id': 'test-tenant_id',
+            'password': 'test-password'
+        }
+        self.rest_client = rest_client.RestClient(
+            fake_auth_provider.FakeAuthProvider(creds_dict=creds_dict),
+            None, None)
+
+    def test_properties(self):
+        self.assertEqual('test-user', self.rest_client.user)
+        self.assertEqual('test-user_id', self.rest_client.user_id)
+        self.assertEqual('test-tenant_name', self.rest_client.tenant_name)
+        self.assertEqual('test-tenant_id', self.rest_client.tenant_id)
+        self.assertEqual('test-password', self.rest_client.password)
+
+        self.rest_client.api_version = 'v1'
+        expected = {'api_version': 'v1',
+                    'endpoint_type': 'publicURL',
+                    'region': None,
+                    'name': None,
+                    'service': None,
+                    'skip_path': True}
+        self.rest_client.skip_path()
+        self.assertEqual(expected, self.rest_client.filters)
+
+        self.rest_client.reset_path()
+        self.rest_client.api_version = 'v1'
+        expected = {'api_version': 'v1',
+                    'endpoint_type': 'publicURL',
+                    'region': None,
+                    'name': None,
+                    'service': None}
+        self.assertEqual(expected, self.rest_client.filters)
+
+
+class TestExpectedSuccess(BaseRestClientTestClass):
+
+    def setUp(self):
+        self.fake_http = fake_http.fake_httplib2()
+        super(TestExpectedSuccess, self).setUp()
+
+    def test_expected_succes_int_match(self):
+        expected_code = 202
+        read_code = 202
+        resp = self.rest_client.expected_success(expected_code, read_code)
+        # Assert None resp on success
+        self.assertFalse(resp)
+
+    def test_expected_succes_int_no_match(self):
+        expected_code = 204
+        read_code = 202
+        self.assertRaises(exceptions.InvalidHttpSuccessCode,
+                          self.rest_client.expected_success,
+                          expected_code, read_code)
+
+    def test_expected_succes_list_match(self):
+        expected_code = [202, 204]
+        read_code = 202
+        resp = self.rest_client.expected_success(expected_code, read_code)
+        # Assert None resp on success
+        self.assertFalse(resp)
+
+    def test_expected_succes_list_no_match(self):
+        expected_code = [202, 204]
+        read_code = 200
+        self.assertRaises(exceptions.InvalidHttpSuccessCode,
+                          self.rest_client.expected_success,
+                          expected_code, read_code)
+
+    def test_non_success_expected_int(self):
+        expected_code = 404
+        read_code = 202
+        self.assertRaises(AssertionError, self.rest_client.expected_success,
+                          expected_code, read_code)
+
+    def test_non_success_expected_list(self):
+        expected_code = [404, 202]
+        read_code = 202
+        self.assertRaises(AssertionError, self.rest_client.expected_success,
+                          expected_code, read_code)
+
+    def test_non_success_read_code_as_string(self):
+        expected_code = 202
+        read_code = '202'
+        self.assertRaises(TypeError, self.rest_client.expected_success,
+                          expected_code, read_code)
+
+    def test_non_success_read_code_as_list(self):
+        expected_code = 202
+        read_code = [202]
+        self.assertRaises(TypeError, self.rest_client.expected_success,
+                          expected_code, read_code)
+
+    def test_non_success_expected_code_as_non_int(self):
+        expected_code = ['201', 202]
+        read_code = 202
+        self.assertRaises(AssertionError, self.rest_client.expected_success,
+                          expected_code, read_code)
+
+
+class TestResponseBody(base.TestCase):
+
+    def test_str(self):
+        response = {'status': 200}
+        body = {'key1': 'value1'}
+        actual = rest_client.ResponseBody(response, body)
+        self.assertEqual("response: %s\nBody: %s" % (response, body),
+                         str(actual))
+
+
+class TestResponseBodyData(base.TestCase):
+
+    def test_str(self):
+        response = {'status': 200}
+        data = 'data1'
+        actual = rest_client.ResponseBodyData(response, data)
+        self.assertEqual("response: %s\nBody: %s" % (response, data),
+                         str(actual))
+
+
+class TestResponseBodyList(base.TestCase):
+
+    def test_str(self):
+        response = {'status': 200}
+        body = ['value1', 'value2', 'value3']
+        actual = rest_client.ResponseBodyList(response, body)
+        self.assertEqual("response: %s\nBody: %s" % (response, body),
+                         str(actual))
+
+
+class TestJSONSchemaValidationBase(base.TestCase):
+
+    class Response(dict):
+
+        def __getattr__(self, attr):
+            return self[attr]
+
+        def __setattr__(self, attr, value):
+            self[attr] = value
+
+    def setUp(self):
+        super(TestJSONSchemaValidationBase, self).setUp()
+        self.fake_auth_provider = fake_auth_provider.FakeAuthProvider()
+        self.rest_client = rest_client.RestClient(
+            self.fake_auth_provider, None, None)
+
+    def _test_validate_pass(self, schema, resp_body, status=200):
+        resp = self.Response()
+        resp.status = status
+        self.rest_client.validate_response(schema, resp, resp_body)
+
+    def _test_validate_fail(self, schema, resp_body, status=200,
+                            error_msg="HTTP response body is invalid"):
+        resp = self.Response()
+        resp.status = status
+        ex = self.assertRaises(exceptions.InvalidHTTPResponseBody,
+                               self.rest_client.validate_response,
+                               schema, resp, resp_body)
+        self.assertIn(error_msg, ex._error_string)
+
+
+class TestRestClientJSONSchemaValidation(TestJSONSchemaValidationBase):
+
+    schema = {
+        'status_code': [200],
+        'response_body': {
+            'type': 'object',
+            'properties': {
+                'foo': {
+                    'type': 'integer',
+                },
+            },
+            'required': ['foo']
+        }
+    }
+
+    def test_validate_pass_with_http_success_code(self):
+        body = {'foo': 12}
+        self._test_validate_pass(self.schema, body, status=200)
+
+    def test_validate_pass_with_http_redirect_code(self):
+        body = {'foo': 12}
+        schema = copy.deepcopy(self.schema)
+        schema['status_code'] = 300
+        self._test_validate_pass(schema, body, status=300)
+
+    def test_validate_not_http_success_code(self):
+        schema = {
+            'status_code': [200]
+        }
+        body = {}
+        self._test_validate_pass(schema, body, status=400)
+
+    def test_validate_multiple_allowed_type(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'foo': {
+                        'type': ['integer', 'string'],
+                    },
+                },
+                'required': ['foo']
+            }
+        }
+        body = {'foo': 12}
+        self._test_validate_pass(schema, body)
+        body = {'foo': '12'}
+        self._test_validate_pass(schema, body)
+
+    def test_validate_enable_additional_property_pass(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'foo': {'type': 'integer'}
+                },
+                'additionalProperties': True,
+                'required': ['foo']
+            }
+        }
+        body = {'foo': 12, 'foo2': 'foo2value'}
+        self._test_validate_pass(schema, body)
+
+    def test_validate_disable_additional_property_pass(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'foo': {'type': 'integer'}
+                },
+                'additionalProperties': False,
+                'required': ['foo']
+            }
+        }
+        body = {'foo': 12}
+        self._test_validate_pass(schema, body)
+
+    def test_validate_disable_additional_property_fail(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'foo': {'type': 'integer'}
+                },
+                'additionalProperties': False,
+                'required': ['foo']
+            }
+        }
+        body = {'foo': 12, 'foo2': 'foo2value'}
+        self._test_validate_fail(schema, body)
+
+    def test_validate_wrong_status_code(self):
+        schema = {
+            'status_code': [202]
+        }
+        body = {}
+        resp = self.Response()
+        resp.status = 200
+        ex = self.assertRaises(exceptions.InvalidHttpSuccessCode,
+                               self.rest_client.validate_response,
+                               schema, resp, body)
+        self.assertIn("Unexpected http success status code", ex._error_string)
+
+    def test_validate_wrong_attribute_type(self):
+        body = {'foo': 1.2}
+        self._test_validate_fail(self.schema, body)
+
+    def test_validate_unexpected_response_body(self):
+        schema = {
+            'status_code': [200],
+        }
+        body = {'foo': 12}
+        self._test_validate_fail(
+            schema, body,
+            error_msg="HTTP response body should not exist")
+
+    def test_validate_missing_response_body(self):
+        body = {}
+        self._test_validate_fail(self.schema, body)
+
+    def test_validate_missing_required_attribute(self):
+        body = {'notfoo': 12}
+        self._test_validate_fail(self.schema, body)
+
+    def test_validate_response_body_not_list(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'list_items': {
+                        'type': 'array',
+                        'items': {'foo': {'type': 'integer'}}
+                    }
+                },
+                'required': ['list_items'],
+            }
+        }
+        body = {'foo': 12}
+        self._test_validate_fail(schema, body)
+
+    def test_validate_response_body_list_pass(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'list_items': {
+                        'type': 'array',
+                        'items': {'foo': {'type': 'integer'}}
+                    }
+                },
+                'required': ['list_items'],
+            }
+        }
+        body = {'list_items': [{'foo': 12}, {'foo': 10}]}
+        self._test_validate_pass(schema, body)
+
+
+class TestRestClientJSONHeaderSchemaValidation(TestJSONSchemaValidationBase):
+
+    schema = {
+        'status_code': [200],
+        'response_header': {
+            'type': 'object',
+            'properties': {
+                'foo': {'type': 'integer'}
+            },
+            'required': ['foo']
+        }
+    }
+
+    def test_validate_header_schema_pass(self):
+        resp_body = {}
+        resp = self.Response()
+        resp.status = 200
+        resp.foo = 12
+        self.rest_client.validate_response(self.schema, resp, resp_body)
+
+    def test_validate_header_schema_fail(self):
+        resp_body = {}
+        resp = self.Response()
+        resp.status = 200
+        resp.foo = 1.2
+        ex = self.assertRaises(exceptions.InvalidHTTPResponseHeader,
+                               self.rest_client.validate_response,
+                               self.schema, resp, resp_body)
+        self.assertIn("HTTP response header is invalid", ex._error_string)
+
+
+class TestRestClientJSONSchemaFormatValidation(TestJSONSchemaValidationBase):
+
+    schema = {
+        'status_code': [200],
+        'response_body': {
+            'type': 'object',
+            'properties': {
+                'foo': {
+                    'type': 'string',
+                    'format': 'email'
+                }
+            },
+            'required': ['foo']
+        }
+    }
+
+    def test_validate_format_pass(self):
+        body = {'foo': 'example@example.com'}
+        self._test_validate_pass(self.schema, body)
+
+    def test_validate_format_fail(self):
+        body = {'foo': 'wrong_email'}
+        self._test_validate_fail(self.schema, body)
+
+    def test_validate_formats_in_oneOf_pass(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'foo': {
+                        'type': 'string',
+                        'oneOf': [
+                            {'format': 'ipv4'},
+                            {'format': 'ipv6'}
+                        ]
+                    }
+                },
+                'required': ['foo']
+            }
+        }
+        body = {'foo': '10.0.0.0'}
+        self._test_validate_pass(schema, body)
+        body = {'foo': 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329'}
+        self._test_validate_pass(schema, body)
+
+    def test_validate_formats_in_oneOf_fail_both_match(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'foo': {
+                        'type': 'string',
+                        'oneOf': [
+                            {'format': 'ipv4'},
+                            {'format': 'ipv4'}
+                        ]
+                    }
+                },
+                'required': ['foo']
+            }
+        }
+        body = {'foo': '10.0.0.0'}
+        self._test_validate_fail(schema, body)
+
+    def test_validate_formats_in_oneOf_fail_no_match(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'foo': {
+                        'type': 'string',
+                        'oneOf': [
+                            {'format': 'ipv4'},
+                            {'format': 'ipv6'}
+                        ]
+                    }
+                },
+                'required': ['foo']
+            }
+        }
+        body = {'foo': 'wrong_ip_format'}
+        self._test_validate_fail(schema, body)
+
+    def test_validate_formats_in_anyOf_pass(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'foo': {
+                        'type': 'string',
+                        'anyOf': [
+                            {'format': 'ipv4'},
+                            {'format': 'ipv6'}
+                        ]
+                    }
+                },
+                'required': ['foo']
+            }
+        }
+        body = {'foo': '10.0.0.0'}
+        self._test_validate_pass(schema, body)
+        body = {'foo': 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329'}
+        self._test_validate_pass(schema, body)
+
+    def test_validate_formats_in_anyOf_pass_both_match(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'foo': {
+                        'type': 'string',
+                        'anyOf': [
+                            {'format': 'ipv4'},
+                            {'format': 'ipv4'}
+                        ]
+                    }
+                },
+                'required': ['foo']
+            }
+        }
+        body = {'foo': '10.0.0.0'}
+        self._test_validate_pass(schema, body)
+
+    def test_validate_formats_in_anyOf_fail_no_match(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'foo': {
+                        'type': 'string',
+                        'anyOf': [
+                            {'format': 'ipv4'},
+                            {'format': 'ipv6'}
+                        ]
+                    }
+                },
+                'required': ['foo']
+            }
+        }
+        body = {'foo': 'wrong_ip_format'}
+        self._test_validate_fail(schema, body)
+
+    def test_validate_formats_pass_for_unknow_format(self):
+        schema = {
+            'status_code': [200],
+            'response_body': {
+                'type': 'object',
+                'properties': {
+                    'foo': {
+                        'type': 'string',
+                        'format': 'UNKNOWN'
+                    }
+                },
+                'required': ['foo']
+            }
+        }
+        body = {'foo': 'example@example.com'}
+        self._test_validate_pass(schema, body)
+
+
+class TestRestClientJSONSchemaValidatorVersion(TestJSONSchemaValidationBase):
+
+    schema = {
+        'status_code': [200],
+        'response_body': {
+            'type': 'object',
+            'properties': {
+                'foo': {'type': 'string'}
+            }
+        }
+    }
+
+    def test_current_json_schema_validator_version(self):
+        with mockpatch.PatchObject(jsonschema.Draft4Validator,
+                                   "check_schema") as chk_schema:
+            body = {'foo': 'test'}
+            self._test_validate_pass(self.schema, body)
+            chk_schema.mock.assert_called_once_with(
+                self.schema['response_body'])
diff --git a/tempest/tests/lib/test_ssh.py b/tempest/tests/lib/test_ssh.py
new file mode 100644
index 0000000..8a0a84c
--- /dev/null
+++ b/tempest/tests/lib/test_ssh.py
@@ -0,0 +1,258 @@
+# Copyright 2014 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.
+
+from io import StringIO
+import socket
+
+import mock
+import six
+import testtools
+
+from tempest.lib.common import ssh
+from tempest.lib import exceptions
+from tempest.tests import base
+import tempest.tests.utils as utils
+
+
+class TestSshClient(base.TestCase):
+
+    SELECT_POLLIN = 1
+
+    @mock.patch('paramiko.RSAKey.from_private_key')
+    @mock.patch('six.StringIO')
+    def test_pkey_calls_paramiko_RSAKey(self, cs_mock, rsa_mock):
+        cs_mock.return_value = mock.sentinel.csio
+        pkey = 'mykey'
+        ssh.Client('localhost', 'root', pkey=pkey)
+        rsa_mock.assert_called_once_with(mock.sentinel.csio)
+        cs_mock.assert_called_once_with('mykey')
+        rsa_mock.reset_mock()
+        cs_mock.reset_mock()
+        pkey = mock.sentinel.pkey
+        # Shouldn't call out to load a file from RSAKey, since
+        # a sentinel isn't a basestring...
+        ssh.Client('localhost', 'root', pkey=pkey)
+        self.assertEqual(0, rsa_mock.call_count)
+        self.assertEqual(0, cs_mock.call_count)
+
+    def _set_ssh_connection_mocks(self):
+        client_mock = mock.MagicMock()
+        client_mock.connect.return_value = True
+        return (self.patch('paramiko.SSHClient'),
+                self.patch('paramiko.AutoAddPolicy'),
+                client_mock)
+
+    def test_get_ssh_connection(self):
+        c_mock, aa_mock, client_mock = self._set_ssh_connection_mocks()
+        s_mock = self.patch('time.sleep')
+
+        c_mock.return_value = client_mock
+        aa_mock.return_value = mock.sentinel.aa
+
+        # Test normal case for successful connection on first try
+        client = ssh.Client('localhost', 'root', timeout=2)
+        client._get_ssh_connection(sleep=1)
+
+        aa_mock.assert_called_once_with()
+        client_mock.set_missing_host_key_policy.assert_called_once_with(
+            mock.sentinel.aa)
+        expected_connect = [mock.call(
+            'localhost',
+            port=22,
+            username='root',
+            pkey=None,
+            key_filename=None,
+            look_for_keys=False,
+            timeout=10.0,
+            password=None
+        )]
+        self.assertEqual(expected_connect, client_mock.connect.mock_calls)
+        self.assertEqual(0, s_mock.call_count)
+
+    @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
+        client_mock.connect.side_effect = [
+            socket.error,
+            mock.MagicMock()
+        ]
+
+        client = ssh.Client('localhost', 'root', timeout=1)
+        client._get_ssh_connection(sleep=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,
+            socket.error,
+            socket.error,
+        ]
+
+        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):
+        chan_mock, poll_mock, _ = self._set_mocks_for_select([0, 0, 0], True)
+
+        # Test for a timeout condition immediately raised
+        client = ssh.Client('localhost', 'root', timeout=2)
+        with testtools.ExpectedException(exceptions.TimeoutException):
+            client.exec_command("test")
+
+        chan_mock.fileno.assert_called_once_with()
+        chan_mock.exec_command.assert_called_once_with("test")
+        chan_mock.shutdown_write.assert_called_once_with()
+
+        poll_mock.register.assert_called_once_with(
+            chan_mock, self.SELECT_POLLIN)
+        poll_mock.poll.assert_called_once_with(10)
+
+    @mock.patch('select.POLLIN', SELECT_POLLIN, create=True)
+    def test_exec_command(self):
+        chan_mock, poll_mock, select_mock = (
+            self._set_mocks_for_select([[1, 0, 0]], True))
+
+        chan_mock.recv_exit_status.return_value = 0
+        chan_mock.recv.return_value = b''
+        chan_mock.recv_stderr.return_value = b''
+
+        client = ssh.Client('localhost', 'root', timeout=2)
+        client.exec_command("test")
+
+        chan_mock.fileno.assert_called_once_with()
+        chan_mock.exec_command.assert_called_once_with("test")
+        chan_mock.shutdown_write.assert_called_once_with()
+
+        select_mock.assert_called_once_with()
+        poll_mock.register.assert_called_once_with(
+            chan_mock, self.SELECT_POLLIN)
+        poll_mock.poll.assert_called_once_with(10)
+        chan_mock.recv_ready.assert_called_once_with()
+        chan_mock.recv.assert_called_once_with(1024)
+        chan_mock.recv_stderr_ready.assert_called_once_with()
+        chan_mock.recv_stderr.assert_called_once_with(1024)
+        chan_mock.recv_exit_status.assert_called_once_with()
+
+    def _set_mocks_for_select(self, poll_data, ito_value=False):
+        gsc_mock = self.patch('tempest.lib.common.ssh.Client.'
+                              '_get_ssh_connection')
+        ito_mock = self.patch('tempest.lib.common.ssh.Client._is_timed_out')
+        csp_mock = self.patch(
+            'tempest.lib.common.ssh.Client._can_system_poll')
+        csp_mock.return_value = True
+
+        select_mock = self.patch('select.poll', create=True)
+        client_mock = mock.MagicMock()
+        tran_mock = mock.MagicMock()
+        chan_mock = mock.MagicMock()
+        poll_mock = mock.MagicMock()
+
+        select_mock.return_value = poll_mock
+        gsc_mock.return_value = client_mock
+        ito_mock.return_value = ito_value
+        client_mock.get_transport.return_value = tran_mock
+        tran_mock.open_session().__enter__.return_value = chan_mock
+        if isinstance(poll_data[0], list):
+            poll_mock.poll.side_effect = poll_data
+        else:
+            poll_mock.poll.return_value = poll_data
+
+        return chan_mock, poll_mock, select_mock
+
+    _utf8_string = six.unichr(1071)
+    _utf8_bytes = _utf8_string.encode("utf-8")
+
+    @mock.patch('select.POLLIN', SELECT_POLLIN, create=True)
+    def test_exec_good_command_output(self):
+        chan_mock, poll_mock, _ = self._set_mocks_for_select([1, 0, 0])
+        closed_prop = mock.PropertyMock(return_value=True)
+        type(chan_mock).closed = closed_prop
+
+        chan_mock.recv_exit_status.return_value = 0
+        chan_mock.recv.side_effect = [self._utf8_bytes[0:1],
+                                      self._utf8_bytes[1:], b'R', b'']
+        chan_mock.recv_stderr.return_value = b''
+
+        client = ssh.Client('localhost', 'root', timeout=2)
+        out_data = client.exec_command("test")
+        self.assertEqual(self._utf8_string + 'R', out_data)
+
+    @mock.patch('select.POLLIN', SELECT_POLLIN, create=True)
+    def test_exec_bad_command_output(self):
+        chan_mock, poll_mock, _ = self._set_mocks_for_select([1, 0, 0])
+        closed_prop = mock.PropertyMock(return_value=True)
+        type(chan_mock).closed = closed_prop
+
+        chan_mock.recv_exit_status.return_value = 1
+        chan_mock.recv.return_value = b''
+        chan_mock.recv_stderr.side_effect = [b'R', self._utf8_bytes[0:1],
+                                             self._utf8_bytes[1:], b'']
+
+        client = ssh.Client('localhost', 'root', timeout=2)
+        exc = self.assertRaises(exceptions.SSHExecCommandFailed,
+                                client.exec_command, "test")
+        self.assertIn('R' + self._utf8_string, six.text_type(exc))
+
+    def test_exec_command_no_select(self):
+        gsc_mock = self.patch('tempest.lib.common.ssh.Client.'
+                              '_get_ssh_connection')
+        csp_mock = self.patch(
+            'tempest.lib.common.ssh.Client._can_system_poll')
+        csp_mock.return_value = False
+
+        select_mock = self.patch('select.poll', create=True)
+        client_mock = mock.MagicMock()
+        tran_mock = mock.MagicMock()
+        chan_mock = mock.MagicMock()
+
+        # Test for proper reading of STDOUT and STDERROR
+
+        gsc_mock.return_value = client_mock
+        client_mock.get_transport.return_value = tran_mock
+        tran_mock.open_session().__enter__.return_value = chan_mock
+        chan_mock.recv_exit_status.return_value = 0
+
+        std_out_mock = mock.MagicMock(StringIO)
+        std_err_mock = mock.MagicMock(StringIO)
+        chan_mock.makefile.return_value = std_out_mock
+        chan_mock.makefile_stderr.return_value = std_err_mock
+
+        client = ssh.Client('localhost', 'root', timeout=2)
+        client.exec_command("test")
+
+        chan_mock.makefile.assert_called_once_with('rb', 1024)
+        chan_mock.makefile_stderr.assert_called_once_with('rb', 1024)
+        std_out_mock.read.assert_called_once_with()
+        std_err_mock.read.assert_called_once_with()
+        self.assertFalse(select_mock.called)
diff --git a/tempest/tests/lib/test_tempest_lib.py b/tempest/tests/lib/test_tempest_lib.py
new file mode 100644
index 0000000..4d9f099
--- /dev/null
+++ b/tempest/tests/lib/test_tempest_lib.py
@@ -0,0 +1,26 @@
+# 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.
+
+"""
+test_tempest.lib
+----------------------------------
+
+Tests for `tempest.lib` module.
+"""
+
+from tempest.tests import base
+
+
+class TestTempest_lib(base.TestCase):
+
+    def test_something(self):
+        pass
diff --git a/tempest/tests/negative/test_negative_auto_test.py b/tempest/tests/negative/test_negative_auto_test.py
deleted file mode 100644
index 7a127cd..0000000
--- a/tempest/tests/negative/test_negative_auto_test.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright 2014 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.
-
-from tempest import config
-import tempest.test as test
-from tempest.tests import base
-from tempest.tests import fake_config
-
-
-class TestNegativeAutoTest(base.TestCase):
-    # Fake entries
-    _service = 'compute'
-
-    fake_input_desc = {"name": "list-flavors-with-detail",
-                       "http-method": "GET",
-                       "url": "flavors/detail",
-                       "json-schema": {"type": "object",
-                                       "properties":
-                                       {"minRam": {"type": "integer"},
-                                        "minDisk": {"type": "integer"}}
-                                       },
-                       "resources": ["flavor", "volume", "image"]
-                       }
-
-    def setUp(self):
-        super(TestNegativeAutoTest, self).setUp()
-        self.useFixture(fake_config.ConfigFixture())
-        self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
-
-    def _check_prop_entries(self, result, entry):
-        entries = [a for a in result if entry in a[0]]
-        self.assertIsNotNone(entries)
-        self.assertGreater(len(entries), 1)
-        for entry in entries:
-            self.assertIsNotNone(entry[1]['_negtest_name'])
-
-    def _check_resource_entries(self, result, entry):
-        entries = [a for a in result if entry in a[0]]
-        self.assertIsNotNone(entries)
-        self.assertIs(len(entries), 3)
-        for entry in entries:
-            self.assertIsNotNone(entry[1]['resource'])
-
-    def test_generate_scenario(self):
-        scenarios = test.NegativeAutoTest.\
-            generate_scenario(self.fake_input_desc)
-        self.assertIsInstance(scenarios, list)
-        for scenario in scenarios:
-            self.assertIsInstance(scenario, tuple)
-            self.assertIsInstance(scenario[0], str)
-            self.assertIsInstance(scenario[1], dict)
-        self._check_prop_entries(scenarios, "minRam")
-        self._check_prop_entries(scenarios, "minDisk")
-        self._check_resource_entries(scenarios, "inv_res")
diff --git a/tempest/tests/negative/test_negative_generators.py b/tempest/tests/negative/test_negative_generators.py
deleted file mode 100644
index 78fd80d..0000000
--- a/tempest/tests/negative/test_negative_generators.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# Copyright 2014 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 copy
-
-import jsonschema
-import mock
-import six
-
-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
-
-
-class TestNegativeBasicGenerator(base.TestCase):
-    valid_desc = {
-        "name": "list-flavors-with-detail",
-        "http-method": "GET",
-        "url": "flavors/detail",
-        "json-schema": {
-            "type": "object",
-            "properties": {
-                "minRam": {"type": "integer"},
-                "minDisk": {"type": "integer"}
-            }
-        },
-        "resources": ["flavor", "volume", "image"]
-    }
-
-    minimal_desc = {
-        "name": "list-flavors-with-detail",
-        "http-method": "GET",
-        "url": "flavors/detail",
-    }
-
-    add_prop_desc = {
-        "name": "list-flavors-with-detail",
-        "http-method": "GET",
-        "url": "flavors/detail",
-        "unknown_field": [12]
-    }
-
-    invalid_json_schema_desc = {
-        "name": "list-flavors-with-detail",
-        "http-method": "GET",
-        "url": "flavors/detail",
-        "json-schema": {"type": "NotExistingType"}
-    }
-
-    def setUp(self):
-        super(TestNegativeBasicGenerator, self).setUp()
-        self.generator = base_generator.BasicGeneratorSet()
-
-    def _assert_valid_jsonschema_call(self, jsonschema_mock, desc):
-        self.assertEqual(jsonschema_mock.call_count, 1)
-        jsonschema_mock.assert_called_with(desc, self.generator.schema)
-
-    @mock.patch('jsonschema.validate', wraps=jsonschema.validate)
-    def test_validate_schema_with_valid_input(self, jsonschema_mock):
-        self.generator.validate_schema(self.valid_desc)
-        self._assert_valid_jsonschema_call(jsonschema_mock, self.valid_desc)
-
-    @mock.patch('jsonschema.validate', wraps=jsonschema.validate)
-    def test_validate_schema_with_minimal_input(self, jsonschema_mock):
-        self.generator.validate_schema(self.minimal_desc)
-        self._assert_valid_jsonschema_call(jsonschema_mock, self.minimal_desc)
-
-    def test_validate_schema_with_invalid_input(self):
-        self.assertRaises(jsonschema.ValidationError,
-                          self.generator.validate_schema, self.add_prop_desc)
-        self.assertRaises(jsonschema.SchemaError,
-                          self.generator.validate_schema,
-                          self.invalid_json_schema_desc)
-
-
-class BaseNegativeGenerator(object):
-    types = ['string', 'integer', 'object']
-
-    fake_input_obj = {"type": "object",
-                      "properties": {"minRam": {"type": "integer"},
-                                     "diskName": {"type": "string"},
-                                     "maxRam": {"type": "integer", }
-                                     }
-                      }
-
-    unknown_type_schema = {
-        "type": "not_defined"
-    }
-
-    class fake_test_class(object):
-        def __init__(self, scenario):
-            for k, v in six.iteritems(scenario):
-                setattr(self, k, v)
-
-    def _validate_result(self, valid_schema, invalid_schema):
-        for k, v in six.iteritems(valid_schema):
-            self.assertTrue(k in invalid_schema)
-
-    def test_generator_mandatory_functions(self):
-        for data_type in self.types:
-            self.assertIn(data_type, self.generator.types_dict)
-
-    def test_generate_with_unknown_type(self):
-        self.assertRaises(TypeError, self.generator.generate_payload,
-                          self.unknown_type_schema)
-
-
-class TestNegativeValidGenerator(base.TestCase, BaseNegativeGenerator):
-    def setUp(self):
-        super(TestNegativeValidGenerator, self).setUp()
-        self.generator = valid_generator.ValidTestGenerator()
-
-    def test_generate_valid(self):
-        result = self.generator.generate_valid(self.fake_input_obj)
-        self.assertIn("minRam", result)
-        self.assertIsInstance(result["minRam"], int)
-        self.assertIn("diskName", result)
-        self.assertIsInstance(result["diskName"], str)
-
-
-class TestNegativeNegativeGenerator(base.TestCase, BaseNegativeGenerator):
-    def setUp(self):
-        super(TestNegativeNegativeGenerator, self).setUp()
-        self.generator = negative_generator.NegativeTestGenerator()
-
-    def test_generate_obj(self):
-        schema = self.fake_input_obj
-        scenarios = self.generator.generate_scenarios(schema)
-        for scenario in scenarios:
-            test = self.fake_test_class(scenario)
-            valid_schema = \
-                valid_generator.ValidTestGenerator().generate_valid(schema)
-            schema_under_test = copy.copy(valid_schema)
-            expected_result = \
-                self.generator.generate_payload(test, schema_under_test)
-            self.assertEqual(expected_result, None)
-            self._validate_result(valid_schema, schema_under_test)
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_base_compute_client.py b/tempest/tests/services/compute/test_base_compute_client.py
deleted file mode 100644
index 7a55cdb..0000000
--- a/tempest/tests/services/compute/test_base_compute_client.py
+++ /dev/null
@@ -1,136 +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 httplib2
-from oslotest import mockpatch
-from tempest_lib.common import rest_client
-
-from tempest import exceptions
-from tempest.services.compute.json import base_compute_client
-from tempest.tests import fake_auth_provider
-from tempest.tests.services.compute import base
-
-
-class TestMicroversionHeaderCheck(base.BaseComputeServiceTest):
-
-    def setUp(self):
-        super(TestMicroversionHeaderCheck, self).setUp()
-        fake_auth = fake_auth_provider.FakeAuthProvider()
-        self.client = base_compute_client.BaseComputeClient(
-            fake_auth, 'compute', 'regionOne')
-        self.client.set_api_microversion('2.2')
-
-    def _check_microverion_header_in_response(self, fake_response):
-        def request(*args, **kwargs):
-            return (httplib2.Response(fake_response), {})
-
-        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)
-        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)
-        self.assertRaises(exceptions.InvalidHTTPResponseHeader,
-                          self.client.get, 'fake_url')
-
-    def test_no_microverion_header_in_response(self):
-        self._check_microverion_header_in_response({})
-        self.assertRaises(exceptions.InvalidHTTPResponseHeader,
-                          self.client.get, 'fake_url')
-
-
-class DummyServiceClient1(base_compute_client.BaseComputeClient):
-    schema_versions_info = [
-        {'min': None, 'max': '2.1', 'schema': 'schemav21'},
-        {'min': '2.2', 'max': '2.9', 'schema': 'schemav22'},
-        {'min': '2.10', 'max': None, 'schema': 'schemav210'}]
-
-    def return_selected_schema(self):
-        return self.get_schema(self.schema_versions_info)
-
-
-class TestSchemaVersionsNone(base.BaseComputeServiceTest):
-    api_microversion = None
-    expected_schema = 'schemav21'
-
-    def setUp(self):
-        super(TestSchemaVersionsNone, self).setUp()
-        fake_auth = fake_auth_provider.FakeAuthProvider()
-        self.client = DummyServiceClient1(fake_auth, 'compute', 'regionOne')
-        self.client.api_microversion = self.api_microversion
-
-    def test_schema(self):
-        self.assertEqual(self.expected_schema,
-                         self.client.return_selected_schema())
-
-
-class TestSchemaVersionsV21(TestSchemaVersionsNone):
-    api_microversion = '2.1'
-    expected_schema = 'schemav21'
-
-
-class TestSchemaVersionsV22(TestSchemaVersionsNone):
-    api_microversion = '2.2'
-    expected_schema = 'schemav22'
-
-
-class TestSchemaVersionsV25(TestSchemaVersionsNone):
-    api_microversion = '2.5'
-    expected_schema = 'schemav22'
-
-
-class TestSchemaVersionsV29(TestSchemaVersionsNone):
-    api_microversion = '2.9'
-    expected_schema = 'schemav22'
-
-
-class TestSchemaVersionsV210(TestSchemaVersionsNone):
-    api_microversion = '2.10'
-    expected_schema = 'schemav210'
-
-
-class TestSchemaVersionsLatest(TestSchemaVersionsNone):
-    api_microversion = 'latest'
-    expected_schema = 'schemav210'
-
-
-class DummyServiceClient2(base_compute_client.BaseComputeClient):
-    schema_versions_info = [
-        {'min': None, 'max': '2.1', 'schema': 'schemav21'},
-        {'min': '2.2', 'max': '2.9', 'schema': 'schemav22'}]
-
-    def return_selected_schema(self):
-        return self.get_schema(self.schema_versions_info)
-
-
-class TestSchemaVersionsNotFound(base.BaseComputeServiceTest):
-    api_microversion = '2.10'
-    expected_schema = 'schemav210'
-
-    def setUp(self):
-        super(TestSchemaVersionsNotFound, self).setUp()
-        fake_auth = fake_auth_provider.FakeAuthProvider()
-        self.client = DummyServiceClient2(fake_auth, 'compute', 'regionOne')
-        self.client.api_microversion = self.api_microversion
-
-    def test_schema(self):
-        self.assertRaises(exceptions.JSONSchemaNotFound,
-                          self.client.return_selected_schema)
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 03aee53..0000000
--- a/tempest/tests/services/compute/test_keypairs_client.py
+++ /dev/null
@@ -1,95 +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_lib.tests import fake_auth_provider
-
-from tempest.services.compute.json import keypairs_client
-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/services/compute/__init__.py b/tempest/tests/services/object_storage/__init__.py
similarity index 100%
copy from tempest/tests/services/compute/__init__.py
copy to tempest/tests/services/object_storage/__init__.py
diff --git a/tempest/tests/services/object_storage/test_object_client.py b/tempest/tests/services/object_storage/test_object_client.py
new file mode 100644
index 0000000..748614c
--- /dev/null
+++ b/tempest/tests/services/object_storage/test_object_client.py
@@ -0,0 +1,108 @@
+# Copyright 2016 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 mock
+
+from tempest.lib import exceptions
+from tempest.services.object_storage import object_client
+from tempest.tests import base
+from tempest.tests.lib import fake_auth_provider
+
+
+class TestObjectClient(base.TestCase):
+
+    def setUp(self):
+        super(TestObjectClient, self).setUp()
+        self.fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.url = self.fake_auth.base_url(None)
+        self.object_client = object_client.ObjectClient(self.fake_auth,
+                                                        'swift', 'region1')
+
+    @mock.patch.object(object_client, 'create_connection')
+    def test_create_object_continue_no_data(self, mock_poc):
+        self._validate_create_object_continue(None, mock_poc)
+
+    @mock.patch.object(object_client, 'create_connection')
+    def test_create_object_continue_with_data(self, mock_poc):
+        self._validate_create_object_continue('hello', mock_poc)
+
+    @mock.patch.object(object_client, 'create_connection')
+    def test_create_continue_with_no_continue_received(self, mock_poc):
+        self._validate_create_object_continue('hello', mock_poc,
+                                              initial_status=201)
+
+    def _validate_create_object_continue(self, req_data,
+                                         mock_poc, initial_status=100):
+
+        expected_hdrs = {
+            'X-Auth-Token': self.fake_auth.get_token(),
+            'content-length': 0 if req_data is None else len(req_data),
+            'Expect': '100-continue'}
+
+        # Setup the Mocks prior to invoking the object creation
+        mock_resp_cls = mock.Mock()
+        mock_resp_cls._read_status.return_value = ("1", initial_status, "OK")
+
+        mock_poc.return_value.response_class.return_value = mock_resp_cls
+
+        # This is the final expected return value
+        mock_poc.return_value.getresponse.return_value.status = 201
+        mock_poc.return_value.getresponse.return_value.reason = 'OK'
+
+        # Call method to PUT object using expect:100-continue
+        cnt = "container1"
+        obj = "object1"
+        path = "/%s/%s" % (cnt, obj)
+
+        # If the expected initial status is not 100, then an exception
+        # should be thrown and the connection closed
+        if initial_status is 100:
+            status, reason = \
+                self.object_client.create_object_continue(cnt, obj, req_data)
+        else:
+            self.assertRaises(exceptions.UnexpectedResponseCode,
+                              self.object_client.create_object_continue, cnt,
+                              obj, req_data)
+            mock_poc.return_value.close.assert_called_once_with()
+
+        # Verify that putrequest is called 1 time with the appropriate values
+        mock_poc.return_value.putrequest.assert_called_once_with('PUT', path)
+
+        # Verify that headers were written, including "Expect:100-continue"
+        calls = []
+
+        for header, value in expected_hdrs.items():
+            calls.append(mock.call(header, value))
+
+        mock_poc.return_value.putheader.assert_has_calls(calls, False)
+        mock_poc.return_value.endheaders.assert_called_once_with()
+
+        # The following steps are only taken if the initial status is 100
+        if initial_status is 100:
+            # Verify that the method returned what it was supposed to
+            self.assertEqual(status, 201)
+
+            # Verify that _safe_read was called once to remove the CRLF
+            # after the 100 response
+            mock_rc = mock_poc.return_value.response_class.return_value
+            mock_rc._safe_read.assert_called_once_with(2)
+
+            # Verify the actual data was written via send
+            mock_poc.return_value.send.assert_called_once_with(req_data)
+
+            # Verify that the getresponse method was called to receive
+            # the final
+            mock_poc.return_value.getresponse.assert_called_once_with()
diff --git a/tempest/tests/services/test_base_microversion_client.py b/tempest/tests/services/test_base_microversion_client.py
deleted file mode 100644
index 11b8170..0000000
--- a/tempest/tests/services/test_base_microversion_client.py
+++ /dev/null
@@ -1,75 +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 httplib2
-import mock
-from tempest_lib.common import rest_client
-
-from tempest.services import base_microversion_client
-from tempest.tests import fake_auth_provider
-from tempest.tests.services.compute import base
-
-
-class TestClientWithoutMicroversionHeader(base.BaseComputeServiceTest):
-
-    def setUp(self):
-        super(TestClientWithoutMicroversionHeader, self).setUp()
-        fake_auth = fake_auth_provider.FakeAuthProvider()
-        self.client = base_microversion_client.BaseMicroversionClient(
-            fake_auth, 'compute', 'regionOne', 'X-OpenStack-Nova-API-Version')
-
-    def test_no_microverion_header(self):
-        header = self.client.get_headers()
-        self.assertNotIn(self.client.api_microversion_header_name, header)
-
-    def test_no_microverion_header_in_raw_request(self):
-        def raw_request(*args, **kwargs):
-            self.assertNotIn(self.client.api_microversion_header_name,
-                             kwargs['headers'])
-            return (httplib2.Response({'status': 200}), {})
-
-        with mock.patch.object(rest_client.RestClient,
-                               'raw_request') as mock_get:
-            mock_get.side_effect = raw_request
-            self.client.get('fake_url')
-
-
-class TestClientWithMicroversionHeader(base.BaseComputeServiceTest):
-
-    def setUp(self):
-        super(TestClientWithMicroversionHeader, self).setUp()
-        fake_auth = fake_auth_provider.FakeAuthProvider()
-        self.client = base_microversion_client.BaseMicroversionClient(
-            fake_auth, 'compute', 'regionOne', 'X-OpenStack-Nova-API-Version')
-        self.client.set_api_microversion('2.2')
-
-    def test_microverion_header(self):
-        header = self.client.get_headers()
-        self.assertIn(self.client.api_microversion_header_name, header)
-        self.assertEqual(self.client.api_microversion,
-                         header[self.client.api_microversion_header_name])
-
-    def test_microverion_header_in_raw_request(self):
-        def raw_request(*args, **kwargs):
-            self.assertIn(self.client.api_microversion_header_name,
-                          kwargs['headers'])
-            self.assertEqual(
-                self.client.api_microversion,
-                kwargs['headers'][self.client.api_microversion_header_name])
-            return (httplib2.Response({'status': 200}), {})
-
-        with mock.patch.object(rest_client.RestClient,
-                               'raw_request') as mock_get:
-            mock_get.side_effect = raw_request
-            self.client.get('fake_url')
diff --git a/tempest/tests/stress/test_stress.py b/tempest/tests/stress/test_stress.py
deleted file mode 100644
index 0ec2a5d..0000000
--- a/tempest/tests/stress/test_stress.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2013 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 shlex
-import subprocess
-
-from tempest_lib import exceptions
-
-from oslo_log import log as logging
-from tempest.tests import base
-
-LOG = logging.getLogger(__name__)
-
-
-class StressFrameworkTest(base.TestCase):
-    """Basic test for the stress test framework."""
-
-    def _cmd(self, cmd, param):
-        """Executes specified command."""
-        cmd = ' '.join([cmd, param])
-        LOG.info("running: '%s'" % cmd)
-        cmd_str = cmd
-        cmd = shlex.split(cmd)
-        result = ''
-        result_err = ''
-        try:
-            stdout = subprocess.PIPE
-            stderr = subprocess.PIPE
-            proc = subprocess.Popen(
-                cmd, stdout=stdout, stderr=stderr)
-            result, result_err = proc.communicate()
-            if proc.returncode != 0:
-                LOG.debug('error of %s:\n%s' % (cmd_str, result_err))
-                raise exceptions.CommandFailed(proc.returncode,
-                                               cmd,
-                                               result)
-        finally:
-            LOG.debug('output of %s:\n%s' % (cmd_str, result))
-        return proc.returncode
-
-    def test_help_function(self):
-        result = self._cmd("python", "-m tempest.cmd.run_stress -h")
-        self.assertEqual(0, result)
diff --git a/tempest/tests/stress/test_stressaction.py b/tempest/tests/stress/test_stressaction.py
deleted file mode 100644
index 1a1bb67..0000000
--- a/tempest/tests/stress/test_stressaction.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright 2013 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 tempest.stress.stressaction as stressaction
-import tempest.test
-
-
-class FakeStressAction(stressaction.StressAction):
-    def __init__(self, manager, max_runs=None, stop_on_error=False):
-        super(self.__class__, self).__init__(manager, max_runs, stop_on_error)
-        self._run_called = False
-
-    def run(self):
-        self._run_called = True
-
-    @property
-    def run_called(self):
-        return self._run_called
-
-
-class FakeStressActionFailing(stressaction.StressAction):
-    def run(self):
-        raise Exception('FakeStressActionFailing raise exception')
-
-
-class TestStressAction(tempest.test.BaseTestCase):
-    def _bulid_stats_dict(self, runs=0, fails=0):
-        return {'runs': runs, 'fails': fails}
-
-    def testStressTestRun(self):
-        stressAction = FakeStressAction(manager=None, max_runs=1)
-        stats = self._bulid_stats_dict()
-        stressAction.execute(stats)
-        self.assertTrue(stressAction.run_called)
-        self.assertEqual(stats['runs'], 1)
-        self.assertEqual(stats['fails'], 0)
-
-    def testStressMaxTestRuns(self):
-        stressAction = FakeStressAction(manager=None, max_runs=500)
-        stats = self._bulid_stats_dict(runs=499)
-        stressAction.execute(stats)
-        self.assertTrue(stressAction.run_called)
-        self.assertEqual(stats['runs'], 500)
-        self.assertEqual(stats['fails'], 0)
-
-    def testStressTestRunWithException(self):
-        stressAction = FakeStressActionFailing(manager=None, max_runs=1)
-        stats = self._bulid_stats_dict()
-        stressAction.execute(stats)
-        self.assertEqual(stats['runs'], 1)
-        self.assertEqual(stats['fails'], 1)
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
new file mode 100644
index 0000000..01b8a72
--- /dev/null
+++ b/tempest/tests/test_base_test.py
@@ -0,0 +1,110 @@
+# Copyright 2016 IBM Corp.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import mock
+
+from tempest import clients
+from tempest.common import credentials_factory as credentials
+from tempest.common import fixed_network
+from tempest import config
+from tempest import test
+from tempest.tests import base
+from tempest.tests import fake_config
+
+
+class TestBaseTestCase(base.TestCase):
+    def setUp(self):
+        super(TestBaseTestCase, self).setUp()
+        self.useFixture(fake_config.ConfigFixture())
+        self.fixed_network_name = 'fixed-net'
+        config.CONF.compute.fixed_network_name = self.fixed_network_name
+        config.CONF.service_available.neutron = True
+
+    @mock.patch.object(test.BaseTestCase, 'get_client_manager')
+    @mock.patch.object(test.BaseTestCase, '_get_credentials_provider')
+    @mock.patch.object(fixed_network, 'get_tenant_network')
+    def test_get_tenant_network(self, mock_gtn, mock_gprov, mock_gcm):
+        net_client = mock.Mock()
+        mock_prov = mock.Mock()
+        mock_gcm.return_value.compute_networks_client = net_client
+        mock_gprov.return_value = mock_prov
+
+        test.BaseTestCase.get_tenant_network()
+
+        mock_gcm.assert_called_once_with(credential_type='primary')
+        mock_gprov.assert_called_once_with()
+        mock_gtn.assert_called_once_with(mock_prov, net_client,
+                                         self.fixed_network_name)
+
+    @mock.patch.object(test.BaseTestCase, 'get_client_manager')
+    @mock.patch.object(test.BaseTestCase, '_get_credentials_provider')
+    @mock.patch.object(fixed_network, 'get_tenant_network')
+    @mock.patch.object(test.BaseTestCase, 'get_identity_version')
+    @mock.patch.object(credentials, 'is_admin_available')
+    @mock.patch.object(clients, 'Manager')
+    def test_get_tenant_network_with_nova_net(self, mock_man, mock_iaa,
+                                              mock_giv, mock_gtn, mock_gcp,
+                                              mock_gcm):
+        config.CONF.service_available.neutron = False
+        mock_prov = mock.Mock()
+        mock_admin_man = mock.Mock()
+        mock_iaa.return_value = True
+        mock_gcp.return_value = mock_prov
+        mock_man.return_value = mock_admin_man
+
+        test.BaseTestCase.get_tenant_network()
+
+        mock_man.assert_called_once_with(
+            mock_prov.get_admin_creds.return_value.credentials)
+        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_config.py b/tempest/tests/test_config.py
new file mode 100644
index 0000000..2808a9c
--- /dev/null
+++ b/tempest/tests/test_config.py
@@ -0,0 +1,89 @@
+# Copyright (c) 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.
+
+import testtools
+
+from tempest import config
+from tempest.lib import exceptions
+from tempest.tests import base
+from tempest.tests import fake_config
+
+
+class TestServiceClientConfig(base.TestCase):
+
+    expected_common_params = set(['disable_ssl_certificate_validation',
+                                  'ca_certs', 'trace_requests'])
+    expected_extra_params = set(['service', 'endpoint_type', 'region',
+                                 'build_timeout', 'build_interval'])
+
+    def setUp(self):
+        super(TestServiceClientConfig, self).setUp()
+        self.useFixture(fake_config.ServiceClientsConfigFixture())
+        self.patchobject(config, 'CONF',
+                         fake_config.ServiceClientsFakePrivate())
+        self.CONF = config.CONF
+
+    def test_service_client_config_no_service(self):
+        params = config.service_client_config()
+        for param_name in self.expected_common_params:
+            self.assertIn(param_name, params)
+        for param_name in self.expected_extra_params:
+            self.assertNotIn(param_name, params)
+        self.assertEqual(
+            self.CONF.identity.disable_ssl_certificate_validation,
+            params['disable_ssl_certificate_validation'])
+        self.assertEqual(self.CONF.identity.ca_certificates_file,
+                         params['ca_certs'])
+        self.assertEqual(self.CONF.debug.trace_requests,
+                         params['trace_requests'])
+
+    def test_service_client_config_service_all(self):
+        params = config.service_client_config(
+            service_client_name='fake-service1')
+        for param_name in self.expected_common_params:
+            self.assertIn(param_name, params)
+        for param_name in self.expected_extra_params:
+            self.assertIn(param_name, params)
+        self.assertEqual(self.CONF.fake_service1.catalog_type,
+                         params['service'])
+        self.assertEqual(self.CONF.fake_service1.endpoint_type,
+                         params['endpoint_type'])
+        self.assertEqual(self.CONF.fake_service1.region, params['region'])
+        self.assertEqual(self.CONF.fake_service1.build_timeout,
+                         params['build_timeout'])
+        self.assertEqual(self.CONF.fake_service1.build_interval,
+                         params['build_interval'])
+
+    def test_service_client_config_service_minimal(self):
+        params = config.service_client_config(
+            service_client_name='fake-service2')
+        for param_name in self.expected_common_params:
+            self.assertIn(param_name, params)
+        for param_name in self.expected_extra_params:
+            self.assertIn(param_name, params)
+        self.assertEqual(self.CONF.fake_service2.catalog_type,
+                         params['service'])
+        self.assertEqual(self.CONF.fake_service2.endpoint_type,
+                         params['endpoint_type'])
+        self.assertEqual(self.CONF.identity.region, params['region'])
+        self.assertEqual(self.CONF.compute.build_timeout,
+                         params['build_timeout'])
+        self.assertEqual(self.CONF.compute.build_interval,
+                         params['build_interval'])
+
+    def test_service_client_config_service_unknown(self):
+        unknown_service = 'unknown_service'
+        with testtools.ExpectedException(exceptions.UnknownServiceClient,
+                                         '.*' + unknown_service + '.*'):
+            config.service_client_config(service_client_name=unknown_service)
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index 98b045a..ae2f2a3 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -12,15 +12,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
-import mock
 from oslo_config import cfg
 from oslotest import mockpatch
 import testtools
 
 from tempest import config
 from tempest import exceptions
+from tempest.lib.common.utils import data_utils
 from tempest import test
 from tempest.tests import base
 from tempest.tests import fake_config
@@ -30,7 +28,8 @@
     def setUp(self):
         super(BaseDecoratorsTest, self).setUp()
         self.config_fixture = self.useFixture(fake_config.ConfigFixture())
-        self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
 
 
 class TestAttrDecorator(BaseDecoratorsTest):
@@ -79,13 +78,13 @@
         return foo
 
     def test_positive(self):
-        _id = str(uuid.uuid4())
+        _id = data_utils.rand_uuid()
         foo = self._test_helper(_id)
         self.assertIn('id-%s' % _id, getattr(foo, '__testtools_attrs'))
         self.assertTrue(foo.__doc__.startswith('Test idempotent id: %s' % _id))
 
     def test_positive_without_doc(self):
-        _id = str(uuid.uuid4())
+        _id = data_utils.rand_uuid()
         foo = self._test_helper_without_doc(_id)
         self.assertTrue(foo.__doc__.startswith('Test idempotent id: %s' % _id))
 
@@ -154,36 +153,6 @@
                 continue
 
 
-class TestStressDecorator(BaseDecoratorsTest):
-    def _test_stresstest_helper(self, expected_frequency='process',
-                                expected_inheritance=False,
-                                **decorator_args):
-        @test.stresstest(**decorator_args)
-        def foo():
-            pass
-        self.assertEqual(getattr(foo, 'st_class_setup_per'),
-                         expected_frequency)
-        self.assertEqual(getattr(foo, 'st_allow_inheritance'),
-                         expected_inheritance)
-        self.assertEqual(set(['stress']), getattr(foo, '__testtools_attrs'))
-
-    def test_stresstest_decorator_default(self):
-        self._test_stresstest_helper()
-
-    def test_stresstest_decorator_class_setup_frequency(self):
-        self._test_stresstest_helper('process', class_setup_per='process')
-
-    def test_stresstest_decorator_class_setup_frequency_non_default(self):
-        self._test_stresstest_helper(expected_frequency='application',
-                                     class_setup_per='application')
-
-    def test_stresstest_decorator_set_frequency_and_inheritance(self):
-        self._test_stresstest_helper(expected_frequency='application',
-                                     expected_inheritance=True,
-                                     class_setup_per='application',
-                                     allow_inheritance=True)
-
-
 class TestRequiresExtDecorator(BaseDecoratorsTest):
     def setUp(self):
         super(TestRequiresExtDecorator, self).setUp()
@@ -201,7 +170,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 +188,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',
@@ -226,17 +201,94 @@
                           service='bad_service')
 
 
-class TestSimpleNegativeDecorator(BaseDecoratorsTest):
-    @test.SimpleNegativeAutoTest
-    class FakeNegativeJSONTest(test.NegativeAutoTest):
-        _schema = {}
+class TestConfigDecorators(BaseDecoratorsTest):
+    def setUp(self):
+        super(TestConfigDecorators, self).setUp()
+        cfg.CONF.set_default('nova', True, 'service_available')
+        cfg.CONF.set_default('glance', False, 'service_available')
 
-    def test_testfunc_exist(self):
-        self.assertIn("test_fake_negative", dir(self.FakeNegativeJSONTest))
+    def _assert_skip_message(self, func, skip_msg):
+        try:
+            func()
+            self.fail()
+        except testtools.TestCase.skipException as skip_exc:
+            self.assertEqual(skip_exc.args[0], skip_msg)
 
-    @mock.patch('tempest.test.NegativeAutoTest.execute')
-    def test_testfunc_calls_execute(self, mock):
-        obj = self.FakeNegativeJSONTest("test_fake_negative")
-        self.assertIn("test_fake_negative", dir(obj))
-        obj.test_fake_negative()
-        mock.assert_called_once_with(self.FakeNegativeJSONTest._schema)
+    def _test_skip_unless_config(self, expected_to_skip=True, *decorator_args):
+
+        class TestFoo(test.BaseTestCase):
+            @config.skip_unless_config(*decorator_args)
+            def test_bar(self):
+                return 0
+
+        t = TestFoo('test_bar')
+        if expected_to_skip:
+            self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+            if (len(decorator_args) >= 3):
+                # decorator_args[2]: skip message specified
+                self._assert_skip_message(t.test_bar, decorator_args[2])
+        else:
+            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_skip_if_config(self, expected_to_skip=True,
+                             *decorator_args):
+
+        class TestFoo(test.BaseTestCase):
+            @config.skip_if_config(*decorator_args)
+            def test_bar(self):
+                return 0
+
+        t = TestFoo('test_bar')
+        if expected_to_skip:
+            self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+            if (len(decorator_args) >= 3):
+                # decorator_args[2]: skip message specified
+                self._assert_skip_message(t.test_bar, decorator_args[2])
+        else:
+            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_skip_unless_no_group(self):
+        self._test_skip_unless_config(True, 'fake_group', 'an_option')
+
+    def test_skip_unless_no_option(self):
+        self._test_skip_unless_config(True, 'service_available',
+                                      'not_an_option')
+
+    def test_skip_unless_false_option(self):
+        self._test_skip_unless_config(True, 'service_available', 'glance')
+
+    def test_skip_unless_false_option_msg(self):
+        self._test_skip_unless_config(True, 'service_available', 'glance',
+                                      'skip message')
+
+    def test_skip_unless_true_option(self):
+        self._test_skip_unless_config(False,
+                                      'service_available', 'nova')
+
+    def test_skip_if_no_group(self):
+        self._test_skip_if_config(False, 'fake_group', 'an_option')
+
+    def test_skip_if_no_option(self):
+        self._test_skip_if_config(False, 'service_available', 'not_an_option')
+
+    def test_skip_if_false_option(self):
+        self._test_skip_if_config(False, 'service_available', 'glance')
+
+    def test_skip_if_true_option(self):
+        self._test_skip_if_config(True, 'service_available', 'nova')
+
+    def test_skip_if_true_option_msg(self):
+        self._test_skip_if_config(True, 'service_available', 'nova',
+                                  'skip message')
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
deleted file mode 100644
index db9db34..0000000
--- a/tempest/tests/test_glance_http.py
+++ /dev/null
@@ -1,156 +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 mock
-from oslotest import mockpatch
-import six
-from six.moves import http_client as httplib
-
-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
-
-
-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()
-
-        self.fake_auth.base_url = mock.MagicMock(return_value=self.endpoint)
-
-        self.useFixture(mockpatch.PatchObject(
-            httplib.HTTPConnection,
-            'request',
-            side_effect=self.fake_http.request(self.endpoint)[1]))
-        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))
-        self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
-                        'getresponse', return_value=resp))
-        return resp
-
-    def test_raw_request(self):
-        self._set_response_fixture({}, 200, 'fake_response_body')
-        resp, body = self.client.raw_request('GET', '/images')
-        self.assertEqual(200, resp.status)
-        self.assertEqual('fake_response_body', body.read())
-
-    def test_raw_request_with_response_chunked(self):
-        self._set_response_fixture({}, 200, 'fake_response_body')
-        self.useFixture(mockpatch.PatchObject(glance_http,
-                                              'CHUNKSIZE', 1))
-        resp, body = self.client.raw_request('GET', '/images')
-        self.assertEqual(200, resp.status)
-        self.assertEqual('fake_response_body', body.read())
-
-    def test_raw_request_chunked(self):
-        self.useFixture(mockpatch.PatchObject(glance_http,
-                                              'CHUNKSIZE', 1))
-        self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
-                        'endheaders'))
-        self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
-                        'send'))
-
-        self._set_response_fixture({}, 200, 'fake_response_body')
-        req_body = six.StringIO('fake_request_body')
-        resp, body = self.client.raw_request('PUT', '/images', body=req_body)
-        self.assertEqual(200, resp.status)
-        self.assertEqual('fake_response_body', body.read())
-        call_count = httplib.HTTPConnection.send.call_count
-        self.assertEqual(call_count - 1, req_body.tell())
-
-    def test_get_connection_class_for_https(self):
-        conn_class = self.client._get_connection_class('https')
-        self.assertEqual(glance_http.VerifiedHTTPSConnection, conn_class)
-
-    def test_get_connection_class_for_http(self):
-        conn_class = (self.client._get_connection_class('http'))
-        self.assertEqual(httplib.HTTPConnection, conn_class)
-
-    def test_get_connection_http(self):
-        self.assertTrue(isinstance(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))
-
-    def test_get_connection_url_not_fount(self):
-        self.useFixture(mockpatch.PatchObject(self.client, 'connection_class',
-                                              side_effect=httplib.InvalidURL()
-                                              ))
-        self.assertRaises(exceptions.EndpointNotFound,
-                          self.client._get_connection)
-
-    def test_get_connection_kwargs_default_for_http(self):
-        kwargs = self.client._get_connection_kwargs('http')
-        self.assertEqual(600, kwargs['timeout'])
-        self.assertEqual(1, len(kwargs.keys()))
-
-    def test_get_connection_kwargs_set_timeout_for_http(self):
-        kwargs = self.client._get_connection_kwargs('http', timeout=10,
-                                                    ca_certs='foo')
-        self.assertEqual(10, kwargs['timeout'])
-        # nothing more than timeout is evaluated for http connections
-        self.assertEqual(1, len(kwargs.keys()))
-
-    def test_get_connection_kwargs_default_for_https(self):
-        kwargs = self.client._get_connection_kwargs('https')
-        self.assertEqual(600, kwargs['timeout'])
-        self.assertIsNone(kwargs['ca_certs'])
-        self.assertIsNone(kwargs['cert_file'])
-        self.assertIsNone(kwargs['key_file'])
-        self.assertEqual(False, kwargs['insecure'])
-        self.assertEqual(True, kwargs['ssl_compression'])
-        self.assertEqual(6, len(kwargs.keys()))
-
-    def test_get_connection_kwargs_set_params_for_https(self):
-        kwargs = self.client._get_connection_kwargs('https', timeout=10,
-                                                    ca_certs='foo',
-                                                    cert_file='/foo/bar.cert',
-                                                    key_file='/foo/key.pem',
-                                                    insecure=True,
-                                                    ssl_compression=False)
-        self.assertEqual(10, kwargs['timeout'])
-        self.assertEqual('foo', kwargs['ca_certs'])
-        self.assertEqual('/foo/bar.cert', kwargs['cert_file'])
-        self.assertEqual('/foo/key.pem', kwargs['key_file'])
-        self.assertEqual(True, kwargs['insecure'])
-        self.assertEqual(False, kwargs['ssl_compression'])
-        self.assertEqual(6, len(kwargs.keys()))
-
-
-class TestResponseBodyIterator(base.TestCase):
-
-    def test_iter_default_chunk_size_64k(self):
-        resp = fake_http.fake_httplib({}, six.StringIO(
-            'X' * (glance_http.CHUNKSIZE + 1)))
-        iterator = glance_http.ResponseBodyIterator(resp)
-        chunks = list(iterator)
-        self.assertEqual(chunks, ['X' * glance_http.CHUNKSIZE, 'X'])
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index 55f00ef..f005c21 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -147,3 +147,36 @@
             " @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'))))
+
+    def test_dont_use_config_in_tempest_lib(self):
+        self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
+            'from tempest import config', './tempest/common/compute.py')))
+        self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
+            'from oslo_concurrency import lockutils',
+            './tempest/lib/auth.py')))
+        self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
+            'from tempest import config', './tempest/lib/auth.py')))
+        self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
+            'from oslo_config import cfg', './tempest/lib/decorators.py')))
+        self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
+            'import tempest.config', './tempest/lib/common/rest_client.py')))
diff --git a/tempest/tests/test_microversions.py b/tempest/tests/test_microversions.py
index 6738641..173accb 100644
--- a/tempest/tests/test_microversions.py
+++ b/tempest/tests/test_microversions.py
@@ -17,7 +17,7 @@
 
 from tempest.api.compute import base as compute_base
 from tempest import config
-from tempest import exceptions
+from tempest.lib import exceptions
 from tempest.tests import base
 from tempest.tests import fake_config
 
@@ -57,16 +57,16 @@
     def setUp(self):
         super(TestMicroversionsTestsClass, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
-        self.stubs.Set(config, 'TempestConfigPrivate',
-                       fake_config.FakePrivate)
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
 
     def _test_version(self, cfg_min, cfg_max,
                       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
deleted file mode 100644
index ce95739..0000000
--- a/tempest/tests/test_negative_rest_client.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# (c) 2015 Deutsche Telekom AG
-# Copyright 2015 Red Hat, Inc.
-# 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 httplib2
-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
-
-
-class TestNegativeRestClient(base.TestCase):
-
-    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):
-        __, return_dict = self.negative_rest_client.send_request('POST',
-                                                                 self.url,
-                                                                 [], {})
-        self.assertEqual('POST', return_dict['method'])
-
-    def test_get(self):
-        __, return_dict = self.negative_rest_client.send_request('GET',
-                                                                 self.url,
-                                                                 [])
-        self.assertEqual('GET', return_dict['method'])
-
-    def test_delete(self):
-        __, return_dict = self.negative_rest_client.send_request('DELETE',
-                                                                 self.url,
-                                                                 [])
-        self.assertEqual('DELETE', return_dict['method'])
-
-    def test_patch(self):
-        __, return_dict = self.negative_rest_client.send_request('PATCH',
-                                                                 self.url,
-                                                                 [], {})
-        self.assertEqual('PATCH', return_dict['method'])
-
-    def test_put(self):
-        __, return_dict = self.negative_rest_client.send_request('PUT',
-                                                                 self.url,
-                                                                 [], {})
-        self.assertEqual('PUT', return_dict['method'])
-
-    def test_head(self):
-        self.useFixture(mockpatch.PatchObject(self.negative_rest_client,
-                                              'response_checker'))
-        __, return_dict = self.negative_rest_client.send_request('HEAD',
-                                                                 self.url,
-                                                                 [])
-        self.assertEqual('HEAD', return_dict['method'])
-
-    def test_copy(self):
-        __, return_dict = self.negative_rest_client.send_request('COPY',
-                                                                 self.url,
-                                                                 [])
-        self.assertEqual('COPY', return_dict['method'])
-
-    def test_other(self):
-        self.assertRaises(AssertionError,
-                          self.negative_rest_client.send_request,
-                          'OTHER', self.url, [])
diff --git a/tempest/tests/test_tempest_plugin.py b/tempest/tests/test_tempest_plugin.py
index c07e98c..13e2499 100644
--- a/tempest/tests/test_tempest_plugin.py
+++ b/tempest/tests/test_tempest_plugin.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.lib.services import clients
 from tempest.test_discover import plugins
 from tempest.tests import base
 from tempest.tests import fake_tempest_plugin as fake_plugin
@@ -42,3 +43,37 @@
                          result['fake01'])
         self.assertEqual(fake_plugin.FakePlugin.expected_load_test,
                          result['fake02'])
+
+    def test__register_service_clients_with_one_plugin(self):
+        registry = clients.ClientsRegistry()
+        manager = plugins.TempestTestPluginManager()
+        fake_obj = fake_plugin.FakeStevedoreObj()
+        manager.ext_plugins = [fake_obj]
+        manager._register_service_clients()
+        expected_result = fake_plugin.FakePlugin.expected_service_clients
+        registered_clients = registry.get_service_clients()
+        self.assertIn(fake_obj.name, registered_clients)
+        self.assertEqual(expected_result, registered_clients[fake_obj.name])
+
+    def test__get_service_clients_with_two_plugins(self):
+        registry = clients.ClientsRegistry()
+        manager = plugins.TempestTestPluginManager()
+        obj1 = fake_plugin.FakeStevedoreObj('fake01')
+        obj2 = fake_plugin.FakeStevedoreObj('fake02')
+        manager.ext_plugins = [obj1, obj2]
+        manager._register_service_clients()
+        expected_result = fake_plugin.FakePlugin.expected_service_clients
+        registered_clients = registry.get_service_clients()
+        self.assertIn('fake01', registered_clients)
+        self.assertIn('fake02', registered_clients)
+        self.assertEqual(expected_result, registered_clients['fake01'])
+        self.assertEqual(expected_result, registered_clients['fake02'])
+
+    def test__register_service_clients_one_plugin_no_service_clients(self):
+        registry = clients.ClientsRegistry()
+        manager = plugins.TempestTestPluginManager()
+        fake_obj = fake_plugin.FakeStevedoreObjNoServiceClients()
+        manager.ext_plugins = [fake_obj]
+        manager._register_service_clients()
+        registered_clients = registry.get_service_clients()
+        self.assertNotIn(fake_obj.name, registered_clients)
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/tempest/version.py b/tempest/version.py
new file mode 100644
index 0000000..bc9f651
--- /dev/null
+++ b/tempest/version.py
@@ -0,0 +1,18 @@
+# Copyright (c) 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.
+
+
+import pbr.version
+
+version_info = pbr.version.VersionInfo('tempest')
diff --git a/test-requirements.txt b/test-requirements.txt
index eb43f31..475fb16 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,12 +1,11 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-hacking<0.11,>=0.10.0
+hacking<0.13,>=0.12.0 # Apache-2.0
 # needed for doc build
-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
-mox>=0.5.3 # Apache-2.0
-mock>=1.2 # BSD
-coverage>=3.6 # Apache-2.0
+sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
+oslosphinx>=4.7.0 # Apache-2.0
+reno>=1.8.0 # Apache-2.0
+mock>=2.0 # BSD
+coverage>=4.0 # Apache-2.0
 oslotest>=1.10.0 # Apache-2.0
diff --git a/tools/check_logs.py b/tools/check_logs.py
index fa7129d..caad85c 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -20,8 +20,8 @@
 import os
 import re
 import six
+import six.moves.urllib.request as urlreq
 import sys
-import urllib2
 
 import yaml
 
@@ -54,7 +54,6 @@
     'q-meta',
     'q-metering',
     'q-svc',
-    'q-vpn',
     's-proxy'])
 
 
@@ -68,9 +67,9 @@
                 logs_with_errors.append(name)
     for (name, url) in url_specs:
         whitelist = whitelists.get(name, [])
-        req = urllib2.Request(url)
+        req = urlreq.Request(url)
         req.add_header('Accept-Encoding', 'gzip')
-        page = urllib2.urlopen(req)
+        page = urlreq.urlopen(req)
         buf = six.StringIO(page.read())
         f = gzip.GzipFile(fileobj=buf)
         if scan_content(name, f.read().splitlines(), regexp, whitelist):
@@ -96,7 +95,7 @@
 
 
 def collect_url_logs(url):
-    page = urllib2.urlopen(url)
+    page = urlreq.urlopen(url)
     content = page.read()
     logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
     return logs
diff --git a/tools/find_stack_traces.py b/tools/find_stack_traces.py
index 49a42fe..f2da27a 100755
--- a/tools/find_stack_traces.py
+++ b/tools/find_stack_traces.py
@@ -19,8 +19,8 @@
 import pprint
 import re
 import six
+import six.moves.urllib.request as urlreq
 import sys
-import urllib2
 
 
 pp = pprint.PrettyPrinter()
@@ -65,9 +65,9 @@
 
 def hunt_for_stacktrace(url):
     """Return TRACE or ERROR lines out of logs."""
-    req = urllib2.Request(url)
+    req = urlreq.Request(url)
     req.add_header('Accept-Encoding', 'gzip')
-    page = urllib2.urlopen(req)
+    page = urlreq.urlopen(req)
     buf = six.StringIO(page.read())
     f = gzip.GzipFile(fileobj=buf)
     content = f.read()
@@ -105,7 +105,7 @@
 
 
 def collect_logs(url):
-    page = urllib2.urlopen(url)
+    page = urlreq.urlopen(url)
     content = page.read()
     logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
     return logs
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
new file mode 100644
index 0000000..03e838e
--- /dev/null
+++ b/tools/generate-tempest-plugins-list.py
@@ -0,0 +1,72 @@
+#! /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):
+    if proj.startswith('openstack/deb-'):
+        return False
+    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/pretty_tox.sh b/tools/pretty_tox.sh
index fb4e6d5..0b83b91 100755
--- a/tools/pretty_tox.sh
+++ b/tools/pretty_tox.sh
@@ -1,5 +1,7 @@
 #!/usr/bin/env bash
 
+echo "WARNING: This script is deprecated and will be removed in the near future. Please migrate to tempest run or another method of launching a test runner"
+
 set -o pipefail
 
 TESTRARGS=$1
diff --git a/tools/pretty_tox_serial.sh b/tools/pretty_tox_serial.sh
index e0fca0f..1f8204e 100755
--- a/tools/pretty_tox_serial.sh
+++ b/tools/pretty_tox_serial.sh
@@ -1,5 +1,7 @@
 #!/usr/bin/env bash
 
+echo "WARNING: This script is deprecated and will be removed in the near future. Please migrate to tempest run or another method of launching a test runner"
+
 set -o pipefail
 
 TESTRARGS=$@
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index a47e217..9735f77 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -20,23 +20,25 @@
 is fixed but a skip is still in the Tempest test code
 """
 
-import logging
 import os
 import re
 
 from launchpadlib import launchpad
+from oslo_log import log as logging
 
 BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
 TESTDIR = os.path.join(BASEDIR, 'tempest')
 LPCACHEDIR = os.path.expanduser('~/.launchpadlib/cache')
 
+LOG = logging.getLogger(__name__)
+
 
 def info(msg, *args, **kwargs):
-    logging.info(msg, *args, **kwargs)
+    LOG.info(msg, *args, **kwargs)
 
 
 def debug(msg, *args, **kwargs):
-    logging.debug(msg, *args, **kwargs)
+    LOG.debug(msg, *args, **kwargs)
 
 
 def find_skips(start=TESTDIR):
@@ -73,35 +75,35 @@
     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
 
 
 def get_results(result_dict):
     results = []
-    for bug_no in result_dict.keys():
+    for bug_no in result_dict:
         for method in result_dict[bug_no]:
             results.append((method, bug_no))
     return results
 
 
 if __name__ == '__main__':
-    logging.basicConfig(format='%(levelname)s: %(message)s',
-                        level=logging.INFO)
     results = find_skips()
     unique_bugs = sorted(set([bug for (method, bug) in get_results(results)]))
     unskips = []
diff --git a/tools/use_tempest_lib.sh b/tools/use_tempest_lib.sh
deleted file mode 100755
index ca62c4a..0000000
--- a/tools/use_tempest_lib.sh
+++ /dev/null
@@ -1,141 +0,0 @@
-#!/bin/bash
-#
-# Use this script to use interfaces/files from tempest-lib.
-# Many files have been migrated to tempest-lib and tempest has
-# its own copy too.
-# This script helps to remove those from tempest and make use of tempest-lib.
-# It adds the change-id of each file on which they were migrated in lib.
-# This should only be done for files which were migrated to lib with
-# "Migrated" in commit message as done by tempest-lib/tools/migrate_from_tempest.sh script.
-# "Migrated" keyword is used to fetch the migration commit history from lib.
-# To use:
-#  1. Create a new branch in the tempest repo so not to destroy your current
-#     working branch
-#  2. Run the script from the repo dir and specify the file paths relative to
-#     the root tempest dir(only code and unit tests):
-#
-#   tools/use_tempest_lib.sh.sh tempest/file1.py tempest/file2.py
-
-
-function usage {
-    echo "Usage: $0 [OPTION] file1 file2 .."
-    echo "Use files from tempest-lib"
-    echo -e "Input files should be tempest files with path. \n  Example- tempest/file1.py tempest/file2.py .."
-    echo ""
-    echo "-s, --service_client Specify if files are service clients."
-    echo "-u, --tempest_lib_git_url Specify the repo to clone tempest-lib from."
-}
-
-function check_all_files_valid {
-    failed=0
-    for file in $files; do
-        # Get the latest change-id for each file
-        latest_commit_id=`git log -n1 -- $file | grep "^commit" | awk '{print $2}'`
-        cd $tmpdir
-        filename=`basename $file`
-        lib_path=`find ./ -name $filename`
-        if [ -z $lib_path ]; then
-            echo "ERROR: $filename does not exist in tempest-lib."
-            failed=$(( failed + 1))
-            cd - > /dev/null
-            continue
-        fi
-        # Get the CHANGE_ID of tempest-lib patch where file was migrated
-        migration_change_id=`git log  -n1 --grep "Migrated" -- $lib_path | grep "Change-Id: " | awk '{print $2}'`
-        MIGRATION_IDS=`echo -e "$MIGRATION_IDS\n * $filename: $migration_change_id"`
-        # Get tempest CHANGE_ID of file which was migrated to lib
-        migrated_change_id=`git log  -n1 --grep "Migrated" -- $lib_path | grep "* $filename"`
-        migrated_change_id=${migrated_change_id#*:}
-        cd - > /dev/null
-        # Get the commit-id of tempest which was migrated to tempest-lib
-        migrated_commit_id=`git log --grep "$migrated_change_id" -- $file | grep "^commit" | awk '{print $2}'`
-        DIFF=$(git diff $latest_commit_id $migrated_commit_id $file)
-        if [ "$DIFF" != "" ]; then
-            echo "ERROR: $filename in tempest has been updated after migration to tempest-lib. First sync the file to tempest-lib."
-            failed=$(( failed + 1))
-        fi
-    done
-    if [[ $failed -gt 0 ]]; then
-        echo "$failed files had issues"
-        exit $failed
-    fi
-}
-
-set -e
-
-service_client=0
-file_list=''
-
-while [ $# -gt 0 ]; do
-    case "$1" in
-        -h|--help) usage; exit;;
-        -u|--tempest_lib_git_url) tempest_lib_git_url="$2"; shift;;
-        -s|--service_client) service_client=1;;
-        *) files="$files $1";;
-    esac
-    shift
-done
-
-if [ -z "$files" ]; then
-    usage; exit
-fi
-
-TEMPEST_LIB_GIT_URL=${tempest_lib_git_url:-git://git.openstack.org/openstack/tempest-lib}
-
-tmpdir=$(mktemp -d -t use-tempest-lib.XXXX)
-
-# Clone tempest-lib
-git clone $TEMPEST_LIB_GIT_URL $tmpdir
-
-# Checks all provided files are present in lib and
-# not updated in tempest after migration to lib.
-check_all_files_valid
-
-for file in $files; do
-    rm -f $file
-    tempest_dir=`pwd`
-    tempest_dir="$tempest_dir/tempest/"
-    tempest_dirname=`dirname $file`
-    lib_dirname=`echo $tempest_dirname | sed s@tempest\/@tempest_lib/\@`
-    # Convert tempest dirname to import string
-    tempest_import="${tempest_dirname//\//.}"
-    tempest_import=${tempest_import:2:${#tempest_import}}
-    if [ $service_client -eq 1 ]; then
-        # Remove /json path because tempest-lib supports JSON only without XML
-        lib_dirname=`echo $lib_dirname | sed s@\/json@@`
-    fi
-    # Convert tempest-lib dirname to import string
-    tempest_lib_import="${lib_dirname//\//.}"
-    tempest_lib_import=${tempest_lib_import:2:${#tempest_lib_import}}
-    module_name=`basename $file .py`
-    tempest_import1="from $tempest_import.$module_name"
-    tempest_lib_import1="from $tempest_lib_import.$module_name"
-    tempest_import2="from $tempest_import import $module_name"
-    tempest_lib_import2="from $tempest_lib_import import $module_name"
-    set +e
-    grep -rl "$tempest_import1" $tempest_dir | xargs sed -i'' s/"$tempest_import1"/"$tempest_lib_import1"/g 2> /dev/null
-    grep -rl "$tempest_import2" $tempest_dir | xargs sed -i'' s/"$tempest_import2"/"$tempest_lib_import2"/g 2> /dev/null
-    set -e
-    if [[ -z "$file_list" ]]; then
-        file_list="$module_name"
-    else
-        tmp_file_list="$file_list, $module_name"
-        char_size=`echo $tmp_file_list | wc -c`
-        if [ $char_size -lt 27 ]; then
-            file_list="$file_list, $module_name"
-        fi
-    fi
-done
-
-rm -rf $tmpdir
-echo "Completed. Run pep8 and fix error if any"
-
-git add -A tempest/
-# Generate a migration commit
-commit_message="Use $file_list from tempest-lib"
-pre_list=$"The files below have been migrated to tempest-lib\n"
-pre_list=`echo -e $pre_list`
-post_list=$"Now Tempest-lib provides those as stable interfaces. So Tempest should\nstart using those from lib and remove its own copy."
-post_list=`echo -e $post_list`
-
-git commit -m "$commit_message" -m "$pre_list" -m "$MIGRATION_IDS" -m "$post_list"
diff --git a/tox.ini b/tox.ini
index 95f2cf1..46823d8 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,92 +1,107 @@
 [tox]
-envlist = pep8,py34,py27
+envlist = pep8,py35,py34,py27,pip-check-reqs
 minversion = 2.3.1
 skipsdist = True
 
 [tempestenv]
 sitepackages = False
-setenv = VIRTUAL_ENV={envdir}
-         OS_TEST_PATH=./tempest/test_discover
-deps = setuptools
-       -r{toxinidir}/requirements.txt
+setenv =
+    VIRTUAL_ENV={envdir}
+    OS_TEST_PATH=./tempest/test_discover
+deps =
+    setuptools
+    -r{toxinidir}/requirements.txt
 
 [testenv]
-setenv = VIRTUAL_ENV={envdir}
-         OS_TEST_PATH=./tempest/tests
+setenv =
+    VIRTUAL_ENV={envdir}
+    OS_TEST_PATH=./tempest/tests
+    PYTHONWARNINGS=default::DeprecationWarning
 passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH OS_TEST_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY
 usedevelop = True
 install_command = pip install -U {opts} {packages}
 whitelist_externals = *
-deps = -r{toxinidir}/requirements.txt
-       -r{toxinidir}/test-requirements.txt
+deps =
+    -r{toxinidir}/requirements.txt
+    -r{toxinidir}/test-requirements.txt
 commands =
-         find . -type f -name "*.pyc" -delete
-         bash tools/pretty_tox.sh '{posargs}'
+    find . -type f -name "*.pyc" -delete
+    ostestr {posargs}
 
 [testenv:genconfig]
-commands = oslo-config-generator --config-file etc/config-generator.tempest.conf
+commands = oslo-config-generator --config-file tempest/cmd/config-generator.tempest.conf
 
 [testenv:cover]
 setenv = OS_TEST_PATH=./tempest/tests
 commands = python setup.py testr --coverage --testr-arg='tempest\.tests {posargs}'
 
 [testenv:all]
+envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
 # 'all' includes slow tests
-setenv = {[tempestenv]setenv}
-         OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:1200}
+setenv =
+    {[tempestenv]setenv}
+    OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:1200}
 deps = {[tempestenv]deps}
 commands =
-  find . -type f -name "*.pyc" -delete
-  bash tools/pretty_tox.sh '{posargs}'
+    find . -type f -name "*.pyc" -delete
+    tempest run --regex {posargs}
+
+[testenv:ostestr]
+sitepackages = {[tempestenv]sitepackages}
+# 'all' includes slow tests
+setenv =
+    {[tempestenv]setenv}
+    OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:1200}
+deps = {[tempestenv]deps}
+commands =
+    find . -type f -name "*.pyc" -delete
+    ostestr {posargs}
 
 [testenv:all-plugin]
 sitepackages = True
 # 'all' includes slow tests
-setenv = {[tempestenv]setenv}
-         OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:1200}
+setenv =
+    {[tempestenv]setenv}
+    OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:1200}
 deps = {[tempestenv]deps}
 commands =
-  find . -type f -name "*.pyc" -delete
-  bash tools/pretty_tox.sh '{posargs}'
+    find . -type f -name "*.pyc" -delete
+    tempest run --regex {posargs}
 
 [testenv:full]
+envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # The regex below is used to select which tests to run and exclude the slow tag:
 # See the testrepository bug: https://bugs.launchpad.net/testrepository/+bug/1208610
 commands =
-  find . -type f -name "*.pyc" -delete
-  bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty)) {posargs}'
+    find . -type f -name "*.pyc" -delete
+    tempest run --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))' {posargs}
 
 [testenv:full-serial]
+envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # The regex below is used to select which tests to run and exclude the slow tag:
 # See the testrepository bug: https://bugs.launchpad.net/testrepository/+bug/1208610
 commands =
-  find . -type f -name "*.pyc" -delete
-  bash tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty)) {posargs}'
-
-[testenv:large-ops]
-sitepackages = {[tempestenv]sitepackages}
-setenv = {[tempestenv]setenv}
-deps = {[tempestenv]deps}
-commands =
-  find . -type f -name "*.pyc" -delete
-  python setup.py testr --slowest --testr-args='tempest.scenario.test_large_ops {posargs}'
+    find . -type f -name "*.pyc" -delete
+    tempest run --serial --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))' {posargs}
 
 [testenv:smoke]
+envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 commands =
-  find . -type f -name "*.pyc" -delete
-   bash tools/pretty_tox.sh '\[.*\bsmoke\b.*\] {posargs}'
+    find . -type f -name "*.pyc" -delete
+    tempest run --regex '\[.*\bsmoke\b.*\]' {posargs}
 
 [testenv:smoke-serial]
+envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
@@ -94,31 +109,31 @@
 # https://bugs.launchpad.net/tempest/+bug/1216076 so the neutron smoke
 # job would fail if we moved it to parallel.
 commands =
-  find . -type f -name "*.pyc" -delete
-   bash tools/pretty_tox_serial.sh '\[.*\bsmoke\b.*\] {posargs}'
-
-[testenv:stress]
-sitepackages = {[tempestenv]sitepackages}
-setenv = {[tempestenv]setenv}
-deps = {[tempestenv]deps}
-commands =
-    run-tempest-stress {posargs}
+    find . -type f -name "*.pyc" -delete
+    tempest run --serial --regex '\[.*\bsmoke\b.*\]' {posargs}
 
 [testenv:venv]
 commands = {posargs}
 
+[testenv:venv-tempest]
+envdir = .tox/tempest
+sitepackages = {[tempestenv]sitepackages}
+setenv = {[tempestenv]setenv}
+deps = {[tempestenv]deps}
+commands = {posargs}
+
 [testenv:docs]
 commands =
-   python setup.py build_sphinx {posargs}
+    python setup.py build_sphinx {posargs}
 
 [testenv:pep8]
 commands =
-   flake8 {posargs}
-   check-uuid
+    flake8 {posargs}
+    check-uuid
 
 [testenv:uuidgen]
 commands =
-   check-uuid --fix
+    check-uuid --fix
 
 [hacking]
 local-check-factory = tempest.hacking.checks.factory
@@ -128,7 +143,30 @@
 # E125 is a won't fix until https://github.com/jcrocholl/pep8/issues/126 is resolved.  For further detail see https://review.openstack.org/#/c/36788/
 # E123 skipped because it is ignored by default in the default pep8
 # E129 skipped because it is too limiting when combined with other rules
-# Skipped because of new hacking 0.9: H405
 ignore = E125,E123,E129
 show-source = True
-exclude = .git,.venv,.tox,dist,doc,openstack,*egg
+exclude = .git,.venv,.tox,dist,doc,*egg
+enable-extensions = H106,H203,H904
+
+[testenv:releasenotes]
+commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
+
+[testenv:pip-check-reqs]
+# Do not install test-requirements as that will pollute the virtualenv for
+# determining missing packages.
+# This also means that pip-check-reqs must be installed separately, outside
+# of the requirements.txt files
+deps = pip_check_reqs
+       -r{toxinidir}/requirements.txt
+commands=
+    pip-extra-reqs -d --ignore-file=tempest/tests/* tempest
+    pip-missing-reqs -d --ignore-file=tempest/tests/* tempest
+
+
+[testenv:bindep]
+# Do not install any requirements. We want this to be fast and work even if
+# system dependencies are missing, since it's used to tell you what system
+# dependencies are missing! This also means that bindep must be installed
+# separately, outside of the requirements files.
+deps = bindep
+commands = bindep test