Merge "Add vcs string to version option output"
diff --git a/.gitignore b/.gitignore
index 5b87cec..e96deb1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,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 480650c..432db7d 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -160,11 +160,17 @@
 
 Negative Tests
 --------------
-TODO: Write the guideline related to negative tests.
+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. The
+exception to this rule is API tests used for interoperability testing.
 
 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.
diff --git a/README.rst b/README.rst
index 725a890..53c7de5 100644
--- a/README.rst
+++ b/README.rst
@@ -1,13 +1,8 @@
 Tempest - The OpenStack Integration Test Suite
 ==============================================
 
-.. image:: https://img.shields.io/pypi/v/tempest.svg
-    :target: https://pypi.python.org/pypi/tempest/
-    :alt: Latest Version
-
-.. image:: https://img.shields.io/pypi/dm/tempest.svg
-    :target: https://pypi.python.org/pypi/tempest/
-    :alt: Downloads
+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,
@@ -63,19 +58,21 @@
    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 by using the tempest init
+#. Setup a local Tempest workspace. This is done by using the tempest init
    command::
 
     $ tempest init cloud-01
 
-   works the same as::
+   which also works the same as::
 
     $ mkdir cloud-01 && cd cloud-01 && tempest init
 
@@ -88,11 +85,23 @@
    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::
+
+     $ 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))'
 
diff --git a/REVIEWING.rst b/REVIEWING.rst
index bd6018d..676a217 100644
--- a/REVIEWING.rst
+++ b/REVIEWING.rst
@@ -72,6 +72,19 @@
 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/
 
 When to approve
 ---------------
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 7e4503d..127613d 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -15,6 +15,17 @@
 import os
 import subprocess
 
+# 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
 # documentation root, use os.path.abspath to make it absolute, like shown here.
@@ -34,7 +45,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
@@ -135,7 +146,7 @@
 
 # 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 9a7ce15..e4b104f 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -26,6 +26,11 @@
 - 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
@@ -87,6 +92,14 @@
 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 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
 """""""""""""""""""""""""""
@@ -124,6 +137,18 @@
 to the tests using the credentials, and failure to do this will likely cause
 unexpected failures in some tests.
 
+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.
+
+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.
+
 Pre-Provisioned Credentials are also know as accounts.yaml or accounts file.
 
 Compute
@@ -205,6 +230,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
@@ -214,23 +241,8 @@
 run. This section covers the different methods of configuring Tempest to provide
 a network when creating servers.
 
-The ``validation`` group gathers all the connection options to remotely access the
-created servers.
-
-To enable remote access to servers, at least the three following options need to be
-set:
-
-* The ``run_validation`` option needs be set to ``true``.
-
-* The ``connect_method`` option. Two connect methods are available: ``fixed`` and
-  ``floating``, the later being set by default.
-
-* The ``auth_method`` option. Currently, only authentication by keypair is
-  available.
-
-
 Fixed Network Name
-""""""""""""""""""
+''''''''''''''''''
 This is the simplest method of specifying how networks should be used. You can
 just specify a single network name/label to use for all server creations. The
 limitation with this is that all projects and users must be able to see
@@ -252,7 +264,7 @@
 
 
 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 project and user pair basis. This provides
@@ -277,7 +289,7 @@
 
 
 With Dynamic Credentials
-""""""""""""""""""""""""
+''''''''''''''''''''''''
 With dynamic credentials enabled and using nova-network, your only option for
 configuration is to either set a fixed network name or not. However, in most
 cases it shouldn't matter because nova-network should have no problem booting a
@@ -302,6 +314,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
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 10364db..f1ede06 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -2,18 +2,14 @@
 Tempest Testing Project
 =======================
 
-Contents:
+--------
+Overview
+--------
 
 .. toctree::
    :maxdepth: 2
 
    overview
-   HACKING
-   REVIEWING
-   plugin
-   plugin-registry
-   library
-   microversion_testing
 
 ------------
 Field Guides
@@ -31,6 +27,10 @@
    field_guide/stress
    field_guide/unit_tests
 
+=========
+For users
+=========
+
 ---------------------------
 Tempest Configuration Guide
 ---------------------------
@@ -51,6 +51,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/library.rst b/doc/source/library.rst
index a89512c..6a2fb83 100644
--- a/doc/source/library.rst
+++ b/doc/source/library.rst
@@ -66,3 +66,4 @@
    library/rest_client
    library/utils
    library/api_microversion_testing
+   library/auth
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 3568470..17059e4 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -19,6 +19,7 @@
   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
 
@@ -130,8 +131,9 @@
 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.*
+.. 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::
@@ -211,3 +213,7 @@
  * `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
diff --git a/doc/source/plugin-registry.rst b/doc/source/plugin-registry.rst
deleted file mode 100644
index 517e5b8..0000000
--- a/doc/source/plugin-registry.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-..
-  Note to patch submitters: this file is covered by a periodic proposal
-  job.  You should edit the files data/tempest-plugins-registry.footer
-  data/tempest-plugins-registry.header instead of this one.
-
-==========================
- Tempest Plugin Registry
-==========================
-
-Since we've created the external plugin mechanism, it's gotten used by
-a lot of projects. The following is a list of plugins that currently
-exist.
-
-Detected Plugins
-================
-
-The following will list plugins that a script has found in the openstack/
-namespace, which includes but is not limited to official OpenStack
-projects.
-
-+----------------------------+-------------------------------------------------------------------------+
-|Plugin Name                 |URL                                                                      |
-+----------------------------+-------------------------------------------------------------------------+
diff --git a/doc/source/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..6570bb7
--- /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 funcitonal 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 agend 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 mantain 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/releasenotes/notes/.placeholder b/releasenotes/notes/.placeholder
deleted file mode 100644
index e69de29..0000000
--- a/releasenotes/notes/.placeholder
+++ /dev/null
diff --git a/releasenotes/notes/add-network-versions-client-d90e8334e1443f5c.yaml b/releasenotes/notes/add-network-versions-client-d90e8334e1443f5c.yaml
new file mode 100644
index 0000000..07e3151
--- /dev/null
+++ b/releasenotes/notes/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/add-tempest-run-3d0aaf69c2ca4115.yaml b/releasenotes/notes/add-tempest-run-3d0aaf69c2ca4115.yaml
new file mode 100644
index 0000000..429bf52
--- /dev/null
+++ b/releasenotes/notes/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/add-tempest-workspaces-228a2ba4690b5589.yaml b/releasenotes/notes/add-tempest-workspaces-228a2ba4690b5589.yaml
new file mode 100644
index 0000000..9a1cef6
--- /dev/null
+++ b/releasenotes/notes/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/add_subunit_describe_calls-5498a37e6cd66c4b.yaml b/releasenotes/notes/add_subunit_describe_calls-5498a37e6cd66c4b.yaml
new file mode 100644
index 0000000..b457ddd
--- /dev/null
+++ b/releasenotes/notes/add_subunit_describe_calls-5498a37e6cd66c4b.yaml
@@ -0,0 +1,4 @@
+---
+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.
diff --git a/releasenotes/notes/bug-1486834-7ebca15836ae27a9.yaml b/releasenotes/notes/bug-1486834-7ebca15836ae27a9.yaml
new file mode 100644
index 0000000..b2190f3
--- /dev/null
+++ b/releasenotes/notes/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/identity-clients-as-library-e663c6132fcac6c2.yaml b/releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml
new file mode 100644
index 0000000..f9173a0
--- /dev/null
+++ b/releasenotes/notes/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/image-clients-as-library-86d17caa26ce3961.yaml b/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
new file mode 100644
index 0000000..1fa4ddd
--- /dev/null
+++ b/releasenotes/notes/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/remove-input-scenarios-functionality-01308e6d4307f580.yaml b/releasenotes/notes/remove-input-scenarios-functionality-01308e6d4307f580.yaml
new file mode 100644
index 0000000..4ee883f
--- /dev/null
+++ b/releasenotes/notes/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/remove-trove-tests-666522e9113549f9.yaml b/releasenotes/notes/remove-trove-tests-666522e9113549f9.yaml
new file mode 100644
index 0000000..1157a4f
--- /dev/null
+++ b/releasenotes/notes/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 the trove project.
diff --git a/releasenotes/notes/service_client_config-8a1d7b4de769c633.yaml b/releasenotes/notes/service_client_config-8a1d7b4de769c633.yaml
new file mode 100644
index 0000000..3e43f9a
--- /dev/null
+++ b/releasenotes/notes/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/tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml b/releasenotes/notes/tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
new file mode 100644
index 0000000..eeda921
--- /dev/null
+++ b/releasenotes/notes/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/requirements.txt b/requirements.txt
index 45fe345..216dd50 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,16 +5,16 @@
 cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
 jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
 testtools>=1.4.0 # MIT
-paramiko>=2.0 # LGPL
+paramiko>=2.0 # LGPLv2.1+
 netaddr!=0.7.16,>=0.7.12 # BSD
 testrepository>=0.0.18 # Apache-2.0/BSD
 pyOpenSSL>=0.14 # Apache-2.0
 oslo.concurrency>=3.8.0 # Apache-2.0
-oslo.config>=3.10.0 # Apache-2.0
+oslo.config>=3.12.0 # Apache-2.0
 oslo.i18n>=2.1.0 # Apache-2.0
 oslo.log>=1.14.0 # Apache-2.0
 oslo.serialization>=1.10.0 # Apache-2.0
-oslo.utils>=3.11.0 # Apache-2.0
+oslo.utils>=3.15.0 # Apache-2.0
 six>=1.9.0 # MIT
 fixtures>=3.0.0 # Apache-2.0/BSD
 testscenarios>=0.4 # Apache-2.0/BSD
diff --git a/setup.cfg b/setup.cfg
index 24e0214..2a3000d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -34,6 +34,7 @@
     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
@@ -41,6 +42,8 @@
     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/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 25efd2e..84b00a7 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.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 import tempest_fixtures as fixtures
 from tempest.common.utils import data_utils
@@ -36,10 +38,17 @@
         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']
+        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:
+            raise testtools.TestCase.failureException(
+                "no available compute node found")
 
     @test.idempotent_id('0d148aa3-d54c-4317-aa8d-42040a475e20')
     def test_aggregate_create_delete(self):
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index 0724566..6f80730 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 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)
+        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_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index af840cc..9017461 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 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('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)
+            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/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 07423ff..da9d548 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -136,7 +136,10 @@
             self.validation_resources['keypair']['private_key'],
             server=self.server,
             servers_client=self.client)
-        self.assertTrue(linux_client.hostname_equals_servername(self.name))
+        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, hostname, msg)
 
     @test.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
     def test_create_server_with_scheduler_hint_group(self):
diff --git a/tempest/api/compute/servers/test_disk_config.py b/tempest/api/compute/servers/test_disk_config.py
index 617cdd5..aba0240 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,24 @@
         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,
+        self.client.resize_server(server['id'], self.flavor_ref_alt,
                                   disk_config='AUTO')
-        waiters.wait_for_server_status(self.client, self.server_id,
+        waiters.wait_for_server_status(self.client, server['id'],
                                        'VERIFY_RESIZE')
-        self.client.confirm_resize_server(self.server_id)
-        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        self.client.confirm_resize_server(server['id'])
+        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
 
-        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 +109,31 @@
                           '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,
+        self.client.resize_server(server['id'], self.flavor_ref_alt,
                                   disk_config='MANUAL')
-        waiters.wait_for_server_status(self.client, self.server_id,
+        waiters.wait_for_server_status(self.client, server['id'],
                                        'VERIFY_RESIZE')
-        self.client.confirm_resize_server(self.server_id)
-        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        self.client.confirm_resize_server(server['id'])
+        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
 
-        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_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index b18789e..357c907 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -109,9 +109,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_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 66aec84..0d06119 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -24,6 +24,7 @@
 from tempest.common.utils.linux import remote_client
 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
@@ -320,6 +321,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 exceptions.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',
@@ -331,7 +345,7 @@
         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:
@@ -341,7 +355,7 @@
 
         image1_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(_clean_oldest_backup, image1_id)
-        waiters.wait_for_image_status(self.os.image_client,
+        waiters.wait_for_image_status(glance_client,
                                       image1_id, 'active')
 
         backup2 = data_utils.rand_name('backup-2')
@@ -351,8 +365,8 @@
                                          rotation=2,
                                          name=backup2).response
         image2_id = data_utils.parse_image_id(resp['location'])
-        self.addCleanup(self.os.image_client.delete_image, image2_id)
-        waiters.wait_for_image_status(self.os.image_client,
+        self.addCleanup(glance_client.delete_image, image2_id)
+        waiters.wait_for_image_status(glance_client,
                                       image2_id, 'active')
 
         # verify they have been created
@@ -361,12 +375,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']))
@@ -380,17 +404,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 '
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 32faf7d..9834d02 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -61,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
@@ -72,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/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index fa3fdfe..05c23ee 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -200,7 +200,7 @@
             server=self.server,
             servers_client=self.servers_client)
 
-        command = 'grep vd /proc/partitions | wc -l'
+        command = 'grep [vs]d /proc/partitions | wc -l'
         nb_partitions = linux_client.exec_command(command).strip()
         self.assertEqual(number_of_partition, nb_partitions)
 
diff --git a/tempest/api/data_processing/base.py b/tempest/api/data_processing/base.py
index decccf3..d5ba76c 100644
--- a/tempest/api/data_processing/base.py
+++ b/tempest/api/data_processing/base.py
@@ -349,7 +349,7 @@
             return None
 
         for plugin in CONF.data_processing_feature_enabled.plugins:
-            if plugin in DEFAULT_TEMPLATES.keys():
+            if plugin in DEFAULT_TEMPLATES:
                 break
         else:
             plugin = ''
diff --git a/tempest/api/data_processing/test_node_group_templates.py b/tempest/api/data_processing/test_node_group_templates.py
index 388bb58..c2dae85 100644
--- a/tempest/api/data_processing/test_node_group_templates.py
+++ b/tempest/api/data_processing/test_node_group_templates.py
@@ -25,10 +25,6 @@
         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.
 
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/__init__.py b/tempest/api/database/flavors/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/database/flavors/__init__.py
+++ /dev/null
diff --git a/tempest/api/database/flavors/test_flavors.py b/tempest/api/database/flavors/test_flavors.py
deleted file mode 100644
index bb7a0a4..0000000
--- a/tempest/api/database/flavors/test_flavors.py
+++ /dev/null
@@ -1,77 +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.lib import decorators
-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')
-    @decorators.skip_because(bug='1567134')
-    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')
-    @decorators.skip_because(bug='1567134')
-    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')
-    @decorators.skip_because(bug='1567134')
-    def test_compare_db_flavors_with_os(self):
-        db_flavors = self.client.list_db_flavors()['flavors']
-        os_flavors = (self.os_flavors_client.list_flavors(detail=True)
-                      ['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 cd2981b..0000000
--- a/tempest/api/database/flavors/test_flavors_negative.py
+++ /dev/null
@@ -1,33 +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.lib import exceptions as lib_exc
-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..0924619 100644
--- a/tempest/api/identity/admin/v2/test_roles.py
+++ b/tempest/api/identity/admin/v2/test_roles.py
@@ -84,28 +84,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 fd56285..770bb14 100644
--- a/tempest/api/identity/admin/v2/test_roles_negative.py
+++ b/tempest/api/identity/admin/v2/test_roles_negative.py
@@ -123,9 +123,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')
@@ -134,9 +135,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'])
@@ -145,7 +147,8 @@
         # Attempt to assign a non existent role to user should fail
         (user, tenant, role) = self._get_role_params()
         non_existent_role = data_utils.rand_uuid_hex()
-        self.assertRaises(lib_exc.NotFound, self.roles_client.assign_user_role,
+        self.assertRaises(lib_exc.NotFound,
+                          self.roles_client.create_user_role_on_project,
                           tenant['id'], user['id'], non_existent_role)
 
     @test.attr(type=['negative'])
@@ -154,7 +157,8 @@
         # Attempt to assign a role on a non existent tenant should fail
         (user, tenant, role) = self._get_role_params()
         non_existent_tenant = data_utils.rand_uuid_hex()
-        self.assertRaises(lib_exc.NotFound, self.roles_client.assign_user_role,
+        self.assertRaises(lib_exc.NotFound,
+                          self.roles_client.create_user_role_on_project,
                           non_existent_tenant, user['id'], role['id'])
 
     @test.attr(type=['negative'])
@@ -162,9 +166,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'])
@@ -173,26 +179,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'])
@@ -200,11 +207,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'])
+        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_user_role,
+        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'])
@@ -212,11 +220,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'])
+        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_user_role,
+        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'])
@@ -225,11 +234,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')
@@ -240,7 +251,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 fe83759..6c9b564 100644
--- a/tempest/api/identity/admin/v2/test_services.py
+++ b/tempest/api/identity/admin/v2/test_services.py
@@ -35,10 +35,11 @@
         # 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']
+            name=name, type=s_type,
+            description=description)['OS-KSADM:service']
         self.assertFalse(service_data['id'] is None)
         self.addCleanup(self._del_service, service_data['id'])
         # Verifying response body of create service
@@ -46,7 +47,7 @@
         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,15 +69,15 @@
     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')
@@ -85,10 +86,12 @@
         services = []
         for _ in moves.xrange(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)
 
diff --git a/tempest/api/identity/admin/v2/test_tenant_negative.py b/tempest/api/identity/admin/v2/test_tenant_negative.py
index 5169dae..d1e0217 100644
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ b/tempest/api/identity/admin/v2/test_tenant_negative.py
@@ -43,7 +43,7 @@
     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']
+        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
         self.data.tenants.append(tenant)
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_tenants_client.delete_tenant,
@@ -54,7 +54,7 @@
     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']
+        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
         self.data.tenants.append(tenant)
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
@@ -75,7 +75,7 @@
     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']
+        body = self.tenants_client.create_tenant(name=tenant_name)['tenant']
         tenant = body
         self.data.tenants.append(tenant)
         tenant1_id = body.get('id')
@@ -83,7 +83,7 @@
         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')
@@ -92,7 +92,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')
@@ -103,7 +103,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'])
@@ -121,7 +121,7 @@
         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')
@@ -135,7 +135,7 @@
     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']
+        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
         self.data.tenants.append(tenant)
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_tenants_client.update_tenant,
@@ -146,7 +146,7 @@
     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']
+        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
         self.data.tenants.append(tenant)
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
diff --git a/tempest/api/identity/admin/v2/test_tenants.py b/tempest/api/identity/admin/v2/test_tenants.py
index 8d0b9b1..1aa80df 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -28,7 +28,8 @@
         tenants = []
         for _ in moves.xrange(3):
             tenant_name = data_utils.rand_name(name='tenant-new')
-            tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
+            tenant = self.tenants_client.create_tenant(
+                name=tenant_name)['tenant']
             self.data.tenants.append(tenant)
             tenants.append(tenant)
         tenant_ids = map(lambda x: x['id'], tenants)
@@ -49,7 +50,7 @@
         # 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)
@@ -68,7 +69,8 @@
     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)
         tenant_id = tenant['id']
@@ -84,7 +86,8 @@
     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)
         tenant_id = tenant['id']
@@ -102,7 +105,7 @@
     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)
 
@@ -129,7 +132,8 @@
         # 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)
 
@@ -157,7 +161,7 @@
         # 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)
 
diff --git a/tempest/api/identity/admin/v2/test_tokens.py b/tempest/api/identity/admin/v2/test_tokens.py
index ee04420..5cf337b 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -27,11 +27,13 @@
         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']
+        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
         self.data.tenants.append(tenant)
         # second:create a user
-        user = self.users_client.create_user(user_name, user_password,
-                                             tenant['id'], '')['user']
+        user = self.users_client.create_user(name=user_name,
+                                             password=user_password,
+                                             tenantId=tenant['id'],
+                                             email='')['user']
         self.data.users.append(user)
         # then get a token for the user
         body = self.token_client.auth(user_name,
@@ -62,17 +64,21 @@
         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']
+        user = self.users_client.create_user(name=user_name,
+                                             password=user_password,
+                                             tenantId=tenant_id,
+                                             email=email)['user']
         self.data.users.append(user)
 
         # Create a couple tenants.
         tenant1_name = data_utils.rand_name(name='tenant')
-        tenant1 = self.tenants_client.create_tenant(tenant1_name)['tenant']
+        tenant1 = self.tenants_client.create_tenant(
+            name=tenant1_name)['tenant']
         self.data.tenants.append(tenant1)
 
         tenant2_name = data_utils.rand_name(name='tenant')
-        tenant2 = self.tenants_client.create_tenant(tenant2_name)['tenant']
+        tenant2 = self.tenants_client.create_tenant(
+            name=tenant2_name)['tenant']
         self.data.tenants.append(tenant2)
 
         # Create a role
@@ -81,11 +87,13 @@
         self.data.roles.append(role)
 
         # 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 d860d2f..167cbc7 100644
--- a/tempest/api/identity/admin/v2/test_users.py
+++ b/tempest/api/identity/admin/v2/test_users.py
@@ -36,9 +36,10 @@
     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']
+        user = self.users_client.create_user(name=self.alt_user,
+                                             password=self.alt_password,
+                                             tenantId=self.data.tenant['id'],
+                                             email=self.alt_email)['user']
         self.data.users.append(user)
         self.assertEqual(self.alt_user, user['name'])
 
@@ -47,9 +48,10 @@
         # Create a user with enabled : False
         self.data.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=self.data.tenant['id'],
+                                             email=self.alt_email,
                                              enabled=False)['user']
         self.data.users.append(user)
         self.assertEqual(name, user['name'])
@@ -61,9 +63,10 @@
         # 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']
+        user = self.users_client.create_user(name=test_user,
+                                             password=self.alt_password,
+                                             tenantId=self.data.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
@@ -87,9 +90,10 @@
         # 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']
+        user = self.users_client.create_user(name=test_user,
+                                             password=self.alt_password,
+                                             tenantId=self.data.tenant['id'],
+                                             email=self.alt_email)['user']
         self.users_client.delete_user(user['id'])
 
     @test.idempotent_id('aca696c3-d645-4f45-b728-63646045beb1')
@@ -139,16 +143,18 @@
         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=self.data.tenant['id'],
+                                              email='user1@123')['user']
         user_ids.append(user1['id'])
         self.data.users.append(user1)
         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=self.data.tenant['id'],
+                                              email='user2@123')['user']
         user_ids.append(user2['id'])
         self.data.users.append(user2)
         # List of users for the respective tenant ID
@@ -175,19 +181,20 @@
         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=self.data.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']
+        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'])
diff --git a/tempest/api/identity/admin/v2/test_users_negative.py b/tempest/api/identity/admin/v2/test_users_negative.py
index 5fda4c14..78b89fa 100644
--- a/tempest/api/identity/admin/v2/test_users_negative.py
+++ b/tempest/api/identity/admin/v2/test_users_negative.py
@@ -35,9 +35,9 @@
         self.data.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=self.data.tenant['id'],
+                          email=self.alt_email)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('d80d0c2f-4514-4d1e-806d-0930dfc5a187')
@@ -45,8 +45,9 @@
         # User with an empty name should not be created
         self.data.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=self.data.tenant['id'],
+                          email=self.alt_email)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('7704b4f3-3b75-4b82-87cc-931d41c8f780')
@@ -54,8 +55,9 @@
         # Length of user name filed should be restricted to 255 characters
         self.data.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=self.data.tenant['id'],
+                          email=self.alt_email)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('57ae8558-120c-4723-9308-3751474e7ecf')
@@ -63,16 +65,20 @@
         # Duplicate user should not be created
         self.data.setup_test_user()
         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=self.data.user['name'],
+                          password=self.data.user_password,
+                          tenantId=self.data.tenant['id'],
+                          email=self.data.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')
@@ -88,8 +94,9 @@
         self.addCleanup(self.client.auth_provider.clear_auth)
 
         self.assertRaises(lib_exc.Unauthorized, self.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=self.data.tenant['id'],
+                          email=self.alt_email)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('23a2f3da-4a1a-41da-abdd-632328a861ad')
@@ -98,9 +105,9 @@
         self.data.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=self.data.tenant['id'],
+                          email=self.alt_email, enabled=3)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('3d07e294-27a0-4144-b780-a2a1bf6fee19')
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 18a50d0..a540da7 100644
--- a/tempest/api/identity/admin/v3/test_default_project_id.py
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -9,13 +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.api.identity import base
 from tempest import clients
 from tempest.common.utils import data_utils
 from tempest import config
 from tempest.lib import auth
-from tempest import manager
 from tempest import test
 
 CONF = config.CONF
@@ -78,7 +76,7 @@
         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 27ff15d..24a7a4e 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):
 
+    @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(
+                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(self, domain_id):
         # It is necessary to disable the domain before deleting,
         # or else it would result in unauthorized error
         self.domains_client.update_domain(domain_id, enabled=False)
         self.domains_client.delete_domain(domain_id)
-        # Asserting that the domain is not found in the list
-        # after deletion
-        body = self.domains_client.list_domains()['domains']
-        domains_list = [d['id'] for d in body]
-        self.assertNotIn(domain_id, domains_list)
 
     @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.domains_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.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=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=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.domains_client.create_domain(
             d_name, description=d_desc)['domain']
-        self.addCleanup(self._delete_domain, domain['id'])
+        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.domains_client.update_domain(
-            domain['id'], name=new_name, description=new_desc)['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,13 +119,18 @@
         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'])
-
+        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):
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 928437c..86f6b12 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -37,9 +37,15 @@
         cls.p2 = cls.projects_client.create_project(p2_name)['project']
         cls.data.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.data.projects.append(cls.p3)
+        cls.project_ids.append(cls.p3['id'])
 
     @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']
 
@@ -63,6 +69,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_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index 607bebe..bee77df 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -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):
 
@@ -52,6 +57,37 @@
         self.assertEqual(project_name, body['name'])
         self.assertEqual(self.data.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
+        self.data.setup_test_domain()
+        domain_id = self.data.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):
         # Create a project that is enabled
diff --git a/tempest/api/identity/admin/v3/test_regions.py b/tempest/api/identity/admin/v3/test_regions.py
index ece36b9..95894a6 100644
--- a/tempest/api/identity/admin/v3/test_regions.py
+++ b/tempest/api/identity/admin/v3/test_regions.py
@@ -15,7 +15,7 @@
 
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import test_utils
 from tempest import test
 
 
@@ -42,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'])
@@ -71,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')
@@ -80,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'])
@@ -95,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_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index 6f12939..89cfd5b 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import six
+
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
 from tempest.lib import exceptions as lib_exc
@@ -97,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'])
@@ -118,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 09ae468..9c8f1f6 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -98,7 +98,8 @@
             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.trusts_client
 
@@ -266,7 +267,18 @@
     @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()
+        # 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]
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 2d4ff17..df39390 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -29,7 +29,7 @@
     @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):
@@ -81,14 +81,6 @@
         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):
 
@@ -137,10 +129,6 @@
         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):
 
@@ -162,6 +150,12 @@
         cls.creds_client = cls.os_adm.credentials_client
         cls.groups_client = cls.os_adm.groups_client
         cls.projects_client = cls.os_adm.projects_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):
@@ -209,11 +203,10 @@
         self.domains = []
 
     def _create_test_user(self, **kwargs):
-        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']
+            password=self.user_password,
+            **kwargs)['user']
         self.users.append(self.user)
 
     def setup_test_role(self):
@@ -222,27 +215,24 @@
             name=data_utils.rand_name('test_role'))['role']
         self.roles.append(self.role)
 
-    @staticmethod
-    def _try_wrapper(func, item, **kwargs):
-        try:
-            test_utils.call_and_ignore_notfound_exc(func, item['id'], **kwargs)
-        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)
+            test_utils.call_and_ignore_notfound_exc(
+                self.users_client.delete_user, user['id'])
         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)
+            test_utils.call_and_ignore_notfound_exc(
+                self.projects_client.delete_tenant, tenant['id'])
+        for project in reversed(self.projects):
+            test_utils.call_and_ignore_notfound_exc(
+                self.projects_client.delete_project, project['id'])
         for role in self.roles:
-            self._try_wrapper(self.roles_client.delete_role, role)
+            test_utils.call_and_ignore_notfound_exc(
+                self.roles_client.delete_role, role['id'])
         for domain in self.domains:
-            self._try_wrapper(self.domains_client.update_domain, domain,
-                              enabled=False)
-            self._try_wrapper(self.domains_client.delete_domain, domain)
+            test_utils.call_and_ignore_notfound_exc(
+                self.domains_client.update_domain, domain['id'], enabled=False)
+            test_utils.call_and_ignore_notfound_exc(
+                self.domains_client.delete_domain, domain['id'])
 
 
 class DataGeneratorV2(BaseDataGenerator):
@@ -250,7 +240,10 @@
     def setup_test_user(self):
         """Set up a test user."""
         self.setup_test_tenant()
-        self._create_test_user(tenant_id=self.tenant['id'])
+        username = data_utils.rand_name('test_user')
+        email = username + '@testmail.tm'
+        self._create_test_user(name=username, email=email,
+                               tenantId=self.tenant['id'])
 
     def setup_test_tenant(self):
         """Set up a test tenant."""
@@ -265,7 +258,10 @@
     def setup_test_user(self):
         """Set up a test user."""
         self.setup_test_project()
-        self._create_test_user(project_id=self.project['id'])
+        username = data_utils.rand_name('test_user')
+        email = username + '@testmail.tm'
+        self._create_test_user(user_name=username, email=email,
+                               project_id=self.project['id'])
 
     def setup_test_project(self):
         """Set up a test project."""
diff --git a/tempest/api/identity/v2/test_ec2_credentials.py b/tempest/api/identity/v2/test_ec2_credentials.py
index 8600980..3c379f0 100644
--- a/tempest/api/identity/v2/test_ec2_credentials.py
+++ b/tempest/api/identity/v2/test_ec2_credentials.py
@@ -33,19 +33,19 @@
         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):
@@ -53,25 +53,25 @@
         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
@@ -81,32 +81,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 b742e69..cc6de47 100644
--- a/tempest/api/identity/v2/test_tenants.py
+++ b/tempest/api/identity/v2/test_tenants.py
@@ -24,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 79f2576..4833f9e 100644
--- a/tempest/api/identity/v2/test_users.py
+++ b/tempest/api/identity/v2/test_users.py
@@ -46,9 +46,9 @@
             client.auth_provider.clear_auth()
             client.auth_provider.set_auth()
 
-        old_pass = self.creds.credentials.password
+        old_pass = self.creds.password
         new_pass = data_utils.rand_password()
-        user_id = self.creds.credentials.user_id
+        user_id = self.creds.user_id
         # to change password back. important for allow_tenant_isolation = false
         self.addCleanup(_restore_password, self.non_admin_users_client,
                         user_id, old_pass=old_pass, new_pass=new_pass)
diff --git a/tempest/api/identity/v3/test_projects.py b/tempest/api/identity/v3/test_projects.py
index 1574ab7..26cb90b 100644
--- a/tempest/api/identity/v3/test_projects.py
+++ b/tempest/api/identity/v3/test_projects.py
@@ -25,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)
 
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index 76b46c0..c92e750 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -46,9 +46,9 @@
             client.auth_provider.clear_auth()
             client.auth_provider.set_auth()
 
-        old_pass = self.creds.credentials.password
+        old_pass = self.creds.password
         new_pass = data_utils.rand_password()
-        user_id = self.creds.credentials.user_id
+        user_id = self.creds.user_id
         # to change password back. important for allow_tenant_isolation = false
         self.addCleanup(_restore_password, self.non_admin_users_client,
                         user_id, old_pass=old_pass, new_pass=new_pass)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index bd04c0d..6fd6ea6 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -14,6 +14,7 @@
 
 from six import moves
 
+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
@@ -55,14 +56,20 @@
         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")
             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:
@@ -70,6 +77,10 @@
         cls.created_images.append(image['id'])
         return image
 
+    @classmethod
+    def _get_create_params(cls, **kwargs):
+        return kwargs
+
 
 class BaseV1ImageTest(BaseImageTest):
 
@@ -85,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):
 
@@ -93,12 +108,14 @@
     @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())
@@ -123,6 +140,9 @@
     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.schemas_client = cls.os.schemas_client
 
 
 class BaseV2MemberImageTest(BaseV2ImageTest):
@@ -132,13 +152,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']
@@ -147,11 +168,11 @@
 
     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 = self.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)
+        self.addCleanup(self.client.delete_image, image_id)
         return image_id
 
 
diff --git a/tempest/api/image/v1/test_image_members.py b/tempest/api/image/v1/test_image_members.py
index eb6969b..94edb6c 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -22,8 +22,8 @@
     @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)
         self.assertIn(self.alt_tenant_id, members)
@@ -33,10 +33,12 @@
     @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)
         self.assertIn(share_image, images)
@@ -45,8 +47,10 @@
     @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))
diff --git a/tempest/api/image/v1/test_image_members_negative.py b/tempest/api/image/v1/test_image_members_negative.py
index 16a4ba6..2538781 100644
--- a/tempest/api/image/v1/test_image_members_negative.py
+++ b/tempest/api/image/v1/test_image_members_negative.py
@@ -25,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')
@@ -42,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 6d5559d..def7750 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -16,6 +16,7 @@
 from six import moves
 
 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
@@ -306,7 +307,8 @@
     @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.check_image(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'])
 
@@ -314,12 +316,16 @@
     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.check_image(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']
+        headers = common_image.image_meta_to_headers(
+            properties=metadata['properties'])
+        metadata = self.client.update_image(self.image_id,
+                                            headers=headers)['image']
 
-        resp_metadata = self.client.check_image(self.image_id)
+        resp = self.client.check_image(self.image_id)
+        resp_metadata = common_image.get_image_meta_from_headers(resp)
         expected = {'key1': 'alt1', 'key2': 'value2'}
         self.assertEqual(expected, resp_metadata['properties'])
diff --git a/tempest/api/image/v1/test_images_negative.py b/tempest/api/image/v1/test_images_negative.py
index babee74..9e67c25 100644
--- a/tempest/api/image/v1/test_images_negative.py
+++ b/tempest/api/image/v1/test_images_negative.py
@@ -27,17 +27,17 @@
     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',)
+                          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('bb016f15-0820-4f27-a92d-09b2f67d2488')
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 04582c6..1fb9c52 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -254,12 +254,12 @@
     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'])
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index bb73318..fe8dd65 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -19,17 +19,17 @@
     @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.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 +40,29 @@
     @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.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.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 +70,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 388eb08..fa29a92 100644
--- a/tempest/api/image/v2/test_images_member_negative.py
+++ b/tempest/api/image/v2/test_images_member_negative.py
@@ -21,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')
 
@@ -33,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_namespaces.py b/tempest/api/image/v2/test_images_metadefs_namespaces.py
index da0f4c1..6fced00 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespaces.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespaces.py
@@ -26,43 +26,47 @@
     @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)
+        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)
         # 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):
-        body = self.client.show_namespace(namespace_name)
+        body = self.namespaces_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)
+        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/network/base.py b/tempest/api/network/base.py
index 89fa576..9e7c795 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -80,6 +80,7 @@
         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
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/network/test_allowed_address_pair.py b/tempest/api/network/test_allowed_address_pair.py
index b2892e5..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):
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 1d96439..bf80ff5 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -565,9 +565,9 @@
         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(NetworksIpV6Test):
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/orchestration/stacks/test_soft_conf.py b/tempest/api/orchestration/stacks/test_soft_conf.py
index 6a4e2b9..aa0b46a 100644
--- a/tempest/api/orchestration/stacks/test_soft_conf.py
+++ b/tempest/api/orchestration/stacks/test_soft_conf.py
@@ -45,7 +45,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/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 00acc7d..5615cf3 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -63,9 +63,8 @@
             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'])
+        self.type = self.create_volume_type(name=type_name,
+                                            extra_specs=extra_specs)
 
         params = {self.name_field: vol_name, 'volume_type': type_name}
 
@@ -92,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')
diff --git a/tempest/api/volume/admin/test_qos.py b/tempest/api/volume/admin/test_qos.py
index 722a39a..9402668 100644
--- a/tempest/api/volume/admin/test_qos.py
+++ b/tempest/api/volume/admin/test_qos.py
@@ -43,27 +43,19 @@
         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'])
+        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.volume_qos_client.list_qos()['qos_specs']
+        list_qos = self.admin_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.admin_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(
+        body = self.admin_volume_qos_client.show_association_qos(
             self.created_qos['id'])['qos_associations']
 
         associations = []
@@ -99,7 +91,7 @@
     @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(
+        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'])
@@ -107,28 +99,29 @@
     @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']
+        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.volume_qos_client.set_qos_key(
+        body = self.admin_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(
+        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.volume_qos_client.unset_qos_key(self.created_qos['id'], keys)
+        self.admin_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.admin_volume_qos_client.wait_for_qos_operations(
+            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'])
 
@@ -145,7 +138,7 @@
         # create a test volume-type
         vol_type = []
         for _ in range(0, 3):
-            vol_type.append(self._create_test_volume_type())
+            vol_type.append(self.create_volume_type())
 
         # associate the qos-specs with volume-types
         for i in range(0, 3):
@@ -158,21 +151,20 @@
             self.assertIn(vol_type[i]['id'], associations)
 
         # disassociate a volume-type with qos-specs
-        self.volume_qos_client.disassociate_qos(
+        self.admin_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'])
+        self.admin_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.admin_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)
+        self.admin_volume_qos_client.wait_for_qos_operations(
+            self.created_qos['id'], operation)
         associations = self._test_get_association_qos()
         self.assertEmpty(associations)
 
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 26a5a45..a17cc69 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -61,8 +61,7 @@
         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'])
+        cls.delete_volume(cls.volumes_client, cls.volume['id'])
 
         super(SnapshotsActionsV2Test, cls).resource_cleanup()
 
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index b28488a..b58c525 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -21,7 +21,7 @@
 
     @test.idempotent_id('d5f3efa2-6684-4190-9ced-1c2f526352ad')
     def test_list_hosts(self):
-        hosts = self.hosts_client.list_hosts()['hosts']
+        hosts = self.admin_hosts_client.list_hosts()['hosts']
         self.assertTrue(len(hosts) >= 2, "No. of hosts are < 2,"
                         "response of list hosts is: % s" % hosts)
 
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index cf05f5f..ba17d9c 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -13,9 +13,9 @@
 #    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']
@@ -25,26 +25,24 @@
 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
-
-    def _delete_volume(self, volume_id):
-        # Delete the specified volume using admin credentials
-        self.admin_volume_client.delete_volume(volume_id)
-        self.admin_volume_client.wait_for_resource_deletion(volume_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)
@@ -52,21 +50,21 @@
     @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}
 
         # 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)
             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
@@ -75,7 +73,7 @@
 
     @test.idempotent_id('18c51ae9-cb03-48fc-b234-14a19374dbed')
     def test_show_quota_usage(self):
-        quota_usage = self.quotas_client.show_quota_usage(
+        quota_usage = self.admin_quotas_client.show_quota_usage(
             self.os_adm.credentials.tenant_id)['quota_set']
         for key in QUOTA_KEYS:
             self.assertIn(key, quota_usage)
@@ -84,13 +82,14 @@
 
     @test.idempotent_id('ae8b6091-48ad-4bfa-a188-bbf5cc02115f')
     def test_quota_usage(self):
-        quota_usage = self.quotas_client.show_quota_usage(
+        quota_usage = self.admin_quotas_client.show_quota_usage(
             self.demo_tenant_id)['quota_set']
 
         volume = self.create_volume()
-        self.addCleanup(self._delete_volume, volume['id'])
+        self.addCleanup(self.delete_volume,
+                        self.admin_volume_client, volume['id'])
 
-        new_quota_usage = self.quotas_client.show_quota_usage(
+        new_quota_usage = self.admin_quotas_client.show_quota_usage(
             self.demo_tenant_id)['quota_set']
 
         self.assertEqual(quota_usage['volumes']['in_use'] + 1,
@@ -109,18 +108,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_usage(
+            self.demo_tenant_id)['quota_set']
+
+        alt_quota = self.admin_quotas_client.show_quota_usage(
+            self.alt_client.tenant_id)['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_usage(
+            self.demo_tenant_id)['quota_set']
+
+        new_alt_quota = self.admin_quotas_client.show_quota_usage(
+            self.alt_client.tenant_id)['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 a43ee8e..dde8915 100644
--- a/tempest/api/volume/admin/test_volume_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_quotas_negative.py
@@ -38,7 +38,7 @@
 
         # 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)
 
@@ -58,12 +58,12 @@
         # 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,
                          '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,
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 b7f70ba..1565a8c 100644
--- a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
@@ -44,7 +44,7 @@
 
         # 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)
 
@@ -63,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_type_access.py b/tempest/api/volume/admin/test_volume_type_access.py
new file mode 100644
index 0000000..fac71a8
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_type_access.py
@@ -0,0 +1,94 @@
+# 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.common import waiters
+from tempest.lib import exceptions as lib_exc
+from tempest import test
+
+
+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'])
+
+        # 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.volumes_client.create_volume(
+            volume_type=volume_type['id'])['volume']
+        self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+        waiters.wait_for_volume_status(self.volumes_client, volume['id'],
+                                       'available')
+
+        # 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/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 6fc6f1c..27f6ccb 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -24,14 +24,11 @@
 
 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)
-
     @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')
@@ -47,18 +44,16 @@
         # 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(
+            vol_type = self.create_volume_type(
                 name=vol_type_name,
-                extra_specs=extra_specs)['volume_type']
+                extra_specs=extra_specs)
             volume_types.append(vol_type)
-            self.addCleanup(self.volume_types_client.delete_volume_type,
-                            vol_type['id'])
         params = {self.name_field: vol_name,
                   'volume_type': volume_types[0]['id']}
 
         # Create volume
         volume = self.volumes_client.create_volume(**params)['volume']
-        self.addCleanup(self._delete_volume, volume['id'])
+        self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
         self.assertEqual(volume_types[0]['name'], volume["volume_type"])
         self.assertEqual(volume[self.name_field], vol_name,
                          "The created volume name is not equal "
@@ -97,19 +92,17 @@
         vendor = CONF.volume.vendor_name
         extra_specs = {"storage_protocol": proto,
                        "vendor_name": vendor}
-        body = self.volume_types_client.create_volume_type(
+        body = self.create_volume_type(
             name=name,
-            extra_specs=extra_specs)['volume_type']
+            extra_specs=extra_specs)
         self.assertIn('id', body)
-        self.addCleanup(self.volume_types_client.delete_volume_type,
-                        body['id'])
         self.assertIn('name', body)
         self.assertEqual(body['name'], 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(
+        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 '
@@ -127,15 +120,12 @@
         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.volume_types_client.delete_volume_type,
-                        body['id'])
-
+        body = self.create_volume_type(name=name)
         # 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_volume_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 "
@@ -146,7 +136,7 @@
 
         # Get encryption type
         fetched_encryption_type = (
-            self.volume_types_client.show_encryption_type(
+            self.admin_volume_types_client.show_encryption_type(
                 encryption_type['volume_type_id']))
         self.assertEqual(provider,
                          fetched_encryption_type['provider'],
@@ -158,13 +148,13 @@
                          'different from the created encryption_type')
 
         # Delete encryption type
-        self.volume_types_client.delete_encryption_type(
+        self.admin_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)
+        self.admin_volume_types_client.wait_for_resource_deletion(resource)
         deleted_encryption_type = (
-            self.volume_types_client.show_encryption_type(
+            self.admin_volume_types_client.show_encryption_type(
                 encryption_type['volume_type_id']))
         self.assertEmpty(deleted_encryption_type)
 
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..9e49b94 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -24,23 +24,17 @@
     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(name=vol_type_name)
 
     @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 +43,36 @@
     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(
+        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)
 
 
 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 f3e52e9..2193aa6 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
@@ -26,23 +26,16 @@
         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(name=vol_type_name,
+                                                 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):
@@ -50,7 +43,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'], data_utils.rand_uuid(),
             extra_spec)
 
@@ -60,7 +53,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')
@@ -70,8 +63,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')
@@ -81,7 +74,7 @@
         extra_specs = {"spec2": "val1"}
         self.assertRaises(
             lib_exc.NotFound,
-            self.volume_types_client.create_volume_type_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')
@@ -89,7 +82,7 @@
         # 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')
@@ -97,35 +90,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,
-            data_utils.rand_uuid(), 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,
+            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,
-            data_utils.rand_uuid(), 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):
@@ -133,7 +124,7 @@
             # id.
         self.assertRaises(
             lib_exc.NotFound,
-            self.volume_types_client.show_volume_type_extra_specs,
+            self.admin_volume_types_client.show_volume_type_extra_specs,
             self.volume_type['id'], data_utils.rand_uuid())
 
 
diff --git a/tempest/api/volume/admin/test_volume_types_negative.py b/tempest/api/volume/admin/test_volume_types_negative.py
index aff5466..857e7d2 100644
--- a/tempest/api/volume/admin/test_volume_types_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_negative.py
@@ -33,21 +33,22 @@
     @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,
+                          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,
+                          self.admin_volume_types_client.delete_volume_type,
                           data_utils.rand_uuid())
 
 
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index bdb313f..5388f7f 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -42,8 +42,7 @@
     @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'])
+        cls.delete_volume(cls.client, cls.volume['id'])
 
         super(VolumesActionsV2Test, cls).resource_cleanup()
 
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 1289297..b6dc488 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -27,23 +27,23 @@
 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()
+        super(VolumesBackupsAdminV2Test, cls).resource_setup()
 
         cls.volume = cls.create_volume()
 
     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_backup_deletion(backup_id)
 
     def _decode_url(self, backup_url):
         return json.loads(base64.decodestring(backup_url))
@@ -63,36 +63,37 @@
     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
+        create_backup = self.admin_backups_client.create_backup
         backup = create_backup(volume_id=self.volume['id'],
                                name=backup_name)['backup']
-        self.addCleanup(self.backups_adm_client.delete_backup,
+        self.addCleanup(self.admin_backups_client.delete_backup,
                         backup['id'])
         self.assertEqual(backup_name, backup['name'])
         waiters.wait_for_volume_status(self.admin_volume_client,
                                        self.volume['id'], 'available')
-        self.backups_adm_client.wait_for_backup_status(backup['id'],
-                                                       'available')
+        self.admin_backups_client.wait_for_backup_status(backup['id'],
+                                                         'available')
 
         # Get a given backup
-        backup = self.backups_adm_client.show_backup(backup['id'])['backup']
+        backup = self.admin_backups_client.show_backup(backup['id'])['backup']
         self.assertEqual(backup_name, backup['name'])
 
         # Get all backups with detail
-        backups = self.backups_adm_client.list_backups(detail=True)['backups']
+        backups = self.admin_backups_client.list_backups(
+            detail=True)['backups']
         self.assertIn((backup['name'], backup['id']),
                       [(m['name'], m['id']) for m in backups])
 
         # Restore backup
-        restore = self.backups_adm_client.restore_backup(
+        restore = self.admin_backups_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_backups_client.wait_for_backup_status(backup['id'],
+                                                         'available')
         waiters.wait_for_volume_status(self.admin_volume_client,
                                        restore['volume_id'], 'available')
 
@@ -105,15 +106,15 @@
         """
         # Create backup
         backup_name = data_utils.rand_name('Backup')
-        backup = (self.backups_adm_client.create_backup(
+        backup = (self.admin_backups_client.create_backup(
             volume_id=self.volume['id'], name=backup_name)['backup'])
         self.addCleanup(self._delete_backup, backup['id'])
         self.assertEqual(backup_name, backup['name'])
-        self.backups_adm_client.wait_for_backup_status(backup['id'],
-                                                       'available')
+        self.admin_backups_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)
@@ -132,7 +133,7 @@
             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=new_url)['backup']
 
@@ -142,15 +143,16 @@
         self.addCleanup(self._delete_backup, new_id)
         self.assertIn("id", import_backup)
         self.assertEqual(new_id, import_backup['id'])
-        self.backups_adm_client.wait_for_backup_status(import_backup['id'],
-                                                       'available')
+        self.admin_backups_client.wait_for_backup_status(import_backup['id'],
+                                                         'available')
 
         # Verify Import Backup
-        backups = self.backups_adm_client.list_backups(detail=True)['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(
+        restore = self.admin_backups_client.restore_backup(
             backup['id'])['restore']
         self.addCleanup(self.admin_volume_client.delete_volume,
                         restore['volume_id'])
@@ -161,9 +163,9 @@
         # 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')
+        self.admin_backups_client.wait_for_backup_status(import_backup['id'],
+                                                         'available')
 
 
-class VolumesBackupsV1Test(VolumesBackupsV2Test):
+class VolumesBackupsAdminV1Test(VolumesBackupsAdminV2Test):
     _api_version = 1
diff --git a/tempest/api/volume/admin/test_volumes_list.py b/tempest/api/volume/admin/test_volumes_list.py
new file mode 100644
index 0000000..64041b8
--- /dev/null
+++ b/tempest/api/volume/admin/test_volumes_list.py
@@ -0,0 +1,59 @@
+# 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 test
+
+
+class VolumesListAdminV2TestJSON(base.BaseVolumeAdminTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumesListAdminV2TestJSON, cls).resource_setup()
+        # Create 3 test volumes
+        cls.volume_list = []
+        for i 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()['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 = map(operator.itemgetter(
+            'os-vol-tenant-attr:tenant_id'), fetched_list)
+        expected_tenant_id = [self.volumes_client.tenant_id] * 3
+        self.assertEqual(expected_tenant_id, fetched_tenant_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..6817eaa
--- /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.services.volume.base import base_v3_client
+
+
+class APIMicroversionFixture(fixtures.Fixture):
+
+    def __init__(self, volume_microversion):
+        self.volume_microversion = volume_microversion
+
+    def _setUp(self):
+        super(APIMicroversionFixture, self)._setUp()
+        base_v3_client.VOLUME_MICROVERSION = self.volume_microversion
+        self.addCleanup(self._reset_volume_microversion)
+
+    def _reset_volume_microversion(self):
+        base_v3_client.VOLUME_MICROVERSION = None
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 6116d72..087b9a8 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -45,6 +45,10 @@
             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)
@@ -72,6 +76,7 @@
         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)
@@ -131,6 +136,12 @@
     # only in a single location in the source, and could be more general.
 
     @classmethod
+    def delete_volume(cls, client, volume_id):
+        """Delete volume by the given client"""
+        client.delete_volume(volume_id)
+        client.wait_for_resource_deletion(volume_id)
+
+    @classmethod
     def clear_volumes(cls):
         for volume in cls.volumes:
             try:
@@ -179,53 +190,78 @@
         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_quotas_client = cls.os_adm.volume_quotas_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_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_quotas_client = cls.os_adm.volume_quotas_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('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:
             test_utils.call_and_ignore_notfound_exc(
-                cls.volume_qos_client.delete_qos, qos_id)
+                cls.admin_volume_qos_client.delete_qos, qos_id)
 
         for qos_id in cls.qos_specs:
             test_utils.call_and_ignore_notfound_exc(
-                cls.volume_qos_client.wait_for_resource_deletion, qos_id)
+                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:
+            # Resource dictionary uses for is_resource_deleted method,
+            # to distinguish between volume-type to encryption-type.
+            resource = {'id': vol_type, 'type': 'volume-type'}
+            test_utils.call_and_ignore_notfound_exc(
+                cls.admin_volume_types_client.wait_for_resource_deletion,
+                resource)
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 866db3d..d138490 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -36,16 +36,11 @@
         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(
@@ -74,7 +69,7 @@
     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(
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 38913ce..76cd36c 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -18,6 +18,7 @@
 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 import test
 
@@ -30,7 +31,16 @@
     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):
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
new file mode 100644
index 0000000..87146db
--- /dev/null
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -0,0 +1,72 @@
+# 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")
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumesBackupsV2Test, cls).resource_setup()
+
+        cls.volume = cls.create_volume()
+
+    @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
+        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'])
+        # Attach volume to instance
+        self.servers_client.attach_volume(server['id'],
+                                          volumeId=self.volume['id'])
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       self.volume['id'], 'in-use')
+        self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
+                        self.volume['id'], 'available')
+        self.addCleanup(self.servers_client.detach_volume, server['id'],
+                        self.volume['id'])
+        # Create backup using force flag
+        backup_name = data_utils.rand_name('Backup')
+        backup = self.backups_client.create_backup(
+            volume_id=self.volume['id'],
+            name=backup_name, force=True)['backup']
+        self.addCleanup(self.backups_client.delete_backup, backup['id'])
+        self.backups_client.wait_for_backup_status(backup['id'],
+                                                   'available')
+        self.assertEqual(backup_name, backup['name'])
+
+
+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..f38a068
--- /dev/null
+++ b/tempest/api/volume/test_volumes_clone.py
@@ -0,0 +1,44 @@
+# 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 VolumesCloneTest(base.BaseVolumeTest):
+
+    @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)
+
+
+class VolumesV1CloneTest(VolumesCloneTest):
+    _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..ee51e00
--- /dev/null
+++ b/tempest/api/volume/test_volumes_clone_negative.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 config
+from tempest.lib import exceptions
+from tempest import test
+
+
+CONF = config.CONF
+
+
+class VolumesCloneTest(base.BaseVolumeTest):
+
+    @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 VolumesV1CloneTest(VolumesCloneTest):
+    _api_version = 1
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 5d83bb0..e5fcdfe 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -39,10 +39,6 @@
         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):
         # Create a volume, Get it's details and Delete the volume
         volume = {}
@@ -53,7 +49,7 @@
         kwargs['metadata'] = metadata
         volume = self.client.create_volume(**kwargs)['volume']
         self.assertIn('id', volume)
-        self.addCleanup(self._delete_volume, volume['id'])
+        self.addCleanup(self.delete_volume, self.client, volume['id'])
         waiters.wait_for_volume_status(self.client, volume['id'], 'available')
         self.assertIn(self.name_field, volume)
         self.assertEqual(volume[self.name_field], v_name,
@@ -113,7 +109,7 @@
                   'availability_zone': volume['availability_zone']}
         new_volume = self.client.create_volume(**params)['volume']
         self.assertIn('id', new_volume)
-        self.addCleanup(self._delete_volume, new_volume['id'])
+        self.addCleanup(self.delete_volume, self.client, new_volume['id'])
         waiters.wait_for_volume_status(self.client,
                                        new_volume['id'], 'available')
 
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 38a5a80..a93025d 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -72,8 +72,7 @@
     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)
+            cls.delete_volume(cls.client, volid)
         super(VolumesV2ListTestJSON, cls).resource_cleanup()
 
     def _list_by_param_value_and_assert(self, params, with_detail=False):
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 6707121..c7f1e6e 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -178,17 +178,19 @@
 
     @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']
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume['id'], 'available')
-        self.volumes_client.delete_volume(volume['id'])
-        self.volumes_client.wait_for_resource_deletion(volume['id'])
-        self.cleanup_snapshot(snapshot)
+        # Creates a volume a snapshot passing a size different from the source
+        src_size = CONF.volume.volume_size
+
+        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)
+
+        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)
 
     @test.idempotent_id('db4d8e0a-7a2e-41cc-a712-961f6844e896')
     def test_snapshot_list_param_limit(self):
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index 374979c..2df9523 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -46,6 +46,20 @@
                           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 1fa54c2..5117e6c 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -55,8 +55,7 @@
     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)
+            cls.delete_volume(cls.client, volid)
         super(VolumesV2ListTestJSON, cls).resource_cleanup()
 
     @test.idempotent_id('2a7064eb-b9c3-429b-b888-33928fc5edd3')
diff --git a/tempest/api/database/__init__.py b/tempest/api/volume/v3/__init__.py
similarity index 100%
rename from tempest/api/database/__init__.py
rename to tempest/api/volume/v3/__init__.py
diff --git a/tempest/api/database/__init__.py b/tempest/api/volume/v3/admin/__init__.py
similarity index 100%
copy from tempest/api/database/__init__.py
copy to tempest/api/volume/v3/admin/__init__.py
diff --git a/tempest/api/volume/v3/admin/test_user_messages.py b/tempest/api/volume/v3/admin/test_user_messages.py
new file mode 100644
index 0000000..9d59d1b
--- /dev/null
+++ b/tempest/api/volume/v3/admin/test_user_messages.py
@@ -0,0 +1,94 @@
+# 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.common import waiters
+from tempest import exceptions
+from tempest import test
+
+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('volume-type')
+        bogus_type = self.admin_volume_types_client.create_volume_type(
+            name=vol_type_name,
+            extra_specs=extra_specs)['volume_type']
+        self.addCleanup(self.admin_volume_types_client.delete_volume_type,
+                        bogus_type['id'])
+        params = {'volume_type': bogus_type['id']}
+        volume = self.volumes_client.create_volume(**params)['volume']
+        self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+        try:
+            waiters.wait_for_volume_status(self.volumes_client, volume['id'],
+                                           'error')
+        except exceptions.VolumeBuildErrorException:
+            # Error state is expected and desired
+            pass
+        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/v3/base.py b/tempest/api/volume/v3/base.py
new file mode 100644
index 0000000..c31c83c
--- /dev/null
+++ b/tempest/api/volume/v3/base.py
@@ -0,0 +1,64 @@
+# 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_messages_client
+
+    def setUp(self):
+        super(VolumesV3Test, self).setUp()
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            self.request_microversion))
+
+
+class VolumesV3AdminTest(VolumesV3Test):
+    """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_messages_client
+        cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
diff --git a/tempest/clients.py b/tempest/clients.py
index bc56710..fd010f2 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -20,178 +20,30 @@
 from tempest.common import negative_rest_client
 from tempest import config
 from tempest import exceptions
-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.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 \
-    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.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.subnetpools_client import SubnetpoolsClient
-from tempest.lib.services.network.subnets_client import SubnetsClient
-from tempest import manager
-from tempest.services.baremetal.v1.json.baremetal_client import \
-    BaremetalClient
-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
-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 IdentityServicesClient
-from tempest.services.identity.v2.json.tenants_client import TenantsClient
-from tempest.services.identity.v2.json.users_client import UsersClient
-from tempest.services.identity.v3.json.credentials_client import \
-    CredentialsClient
-from tempest.services.identity.v3.json.domains_client import DomainsClient
-from tempest.services.identity.v3.json.endpoints_client import \
-    EndPointsClient as EndPointsV3Client
-from tempest.services.identity.v3.json.groups_client import GroupsClient
-from tempest.services.identity.v3.json.identity_client import \
-    IdentityClient as IdentityV3Client
-from tempest.services.identity.v3.json.policies_client import PoliciesClient
-from tempest.services.identity.v3.json.projects_client import ProjectsClient
-from tempest.services.identity.v3.json.regions_client import RegionsClient
-from tempest.services.identity.v3.json.roles_client import \
-    RolesClient as RolesV3Client
-from tempest.services.identity.v3.json.services_client import \
-    ServicesClient as IdentityServicesV3Client
-from tempest.services.identity.v3.json.trusts_client import TrustsClient
-from tempest.services.identity.v3.json.users_clients import \
-    UsersClient as UsersV3Client
-from tempest.services.image.v1.json.images_client import ImagesClient
-from tempest.services.image.v2.json.images_client import \
-    ImagesClient as ImagesV2Client
-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.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 compute
+from tempest.lib.services import image
+from tempest.lib.services import network
+from tempest import service_clients
+from tempest.services import baremetal
+from tempest.services import data_processing
+from tempest.services import identity
+from tempest.services import object_storage
+from tempest.services import orchestration
+from tempest.services import volume
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
-class Manager(manager.Manager):
+class Manager(service_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(andreaf) This is only used by data_processing and baremetal clients,
+    # and should be removed once they are out of Tempest
     default_params_with_timeout_values = {
         'build_interval': CONF.compute.build_interval,
         'build_timeout': CONF.compute.build_timeout
@@ -206,141 +58,28 @@
         :param service: Service name
         :param scope: default scope for tokens produced by the auth provider
         """
-        super(Manager, self).__init__(credentials=credentials, scope=scope)
+        _, 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.baremetal_client = baremetal.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.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.routers_client = RoutersClient(
-            self.auth_provider,
-            CONF.network.catalog_type,
-            CONF.network.region or CONF.identity.region,
-            endpoint_type=CONF.network.endpoint_type,
-            build_interval=CONF.network.build_interval,
-            build_timeout=CONF.network.build_timeout,
-            **self.default_params)
-        self.security_group_rules_client = SecurityGroupRulesClient(
-            self.auth_provider,
-            CONF.network.catalog_type,
-            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.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 = ImagesV2Client(
-                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,
@@ -348,7 +87,7 @@
             build_interval=CONF.orchestration.build_interval,
             build_timeout=CONF.orchestration.build_timeout,
             **self.default_params)
-        self.data_processing_client = DataProcessingClient(
+        self.data_processing_client = data_processing.DataProcessingClient(
             self.auth_provider,
             CONF.data_processing.catalog_type,
             CONF.identity.region,
@@ -357,235 +96,321 @@
         self.negative_client = negative_rest_client.NegativeRestClient(
             self.auth_provider, service, **self.default_params)
 
-    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)
+    def _prepare_configuration(self):
+        """Map values from CONF into Manager parameters
 
-        self.agents_client = AgentsClient(self.auth_provider, **params)
-        self.compute_networks_client = ComputeNetworksClient(
+        This uses `config.service_client_config` for all services to collect
+        most configuration items needed to init the clients.
+        """
+        # NOTE(andreaf) Configuration items will be passed in future patches
+        # into ClientFactory objects, but for now we update all the
+        # _set_*_client methods to consume them so we can verify that the
+        # configuration collected is correct
+
+        configuration = {}
+
+        # Setup the parameters for all Tempest services.
+        # NOTE(andreaf) Since client.py is an internal module of Tempest,
+        # it doesn't have to consider plugin configuration.
+        for service in service_clients.tempest_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.warn(
+                    'Could not load configuration for service %s' % service)
+
+        return configuration
+
+    def _set_network_clients(self):
+        params = self.parameters['network']
+        self.network_agents_client = network.AgentsClient(
             self.auth_provider, **params)
-        self.migrations_client = MigrationsClient(self.auth_provider,
-                                                  **params)
+        self.network_extensions_client = network.ExtensionsClient(
+            self.auth_provider, **params)
+        self.networks_client = network.NetworksClient(
+            self.auth_provider, **params)
+        self.subnetpools_client = network.SubnetpoolsClient(
+            self.auth_provider, **params)
+        self.subnets_client = network.SubnetsClient(
+            self.auth_provider, **params)
+        self.ports_client = network.PortsClient(
+            self.auth_provider, **params)
+        self.network_quotas_client = network.QuotasClient(
+            self.auth_provider, **params)
+        self.floating_ips_client = network.FloatingIPsClient(
+            self.auth_provider, **params)
+        self.metering_labels_client = network.MeteringLabelsClient(
+            self.auth_provider, **params)
+        self.metering_label_rules_client = network.MeteringLabelRulesClient(
+            self.auth_provider, **params)
+        self.routers_client = network.RoutersClient(
+            self.auth_provider, **params)
+        self.security_group_rules_client = network.SecurityGroupRulesClient(
+            self.auth_provider, **params)
+        self.security_groups_client = network.SecurityGroupsClient(
+            self.auth_provider, **params)
+        self.network_versions_client = network.NetworkVersionsClient(
+            self.auth_provider, **params)
+
+    def _set_image_clients(self):
+        if CONF.service_available.glance:
+            params = self.parameters['image']
+            self.image_client = image.v1.ImagesClient(
+                self.auth_provider, **params)
+            self.image_member_client = image.v1.ImageMembersClient(
+                self.auth_provider, **params)
+
+            self.image_client_v2 = image.v2.ImagesClient(
+                self.auth_provider, **params)
+            self.image_member_client_v2 = image.v2.ImageMembersClient(
+                self.auth_provider, **params)
+            self.namespaces_client = image.v2.NamespacesClient(
+                self.auth_provider, **params)
+            self.resource_types_client = image.v2.ResourceTypesClient(
+                self.auth_provider, **params)
+            self.schemas_client = image.v2.SchemasClient(
+                self.auth_provider, **params)
+
+    def _set_compute_clients(self):
+        params = self.parameters['compute']
+
+        self.agents_client = compute.AgentsClient(self.auth_provider, **params)
+        self.compute_networks_client = compute.NetworksClient(
+            self.auth_provider, **params)
+        self.migrations_client = compute.MigrationsClient(self.auth_provider,
+                                                          **params)
         self.security_group_default_rules_client = (
-            SecurityGroupDefaultRulesClient(self.auth_provider, **params))
-        self.certificates_client = CertificatesClient(self.auth_provider,
-                                                      **params)
-        self.servers_client = ServersClient(
+            compute.SecurityGroupDefaultRulesClient(self.auth_provider,
+                                                    **params))
+        self.certificates_client = compute.CertificatesClient(
+            self.auth_provider, **params)
+        self.servers_client = compute.ServersClient(
             self.auth_provider,
             enable_instance_password=CONF.compute_feature_enabled
                 .enable_instance_password,
             **params)
-        self.server_groups_client = ServerGroupsClient(
+        self.server_groups_client = compute.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,
+        self.limits_client = compute.LimitsClient(self.auth_provider, **params)
+        self.compute_images_client = compute.ImagesClient(self.auth_provider,
+                                                          **params)
+        self.keypairs_client = compute.KeyPairsClient(self.auth_provider,
+                                                      **params)
+        self.quotas_client = compute.QuotasClient(self.auth_provider, **params)
+        self.quota_classes_client = compute.QuotaClassesClient(
+            self.auth_provider, **params)
+        self.flavors_client = compute.FlavorsClient(self.auth_provider,
+                                                    **params)
+        self.extensions_client = compute.ExtensionsClient(self.auth_provider,
+                                                          **params)
+        self.floating_ip_pools_client = compute.FloatingIPPoolsClient(
+            self.auth_provider, **params)
+        self.floating_ips_bulk_client = compute.FloatingIPsBulkClient(
+            self.auth_provider, **params)
+        self.compute_floating_ips_client = compute.FloatingIPsClient(
+            self.auth_provider, **params)
+        self.compute_security_group_rules_client = (
+            compute.SecurityGroupRulesClient(self.auth_provider, **params))
+        self.compute_security_groups_client = compute.SecurityGroupsClient(
+            self.auth_provider, **params)
+        self.interfaces_client = compute.InterfacesClient(self.auth_provider,
+                                                          **params)
+        self.fixed_ips_client = compute.FixedIPsClient(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.availability_zone_client = compute.AvailabilityZoneClient(
             self.auth_provider, **params)
-        self.floating_ips_bulk_client = FloatingIPsBulkClient(
+        self.aggregates_client = compute.AggregatesClient(self.auth_provider,
+                                                          **params)
+        self.services_client = compute.ServicesClient(self.auth_provider,
+                                                      **params)
+        self.tenant_usages_client = compute.TenantUsagesClient(
             self.auth_provider, **params)
-        self.compute_floating_ips_client = ComputeFloatingIPsClient(
+        self.hosts_client = compute.HostsClient(self.auth_provider, **params)
+        self.hypervisor_client = compute.HypervisorClient(self.auth_provider,
+                                                          **params)
+        self.instance_usages_audit_log_client = (
+            compute.InstanceUsagesAuditLogClient(self.auth_provider, **params))
+        self.tenant_networks_client = compute.TenantNetworksClient(
             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.baremetal_nodes_client = compute.BaremetalNodesClient(
             self.auth_provider, **params)
 
         # 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(
+        # Optional parameters
+        for _key in ('build_interval', 'build_timeout'):
+            _value = self.parameters['volume'].get(_key)
+            if _value:
+                params_volume[_key] = _value
+        self.volumes_extensions_client = compute.VolumesClient(
             self.auth_provider, **params_volume)
-        self.compute_versions_client = VersionsClient(self.auth_provider,
-                                                      **params_volume)
-        self.snapshots_extensions_client = ComputeSnapshotsClient(
+        self.compute_versions_client = compute.VersionsClient(
             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)
+        self.snapshots_extensions_client = compute.SnapshotsClient(
+            self.auth_provider, **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)
+        params = self.parameters['identity']
 
         # Clients below use the admin endpoint type of Keystone API v2
-        params_v2_admin = params.copy()
+        params_v2_admin = copy.copy(params)
         params_v2_admin['endpoint_type'] = CONF.identity.v2_admin_endpoint_type
-        self.endpoints_client = EndpointsClient(self.auth_provider,
-                                                **params_v2_admin)
-        self.identity_client = IdentityClient(self.auth_provider,
-                                              **params_v2_admin)
-        self.tenants_client = TenantsClient(self.auth_provider,
-                                            **params_v2_admin)
-        self.roles_client = RolesClient(self.auth_provider, **params_v2_admin)
-        self.users_client = UsersClient(self.auth_provider, **params_v2_admin)
-        self.identity_services_client = IdentityServicesClient(
+        self.endpoints_client = identity.v2.EndpointsClient(self.auth_provider,
+                                                            **params_v2_admin)
+        self.identity_client = identity.v2.IdentityClient(self.auth_provider,
+                                                          **params_v2_admin)
+        self.tenants_client = identity.v2.TenantsClient(self.auth_provider,
+                                                        **params_v2_admin)
+        self.roles_client = identity.v2.RolesClient(self.auth_provider,
+                                                    **params_v2_admin)
+        self.users_client = identity.v2.UsersClient(self.auth_provider,
+                                                    **params_v2_admin)
+        self.identity_services_client = identity.v2.ServicesClient(
             self.auth_provider, **params_v2_admin)
 
         # Clients below use the public endpoint type of Keystone API v2
-        params_v2_public = params.copy()
+        params_v2_public = copy.copy(params)
         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)
+        self.identity_public_client = identity.v2.IdentityClient(
+            self.auth_provider, **params_v2_public)
+        self.tenants_public_client = identity.v2.TenantsClient(
+            self.auth_provider, **params_v2_public)
+        self.users_public_client = identity.v2.UsersClient(
+            self.auth_provider, **params_v2_public)
 
-        # Clients below use the endpoint type of Keystone API v3
-        params_v3 = params.copy()
+        # Clients below use the endpoint type of Keystone API v3, which is set
+        # in endpoint_type
+        params_v3 = copy.copy(params)
         params_v3['endpoint_type'] = CONF.identity.v3_endpoint_type
-        self.domains_client = DomainsClient(self.auth_provider,
-                                            **params_v3)
-        self.identity_v3_client = IdentityV3Client(self.auth_provider,
-                                                   **params_v3)
-        self.trusts_client = TrustsClient(self.auth_provider, **params_v3)
-        self.users_v3_client = UsersV3Client(self.auth_provider, **params_v3)
-        self.endpoints_v3_client = EndPointsV3Client(self.auth_provider,
-                                                     **params_v3)
-        self.roles_v3_client = RolesV3Client(self.auth_provider, **params_v3)
-        self.identity_services_v3_client = IdentityServicesV3Client(
+        self.domains_client = identity.v3.DomainsClient(self.auth_provider,
+                                                        **params_v3)
+        self.identity_v3_client = identity.v3.IdentityClient(
             self.auth_provider, **params_v3)
-        self.policies_client = PoliciesClient(self.auth_provider, **params_v3)
-        self.projects_client = ProjectsClient(self.auth_provider, **params_v3)
-        self.regions_client = RegionsClient(self.auth_provider, **params_v3)
-        self.credentials_client = CredentialsClient(self.auth_provider,
-                                                    **params_v3)
-        self.groups_client = GroupsClient(self.auth_provider, **params_v3)
+        self.trusts_client = identity.v3.TrustsClient(self.auth_provider,
+                                                      **params_v3)
+        self.users_v3_client = identity.v3.UsersClient(self.auth_provider,
+                                                       **params_v3)
+        self.endpoints_v3_client = identity.v3.EndPointsClient(
+            self.auth_provider, **params_v3)
+        self.roles_v3_client = identity.v3.RolesClient(self.auth_provider,
+                                                       **params_v3)
+        self.identity_services_v3_client = identity.v3.ServicesClient(
+            self.auth_provider, **params_v3)
+        self.policies_client = identity.v3.PoliciesClient(self.auth_provider,
+                                                          **params_v3)
+        self.projects_client = identity.v3.ProjectsClient(self.auth_provider,
+                                                          **params_v3)
+        self.regions_client = identity.v3.RegionsClient(self.auth_provider,
+                                                        **params_v3)
+        self.credentials_client = identity.v3.CredentialsClient(
+            self.auth_provider, **params_v3)
+        self.groups_client = identity.v3.GroupsClient(self.auth_provider,
+                                                      **params_v3)
 
         # Token clients do not use the catalog. They only need default_params.
         # They read auth_url, so they should only be set if the corresponding
         # API version is marked as enabled
         if CONF.identity_feature_enabled.api_v2:
             if CONF.identity.uri:
-                self.token_client = TokenClient(
+                self.token_client = identity.v2.TokenClient(
                     CONF.identity.uri, **self.default_params)
             else:
                 msg = 'Identity v2 API enabled, but no identity.uri set'
                 raise exceptions.InvalidConfiguration(msg)
         if CONF.identity_feature_enabled.api_v3:
             if CONF.identity.uri_v3:
-                self.token_v3_client = V3TokenClient(
+                self.token_v3_client = identity.v3.V3TokenClient(
                     CONF.identity.uri_v3, **self.default_params)
             else:
                 msg = 'Identity v3 API enabled, but no identity.uri_v3 set'
                 raise exceptions.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)
+        # Mandatory parameters (always defined)
+        params = self.parameters['volume']
 
-        self.volume_qos_client = QosSpecsClient(self.auth_provider,
-                                                **params)
-        self.volume_qos_v2_client = QosSpecsV2Client(
+        self.volume_qos_client = volume.v1.QosSpecsClient(self.auth_provider,
+                                                          **params)
+        self.volume_qos_v2_client = volume.v2.QosSpecsClient(
             self.auth_provider, **params)
-        self.volume_services_client = VolumeServicesClient(
+        self.volume_services_client = volume.v1.ServicesClient(
             self.auth_provider, **params)
-        self.volume_services_v2_client = VolumeServicesV2Client(
+        self.volume_services_v2_client = volume.v2.ServicesClient(
             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.backups_client = volume.v1.BackupsClient(self.auth_provider,
+                                                      **params)
+        self.backups_v2_client = volume.v2.BackupsClient(self.auth_provider,
+                                                         **params)
+        self.snapshots_client = volume.v1.SnapshotsClient(self.auth_provider,
+                                                          **params)
+        self.snapshots_v2_client = volume.v2.SnapshotsClient(
+            self.auth_provider, **params)
+        self.volumes_client = volume.v1.VolumesClient(
             self.auth_provider, default_volume_size=CONF.volume.volume_size,
             **params)
-        self.volumes_v2_client = VolumesV2Client(
+        self.volumes_v2_client = volume.v2.VolumesClient(
             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.volume_messages_client = volume.v3.MessagesClient(
             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,
+        self.volume_types_client = volume.v1.TypesClient(self.auth_provider,
+                                                         **params)
+        self.volume_types_v2_client = volume.v2.TypesClient(self.auth_provider,
                                                             **params)
-        self.volumes_extension_client = VolumeExtensionsClient(
+        self.volume_hosts_client = volume.v1.HostsClient(self.auth_provider,
+                                                         **params)
+        self.volume_hosts_v2_client = volume.v2.HostsClient(self.auth_provider,
+                                                            **params)
+        self.volume_quotas_client = volume.v1.QuotasClient(self.auth_provider,
+                                                           **params)
+        self.volume_quotas_v2_client = volume.v2.QuotasClient(
             self.auth_provider, **params)
-        self.volumes_v2_extension_client = VolumeExtensionsV2Client(
+        self.volumes_extension_client = volume.v1.ExtensionsClient(
+            self.auth_provider, **params)
+        self.volumes_v2_extension_client = volume.v2.ExtensionsClient(
             self.auth_provider, **params)
         self.volume_availability_zone_client = \
-            VolumeAvailabilityZoneClient(self.auth_provider, **params)
+            volume.v1.AvailabilityZoneClient(self.auth_provider, **params)
         self.volume_v2_availability_zone_client = \
-            VolumeAvailabilityZoneV2Client(self.auth_provider, **params)
+            volume.v2.AvailabilityZoneClient(self.auth_provider, **params)
 
     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.container_client = object_storage.ContainerClient(
+            self.auth_provider, **params)
+        self.object_client = object_storage.ObjectClient(self.auth_provider,
+                                                         **params)
+
+
+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 exceptions.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 94a646a..f9d7a9b 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -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,11 +87,13 @@
 **--with-admin** (Optional) Creates admin for each concurrent group
 (default: False).
 
+**-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
 
@@ -91,19 +101,10 @@
 from oslo_log import log as logging
 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
-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 routers_client
-from tempest.lib.services.network import subnets_client
-from tempest.services.identity.v2.json import identity_client
-from tempest.services.identity.v2.json import roles_client
-from tempest.services.identity.v2.json import tenants_client
-from tempest.services.identity.v2.json import users_client
+
 
 LOG = None
 CONF = config.CONF
@@ -120,290 +121,84 @@
     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
-    )
-    networks_admin = None
-    routers_admin = None
-    subnets_admin = None
-    neutron_iso_networks = False
-    if (CONF.service_available.neutron and
-        CONF.auth.create_isolated_networks):
-        neutron_iso_networks = True
-        networks_admin = networks_client.NetworksClient(
-            _auth,
-            CONF.network.catalog_type,
-            CONF.network.region or CONF.identity.region,
-            endpoint_type='adminURL',
-            **params)
-        routers_admin = routers_client.RoutersClient(
-            _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, networks_admin, routers_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,
+        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, networks_admin, routers_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(
-                networks_admin, routers_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['name'])
-            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(networks_admin_client,
-                             routers_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.project_network_cidr)
-        mask_bits = CONF.network.project_network_mask_bits
-        for subnet_cidr in base_cidr.subnet(mask_bits):
-            try:
-                resp_body = subnets_admin_client.\
-                    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 = routers_admin_client.create_router(
-            name=router_name,
-            external_gateway_info=external_net_id,
-            tenant_id=tenant_id)
-        return resp_body['router']
-
-    def _add_router_interface(router_id, subnet_id):
-        routers_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):
@@ -420,10 +215,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,
@@ -432,13 +235,20 @@
     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')
@@ -468,12 +278,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
@@ -487,9 +296,16 @@
         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 caba4b5..80de6f5 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()
@@ -233,15 +229,16 @@
     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)
+            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):
@@ -251,8 +248,9 @@
         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)
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 9e86b48..8d2cfdc 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -661,7 +661,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):
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 633b9e9..e3788ab 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -18,9 +18,12 @@
 import sys
 
 from cliff import command
+from oslo_config import generator
 from oslo_log import log as logging
 from six import moves
 
+from tempest.cmd.workspace import WorkspaceManager
+
 LOG = logging.getLogger(__name__)
 
 TESTR_CONF = """[DEFAULT]
@@ -37,45 +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
     global_conf_dir = '/etc/tempest'
-    if (real_prefix is None and
-            (base_prefix is None or base_prefix == prefix) and
-            os.path.isdir(global_conf_dir)):
-        # 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'
+    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:
-        conf_dir = os.path.join(prefix, 'etc/tempest')
-        if os.path.isdir(conf_dir):
-            return conf_dir
-        else:
-            # NOTE: The prefix is gotten from the path which pyconfig.h is
-            # installed under. Some envs contain it under /usr/include, not
-            # /user/local/include. Then prefix becomes /usr on such envs.
-            # However, etc/tempest is installed under /usr/local and the bove
-            # path logic mismatches. This is a workaround for such envs.
-            return os.path.join(prefix, 'local/etc/tempest')
+        os.makedirs(user_global_path)
+        return user_global_path
 
 
 class TempestInit(command.Command):
@@ -89,6 +75,10 @@
                             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")
         return parser
 
     def generate_testr_conf(self, local_path):
@@ -114,17 +104,25 @@
             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)
@@ -149,7 +147,7 @@
         # 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
@@ -159,6 +157,10 @@
             subprocess.call(['testr', 'init'], cwd=local_dir)
 
     def take_action(self, parsed_args):
+        workspace_manager = WorkspaceManager(parsed_args.workspace_path)
+        name = parsed_args.name or parsed_args.dir.split(os.path.sep)[-1]
+        workspace_manager.register_new_workspace(
+            name, parsed_args.dir, init=True)
         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)
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 6a65fcb..a9e5167 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -126,15 +126,15 @@
 from tempest.lib.services.compute import security_group_rules_client
 from tempest.lib.services.compute import security_groups_client
 from tempest.lib.services.compute import servers_client
+from tempest.lib.services.identity.v2 import roles_client
+from tempest.lib.services.identity.v2 import tenants_client
+from tempest.lib.services.identity.v2 import users_client
+from tempest.lib.services.image.v2 import images_client
 from tempest.lib.services.network import networks_client
 from tempest.lib.services.network import ports_client
 from tempest.lib.services.network import routers_client
 from tempest.lib.services.network import subnets_client
 from tempest.services.identity.v2.json import identity_client
-from tempest.services.identity.v2.json import roles_client
-from tempest.services.identity.v2.json import tenants_client
-from tempest.services.identity.v2.json import users_client
-from tempest.services.image.v2.json import images_client
 from tempest.services.object_storage import container_client
 from tempest.services.object_storage import object_client
 from tempest.services.volume.v1.json import volumes_client
@@ -319,7 +319,7 @@
     existing = [x['name'] for x in body]
     for tenant in tenants:
         if tenant not in existing:
-            admin.tenants.create_tenant(tenant)['tenant']
+            admin.tenants.create_tenant(name=tenant)['tenant']
         else:
             LOG.warning("Tenant '%s' already exists in this environment"
                         % tenant)
@@ -361,7 +361,7 @@
     role = next(r for r in roles if r['name'] == swift_role)
     LOG.debug(USERS[user])
     try:
-        admin.roles.assign_user_role(
+        admin.roles.create_user_role_on_project(
             USERS[user]['tenant_id'],
             USERS[user]['id'],
             role['id'])
@@ -391,8 +391,9 @@
                         % u['name'])
         except lib_exc.NotFound:
             admin.users.create_user(
-                u['name'], u['pass'], tenant['id'],
-                "%s@%s" % (u['name'], tenant['id']),
+                name=u['name'], password=u['pass'],
+                tenantId=tenant['id'],
+                email="%s@%s" % (u['name'], tenant['id']),
                 enabled=True)
 
 
diff --git a/tempest/cmd/list_plugins.py b/tempest/cmd/list_plugins.py
index 1f1ff1a..5f6b3e6 100644
--- a/tempest/cmd/list_plugins.py
+++ b/tempest/cmd/list_plugins.py
@@ -30,7 +30,6 @@
 class TempestListPlugins(command.Command):
     def take_action(self, parsed_args):
         self._list_plugins()
-        return 0
 
     def get_description(self):
         return 'List all tempest plugins'
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
new file mode 100644
index 0000000..2eb122e
--- /dev/null
+++ b/tempest/cmd/run.py
@@ -0,0 +1,278 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    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**: 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
+seperated 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**
+
+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
+from oslo_log import log as logging
+from testrepository.commands import run_argv
+
+from tempest.cmd import init
+from tempest.cmd import workspace
+from tempest import config
+
+
+LOG = logging.getLogger(__name__)
+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"] = ""
+
+    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)
+            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', 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',
+                                   help="Path to a whitelist file, this file "
+                                        "contains a seperate regex on each "
+                                        "newline.")
+        list_selector.add_argument('--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', 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)
+            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
index 9c8552f..7502c23 100755
--- a/tempest/cmd/run_stress.py
+++ b/tempest/cmd/run_stress.py
@@ -97,7 +97,6 @@
             LOG.exception("Failure in the stress test framework")
             traceback.print_exc()
             raise
-        return 0
 
     def get_description(self):
         return 'Run tempest stress tests'
diff --git a/tempest/cmd/subunit_describe_calls.py b/tempest/cmd/subunit_describe_calls.py
new file mode 100644
index 0000000..da7f426
--- /dev/null
+++ b/tempest/cmd/subunit_describe_calls.py
@@ -0,0 +1,262 @@
+# 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**: (Required) The path to the subunit file being parsed
+
+**--non-subunit-name, -n**: (Optional) The file_name that the logs are being
+stored in
+
+**--output-file, -o**: (Required) The path where the JSON output will be
+written to
+
+**--ports, -p**: (Optional) The path to a JSON file describing the ports being
+used by different services
+
+Usage
+-----
+
+subunit-describe-calls will take in a file path via the --subunit parameter
+which contains either a subunit v1 or v2 stream. 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"
+          }
+      ]
+  }
+"""
+import argparse
+import collections
+import io
+import json
+import os
+import re
+
+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>.*)')
+
+    # 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():
+            for line in detail.as_text().split("\n"):
+                match = self.url_re.match(line)
+                if match is not None:
+                    calls.append({
+                        "name": match.group("name"),
+                        "verb": match.group("verb"),
+                        "status_code": match.group("code"),
+                        "service": self.get_service(match.group("url")),
+                        "url": self.url_path(match.group("url"))})
+
+        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 = "Argument Parser"
+
+        self.add_argument(
+            "-s", "--subunit", metavar="<subunit file>", required=True,
+            default=None, 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.", required=True)
+
+        self.add_argument(
+            "-p", "--ports", metavar="<ports file>", default=None,
+            help="A JSON file describing the ports for each service.")
+
+
+def parse(subunit_file, non_subunit_name, ports):
+    if ports is not None and os.path.exists(ports):
+        ports = json.loads(open(ports).read())
+
+    url_parser = UrlParser(ports)
+    stream = open(subunit_file, 'rb')
+    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):
+    with open(output_file, "w") as outfile:
+        outfile.write(json.dumps(url_parser.test_logs))
+
+
+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 72ea894..77b88f9 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -285,7 +285,6 @@
         '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()
@@ -384,7 +383,7 @@
     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']:
@@ -418,12 +417,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..6e4b9b4
--- /dev/null
+++ b/tempest/cmd/workspace.py
@@ -0,0 +1,228 @@
+# 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
+from oslo_log import log as logging
+import prettytable
+import yaml
+
+from tempest import config
+
+LOG = logging.getLogger(__name__)
+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"""
+        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.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/compute.py b/tempest/common/compute.py
index 7ebc283..c290b57 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -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
@@ -106,7 +107,7 @@
             '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
diff --git a/tempest/common/cred_client.py b/tempest/common/cred_client.py
index b23bc6f..2ca9f40 100644
--- a/tempest/common/cred_client.py
+++ b/tempest/common/cred_client.py
@@ -40,8 +40,9 @@
         self.roles_client = roles_client
 
     def create_user(self, username, password, project, email):
-        user = self.users_client.create_user(
-            username, password, project['id'], email)
+        params = self._create_user_params(username, password,
+                                          project['id'], email)
+        user = self.users_client.create_user(**params)
         if 'user' in user:
             user = user['user']
         return user
@@ -101,6 +102,13 @@
                                             users_client,
                                             roles_client)
 
+    def _create_user_params(self, username, password, project_id, email):
+        params = {'name': username,
+                  'password': password,
+                  'tenantId': project_id,
+                  'email': email}
+        return params
+
     def create_project(self, name, description):
         tenant = self.projects_client.create_tenant(
             name=name, description=description)['tenant']
@@ -121,8 +129,9 @@
             password=password)
 
     def _assign_user_role(self, project, user, role):
-        self.roles_client.assign_user_role(project['id'], user['id'],
-                                           role['id'])
+        self.roles_client.create_user_role_on_project(project['id'],
+                                                      user['id'],
+                                                      role['id'])
 
 
 class V3CredsClient(CredsClient):
@@ -143,6 +152,13 @@
             msg = "Requested domain %s could not be found" % domain_name
             raise lib_exc.InvalidCredentials(msg)
 
+    def _create_user_params(self, username, password, project_id, email):
+        params = {'user_name': username,
+                  'password': password,
+                  'project_id': project_id,
+                  'email': email}
+        return params
+
     def create_project(self, name, description):
         project = self.projects_client.create_project(
             name=name, description=description,
@@ -155,6 +171,9 @@
     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,
@@ -163,7 +182,9 @@
             project_name=project['name'], project_id=project['id'],
             password=password,
             project_domain_id=self.creds_domain['id'],
-            project_domain_name=self.creds_domain['name'])
+            project_domain_name=self.creds_domain['name'],
+            domain_id=self.creds_domain['id'],
+            domain_name=self.creds_domain['name'])
 
     def _assign_user_role(self, project, user, role):
         self.roles_client.assign_user_role_on_project(project['id'],
diff --git a/tempest/common/cred_provider.py b/tempest/common/cred_provider.py
index 5cce0bb..1b450ab 100644
--- a/tempest/common/cred_provider.py
+++ b/tempest/common/cred_provider.py
@@ -16,8 +16,8 @@
 
 import six
 
-from tempest import exceptions
 from tempest.lib import auth
+from tempest.lib import exceptions
 
 
 @six.add_metaclass(abc.ABCMeta)
@@ -94,7 +94,7 @@
                           self.router)
 
     def set_resources(self, **kwargs):
-        for key in kwargs.keys():
+        for key in kwargs:
             if hasattr(self, key):
                 setattr(self, key, kwargs[key])
 
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 286e01f..c22afc1 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -39,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)),
@@ -80,13 +80,13 @@
             network_resources=network_resources,
             identity_version=identity_version,
             admin_creds=admin_creds,
-            **_get_dynamic_provider_params())
+            **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:
             raise exceptions.InvalidConfiguration(
                 'A valid credential provider is needed')
@@ -106,7 +106,7 @@
     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:
@@ -131,7 +131,7 @@
     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:
         raise exceptions.InvalidConfiguration(
             'A valid credential provider is needed')
@@ -193,17 +193,21 @@
 
 
 # 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:
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index 3a3e3c2..c9b9db1 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -96,28 +96,35 @@
                     os.networks_client, os.routers_client, os.subnets_client,
                     os.ports_client, os.security_groups_client)
         else:
-            return (os.identity_v3_client, os.projects_client,
-                    os.users_v3_client, os.roles_v3_client, os.domains_client,
+            # We use a dedicated client manager for identity client in case we
+            # need a different token scope for them.
+            scope = 'domain' if CONF.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)
 
-    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)
         project_desc = project_name + "-desc"
         project = self.creds_client.create_project(
             name=project_name, description=project_desc)
@@ -126,17 +133,17 @@
         # 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) + "@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':
+            if (self.identity_version == 'v3' and
+                    CONF.identity.admin_domain_scope):
                 self.creds_client.assign_user_role_on_domain(
                     user, CONF.identity.admin_role)
         # Add roles specified in config file
@@ -152,7 +159,10 @@
         # 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)
diff --git a/tempest/common/fixed_network.py b/tempest/common/fixed_network.py
index 5f0685e..f57c18a 100644
--- a/tempest/common/fixed_network.py
+++ b/tempest/common/fixed_network.py
@@ -14,7 +14,7 @@
 from oslo_log import log as logging
 
 from tempest import exceptions
-from tempest.lib.common.utils import misc as misc_utils
+from tempest.lib.common.utils import test_utils
 
 LOG = logging.getLogger(__name__)
 
@@ -31,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)
@@ -84,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'):
diff --git a/tempest/common/image.py b/tempest/common/image.py
new file mode 100644
index 0000000..95a7d1a
--- /dev/null
+++ b/tempest/common/image.py
@@ -0,0 +1,67 @@
+# 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
+
+import six
+
+
+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 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
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index 42c69d5..5992d24 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -86,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)
@@ -165,12 +163,7 @@
         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()
@@ -245,9 +238,6 @@
         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
@@ -329,12 +319,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)
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 0a3e8d3..7cb9ebe 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -99,10 +99,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')
diff --git a/tempest/common/validation_resources.py b/tempest/common/validation_resources.py
index c3c9a41..a55ee32 100644
--- a/tempest/common/validation_resources.py
+++ b/tempest/common/validation_resources.py
@@ -22,6 +22,26 @@
 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,12 +50,15 @@
     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)
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 23d7f88..df08e30 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -15,11 +15,12 @@
 
 from oslo_log import log as logging
 
+from tempest.common import image as common_image
 from tempest import config
 from tempest import exceptions
-from tempest.lib.common.utils import misc as misc_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
-from tempest.services.image.v1.json import images_client as images_v1_client
+from tempest.lib.services.image.v1 import images_client as images_v1_client
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
@@ -90,7 +91,7 @@
                         '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)
@@ -127,7 +128,11 @@
         # 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.
-        show_image = client.check_image
+        def _show_image_v1(image_id):
+            resp = client.check_image(image_id)
+            return common_image.get_image_meta_from_headers(resp)
+
+        show_image = _show_image_v1
     else:
         show_image = client.show_image
 
@@ -157,7 +162,7 @@
                                           'status': status,
                                           'current_status': current_status,
                                           'timeout': client.build_timeout})
-    caller = misc_utils.find_test_caller()
+    caller = test_utils.find_test_caller()
     if caller:
         message = '(%s) %s' % (caller, message)
     raise exceptions.TimeoutException(message)
@@ -230,7 +235,7 @@
                         'status': status,
                         'timeout': client.build_timeout})
             message += ' Current state of %s: %s.' % (attr, status_curr)
-            caller = misc_utils.find_test_caller()
+            caller = test_utils.find_test_caller()
             if caller:
                 message = '(%s) %s' % (caller, message)
             raise exceptions.TimeoutException(message)
diff --git a/tempest/config.py b/tempest/config.py
index 3810ceb..0c2b913 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -25,6 +25,7 @@
 from oslo_log import log as logging
 import testtools
 
+from tempest.lib import exceptions
 from tempest.test_discover import plugins
 
 
@@ -166,6 +167,10 @@
     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")
 ]
 
 identity_feature_group = cfg.OptGroup(name='identity-feature-enabled',
@@ -187,7 +192,12 @@
                 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=False,
+                help='Does the environment support reseller?')
 ]
 
 compute_group = cfg.OptGroup(name='compute',
@@ -362,7 +372,7 @@
                 default=True,
                 help='Does the test environment have the nova cert running?'),
     cfg.BoolOpt('personality',
-                default=True,
+                default=False,
                 help='Does the test environment support server personality'),
     cfg.BoolOpt('attach_encrypted_volume',
                 default=True,
@@ -678,6 +688,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',
@@ -707,6 +735,9 @@
     cfg.BoolOpt('api_v2',
                 default=True,
                 help="Is the v2 volume API enabled"),
+    cfg.BoolOpt('api_v3',
+                default=False,
+                help="Is the v3 volume API enabled"),
     cfg.BoolOpt('bootable',
                 default=True,
                 help='Update bootable status of a volume '
@@ -786,21 +817,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')
 
@@ -977,9 +993,6 @@
     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",
@@ -1010,23 +1023,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."),
+                    "to matching image names.",
+               deprecated_for_removal=True),
 ]
 
 
@@ -1111,7 +1129,6 @@
     (volume_feature_group, VolumeFeaturesGroup),
     (object_storage_group, ObjectStoreGroup),
     (object_storage_feature_group, ObjectStoreFeaturesGroup),
-    (database_group, DatabaseGroup),
     (orchestration_group, OrchestrationGroup),
     (data_processing_group, DataProcessingGroup),
     (data_processing_feature_group, DataProcessingFeaturesGroup),
@@ -1178,7 +1195,6 @@
         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.data_processing = _CONF['data-processing']
         self.data_processing_feature_enabled = _CONF[
@@ -1335,3 +1351,84 @@
             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`
+
+    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
+    }
+
+    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
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 92f335f..f534f30 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -13,157 +13,65 @@
 #    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):
+class InvalidConfiguration(exceptions.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):
+class TimeoutException(exceptions.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 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):
+class VolumeBackupException(exceptions.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 EndpointNotFound(TempestException):
-    message = "Endpoint not found"
-
-
-class IdentityError(TempestException):
-    message = "Got identity error"
-
-
-class ServerUnreachable(TempestException):
+class ServerUnreachable(exceptions.TempestException):
     message = "The server 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
 # be migrated along with them
-class InvalidTestResource(TempestException):
+class InvalidTestResource(exceptions.TempestException):
     message = "%(name) is not a valid %(type), 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 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 09106d1..e2d6585 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -19,7 +19,7 @@
 
 
 PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron',
-                  'trove', 'ironic', 'savanna', 'heat', 'sahara']
+                  'ironic', 'savanna', 'heat', 'sahara']
 
 PYTHON_CLIENT_RE = re.compile('import (%s)client' % '|'.join(PYTHON_CLIENTS))
 TEST_DEFINITION = re.compile(r'^\s*def test.*')
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
index c6c4deb..15224c5 100644
--- 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
@@ -19,7 +19,23 @@
             'type': 'array',
             'items': {'type': 'string'}
         },
-        'log': {'type': 'object'},
+        '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'},
diff --git a/tempest/lib/api_schema/response/compute/v2_1/servers.py b/tempest/lib/api_schema/response/compute/v2_1/servers.py
index 3289f04..44497db 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -174,7 +174,16 @@
     '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'},
+    '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'][
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 974ba82..54a7002 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -1,4 +1,5 @@
 # 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
@@ -179,7 +180,7 @@
         :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
-        :returns a Tuple (url, headers, body)
+        :return: a Tuple (url, headers, body)
         """
         orig_req = dict(url=url, headers=headers, body=body)
 
@@ -223,6 +224,7 @@
         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
@@ -260,7 +262,7 @@
                  disable_ssl_certificate_validation=None,
                  ca_certs=None, trace_requests=None, scope='project'):
         super(KeystoneAuthProvider, self).__init__(credentials, scope)
-        self.dsvm = disable_ssl_certificate_validation
+        self.dscv = disable_ssl_certificate_validation
         self.ca_certs = ca_certs
         self.trace_requests = trace_requests
         self.auth_url = auth_url
@@ -339,7 +341,7 @@
 
     def _auth_client(self, auth_url):
         return json_v2id.TokenClient(
-            auth_url, disable_ssl_certificate_validation=self.dsvm,
+            auth_url, disable_ssl_certificate_validation=self.dscv,
             ca_certs=self.ca_certs, trace_requests=self.trace_requests)
 
     def _auth_params(self):
@@ -368,18 +370,27 @@
     def base_url(self, filters, auth_data=None):
         """Base URL from catalog
 
+        :param filters: Used to filter results
+
         Filters can be:
-        - service: compute, image, etc
-        - region: the service region
-        - endpoint_type: adminURL, publicURL, internalURL
-        - api_version: replace catalog version with this
-        - skip_path: take just the base URL
+
+        - 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:
@@ -388,17 +399,19 @@
         _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 matching, use the first
+                    # 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" %
-                (service, region, endpoint_type))
+                "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):
@@ -415,7 +428,7 @@
 
     def _auth_client(self, auth_url):
         return json_v3id.V3TokenClient(
-            auth_url, disable_ssl_certificate_validation=self.dsvm,
+            auth_url, disable_ssl_certificate_validation=self.dscv,
             ca_certs=self.ca_certs, trace_requests=self.trace_requests)
 
     def _auth_params(self):
@@ -489,18 +502,27 @@
         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: compute, image, etc
-        - region: the service region
-        - endpoint_type: adminURL, publicURL, internalURL
-        - api_version: replace catalog version with this
-        - skip_path: take just the base URL
+
+        - 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:
@@ -510,19 +532,38 @@
             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:
-            service_catalog = service_catalog[0]['endpoints']
+            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
-                raise exceptions.EndpointNotFound(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]
@@ -533,7 +574,7 @@
         filtered_catalog = [ep for ep in filtered_catalog if
                             ep['region'] == region]
         if len(filtered_catalog) == 0:
-            # No matching region, take the first endpoint
+            # 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)
@@ -590,9 +631,9 @@
     creds = credential_class(**kwargs)
     # Fill in the credentials fields that were not specified
     if fill_in:
-        dsvm = disable_ssl_certificate_validation
+        dscv = disable_ssl_certificate_validation
         auth_provider = auth_provider_class(
-            creds, auth_url, disable_ssl_certificate_validation=dsvm,
+            creds, auth_url, disable_ssl_certificate_validation=dscv,
             ca_certs=ca_certs, trace_requests=trace_requests)
         creds = auth_provider.fill_credentials()
     return creds
@@ -624,7 +665,7 @@
                 msg = ('Cannot have conflicting values for %s and %s' %
                        (key1, key2))
                 raise exceptions.InvalidCredentials(msg)
-        for key in attr.keys():
+        for key in attr:
             if key in self.ATTRIBUTES:
                 setattr(self, key, attr[key])
             else:
diff --git a/tempest/lib/cmd/skip_tracker.py b/tempest/lib/cmd/skip_tracker.py
index f35b14c..d95aa46 100755
--- a/tempest/lib/cmd/skip_tracker.py
+++ b/tempest/lib/cmd/skip_tracker.py
@@ -103,7 +103,7 @@
 
 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
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index bf107d2..1b0f53a 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -54,6 +54,8 @@
     :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
@@ -76,10 +78,11 @@
                  endpoint_type='publicURL',
                  build_interval=1, build_timeout=60,
                  disable_ssl_certificate_validation=False, ca_certs=None,
-                 trace_requests=''):
+                 trace_requests='', name=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
@@ -191,7 +194,8 @@
         _filters = dict(
             service=self.service,
             endpoint_type=self.endpoint_type,
-            region=self.region
+            region=self.region,
+            name=self.name
         )
         if self.api_version is not None:
             _filters['api_version'] = self.api_version
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 45e5067..7fcec8c 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -19,6 +19,7 @@
 import string
 import uuid
 
+from oslo_utils import netutils
 import six.moves
 
 
@@ -153,7 +154,7 @@
     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 shrinked to the size.
-    :param int size: a returning charactors size
+    :param int size: a returning characters size
     :param str base_text: a string you want to repeat
     :return: size string
     :rtype: string
@@ -183,7 +184,7 @@
     :rtype: netaddr.IPAddress
     """
     # Check if the prefix is IPv4 address
-    is_ipv4 = netaddr.valid_ipv4(cidr)
+    is_ipv4 = netutils.is_valid_ipv4(cidr)
     if is_ipv4:
         msg = "Unable to generate IP address by EUI64 for IPv4 prefix"
         raise TypeError(msg)
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index 259bbbb..5ca78f9 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -149,6 +149,10 @@
     message = "Unexpected response code received"
 
 
+class InvalidIdentityVersion(TempestException):
+    message = "Invalid version %(identity_version)s of the identity service"
+
+
 class InvalidStructure(TempestException):
     message = "Invalid structure of table with details"
 
@@ -221,3 +225,7 @@
     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"
diff --git a/tempest/lib/services/compute/__init__.py b/tempest/lib/services/compute/__init__.py
index e69de29..91e896a 100644
--- a/tempest/lib/services/compute/__init__.py
+++ 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/aggregates_client.py b/tempest/lib/services/compute/aggregates_client.py
index 168126c..7ad14bc 100644
--- a/tempest/lib/services/compute/aggregates_client.py
+++ b/tempest/lib/services/compute/aggregates_client.py
@@ -41,7 +41,7 @@
         """Create a new aggregate.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#createaggregate
+                              api-ref-compute-v2.1.html#createAggregate
         """
         post_body = json.dumps({'aggregate': kwargs})
         resp, body = self.post('os-aggregates', post_body)
@@ -54,7 +54,7 @@
         """Update an aggregate.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#updateaggregate
+                              api-ref-compute-v2.1.html#updateAggregate
         """
         put_body = json.dumps({'aggregate': kwargs})
         resp, body = self.put('os-aggregates/%s' % aggregate_id, put_body)
@@ -85,7 +85,7 @@
         """Add a host to the given aggregate.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#addhost
+                              api-ref-compute-v2.1.html#addHost
         """
         post_body = json.dumps({'add_host': kwargs})
         resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
@@ -98,7 +98,7 @@
         """Remove a host from the given aggregate.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#removehost
+                              api-ref-compute-v2.1.html#removeAggregateHost
         """
         post_body = json.dumps({'remove_host': kwargs})
         resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
@@ -108,7 +108,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def set_metadata(self, aggregate_id, **kwargs):
-        """Replace the aggregate's existing metadata with new metadata."""
+        """Replace the aggregate's existing metadata with new metadata.
+
+        Available params: see 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)
diff --git a/tempest/lib/services/compute/base_compute_client.py b/tempest/lib/services/compute/base_compute_client.py
index 433c94c..a706a79 100644
--- a/tempest/lib/services/compute/base_compute_client.py
+++ b/tempest/lib/services/compute/base_compute_client.py
@@ -36,11 +36,6 @@
 
     api_microversion_header_name = 'X-OpenStack-Nova-API-Version'
 
-    def __init__(self, auth_provider, service, region,
-                 **kwargs):
-        super(BaseComputeClient, self).__init__(
-            auth_provider, service, region, **kwargs)
-
     def get_headers(self):
         headers = super(BaseComputeClient, self).get_headers()
         if COMPUTE_MICROVERSION:
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index e377c84..5be8272 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -52,7 +52,7 @@
         """Create a new flavor or instance type.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#create-flavors
+                              api-ref-compute-v2.1.html#createFlavor
         """
         if kwargs.get('ephemeral'):
             kwargs['OS-FLV-EXT-DATA:ephemeral'] = kwargs.pop('ephemeral')
@@ -91,7 +91,7 @@
         """Set extra Specs to the mentioned flavor.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#updateFlavorExtraSpec
+                              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,
@@ -123,7 +123,7 @@
         """Update specified extra Specs of the mentioned flavor and key.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#updateflavorspec
+                              api-ref-compute-v2.1.html#updateFlavorExtraSpec
         """
         resp, body = self.put('flavors/%s/os-extra_specs/%s' %
                               (flavor_id, key), json.dumps(kwargs))
diff --git a/tempest/lib/services/compute/migrations_client.py b/tempest/lib/services/compute/migrations_client.py
index 5eae8aa..62246d3 100644
--- a/tempest/lib/services/compute/migrations_client.py
+++ b/tempest/lib/services/compute/migrations_client.py
@@ -26,7 +26,7 @@
         """List all migrations.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#returnmigrations
+                              api-ref-compute-v2.1.html#listMigrations
         """
 
         url = 'os-migrations'
diff --git a/tempest/lib/services/compute/quotas_client.py b/tempest/lib/services/compute/quotas_client.py
index 184a3d7..6d41f4b 100644
--- a/tempest/lib/services/compute/quotas_client.py
+++ b/tempest/lib/services/compute/quotas_client.py
@@ -46,7 +46,7 @@
         """Updates the tenant's quota limits for one or more resources.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#updatesquotatenant
+                              api-ref-compute-v2.1.html#updateQuota
         """
 
         post_body = json.dumps({'quota_set': kwargs})
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 8e4eca1..0d31ac7 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -315,7 +315,11 @@
         return self.action(server_id, 'os-start', **kwargs)
 
     def attach_volume(self, server_id, **kwargs):
-        """Attaches a volume to a server instance."""
+        """Attaches a volume to a server instance.
+
+        Available params: see 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)
diff --git a/tempest/services/identity/v2/json/endpoints_client.py b/tempest/lib/services/identity/v2/endpoints_client.py
similarity index 77%
rename from tempest/services/identity/v2/json/endpoints_client.py
rename to tempest/lib/services/identity/v2/endpoints_client.py
index ba9f867..f7b265d 100644
--- a/tempest/services/identity/v2/json/endpoints_client.py
+++ b/tempest/lib/services/identity/v2/endpoints_client.py
@@ -20,16 +20,14 @@
 class EndpointsClient(rest_client.RestClient):
     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})
+    def create_endpoint(self, **kwargs):
+        """Create an endpoint for service.
+
+        Available params: 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)
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..15c8834
--- /dev/null
+++ b/tempest/lib/services/identity/v2/roles_client.py
@@ -0,0 +1,107 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES 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.
+
+        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 rest_client.ResponseBody(resp, body)
+
+    def show_role(self, role_id_or_name):
+        """Get a role by its id or name.
+
+        Available params: see
+            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.
+
+        Available params: see 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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#deleteRole
+        """
+        resp, body = self.delete('OS-KSADM/roles/%s' % str(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.
+
+        Available params: see
+            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.
+
+        Available params: see
+            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/services/identity/v2/json/services_client.py b/tempest/lib/services/identity/v2/services_client.py
similarity index 83%
rename from tempest/services/identity/v2/json/services_client.py
rename to tempest/lib/services/identity/v2/services_client.py
index d8be6c6..4a63d56 100644
--- a/tempest/services/identity/v2/json/services_client.py
+++ b/tempest/lib/services/identity/v2/services_client.py
@@ -13,6 +13,7 @@
 # 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
 
@@ -20,14 +21,9 @@
 class ServicesClient(rest_client.RestClient):
     api_version = "v2.0"
 
-    def create_service(self, name, type, **kwargs):
+    def create_service(self, **kwargs):
         """Create a service."""
-        post_body = {
-            'name': name,
-            'type': type,
-            'description': kwargs.get('description')
-        }
-        post_body = json.dumps({'OS-KSADM:service': post_body})
+        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)
@@ -41,9 +37,12 @@
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
-    def list_services(self):
+    def list_services(self, **params):
         """List Service - Returns Services."""
-        resp, body = self.get('/OS-KSADM/services')
+        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)
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..77ddaa5
--- /dev/null
+++ b/tempest/lib/services/identity/v2/tenants_client.py
@@ -0,0 +1,98 @@
+# 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
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#createTenant
+        """
+        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.
+
+        Available params: see 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.
+
+        Available params: see
+            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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#admin-listTenants
+        """
+        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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#updateTenant
+        """
+        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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#listUsersForTenant
+        """
+        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/services/identity/v2/json/users_client.py b/tempest/lib/services/identity/v2/users_client.py
similarity index 77%
rename from tempest/services/identity/v2/json/users_client.py
rename to tempest/lib/services/identity/v2/users_client.py
index 5f8127f..4ea17f9 100644
--- a/tempest/services/identity/v2/json/users_client.py
+++ b/tempest/lib/services/identity/v2/users_client.py
@@ -11,6 +11,7 @@
 #    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
 
@@ -18,25 +19,24 @@
 class UsersClient(rest_client.RestClient):
     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})
+    def create_user(self, **kwargs):
+        """Create a user.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-admin-v2.html#admin-createUser
+        """
+        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."""
+        """Updates a user.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-admin-v2.html#admin-updateUser
+        """
         put_body = json.dumps({'user': kwargs})
         resp, body = self.put('users/%s' % user_id, put_body)
         self.expected_success(200, resp.status)
@@ -44,26 +44,41 @@
         return rest_client.ResponseBody(resp, body)
 
     def show_user(self, user_id):
-        """GET a user."""
+        """GET a user.
+
+        Available params: see 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."""
+        """Delete a user.
+
+        Available params: see 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):
-        """Get the list of users."""
-        resp, body = self.get("users")
+    def list_users(self, **params):
+        """Get the list of users.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-admin-v2.html#admin-listUsers
+        """
+        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 enable_disable_user(self, user_id, **kwargs):
+    def update_user_enabled(self, user_id, **kwargs):
         """Enables or disables a user.
 
         Available params: see http://developer.openstack.org/
@@ -106,7 +121,7 @@
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
-    def create_user_ec2_credentials(self, user_id, **kwargs):
+    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.
@@ -117,7 +132,7 @@
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
-    def delete_user_ec2_credentials(self, user_id, access):
+    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)
@@ -129,7 +144,7 @@
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
-    def show_user_ec2_credentials(self, user_id, access):
+    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)
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..e7fa0c9
--- /dev/null
+++ b/tempest/lib/services/image/v1/image_members_client.py
@@ -0,0 +1,63 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES 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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-image-v1.html#listSharedImages-v1
+        """
+
+        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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-image-v1.html#addMember-v1
+        """
+        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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-image-v1.html#removeMember-v1
+        """
+        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..0db98f8
--- /dev/null
+++ b/tempest/lib/services/image/v1/images_client.py
@@ -0,0 +1,154 @@
+# 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('POST', 'images', headers, data, 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('PUT', url, headers, data,
+                            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.
+
+        Available params: 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.
+
+        Available params: 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.
+
+        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 = 'images'
+
+        if detail:
+            url += '/detail'
+
+        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 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..32bad8b
--- /dev/null
+++ b/tempest/lib/services/image/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.image.v2.image_members_client import \
+    ImageMembersClient
+from tempest.lib.services.image.v2.images_client import ImagesClient
+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', '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..d0ab165
--- /dev/null
+++ b/tempest/lib/services/image/v2/image_members_client.py
@@ -0,0 +1,79 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES 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.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-image-v2.html#listImageMembers-v2
+        """
+        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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-image-v2.html#createImageMember-v2
+        """
+        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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-image-v2.html#updateImageMember-v2
+        """
+        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.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-image-v2.html#showImageMember-v2
+        """
+        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.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-image-v2.html#deleteImageMember-v2
+        """
+        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..4276847
--- /dev/null
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -0,0 +1,148 @@
+# 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.
+
+        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('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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-image-v2.html#createImage-v2
+        """
+        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):
+        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):
+        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):
+        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):
+        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):
+        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):
+        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):
+        """Show an image file.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-image-v2.html#showImageFile-v2
+        """
+        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.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-image-v2.html#addImageTag-v2
+        """
+        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.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-image-v2.html#deleteImageTag-v2
+        """
+        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/namespaces_client.py b/tempest/lib/services/image/v2/namespaces_client.py
new file mode 100644
index 0000000..5bd096d
--- /dev/null
+++ b/tempest/lib/services/image/v2/namespaces_client.py
@@ -0,0 +1,69 @@
+# 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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-image-v2.html#createNamespace-v2
+        """
+        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 show_namespace(self, namespace):
+        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.
+
+        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 = '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.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-image-v2.html#deleteNamespace-v2
+        """
+        url = 'metadefs/namespaces/%s' % namespace
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/services/database/json/limits_client.py b/tempest/lib/services/image/v2/resource_types_client.py
similarity index 73%
copy from tempest/services/database/json/limits_client.py
copy to tempest/lib/services/image/v2/resource_types_client.py
index a1c58c2..1349c63 100644
--- a/tempest/services/database/json/limits_client.py
+++ b/tempest/lib/services/image/v2/resource_types_client.py
@@ -1,4 +1,4 @@
-# Copyright 2014 OpenStack Foundation
+# Copyright 2013 IBM Corp.
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -14,18 +14,15 @@
 #    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 DatabaseLimitsClient(rest_client.RestClient):
+class ResourceTypesClient(rest_client.RestClient):
+    api_version = "v2"
 
-    def list_db_limits(self, params=None):
-        """List all limits."""
-        url = 'limits'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
+    def list_resource_types(self):
+        url = 'metadefs/resource_types'
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
diff --git a/tempest/services/database/json/limits_client.py b/tempest/lib/services/image/v2/schemas_client.py
similarity index 73%
rename from tempest/services/database/json/limits_client.py
rename to tempest/lib/services/image/v2/schemas_client.py
index a1c58c2..0c9db40 100644
--- a/tempest/services/database/json/limits_client.py
+++ b/tempest/lib/services/image/v2/schemas_client.py
@@ -1,4 +1,4 @@
-# Copyright 2014 OpenStack Foundation
+# Copyright 2013 IBM Corp.
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -14,18 +14,15 @@
 #    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 DatabaseLimitsClient(rest_client.RestClient):
+class SchemasClient(rest_client.RestClient):
+    api_version = "v2"
 
-    def list_db_limits(self, params=None):
-        """List all limits."""
-        url = 'limits'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
+    def show_schema(self, schema):
+        url = 'schemas/%s' % schema
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
diff --git a/tempest/lib/services/network/__init__.py b/tempest/lib/services/network/__init__.py
index e69de29..c466f07 100644
--- a/tempest/lib/services/network/__init__.py
+++ b/tempest/lib/services/network/__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.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.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',
+           'SubnetpoolsClient', 'SubnetsClient', 'NetworkVersionsClient']
diff --git a/tempest/lib/services/network/networks_client.py b/tempest/lib/services/network/networks_client.py
old mode 100644
new mode 100755
index 24c2ec5..19fa1db
--- a/tempest/lib/services/network/networks_client.py
+++ b/tempest/lib/services/network/networks_client.py
@@ -16,11 +16,21 @@
 class NetworksClient(base.BaseNetworkClient):
 
     def create_network(self, **kwargs):
+        """Creates a network.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-networking-v2.html#createNetwork
+        """
         uri = '/networks'
         post_data = {'network': kwargs}
         return self.create_resource(uri, post_data)
 
     def update_network(self, network_id, **kwargs):
+        """Updates a network.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-networking-v2.html#updateNetwork
+        """
         uri = '/networks/%s' % network_id
         post_data = {'network': kwargs}
         return self.update_resource(uri, post_data)
diff --git a/tempest/lib/services/network/routers_client.py b/tempest/lib/services/network/routers_client.py
index 78ffa77..2ba1938 100644
--- a/tempest/lib/services/network/routers_client.py
+++ b/tempest/lib/services/network/routers_client.py
@@ -55,7 +55,7 @@
         """Remove router interface.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2-ext.html#removeRouterInterface
+                              api-ref-networking-v2-ext.html#deleteRouterInterface
         """
         uri = '/routers/%s/remove_router_interface' % router_id
         return self.update_resource(uri, kwargs)
diff --git a/tempest/lib/services/network/subnets_client.py b/tempest/lib/services/network/subnets_client.py
old mode 100644
new mode 100755
index 63ed13e..9de4a33
--- a/tempest/lib/services/network/subnets_client.py
+++ b/tempest/lib/services/network/subnets_client.py
@@ -16,11 +16,21 @@
 class SubnetsClient(base.BaseNetworkClient):
 
     def create_subnet(self, **kwargs):
+        """Creates a subnet on a network.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-networking-v2.html#createSubnet
+        """
         uri = '/subnets'
         post_data = {'subnet': kwargs}
         return self.create_resource(uri, post_data)
 
     def update_subnet(self, subnet_id, **kwargs):
+        """Updates a subnet.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-networking-v2.html#updateSubnet
+        """
         uri = '/subnets/%s' % subnet_id
         post_data = {'subnet': kwargs}
         return self.update_resource(uri, post_data)
diff --git a/tempest/lib/services/network/versions_client.py b/tempest/lib/services/network/versions_client.py
new file mode 100644
index 0000000..0202927
--- /dev/null
+++ b/tempest/lib/services/network/versions_client.py
@@ -0,0 +1,45 @@
+# 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')
+        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/manager.py b/tempest/manager.py
index cafa5b9..3d495b6 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -13,68 +13,50 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common import cred_provider
+from oslo_log import log as logging
+
+from tempest import clients
 from tempest import config
-from tempest import exceptions
-from tempest.lib import auth
+from tempest import service_clients
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
-class Manager(object):
-    """Base manager class
+class Manager(service_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, scope='project'):
-        """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
-        :param scope: default scope for tokens produced by the auth provider
-        """
-        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,
-                                               scope=scope)
-
-
-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
+        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 = 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(credentials, pre_auth=False, scope='project'):
-    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,
-                                         scope=scope,
-                                         **default_params)
-    if pre_auth:
-        _auth_provider.set_auth()
-    return _auth_provider
+    """Shim to get_auth_provider in clients.py
+
+    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 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 8d0f2f6..f889c44 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -19,9 +19,11 @@
 import netaddr
 from oslo_log import log
 from oslo_serialization import jsonutils as json
+from oslo_utils import netutils
 import six
 
 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 import waiters
@@ -29,7 +31,6 @@
 from tempest import exceptions
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
-from tempest.scenario import network_resources
 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 exceptions.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
@@ -71,8 +79,6 @@
         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
@@ -216,7 +222,7 @@
                 port = self._create_port(network_id=net_id,
                                          client=clients.ports_client,
                                          **create_port_body)
-                ports.append({'port': port.id})
+                ports.append({'port': port['id']})
             if ports:
                 kwargs['networks'] = ports
             self.ports = ports
@@ -376,14 +382,24 @@
             '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'])
         with open(path, 'rb') as image_file:
-            self.image_client.update_image(image['id'], data=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):
@@ -394,7 +410,7 @@
         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, "
+        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))
@@ -450,9 +466,17 @@
             thing_id=image_id, thing_id_param='id',
             cleanup_callable=test_utils.call_and_ignore_notfound_exc,
             cleanup_args=[_image_client.delete_image, image_id])
-        snapshot_image = _image_client.check_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 = snapshot_image.get('properties', {}).get('block_device_mapping')
+        bdm = image_props.get('block_device_mapping')
         if bdm:
             bdm = json.loads(bdm)
             if bdm and 'snapshot_id' in bdm[0]:
@@ -681,12 +705,12 @@
             tenant_id = networks_client.tenant_id
         name = data_utils.rand_name(namestart)
         result = networks_client.create_network(name=name, tenant_id=tenant_id)
-        network = network_resources.DeletableNetwork(
-            networks_client=networks_client, routers_client=routers_client,
-            **result['network'])
-        self.assertEqual(network.name, name)
+        network = result['network']
+
+        self.assertEqual(network['name'], name)
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        network.delete)
+                        self.networks_client.delete_network,
+                        network['id'])
         return network
 
     def _list_networks(self, *args, **kwargs):
@@ -756,13 +780,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
@@ -775,11 +799,13 @@
                 if not is_overlapping_cidr:
                     raise
         self.assertIsNotNone(result, 'Unable to allocate tenant network')
-        subnet = network_resources.DeletableSubnet(
-            subnets_client=subnets_client,
-            routers_client=routers_client, **result['subnet'])
-        self.assertEqual(subnet.cidr, str_cidr)
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc, subnet.delete)
+
+        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 _create_port(self, network_id, client=None, namestart='port-quotatest',
@@ -792,9 +818,9 @@
             network_id=network_id,
             **kwargs)
         self.assertIsNotNone(result, 'Unable to allocate port')
-        port = network_resources.DeletablePort(ports_client=client,
-                                               **result['port'])
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc, port.delete)
+        port = result['port']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        client.delete_port, port['id'])
         return port
 
     def _get_server_port_id_and_ip4(self, server, ip_addr=None):
@@ -811,7 +837,7 @@
         port_map = [(p["id"], fxip["ip_address"])
                     for p in ports
                     for fxip in p["fixed_ips"]
-                    if netaddr.valid_ipv4(fxip["ip_address"])
+                    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:
@@ -829,7 +855,7 @@
         net = self._list_networks(name=network_name)
         self.assertNotEqual(len(net), 0,
                             "Unable to get network by name: %s" % network_name)
-        return network_resources.AttributeDict(net[0])
+        return net[0]
 
     def create_floating_ip(self, thing, external_network_id=None,
                            port_id=None, client=None):
@@ -848,44 +874,51 @@
             tenant_id=thing['tenant_id'],
             fixed_ip_address=ip4
         )
-        floating_ip = network_resources.DeletableFloatingIp(
-            client=client,
-            **result['floatingip'])
+        floating_ip = result['floatingip']
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        floating_ip.delete)
+                        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: network_resources.DeletableFloatingIp floating
-        IP to check status
+        :param dict floating_ip: floating IP dict to check status
         :param status: target status
         :raises: AssertionError if status doesn't match
         """
+        floatingip_id = floating_ip['id']
+
         def refresh():
-            floating_ip.refresh()
-            return status == floating_ip.status
+            result = (self.floating_ips_client.
+                      show_floatingip(floatingip_id)['floatingip'])
+            return status == result['status']
 
         tempest.test.call_until_true(refresh,
                                      CONF.network.build_timeout,
                                      CONF.network.build_interval)
-        self.assertEqual(status, floating_ip.status,
+        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))
@@ -958,8 +991,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,
@@ -971,7 +1004,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
@@ -983,15 +1016,14 @@
                        description=sg_desc)
         sg_dict['tenant_id'] = tenant_id
         result = client.create_security_group(**sg_dict)
-        secgroup = network_resources.DeletableSecurityGroup(
-            client=client, routers_client=self.routers_client,
-            **result['security_group']
-        )
-        self.assertEqual(secgroup.name, sg_name)
-        self.assertEqual(tenant_id, secgroup.tenant_id)
-        self.assertEqual(secgroup.description, sg_desc)
+
+        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,
-                        secgroup.delete)
+                        client.delete_security_group, secgroup['id'])
         return secgroup
 
     def _default_security_group(self, client=None, tenant_id=None):
@@ -1004,13 +1036,12 @@
         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 network_resources.DeletableSecurityGroup(client=client,
-                                                        **sgs[0])
+        return sgs[0]
 
     def _create_security_group_rule(self, secgroup=None,
                                     sec_group_rules_client=None,
@@ -1021,7 +1052,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:
@@ -1043,17 +1074,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 = network_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
 
@@ -1107,7 +1136,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
@@ -1129,10 +1158,11 @@
         network_id = CONF.network.public_network_id
         if router_id:
             body = client.show_router(router_id)
-            return network_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 "
@@ -1148,15 +1178,18 @@
         result = client.create_router(name=name,
                                       admin_state_up=True,
                                       tenant_id=tenant_id)
-        router = network_resources.DeletableRouter(routers_client=client,
-                                                   **result['router'])
-        self.assertEqual(router.name, name)
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc, 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, networks_client=None,
                         routers_client=None, subnets_client=None,
@@ -1189,7 +1222,6 @@
                 tenant_id=tenant_id)
             router = self._get_router(client=routers_client,
                                       tenant_id=tenant_id)
-
             subnet_kwargs = dict(network=network,
                                  subnets_client=subnets_client,
                                  routers_client=routers_client)
@@ -1197,7 +1229,17 @@
             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
 
 
diff --git a/tempest/scenario/network_resources.py b/tempest/scenario/network_resources.py
deleted file mode 100644
index 667476f..0000000
--- a/tempest/scenario/network_resources.py
+++ /dev/null
@@ -1,220 +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 time
-
-import six
-
-from tempest import exceptions
-from tempest.lib.common.utils import misc
-
-
-class AttributeDict(dict):
-    """Provide attribute access (dict.key) to dictionary values."""
-
-    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.networks_client = kwargs.pop('networks_client', None)
-        self.routers_client = kwargs.pop('routers_client', None)
-        self.subnets_client = kwargs.pop('subnets_client', None)
-        self.ports_client = kwargs.pop('ports_client', None)
-        super(DeletableResource, self).__init__(*args, **kwargs)
-
-    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.wait_for_resource_status(helper_get, status)
-
-    def wait_for_resource_status(self, fetch, status):
-        """Waits for a network resource to reach a status
-
-        @param fetch: the callable to be used to query the resource status
-        @type fetch: callable that takes no parameters and returns the resource
-        @param status: the status that the resource has to reach
-        @type status: String
-        """
-        interval = self.build_interval
-        timeout = self.build_timeout
-        start_time = time.time()
-
-        while time.time() - start_time <= timeout:
-            resource = fetch()
-            if resource['status'] == status:
-                return
-            time.sleep(interval)
-
-        # At this point, the wait has timed out
-        message = 'Resource %s' % (str(resource))
-        message += ' failed to reach status %s' % status
-        message += ' (current: %s)' % resource['status']
-        message += ' within the required time %s' % timeout
-        caller = misc.find_test_caller()
-        if caller:
-            message = '(%s) %s' % (caller, message)
-        raise exceptions.TimeoutException(message)
-
-
-class DeletableNetwork(DeletableResource):
-
-    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.routers_client.add_router_interface(router_id,
-                                                 subnet_id=self.id)
-
-    def delete(self):
-        for router_id in self._router_ids.copy():
-            self.routers_client.remove_router_interface(router_id,
-                                                        subnet_id=self.id)
-            self._router_ids.remove(router_id)
-        self.subnets_client.delete_subnet(self.id)
-
-
-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.routers_client.update_router(self.id,
-                                                   *args,
-                                                   **kwargs)
-        return super(DeletableRouter, self).update(**result['router'])
-
-    def delete(self):
-        self.unset_gateway()
-        self.routers_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/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index cace90b..086b82d 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -74,7 +74,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_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 4c2d31b..e4b699e 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -52,15 +52,18 @@
 
     def _setup_network_and_servers(self):
         keypair = self.create_keypair()
-        security_group = self._create_security_group()
+        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')
         floating_ip = self.create_floating_ip(server, public_network_id)
         # Verify that we can indeed connect to the server before we mess with
@@ -78,7 +81,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,
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index f5134e5..402a70c 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -25,7 +25,6 @@
 from tempest import exceptions
 from tempest.lib.common.utils import test_utils
 from tempest.scenario import manager
-from tempest.scenario import network_resources
 from tempest import test
 
 CONF = config.CONF
@@ -114,7 +113,7 @@
         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')
@@ -133,30 +132,30 @@
         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):
         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
 
@@ -198,7 +197,7 @@
         """
         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:
@@ -239,7 +238,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)
@@ -250,7 +249,7 @@
         old_port = port_list[0]
         interface = self.interface_client.create_interface(
             server_id=server['id'],
-            net_id=self.new_net.id)['interfaceAttachment']
+            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,
@@ -270,9 +269,7 @@
                 "Old port: %s. Number of new ports: %d" % (
                     CONF.network.build_timeout, old_port,
                     len(self.new_port_list)))
-        new_port = network_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)
@@ -287,7 +284,8 @@
 
         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):
@@ -307,7 +305,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,
@@ -335,7 +333,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)
@@ -451,7 +449,13 @@
         self._create_server(name, 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)
 
@@ -553,7 +557,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)
@@ -568,9 +572,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():
@@ -695,7 +701,8 @@
 
         # NOTE(kevinbenton): we have to use the admin credentials to check
         # for the distributed flag because self.router only has a project view.
-        admin = self.admin_manager.routers_client.show_router(self.router.id)
+        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)
@@ -704,16 +711,16 @@
 
         # remove resource from agents
         hosting_agents = set(a["id"] for a in
-                             list_hosts(self.router.id)['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
@@ -730,7 +737,7 @@
                         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")
 
@@ -776,12 +783,12 @@
                                      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_by_mac(spoof_port["mac_address"])
         name = data_utils.rand_name('peer')
         peer = self._create_server(name, self.new_net)
-        peer_address = peer['addresses'][self.new_net.name][0]['addr']
+        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)
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index a52d8f9..59ebb7a 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -17,6 +17,7 @@
 import six
 
 from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest.scenario import manager
 from tempest import test
 
@@ -82,8 +83,12 @@
                                    ip_version=4)
 
         router = self._get_router(tenant_id=self.tenant_id)
-        sub4.add_to_router(router_id=router['id'])
-        self.addCleanup(sub4.delete)
+        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,10 +99,14 @@
                                        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
@@ -119,12 +128,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,7 +148,7 @@
         """
         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")
@@ -190,11 +199,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_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index adc9008..86185c8 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -227,22 +227,23 @@
         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)]
@@ -270,7 +271,7 @@
             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',
@@ -353,10 +354,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
@@ -364,7 +365,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)
@@ -388,7 +389,7 @@
     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(
@@ -464,7 +465,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')
@@ -545,7 +546,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))
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index a9f2dff..446c87a 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -14,21 +14,19 @@
 #    under the License.
 
 import json
+import re
 
 from oslo_log import log as logging
 
 from tempest import config
 from tempest 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 +45,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 +57,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):
@@ -103,9 +84,9 @@
     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()
+            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)
             cmd_md = 'sudo cat /mnt/openstack/latest/meta_data.json'
             result = self.ssh_client.exec_command(cmd_md)
diff --git a/tempest/scenario/utils.py b/tempest/scenario/utils.py
deleted file mode 100644
index 75fd000..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
-import testscenarios
-import testtools
-
-from tempest import clients
-from tempest.common import credentials_factory as credentials
-from tempest import config
-from tempest.lib.common.utils import misc
-from tempest.lib import exceptions as exc_lib
-
-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/service_clients.py b/tempest/service_clients.py
new file mode 100644
index 0000000..386e621
--- /dev/null
+++ b/tempest/service_clients.py
@@ -0,0 +1,178 @@
+# 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.
+
+from tempest.lib import auth
+from tempest.lib import exceptions
+
+
+def tempest_modules():
+    """List of service client modules available in Tempest.
+
+    Provides a list of service modules available Tempest.
+    """
+    return set(['compute', 'identity.v2', 'identity.v3', 'image.v1',
+                'image.v2', 'network', 'object-storage', 'volume.v1',
+                'volume.v2', 'volume.v3'])
+
+
+def available_modules():
+    """List of service client modules available in Tempest and plugins"""
+    # TODO(andreaf) For now this returns only tempest_modules
+    return tempest_modules()
+
+
+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 import service_clients
+        >>> johndoe = cred_provider.get_creds_by_role(['johndoe'])
+        >>> johndoe_clients = service_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' have a very different interface
+        - 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.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
+        unversioned_services = set(
+            [x.split('.')[0] for x in available_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()))
+
+    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/services/baremetal/__init__.py b/tempest/services/baremetal/__init__.py
index e69de29..390f40a 100644
--- a/tempest/services/baremetal/__init__.py
+++ b/tempest/services/baremetal/__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.baremetal.v1.json.baremetal_client import \
+    BaremetalClient
+
+__all__ = ['BaremetalClient']
diff --git a/tempest/services/data_processing/__init__.py b/tempest/services/data_processing/__init__.py
index e69de29..c49bc5c 100644
--- a/tempest/services/data_processing/__init__.py
+++ b/tempest/services/data_processing/__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.data_processing.v1_1.data_processing_client import \
+    DataProcessingClient
+
+__all__ = ['DataProcessingClient']
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/flavors_client.py b/tempest/services/database/json/flavors_client.py
deleted file mode 100644
index bd8ffb0..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.lib.common import rest_client
-
-
-class DatabaseFlavorsClient(rest_client.RestClient):
-
-    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 rest_client.ResponseBody(resp, body)
-
-    def show_db_flavor(self, db_flavor_id):
-        resp, body = self.get("flavors/%s" % db_flavor_id)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_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 2f28203..0000000
--- a/tempest/services/database/json/versions_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.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-
-
-class DatabaseVersionsClient(rest_client.RestClient):
-
-    def __init__(self, auth_provider, service, region, **kwargs):
-        super(DatabaseVersionsClient, self).__init__(
-            auth_provider, service, region, **kwargs)
-        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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/__init__.py b/tempest/services/identity/__init__.py
index e69de29..0e24926 100644
--- a/tempest/services/identity/__init__.py
+++ b/tempest/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.services.identity import v2
+from tempest.services.identity import v3
+
+__all__ = ['v2', 'v3']
diff --git a/tempest/services/identity/v2/__init__.py b/tempest/services/identity/v2/__init__.py
index e69de29..ac2a874 100644
--- a/tempest/services/identity/v2/__init__.py
+++ b/tempest/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.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
+from tempest.services.identity.v2.json.identity_client import IdentityClient
+
+__all__ = ['EndpointsClient', 'TokenClient', 'IdentityClient', 'RolesClient',
+           'ServicesClient', 'TenantsClient', 'UsersClient']
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 acd97c6..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.lib.common import rest_client
-
-
-class RolesClient(rest_client.RestClient):
-    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 rest_client.ResponseBody(resp, body)
-
-    def show_role(self, role_id):
-        """Get a role by its id."""
-        resp, body = self.get('OS-KSADM/roles/%s' % role_id)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_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 rest_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 rest_client.ResponseBody(resp, body)
-
-    def delete_user_role(self, tenant_id, user_id, role_id):
-        """Removes a role assignment for a user on a tenant."""
-        resp, body = self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
-                                 (tenant_id, user_id, role_id))
-        self.expected_success(204, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def list_roles(self):
-        """Returns roles."""
-        resp, body = self.get('OS-KSADM/roles')
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/tenants_client.py b/tempest/services/identity/v2/json/tenants_client.py
deleted file mode 100644
index 034938e..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.lib.common import rest_client
-
-
-class TenantsClient(rest_client.RestClient):
-    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 rest_client.ResponseBody(resp, body)
-
-    def delete_tenant(self, tenant_id):
-        """Delete a tenant."""
-        resp, body = self.delete('tenants/%s' % str(tenant_id))
-        self.expected_success(204, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_tenant(self, tenant_id):
-        """Get tenant details."""
-        resp, body = self.get('tenants/%s' % str(tenant_id))
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def list_tenants(self):
-        """Returns tenants."""
-        resp, body = self.get('tenants')
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_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 rest_client.ResponseBody(resp, body)
-
-    def list_tenant_users(self, tenant_id):
-        """List users for a Tenant."""
-        resp, body = self.get('/tenants/%s/users' % tenant_id)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/__init__.py b/tempest/services/identity/v3/__init__.py
index e69de29..144c5a9 100644
--- a/tempest/services/identity/v3/__init__.py
+++ b/tempest/services/identity/v3/__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.identity.v3.token_client import V3TokenClient
+from tempest.services.identity.v3.json.credentials_client import \
+    CredentialsClient
+from tempest.services.identity.v3.json.domains_client import DomainsClient
+from tempest.services.identity.v3.json.endpoints_client import EndPointsClient
+from tempest.services.identity.v3.json.groups_client import GroupsClient
+from tempest.services.identity.v3.json.identity_client import IdentityClient
+from tempest.services.identity.v3.json.policies_client import PoliciesClient
+from tempest.services.identity.v3.json.projects_client import ProjectsClient
+from tempest.services.identity.v3.json.regions_client import RegionsClient
+from tempest.services.identity.v3.json.roles_client import RolesClient
+from tempest.services.identity.v3.json.services_client import ServicesClient
+from tempest.services.identity.v3.json.trusts_client import TrustsClient
+from tempest.services.identity.v3.json.users_clients import UsersClient
+
+__all__ = ['V3TokenClient', 'CredentialsClient', 'DomainsClient',
+           'EndPointsClient', 'GroupsClient', 'IdentityClient',
+           'PoliciesClient', 'ProjectsClient', 'RegionsClient', 'RolesClient',
+           'ServicesClient', 'TrustsClient', 'UsersClient', ]
diff --git a/tempest/services/identity/v3/json/users_clients.py b/tempest/services/identity/v3/json/users_clients.py
index 3ab8eab..73bd343 100644
--- a/tempest/services/identity/v3/json/users_clients.py
+++ b/tempest/services/identity/v3/json/users_clients.py
@@ -44,7 +44,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def update_user(self, user_id, name, **kwargs):
-        """Updates a user."""
+        """Updates a user.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v3.html#updateUser
+        """
         body = self.show_user(user_id)['user']
         email = kwargs.get('email', body['email'])
         en = kwargs.get('enabled', body['enabled'])
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 b45ca22..0000000
--- a/tempest/services/image/v1/json/images_client.py
+++ /dev/null
@@ -1,253 +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 functools
-import os
-
-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 import rest_client
-from tempest.lib import exceptions as lib_exc
-
-LOG = logging.getLogger(__name__)
-CHUNKSIZE = 1024 * 64  # 64kB
-
-
-class ImagesClient(rest_client.RestClient):
-
-    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 _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', '/v1/images',
-                                  headers=headers, body=data, chunked=True)
-        self._error_checker('POST', '/v1/images', headers, data, 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 = '/v1/images/%s' % image_id
-        resp, body = self.request('PUT', url, headers=headers,
-                                  body=data, chunked=True)
-        self._error_checker('PUT', url, headers, data,
-                            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, **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 rest_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 rest_client.ResponseBody(resp, body)
-
-    def delete_image(self, image_id):
-        url = 'v1/images/%s' % image_id
-        resp, body = self.delete(url)
-        self.expected_success(200, resp.status)
-        return rest_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 rest_client.ResponseBody(resp, body)
-
-    def check_image(self, image_id):
-        """Check image metadata."""
-        url = 'v1/images/%s' % image_id
-        resp, __ = self.head(url)
-        self.expected_success(200, resp.status)
-        body = self._image_meta_from_headers(resp)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_image(self, image_id):
-        """Get image details plus the image itself."""
-        url = 'v1/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:
-            if self.check_image(id)['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'
-
-    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 rest_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 rest_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 rest_client.ResponseBody(resp)
-
-    def delete_member(self, member_id, image_id):
-        url = 'v1/images/%s/members/%s' % (image_id, member_id)
-        resp, __ = self.delete(url)
-        self.expected_success(204, resp.status)
-        return rest_client.ResponseBody(resp)
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 83f56cc..0000000
--- a/tempest/services/image/v2/json/images_client.py
+++ /dev/null
@@ -1,233 +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 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):
-
-    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 rest_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 rest_client.ResponseBody(resp, body)
-
-    def deactivate_image(self, image_id):
-        url = 'v2/images/%s/actions/deactivate' % image_id
-        resp, body = self.post(url, None)
-        self.expected_success(204, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def reactivate_image(self, image_id):
-        url = 'v2/images/%s/actions/reactivate' % image_id
-        resp, body = self.post(url, None)
-        self.expected_success(204, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_image(self, image_id):
-        url = 'v2/images/%s' % image_id
-        resp, _ = self.delete(url)
-        self.expected_success(204, resp.status)
-        return rest_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 rest_client.ResponseBody(resp, body)
-
-    def show_image(self, image_id):
-        url = 'v2/images/%s' % image_id
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return 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):
-        url = 'v2/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):
-        url = 'v2/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):
-        url = 'v2/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):
-        url = 'v2/images/%s/tags/%s' % (image_id, tag)
-        resp, _ = self.delete(url)
-        self.expected_success(204, resp.status)
-        return rest_client.ResponseBody(resp)
-
-    def list_image_members(self, image_id):
-        url = 'v2/images/%s/members' % image_id
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_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 rest_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 rest_client.ResponseBody(resp, body)
-
-    def show_image_member(self, image_id, member_id):
-        url = 'v2/images/%s/members/%s' % (image_id, member_id)
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, json.loads(body))
-
-    def delete_image_member(self, image_id, member_id):
-        url = 'v2/images/%s/members/%s' % (image_id, member_id)
-        resp, _ = self.delete(url)
-        self.expected_success(204, resp.status)
-        return rest_client.ResponseBody(resp)
-
-    def show_schema(self, schema):
-        url = 'v2/schemas/%s' % schema
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def list_resource_types(self):
-        url = '/v2/metadefs/resource_types'
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_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 rest_client.ResponseBody(resp, body)
-
-    def show_namespace(self, namespace):
-        url = '/v2/metadefs/namespaces/%s' % namespace
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_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 rest_client.ResponseBody(resp, body)
-
-    def delete_namespace(self, namespace):
-        url = '/v2/metadefs/namespaces/%s' % namespace
-        resp, _ = self.delete(url)
-        self.expected_success(204, resp.status)
-        return rest_client.ResponseBody(resp)
diff --git a/tempest/services/object_storage/__init__.py b/tempest/services/object_storage/__init__.py
index e69de29..96fe4a3 100644
--- a/tempest/services/object_storage/__init__.py
+++ b/tempest/services/object_storage/__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.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
+
+__all__ = ['AccountClient', 'ContainerClient', 'ObjectClient']
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/volume/__init__.py b/tempest/services/volume/__init__.py
index e69de29..4d29cdd 100644
--- a/tempest/services/volume/__init__.py
+++ b/tempest/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.services.volume import v1
+from tempest.services.volume import v2
+from tempest.services.volume import v3
+
+__all__ = ['v1', 'v2', 'v3']
diff --git a/tempest/services/volume/base/admin/base_types_client.py b/tempest/services/volume/base/admin/base_types_client.py
index 95ddff6..e4d9014 100644
--- a/tempest/services/volume/base/admin/base_types_client.py
+++ b/tempest/services/volume/base/admin/base_types_client.py
@@ -179,3 +179,37 @@
             "/types/%s/encryption/provider" % str(vol_type_id))
         self.expected_success(202, 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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html
+                              #createVolumeTypeAccessExt
+        """
+        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.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html
+                              #removeVolumeTypeAccessExt
+        """
+        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."""
+        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/services/volume/base/base_backups_client.py b/tempest/services/volume/base/base_backups_client.py
index 780da7b..3842d66 100644
--- a/tempest/services/volume/base/base_backups_client.py
+++ b/tempest/services/volume/base/base_backups_client.py
@@ -26,7 +26,11 @@
     """Client class to send CRUD Volume backup API requests"""
 
     def create_backup(self, **kwargs):
-        """Creates a backup of volume."""
+        """Creates a backup of volume.
+
+        Available params: see 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)
@@ -34,7 +38,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def restore_backup(self, backup_id, **kwargs):
-        """Restore volume from backup."""
+        """Restore volume from backup.
+
+        Available params: see 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)
diff --git a/tempest/services/volume/base/base_snapshots_client.py b/tempest/services/volume/base/base_snapshots_client.py
index 68503dd..da7bb01 100644
--- a/tempest/services/volume/base/base_snapshots_client.py
+++ b/tempest/services/volume/base/base_snapshots_client.py
@@ -60,7 +60,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def update_snapshot(self, snapshot_id, **kwargs):
-        """Updates a snapshot."""
+        """Updates a snapshot.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html#updateSnapshot
+        """
         put_body = json.dumps({'snapshot': kwargs})
         resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
         body = json.loads(body)
@@ -123,11 +127,11 @@
         return rest_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
+        """Update metadata for the snapshot.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html#updateSnapshotMetadata
+        """
         put_body = json.dumps(kwargs)
         url = "snapshots/%s/metadata" % str(snapshot_id)
         resp, body = self.put(url, put_body)
diff --git a/tempest/services/volume/base/base_v3_client.py b/tempest/services/volume/base/base_v3_client.py
new file mode 100644
index 0000000..ad6f760
--- /dev/null
+++ b/tempest/services/volume/base/base_v3_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 BaseV3Client(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(BaseV3Client, 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(BaseV3Client, 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/services/volume/base/base_volumes_client.py b/tempest/services/volume/base/base_volumes_client.py
old mode 100644
new mode 100755
index 6237745..d694c53
--- a/tempest/services/volume/base/base_volumes_client.py
+++ b/tempest/services/volume/base/base_volumes_client.py
@@ -96,7 +96,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def update_volume(self, volume_id, **kwargs):
-        """Updates the Specified Volume."""
+        """Updates the Specified Volume.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#updateVolume
+        """
         put_body = json.dumps({'volume': kwargs})
         resp, body = self.put('volumes/%s' % volume_id, put_body)
         body = json.loads(body)
@@ -119,7 +123,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def attach_volume(self, volume_id, **kwargs):
-        """Attaches a volume to a given instance on a given mountpoint."""
+        """Attaches a volume to a given instance on a given mountpoint.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#attachVolume
+        """
         post_body = json.dumps({'os-attach': kwargs})
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
@@ -171,7 +179,11 @@
         return 'volume'
 
     def extend_volume(self, volume_id, **kwargs):
-        """Extend a volume."""
+        """Extend a volume.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#extendVolume
+        """
         post_body = json.dumps({'os-extend': kwargs})
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
@@ -179,7 +191,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def reset_volume_status(self, volume_id, **kwargs):
-        """Reset the Specified Volume's Status."""
+        """Reset the Specified Volume's Status.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#resetVolume
+        """
         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)
@@ -202,7 +218,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def create_volume_transfer(self, **kwargs):
-        """Create a volume transfer."""
+        """Create a volume transfer.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#createVolumeTransfer
+        """
         post_body = json.dumps({'transfer': kwargs})
         resp, body = self.post('os-volume-transfer', post_body)
         body = json.loads(body)
@@ -218,7 +238,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def list_volume_transfers(self, **params):
-        """List all the volume transfers created."""
+        """List all the volume transfers created.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#listVolumeTransfer
+        """
         url = 'os-volume-transfer'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -234,7 +258,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def accept_volume_transfer(self, transfer_id, **kwargs):
-        """Accept a volume transfer."""
+        """Accept a volume transfer.
+
+        Available params: see http://developer.openstack.org/
+                                api-ref-blockstorage-v2.html#acceptVolumeTransfer
+        """
         url = 'os-volume-transfer/%s/accept' % transfer_id
         post_body = json.dumps({'accept': kwargs})
         resp, body = self.post(url, post_body)
@@ -299,6 +327,28 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def update_volume_image_metadata(self, volume_id, **kwargs):
+        """Update image metadata for the volume.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html
+                              #setVolumeimagemetadata
+        """
+        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)
+
     def retype_volume(self, volume_id, **kwargs):
         """Updates volume with new volume type."""
         post_body = json.dumps({'os-retype': kwargs})
diff --git a/tempest/services/volume/v1/__init__.py b/tempest/services/volume/v1/__init__.py
index e69de29..6bdb8c4 100644
--- a/tempest/services/volume/v1/__init__.py
+++ b/tempest/services/volume/v1/__init__.py
@@ -0,0 +1,30 @@
+# 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.volume.v1.json.admin.hosts_client import HostsClient
+from tempest.services.volume.v1.json.admin.quotas_client import QuotasClient
+from tempest.services.volume.v1.json.admin.services_client import \
+    ServicesClient
+from tempest.services.volume.v1.json.admin.types_client import TypesClient
+from tempest.services.volume.v1.json.availability_zone_client import \
+    AvailabilityZoneClient
+from tempest.services.volume.v1.json.backups_client import BackupsClient
+from tempest.services.volume.v1.json.extensions_client import ExtensionsClient
+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
+
+__all__ = ['HostsClient', 'QuotasClient', 'ServicesClient', 'TypesClient',
+           'AvailabilityZoneClient', 'BackupsClient', 'ExtensionsClient',
+           'QosSpecsClient', 'SnapshotsClient', 'VolumesClient']
diff --git a/tempest/services/volume/v2/__init__.py b/tempest/services/volume/v2/__init__.py
index e69de29..c75b0e5 100644
--- a/tempest/services/volume/v2/__init__.py
+++ b/tempest/services/volume/v2/__init__.py
@@ -0,0 +1,30 @@
+# 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.volume.v2.json.admin.hosts_client import HostsClient
+from tempest.services.volume.v2.json.admin.quotas_client import QuotasClient
+from tempest.services.volume.v2.json.admin.services_client import \
+    ServicesClient
+from tempest.services.volume.v2.json.admin.types_client import TypesClient
+from tempest.services.volume.v2.json.availability_zone_client import \
+    AvailabilityZoneClient
+from tempest.services.volume.v2.json.backups_client import BackupsClient
+from tempest.services.volume.v2.json.extensions_client import ExtensionsClient
+from tempest.services.volume.v2.json.qos_client import QosSpecsClient
+from tempest.services.volume.v2.json.snapshots_client import SnapshotsClient
+from tempest.services.volume.v2.json.volumes_client import VolumesClient
+
+__all__ = ['HostsClient', 'QuotasClient', 'ServicesClient', 'TypesClient',
+           'AvailabilityZoneClient', 'BackupsClient', 'ExtensionsClient',
+           'QosSpecsClient', 'SnapshotsClient', 'VolumesClient']
diff --git a/tempest/services/volume/v3/__init__.py b/tempest/services/volume/v3/__init__.py
new file mode 100644
index 0000000..d50098c
--- /dev/null
+++ b/tempest/services/volume/v3/__init__.py
@@ -0,0 +1,17 @@
+# 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.volume.v3.json.messages_client import MessagesClient
+
+__all__ = ['MessagesClient']
diff --git a/tempest/services/database/json/__init__.py b/tempest/services/volume/v3/json/__init__.py
similarity index 100%
rename from tempest/services/database/json/__init__.py
rename to tempest/services/volume/v3/json/__init__.py
diff --git a/tempest/services/volume/v3/json/messages_client.py b/tempest/services/volume/v3/json/messages_client.py
new file mode 100644
index 0000000..6be6d59
--- /dev/null
+++ b/tempest/services/volume/v3/json/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.services.volume.base import base_v3_client
+
+
+class MessagesClient(base_v3_client.BaseV3Client):
+    """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/test.py b/tempest/test.py
index d31c509..97ab25c 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -39,6 +39,7 @@
 from tempest import exceptions
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 LOG = logging.getLogger(__name__)
 
@@ -71,14 +72,9 @@
         '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,
-        'data_processing': CONF.service_available.sahara,
-        'database': CONF.service_available.trove
     }
     return service_list
 
@@ -90,9 +86,8 @@
     exercised by a test case.
     """
     def decorator(f):
-        services = ['compute', 'image', 'baremetal', 'volume', 'orchestration',
-                    'network', 'identity', 'object_storage', '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 '
@@ -539,9 +534,10 @@
             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)
+        return cls.client_manager(credentials=creds.credentials,
+                                  service=cls._service)
 
     @classmethod
     def clear_credentials(cls):
@@ -640,7 +636,7 @@
                 credentials.is_admin_available(
                     identity_version=cls.get_identity_version())):
             admin_creds = cred_provider.get_admin_creds()
-            admin_manager = clients.Manager(admin_creds)
+            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)
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 100755
index 0000000..b3931d1
--- /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.services.identity.v3.json.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.services.identity.v3.json.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.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.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.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.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
index 2d0256a..5ec9720 100644
--- a/tempest/tests/cmd/test_javelin.py
+++ b/tempest/tests/cmd/test_javelin.py
@@ -92,7 +92,7 @@
         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'])
+        mocked_function.assert_called_once_with(name=self.fake_object['name'])
 
     def test_create_duplicate_tenant(self):
         self.fake_client.tenants.list_tenants.return_value = {'tenants': [
@@ -120,11 +120,12 @@
         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)
+        mocked_function.assert_called_once_with(
+            name=self.fake_object['name'],
+            password=self.fake_object['password'],
+            tenantId=fake_tenant_id,
+            email=fake_email,
+            enabled=True)
 
     def test_create_user_missing_tenant(self):
         self.useFixture(mockpatch.Patch(
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
new file mode 100644
index 0000000..772391f
--- /dev/null
+++ b/tempest/tests/cmd/test_run.py
@@ -0,0 +1,124 @@
+# 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 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)
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..43b417a
--- /dev/null
+++ b/tempest/tests/cmd/test_subunit_describe_calls.py
@@ -0,0 +1,83 @@
+# 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(
+            subunit_file, "pythonlogging", None)
+        expected_result = {
+            'bar': [{'name': 'AgentsAdminTestJSON:setUp',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents',
+                     'verb': 'POST'},
+                    {'name': 'AgentsAdminTestJSON:test_create_agent',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents',
+                     'verb': 'POST'},
+                    {'name': 'AgentsAdminTestJSON:tearDown',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents/1',
+                     'verb': 'DELETE'},
+                    {'name': 'AgentsAdminTestJSON:_run_cleanups',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents/2',
+                     'verb': 'DELETE'}],
+            'foo': [{'name': 'AgentsAdminTestJSON:setUp',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents',
+                     'verb': 'POST'},
+                    {'name': 'AgentsAdminTestJSON:test_delete_agent',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents/3',
+                     'verb': 'DELETE'},
+                    {'name': 'AgentsAdminTestJSON:test_delete_agent',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents',
+                     'verb': 'GET'},
+                    {'name': 'AgentsAdminTestJSON:tearDown',
+                     '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 685a0b3..031bf4d 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
 
@@ -43,15 +42,12 @@
         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))
diff --git a/tempest/tests/cmd/test_workspace.py b/tempest/tests/cmd/test_workspace.py
new file mode 100644
index 0000000..2639d93
--- /dev/null
+++ b/tempest/tests/cmd/test_workspace.py
@@ -0,0 +1,124 @@
+# 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.workspace import WorkspaceManager
+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 = 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 = 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_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index a7a3a22..b7cc05d 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -21,14 +21,15 @@
 from tempest import config
 from tempest import exceptions
 from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+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 token_client as v3_token_client
 from tempest.lib.services.network import routers_client
 from tempest.services.identity.v2.json import identity_client as v2_iden_client
-from tempest.services.identity.v2.json import roles_client as v2_roles_client
-from tempest.services.identity.v2.json import tenants_client as \
-    v2_tenants_client
-from tempest.services.identity.v2.json import users_client as v2_users_client
 from tempest.services.identity.v3.json import domains_client
 from tempest.services.identity.v3.json import identity_client as v3_iden_client
 from tempest.services.identity.v3.json import projects_client as \
@@ -54,7 +55,7 @@
     users_client = v2_users_client
     token_client_class = token_client.TokenClient
     fake_response = fake_identity._fake_v2_response
-    assign_role_on_project = 'assign_user_role'
+    assign_role_on_project = 'create_user_role_on_project'
     tenants_client_class = tenants_client.TenantsClient
     delete_tenant = 'delete_tenant'
 
@@ -635,3 +636,15 @@
             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/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 7d625cf..e59e08f 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -67,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" \
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index 65164a0..71a4c81 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -57,3 +57,57 @@
     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/lib/fake_auth_provider.py b/tempest/tests/lib/fake_auth_provider.py
index 8095453..fa8ab47 100644
--- a/tempest/tests/lib/fake_auth_provider.py
+++ b/tempest/tests/lib/fake_auth_provider.py
@@ -31,5 +31,5 @@
 class FakeCredentials(object):
 
     def __init__(self, creds_dict):
-        for key in creds_dict.keys():
+        for key in creds_dict:
             setattr(self, key, creds_dict[key])
diff --git a/tempest/tests/lib/fake_identity.py b/tempest/tests/lib/fake_identity.py
index c903e47..831f8b5 100644
--- a/tempest/tests/lib/fake_identity.py
+++ b/tempest/tests/lib/fake_identity.py
@@ -100,7 +100,8 @@
 
     ],
     "type": "compute",
-    "id": "fake_compute_endpoint"
+    "id": "fake_compute_endpoint",
+    "name": "nova"
 }
 
 CATALOG_V3 = [COMPUTE_ENDPOINTS_V3, ]
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_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
index 7925152..dfce9b3 100644
--- a/tempest/tests/lib/services/identity/v2/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v2/test_token_client.py
@@ -25,9 +25,6 @@
 
 class TestTokenClientV2(base.TestCase):
 
-    def setUp(self):
-        super(TestTokenClientV2, self).setUp()
-
     def test_init_without_authurl(self):
         self.assertRaises(exceptions.IdentityError,
                           token_client.TokenClient, None)
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/tests/lib/services/identity/v3/test_token_client.py b/tempest/tests/lib/services/identity/v3/test_token_client.py
index e9ef740..9f4b4cc 100644
--- a/tempest/tests/lib/services/identity/v3/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_token_client.py
@@ -25,9 +25,6 @@
 
 class TestTokenClientV3(base.TestCase):
 
-    def setUp(self):
-        super(TestTokenClientV3, self).setUp()
-
     def test_init_without_authurl(self):
         self.assertRaises(exceptions.IdentityError,
                           token_client.V3TokenClient, None)
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_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/tests/lib/services/network/test_versions_client.py b/tempest/tests/lib/services/network/test_versions_client.py
new file mode 100644
index 0000000..715176b
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_versions_client.py
@@ -0,0 +1,77 @@
+# 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.versions_client import NetworkVersionsClient
+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",
+            "updated": "2013-07-23T11:33:21Z",
+            "version": "2.0",
+            "min_version": "2.0"
+            }
+        }
+
+    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 = (
+            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/test_auth.py b/tempest/tests/lib/test_auth.py
index c253187..12590a3 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -360,6 +360,58 @@
         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',
@@ -750,3 +802,12 @@
         _, 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_credentials.py b/tempest/tests/lib/test_credentials.py
index b6f2cf6..c910d6d 100644
--- a/tempest/tests/lib/test_credentials.py
+++ b/tempest/tests/lib/test_credentials.py
@@ -99,7 +99,7 @@
 
     def _test_is_not_valid(self, ignore_key):
         creds = self._get_credentials()
-        for attr in self.attributes.keys():
+        for attr in self.attributes:
             if attr == ignore_key:
                 continue
             temp_attr = getattr(creds, attr)
diff --git a/tempest/tests/lib/test_rest_client.py b/tempest/tests/lib/test_rest_client.py
index 106a1e5..057f57b 100644
--- a/tempest/tests/lib/test_rest_client.py
+++ b/tempest/tests/lib/test_rest_client.py
@@ -633,6 +633,7 @@
         expected = {'api_version': 'v1',
                     'endpoint_type': 'publicURL',
                     'region': None,
+                    'name': None,
                     'service': None,
                     'skip_path': True}
         self.rest_client.skip_path()
@@ -643,6 +644,7 @@
         expected = {'api_version': 'v1',
                     'endpoint_type': 'publicURL',
                     'region': None,
+                    'name': None,
                     'service': None}
         self.assertEqual(expected, self.rest_client.filters)
 
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
index dc355b4..01b8a72 100644
--- a/tempest/tests/test_base_test.py
+++ b/tempest/tests/test_base_test.py
@@ -66,7 +66,7 @@
         test.BaseTestCase.get_tenant_network()
 
         mock_man.assert_called_once_with(
-            mock_prov.get_admin_creds.return_value)
+            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()
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_service_clients.py b/tempest/tests/test_service_clients.py
new file mode 100644
index 0000000..a559086
--- /dev/null
+++ b/tempest/tests/test_service_clients.py
@@ -0,0 +1,126 @@
+# 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 testtools
+
+from tempest.lib import auth
+from tempest.lib import exceptions
+from tempest import service_clients
+from tempest.tests import base
+from tempest.tests.lib import fake_credentials
+
+
+class TestServiceClients(base.TestCase):
+
+    def setUp(self):
+        super(TestServiceClients, self).setUp()
+        self.useFixture(fixtures.MockPatch(
+            'tempest.service_clients.tempest_modules',
+            return_value=set(['fake_service1', 'fake_service2'])))
+
+    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 = service_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 = service_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):
+            service_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):
+            service_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):
+            service_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 = service_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):
+            service_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 service_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])
diff --git a/test-requirements.txt b/test-requirements.txt
index 763f0ba..04c3d6d 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,12 +1,12 @@
 # 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.12,>=0.11.0 # Apache-2.0
 # needed for doc build
-sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD
+sphinx!=1.3b1,<1.3,>=1.2.1 # BSD
 python-subunit>=0.0.18 # Apache-2.0/BSD
 oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
-reno>=1.6.2 # Apache2
+reno>=1.8.0 # Apache2
 mock>=2.0 # BSD
 coverage>=3.6 # Apache-2.0
 oslotest>=1.10.0 # Apache-2.0
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index b554514..55f41a6 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -95,7 +95,7 @@
 
 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
diff --git a/tox.ini b/tox.ini
index 44162fd..cff222d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -28,7 +28,7 @@
     bash tools/pretty_tox.sh '{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