Merge "Unskip test_reassign_port_between_servers"
diff --git a/.mailmap b/.mailmap
index a43c0b9..3ea6ab0 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,25 +1,27 @@
 <brian.waldon@rackspace.com> <bcwaldon@gmail.com>
 <jeblair@hp.com> <corvus@inaugust.com>
 <jeblair@hp.com> <james.blair@rackspace.com>
-Adam Gandelman <adamg@ubuntu.com> Adam Gandelman <adamg@canonical.com>
-Andrea Frittoli (andreaf) <andrea.frittoli@hpe.com> Andrea Frittoli (andreaf) <andrea.frittoli@hp.com>
-Andrea Frittoli (andreaf) <andrea.frittoli@hpe.com> Andrea Frittoli <andrea.frittoli@hp.com>
-Daryl Walleck <daryl.walleck@rackspace.com> dwalleck <daryl.walleck@rackspace.com>
+Adam Gandelman <adamg@ubuntu.com> <adamg@canonical.com>
+Andrea Frittoli <andrea.frittoli@gmail.com> <andrea.frittoli@hp.com>
+Andrea Frittoli <andrea.frittoli@gmail.com> <andrea.frittoli@hpe.com>
+Daryl Walleck <daryl.walleck@rackspace.com> <daryl.walleck@rackspace.com>
 David Kranz <dkranz@redhat.com> David Kranz <david.kranz@qrclab.com>
-Ghanshyam <ghanshyam.mann@nectechnologies.in> Ghanshyam Mann <ghanshyam.mann@nectechnologies.in>
-Ghanshyam <ghanshyam.mann@nectechnologies.in> ghanshyam <ghanshyam.mann@nectechnologies.in>
-Jay Pipes <jaypipes@gmail.com> Jay Pipes <jpipes@librebox.gateway.2wire.net>
+Ghanshyam <ghanshyam.mann@nectechnologies.in> <ghanshyam.mann@nectechnologies.in>
+Ghanshyam <ghanshyam.mann@nectechnologies.in> <ghanshyam.mann@nectechnologies.in>
+Jay Pipes <jaypipes@gmail.com> <jpipes@librebox.gateway.2wire.net>
 Joe Gordon <joe.gordon0@gmail.com> <jogo@cloudscaling.com>
-Ken'ichi Ohmichi <ken-oomichi@wx.jp.nec.com> Ken'ichi Ohmichi <oomichi@mxs.nes.nec.co.jp>
-Marc Koderer <marc@koderer.com> Marc Koderer <m.koderer@telekom.de>
-Masayuki Igawa <masayuki.igawa@gmail.com> Masayuki Igawa <igawa@mxs.nes.nec.co.jp>
-Masayuki Igawa <masayuki.igawa@gmail.com> Masayuki Igawa <mas-igawa@ut.jp.nec.com>
-Matthew Treinish <mtreinish@kortar.org> Matthew Treinish <treinish@linux.vnet.ibm.com>
-Nayna Patel <nayna.patel@hp.com> nayna-patel <nayna.patel@hp.com>
-ravikumar-venkatesan <ravikumar.venkatesan@hp.com> Ravikumar Venkatesan <ravikumar.venkatesan@hp.com>
-ravikumar-venkatesan <ravikumar.venkatesan@hp.com> ravikumar venkatesan <ravikumar.venkatesan@hp.com>
-Rohit Karajgi <rohit.karajgi@nttdata.com> Rohit Karajgi <rohit.karajgi@vertex.co.in>
-Sean Dague <sean@dague.net> Sean Dague <sdague@linux.vnet.ibm.com>
-Sean Dague <sean@dague.net> Sean Dague <sean.dague@samsung.com>
-Yuiko Takada <takada-yuiko@mxn.nes.nec.co.jp> YuikoTakada <takada-yuiko@mxn.nes.nec.co.jp>
-Zhi Kun Liu <zhikunli@cn.ibm.com> Liu, Zhi Kun <zhikunli@cn.ibm.com>
+Ken'ichi Ohmichi <ken-oomichi@wx.jp.nec.com> <oomichi@mxs.nes.nec.co.jp>
+Ken'ichi Ohmichi <ken-oomichi@wx.jp.nec.com> <ken1ohmichi@gmail.com>
+Marc Koderer <marc@koderer.com> <m.koderer@telekom.de>
+Masayuki Igawa <masayuki@igawa.me> <igawa@mxs.nes.nec.co.jp>
+Masayuki Igawa <masayuki@igawa.me> <mas-igawa@ut.jp.nec.com>
+Masayuki Igawa <masayuki@igawa.me> <masayuki.igawa@gmail.com>
+Matthew Treinish <mtreinish@kortar.org> <treinish@linux.vnet.ibm.com>
+Nayna Patel <nayna.patel@hp.com> <nayna.patel@hp.com>
+ravikumar-venkatesan <ravikumar.venkatesan@hp.com> <ravikumar.venkatesan@hp.com>
+ravikumar-venkatesan <ravikumar.venkatesan@hp.com> <ravikumar.venkatesan@hp.com>
+Rohit Karajgi <rohit.karajgi@nttdata.com> <rohit.karajgi@vertex.co.in>
+Sean Dague <sean@dague.net> <sdague@linux.vnet.ibm.com>
+Sean Dague <sean@dague.net> <sean.dague@samsung.com>
+Yuiko Takada <takada-yuiko@mxn.nes.nec.co.jp> <takada-yuiko@mxn.nes.nec.co.jp>
+Zhi Kun Liu <zhikunli@cn.ibm.com> <zhikunli@cn.ibm.com>
diff --git a/HACKING.rst b/HACKING.rst
index a209b3f..cb9821e 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -2,7 +2,7 @@
 ====================
 
 - Step 1: Read the OpenStack Style Commandments
-  http://docs.openstack.org/developer/hacking/
+  https://docs.openstack.org/hacking/latest/
 - Step 2: Read on
 
 Tempest Specific Commandments
@@ -22,7 +22,9 @@
 - [T112] Check that tempest.lib should not import local tempest code
 - [T113] Check that tests use data_utils.rand_uuid() instead of uuid.uuid4()
 - [T114] Check that tempest.lib does not use tempest config
+- [T115] Check that admin tests should exist under admin path
 - [N322] Method's default argument shouldn't be mutable
+- [T116] Unsupported 'message' Exception attribute in PY3
 
 Test Data/Configuration
 -----------------------
@@ -101,20 +103,20 @@
 Service Tagging
 ---------------
 Service tagging is used to specify which services are exercised by a particular
-test method. You specify the services with the tempest.test.services decorator.
-For example:
+test method. You specify the services with the ``tempest.test.services``
+decorator. For example:
 
 @services('compute', 'image')
 
 Valid service tag names are the same as the list of directories in tempest.api
 that have tests.
 
-For scenario tests having a service tag is required. For the api tests service
-tags are only needed if the test method makes an api call (either directly or
+For scenario tests having a service tag is required. For the API tests service
+tags are only needed if the test method makes an API call (either directly or
 indirectly through another service) that differs from the parent directory
-name. For example, any test that make an api call to a service other than nova
-in tempest.api.compute would require a service tag for those services, however
-they do not need to be tagged as compute.
+name. For example, any test that make an API call to a service other than Nova
+in ``tempest.api.compute`` would require a service tag for those services,
+however they do not need to be tagged as ``compute``.
 
 Test fixtures and resources
 ---------------------------
@@ -170,7 +172,7 @@
 All negative tests should be based on `API-WG guideline`_ . Such negative
 tests can block any changes from accurate failure code to invalid one.
 
-.. _API-WG guideline: https://github.com/openstack/api-wg/blob/master/guidelines/http.rst#failure-code-clarifications
+.. _API-WG guideline: http://specs.openstack.org/openstack/api-wg/guidelines/http.html#failure-code-clarifications
 
 If facing some gray area which is not clarified on the above guideline, propose
 a new guideline to the API-WG. With a proposal to the API-WG we will be able to
@@ -197,7 +199,7 @@
 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
+bug has been fixed. You should use the ``skip_because`` decorator so that
 Tempest's skip tracking tool can watch the bug status.
 
 Example::
@@ -228,17 +230,17 @@
   require admin privileges are outside of projects.
 
 - Races between methods in the same class are not a problem because
-  parallelization in tempest is at the test class level, but if there is a json
+  parallelization in Tempest is at the test class level, but if there is a json
   and xml version of the same test class there could still be a race between
   methods.
 
-- The rand_name() function from tempest.common.utils.data_utils should be used
-  anywhere a resource is created with a name. Static naming should be avoided
-  to prevent resource conflicts.
+- The rand_name() function from tempest.lib.common.utils.data_utils should be
+  used anywhere a resource is created with a name. Static naming should be
+  avoided to prevent resource conflicts.
 
 - If the execution of a set of tests is required to be serialized then locking
-  can be used to perform this. See AggregatesAdminTest in
-  tempest.api.compute.admin for an example of using locking.
+  can be used to perform this. See usage of ``LockFixture`` for examples of
+  using locking.
 
 Sample Configuration File
 -------------------------
@@ -246,11 +248,11 @@
 to the config variables in tempest/config.py then the sample config file must be
 regenerated. This can be done running::
 
-  tox -egenconfig
+  tox -e genconfig
 
 Unit Tests
 ----------
-Unit tests are a separate class of tests in tempest. They verify tempest
+Unit tests are a separate class of tests in Tempest. They verify Tempest
 itself, and thus have a different set of guidelines around them:
 
 1. They can not require anything running externally. All you should need to
@@ -313,15 +315,15 @@
 qualified test name and track test functionality through refactoring. The
 format of the metadata looks like::
 
-    @test.idempotent_id('585e934c-448e-43c4-acbf-d06a9b899997')
+    @decorators.idempotent_id('585e934c-448e-43c4-acbf-d06a9b899997')
     def test_list_servers_with_detail(self):
         # The created server should be in the detailed list of all servers
         ...
 
 Tempest.lib includes a ``check-uuid`` tool that will test for the existence
 and uniqueness of idempotent_id metadata for every test. If you have
-tempest installed you run the tool against Tempest by calling from the
-tempest repo::
+Tempest installed you run the tool against Tempest by calling from the
+Tempest repo::
 
     check-uuid
 
@@ -336,7 +338,7 @@
 
     check-uuid --fix
 
-The ``check-uuid`` tool is used as part of the tempest gate job
+The ``check-uuid`` tool is used as part of the Tempest gate job
 to ensure that all tests have an ``idempotent_id`` decorator.
 
 Branchless Tempest Considerations
@@ -349,17 +351,17 @@
 proposed commits to Tempest must work against both the master and all the
 currently supported stable branches of the projects. As such there are a few
 special considerations that have to be accounted for when pushing new changes
-to tempest.
+to Tempest.
 
 1. New Tests for new features
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 When adding tests for new features that were not in previous releases of the
 projects the new test has to be properly skipped with a feature flag. Whether
-this is just as simple as using the @test.requires_ext() decorator to check
-if the required extension (or discoverable optional API) is enabled or adding
-a new config option to the appropriate section. If there isn't a method of
-selecting the new **feature** from the config file then there won't be a
+this is just as simple as using the @utils.requires_ext() decorator to
+check if the required extension (or discoverable optional API) is enabled or
+adding a new config option to the appropriate section. If there isn't a method
+of selecting the new **feature** from the config file then there won't be a
 mechanism to disable the test with older stable releases and the new test won't
 be able to merge.
 
@@ -369,14 +371,22 @@
 When trying to land a bug fix which changes a tested API you'll have to use the
 following procedure::
 
-    - Propose change to the project, get a +2 on the change even with failing
-    - Propose skip on Tempest which will only be approved after the
+    1. Propose change to the project, get a +2 on the change even with failing
+    2. Propose skip on Tempest which will only be approved after the
       corresponding change in the project has a +2 on change
-    - Land project change in master and all open stable branches (if required)
-    - Land changed test in Tempest
+    3. Land project change in master and all open stable branches (if required)
+    4. Land changed test in Tempest
 
 Otherwise the bug fix won't be able to land in the project.
 
+Handily, `Zuul’s cross-repository dependencies
+<https://docs.openstack.org/infra/zuul/gating.html#cross-repository-dependencies>`_.
+can be leveraged to do without step 2 and to have steps 3 and 4 happen
+"atomically". To do that, make the patch written in step 1 to depend (refer to
+Zuul's documentation above) on the patch written in step 4. The commit message
+for the Tempest change should have a link to the Gerrit review that justifies
+that change.
+
 3. New Tests for existing features
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/README.rst b/README.rst
index 281516b..2e13fec 100644
--- a/README.rst
+++ b/README.rst
@@ -4,7 +4,6 @@
 
 .. image:: http://governance.openstack.org/badges/tempest.svg
     :target: http://governance.openstack.org/reference/tags/index.html
-    :remote:
 
 .. Change things from this point on
 
@@ -12,7 +11,7 @@
 ==============================================
 
 The documentation for Tempest is officially hosted at:
-http://docs.openstack.org/developer/tempest/
+https://docs.openstack.org/tempest/latest/
 
 This is a set of integration tests to be run against a live OpenStack
 cluster. Tempest has batteries of tests for OpenStack API validation,
@@ -24,7 +23,7 @@
 Tempest Design Principles that we strive to live by.
 
 - Tempest should be able to run against any OpenStack cloud, be it a
-  one node devstack install, a 20 node lxc cloud, or a 1000 node kvm
+  one node DevStack install, a 20 node LXC cloud, or a 1000 node KVM
   cloud.
 - Tempest should be explicit in testing features. It is easy to auto
   discover features of a cloud incorrectly, and give people an
@@ -66,13 +65,13 @@
     $ pip install tempest/
 
    This can be done within a venv, but the assumption for this guide is that
-   the Tempest cli entry point will be in your shell's PATH.
+   the Tempest CLI entry point will be in your shell's PATH.
 
-#. 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
+#. 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``.
@@ -91,12 +90,12 @@
    is that you'll create a new working directory for each to maintain separate
    configuration files and local artifact storage for each.
 
-#. Then cd into the newly created working dir and also modify the local
-   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
+#. Then ``cd`` into the newly created working dir and also modify the local
+   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. For
-   details on configuring tempest refer to the :ref:`tempest-configuration`.
+   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 using the :ref:`tempest_run` command. This can be done by either
@@ -105,7 +104,7 @@
     $ 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
+   argument to run in the workspace you created regardless of your current
    working directory. For example::
 
     $ tempest run --workspace cloud-01
@@ -118,15 +117,15 @@
    will run the same set of tests as the default gate jobs.
 
 .. _testr: https://testrepository.readthedocs.org/en/latest/MANUAL.html
-.. _ostestr: http://docs.openstack.org/developer/os-testr/
+.. _ostestr: https://docs.openstack.org/os-testr/latest/
 
 Library
 -------
 Tempest exposes a library interface. This interface is a stable interface and
 should be backwards compatible (including backwards compatibility with the
 old tempest-lib package, with the exception of the import). If you plan to
-directly consume tempest in your project you should only import code from the
-tempest library interface, other pieces of tempest do not have the same
+directly consume Tempest in your project you should only import code from the
+Tempest library interface, other pieces of Tempest do not have the same
 stable interface and there are no guarantees on the Python API unless otherwise
 stated.
 
@@ -138,7 +137,7 @@
 shows what changes have been released on each version.
 
 Tempest's released versions are broken into 2 sets of information. Depending on
-how you intend to consume tempest you might need
+how you intend to consume Tempest you might need
 
 The version is a set of 3 numbers:
 
@@ -147,12 +146,12 @@
 While this is almost `semver`_ like, the way versioning is handled is slightly
 different:
 
-X is used to represent the supported OpenStack releases for tempest tests
-in-tree, and to signify major feature changes to tempest. It's a monotonically
+X is used to represent the supported OpenStack releases for Tempest tests
+in-tree, and to signify major feature changes to Tempest. It's a monotonically
 increasing integer where each version either indicates a new supported OpenStack
 release, the drop of support for an OpenStack release (which will coincide with
 the upstream stable branch going EOL), or a major feature lands (or is removed)
-from tempest.
+from Tempest.
 
 Y.Z is used to represent library interface changes. This is treated the same
 way as minor and patch versions from `semver`_ but only for the library
@@ -167,16 +166,16 @@
 
 Detailed configuration of Tempest is beyond the scope of this
 document see :ref:`tempest-configuration` for more details on configuring
-Tempest. The etc/tempest.conf.sample attempts to be a self-documenting version
-of the configuration.
+Tempest. The ``etc/tempest.conf.sample`` attempts to be a self-documenting
+version of the configuration.
 
 You can generate a new sample tempest.conf file, run the following
 command from the top level of the Tempest directory::
 
-    $ tox -egenconfig
+    $ tox -e genconfig
 
-The most important pieces that are needed are the user ids, openstack
-endpoint, and basic flavors and images needed to run tests.
+The most important pieces that are needed are the user ids, OpenStack
+endpoints, and basic flavors and images needed to run tests.
 
 Unit Tests
 ----------
@@ -191,13 +190,13 @@
 is OS_TEST_PATH=./tempest/test_discover which will only run test discover on the
 Tempest suite.
 
-Alternatively, there are the py27 and py34 tox jobs which will run the unit
+Alternatively, there are the py27 and py35 tox jobs which will run the unit
 tests with the corresponding version of python.
 
 Python 2.6
 ----------
 
-Starting in the kilo release the OpenStack services dropped all support for
+Starting in the Kilo release the OpenStack services dropped all support for
 python 2.6. This change has been mirrored in Tempest, starting after the
 tempest-2 tag. This means that proposed changes to Tempest which only fix
 python 2.6 compatibility will be rejected, and moving forward more features not
@@ -209,15 +208,9 @@
 Python 3.x
 ----------
 
-Starting during the Liberty release development cycle work began on enabling
-Tempest to run under both Python 2.7 and Python 3.4. Tempest strives to fully
-support running with Python 3.4 and newer. A gating unit test job was added to
-also run Tempest's unit tests under Python 3. This means that the Tempest
-code at least imports under Python 3.4 and things that have unit test coverage
-will work on Python 3.4. However, because large parts of Tempest are
-self-verifying there might be uncaught issues running on Python 3. So until
-there is a gating job which does a full Tempest run using Python 3 there
-isn't any guarantee that running Tempest under Python 3 is bug free.
+Starting during the Pike cycle Tempest has a gating CI job that runs Tempest
+with Python 3. Any Tempest release after 15.0.0 should fully support running
+under Python 3 as well as Python 2.7.
 
 Legacy run method
 -----------------
@@ -246,10 +239,10 @@
 
 .. note::
 
-    If you have a running devstack environment, Tempest will be
+    If you have a running DevStack environment, Tempest will be
     automatically configured and placed in ``/opt/stack/tempest``. It
     will have a configuration file already set up to work with your
-    devstack installation.
+    DevStack installation.
 
 Tempest is not tied to any single test runner, but `testr`_ is the most commonly
 used tool. Also, the nosetests test runner is **not** recommended to run Tempest.
@@ -263,15 +256,13 @@
 
     $ testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
 
-Alternatively, you can use the run_tempest.sh script which will create a venv
-and run the tests or use tox to do the same. Tox also contains several existing
-job configurations. For example::
+Tox also contains several existing job configurations. For example::
 
-    $ tox -efull
+    $ tox -e full
 
 which will run the same set of tests as the OpenStack gate. (it's exactly how
 the gate invokes Tempest) Or::
 
-    $ tox -esmoke
+    $ tox -e smoke
 
 to run the tests tagged as smoke.
diff --git a/REVIEWING.rst b/REVIEWING.rst
index 9b272bb..7d28320 100644
--- a/REVIEWING.rst
+++ b/REVIEWING.rst
@@ -21,6 +21,15 @@
 to a Gerrit review.
 
 
+Execution time
+--------------
+While checking in the job logs that a new test is actually executed, also
+pay attention to the execution time of that test. Keep in mind that each test
+is going to be executed hundreds of time each day, because Tempest tests
+run in many OpenStack projects. It's worth considering how important/critical
+the feature under test is with how costly the new test is.
+
+
 Unit Tests
 ----------
 
@@ -48,6 +57,17 @@
 abstract the duplicated code into a function or method.
 
 
+Tests overlap
+-------------
+When a new test is being proposed, question whether this feature is not already
+tested with Tempest. Tempest has more than 1200 tests, spread amongst many
+directories, so it's easy to introduce test duplication. For example, testing
+volume attachment to a server could be a compute test or a volume test, depending
+on how you see it. So one must look carefully in the entire code base for possible
+overlap. As a rule of thumb, the older a feature is, the more likely it's
+already tested.
+
+
 Being explicit
 --------------
 When tests are being added that depend on a configurable feature or extension,
@@ -60,8 +80,8 @@
 
 Configuration Options
 ---------------------
-With the introduction of the tempest external test plugin interface we needed
-to provide a stable contract for tempest's configuration options. This means
+With the introduction of the Tempest external test plugin interface we needed
+to provide a stable contract for Tempest's configuration options. This means
 we can no longer simply remove a configuration option when it's no longer used.
 Patches proposed that remove options without a deprecation cycle should not
 be approved. Similarly when changing default values with configuration we need
@@ -91,7 +111,7 @@
 anything backwards incompatible or would require a user to take note or do
 something extra.
 
-.. _reno: http://docs.openstack.org/developer/reno/
+.. _reno: https://docs.openstack.org/reno/latest/
 
 Deprecated Code
 ---------------
diff --git a/bindep.txt b/bindep.txt
index 6a28348..efd3a10 100644
--- a/bindep.txt
+++ b/bindep.txt
@@ -1,5 +1,5 @@
 # This file contains runtime (non-python) dependencies
-# More info at: http://docs.openstack.org/infra/bindep/readme.html
+# More info at: https://docs.openstack.org/infra/bindep/readme.html
 
 libffi-dev [platform:dpkg]
 libffi-devel [platform:rpm]
@@ -7,5 +7,7 @@
 gcc [platform:dpkg]
 python-dev [platform:dpkg]
 python-devel [platform:rpm]
+python3-dev [platform:dpkg]
+python3-devel [platform:rpm]
 openssl-devel [platform:rpm]
 libssl-dev [platform:dpkg]
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 04ddfdf..067eb81 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -1,3 +1,16 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # Tempest documentation build configuration file, created by
 # sphinx-quickstart on Tue May 21 17:43:32 2013.
 #
@@ -9,10 +22,8 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys
 import os
 import subprocess
-import warnings
 
 # Build the plugin registry
 def build_plugin_registry(app):
@@ -21,7 +32,8 @@
     subprocess.call(['tools/generate-tempest-plugins-list.sh'], cwd=root_dir)
 
 def setup(app):
-    app.connect('builder-inited', build_plugin_registry)
+    if os.getenv('GENERATE_TEMPEST_PLUGIN_LIST', 'true').lower() == 'true':
+        app.connect('builder-inited', build_plugin_registry)
 
 
 
@@ -40,7 +52,7 @@
 extensions = ['sphinx.ext.autodoc',
               'sphinx.ext.todo',
               'sphinx.ext.viewcode',
-              'oslosphinx',
+              'openstackdocstheme',
               'oslo_config.sphinxconfiggen',
              ]
 
@@ -49,6 +61,14 @@
 
 todo_include_todos = True
 
+# openstackdocstheme options
+repository_name = 'openstack/tempest'
+bug_project = 'tempest'
+bug_tag = ''
+
+# Must set this variable to include year, month, day, hours, and minutes.
+html_last_updated_fmt = '%Y-%m-%d %H:%M'
+
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 
@@ -104,7 +124,7 @@
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'nature'
+html_theme = 'openstackdocs'
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
@@ -137,19 +157,6 @@
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-git_cmd = ["git", "log", "--pretty=format:'%ad, commit %h'", "--date=local",
-   "-n1"]
-try:
-    html_last_updated_fmt = subprocess.Popen(git_cmd,
-                                             stdout=subprocess.PIPE).\
-                                             communicate()[0]
-except Exception:
-    warnings.warn('Cannot get last updated time from git repository. '
-                  'Not setting "html_last_updated_fmt".')
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-html_use_smartypants = False
 
 # Custom sidebar templates, maps document names to template names.
 #html_sidebars = {}
@@ -184,123 +191,5 @@
 # This is the file name suffix for HTML files (e.g. ".xhtml").
 #html_file_suffix = None
 
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'Tempestdoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
-  ('index', 'Tempest.tex', u'Tempest Documentation',
-   u'OpenStack QA Team', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'tempest', u'Tempest Documentation',
-     [u'OpenStack QA Team'], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output ------------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-#  dir menu entry, description, category)
-texinfo_documents = [
-  ('index', 'Tempest', u'Tempest Documentation',
-   u'OpenStack QA Team', 'Tempest', 'One line description of project.',
-   'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-
-# -- Options for Epub output ---------------------------------------------------
-
-# Bibliographic Dublin Core info.
-epub_title = u'Tempest'
-epub_author = u'Sean Dague'
-epub_publisher = u'OpenStack QA Team'
-epub_copyright = u'2013, OpenStack QA Team'
-
-# The language of the text. It defaults to the language option
-# or en if the language is not set.
-#epub_language = ''
-
-# The scheme of the identifier. Typical schemes are ISBN or URL.
-#epub_scheme = ''
-
-# The unique identifier of the text. This can be a ISBN number
-# or the project homepage.
-#epub_identifier = ''
-
-# A unique identification for the text.
-#epub_uid = ''
-
-# A tuple containing the cover image and cover page html template filenames.
-#epub_cover = ()
-
-# HTML files that should be inserted before the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_pre_files = []
-
-# HTML files shat should be inserted after the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_post_files = []
-
-# A list of files that should not be packed into the epub file.
-#epub_exclude_files = []
-
-# The depth of the table of contents in toc.ncx.
-#epub_tocdepth = 3
-
-# Allow duplicate toc entries.
-#epub_tocdup = True
+# A list of warning types to suppress arbitrary warning messages.
+suppress_warnings = ['image.nonlocal_uri']
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 18269bf..8f2865a 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -9,6 +9,8 @@
 config file which explains the purpose of each individual option. You can see
 the sample config file here: :ref:`tempest-sampleconf`
 
+.. _tempest_cred_provider_conf:
+
 Test Credentials
 ----------------
 
@@ -26,7 +28,7 @@
 - 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
+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.
@@ -37,7 +39,7 @@
 number of users available to run tests with.
 You can specify the location of the file in the ``auth`` section in the
 tempest.conf file. To see the specific format used in the file please refer to
-the accounts.yaml.sample file included in Tempest.
+the ``accounts.yaml.sample`` file included in Tempest.
 
 Keystone Connection Info
 ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -45,19 +47,17 @@
 to provide it with information about how it communicates with keystone.
 This involves configuring the following options in the ``identity`` section:
 
- #. ``auth_version``
- #. ``uri``
- #. ``uri_v3``
+ - ``auth_version``
+ - ``uri``
+ - ``uri_v3``
 
 The ``auth_version`` option is used to tell Tempest whether it should be using
-keystone's v2 or v3 api for communicating with keystone. (except for the
-identity api tests which will test a specific version) The two uri options are
+Keystone's v2 or v3 api for communicating with Keystone. The two uri options are
 used to tell Tempest the url of the keystone endpoint. The ``uri`` option is
-used for keystone v2 request and ``uri_v3`` is used for keystone v3. You want to
+used for Keystone v2 request and ``uri_v3`` is used for Keystone v3. You want to
 ensure that which ever version you set for ``auth_version`` has its uri option
 defined.
 
-
 Credential Provider Mechanisms
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -94,7 +94,7 @@
 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
+usable in a cloud where domain scoped tokens are required by Keystone for
 admin operations. Note that the initial pre-provision admin accounts,
 configured in tempest.conf, must have a role on the same domain as well, for
 Dynamic Credentials to work.
@@ -139,10 +139,10 @@
 tenants it's using are empty and may sporadically fail if there are unexpected
 resources present.
 
-When the keystone in the target cloud requires domain scoped tokens to
+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
+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.
 
@@ -166,7 +166,7 @@
  #. ``flavor_ref_alt``
 
 Both of these options are in the ``compute`` section of the config file and take
-in the flavor id (not the name) from nova. The ``flavor_ref`` option is what
+in the flavor id (not the name) from Nova. The ``flavor_ref`` option is what
 will be used for booting almost all of the guests; ``flavor_ref_alt`` is only
 used in tests where two different-sized servers are required (for example, a
 resize test).
@@ -184,7 +184,7 @@
  #. ``image_ref``
  #. ``image_ref_alt``
 
-Both options are expecting an image id (not name) from nova. The ``image_ref``
+Both options are expecting an image id (not name) from Nova. The ``image_ref``
 option is what will be used for booting the majority of servers in Tempest.
 ``image_ref_alt`` is used for tests that require two images such as rebuild. If
 two images are not available you can set both options to the same image id and
@@ -223,7 +223,7 @@
 Networking
 ----------
 OpenStack has a myriad of different networking configurations possible and
-depending on which of the two network backends, nova-network or neutron, you are
+depending on which of the two network backends, nova-network or Neutron, you are
 using things can vary drastically. Due to this complexity Tempest has to provide
 a certain level of flexibility in its configuration to ensure it will work
 against any cloud. This ends up causing a large number of permutations in
@@ -232,6 +232,9 @@
 
 Enabling Remote Access to Created Servers
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. _tempest_conf_network_allocation:
+
 Network Creation/Usage for Servers
 """"""""""""""""""""""""""""""""""
 When Tempest creates servers for testing, some tests require being able to
@@ -298,21 +301,21 @@
 server with multiple networks. If this is not the case for your cloud then using
 an accounts file is recommended because it provides the necessary flexibility to
 describe your configuration. Dynamic credentials is not able to dynamically
-allocate things as necessary if neutron is not enabled.
+allocate things as necessary if Neutron is not enabled.
 
-With neutron and dynamic credentials enabled there should not be any additional
+With Neutron and dynamic credentials enabled there should not be any additional
 configuration necessary to enable Tempest to create servers with working
 networking, assuming you have properly configured the ``network`` section to
-work for your cloud. Tempest will dynamically create the neutron resources
+work for your cloud. Tempest will dynamically create the Neutron resources
 necessary to enable using servers with that network. Also, just as with the
-accounts file, if you specify a fixed network name while using neutron and
+accounts file, if you specify a fixed network name while using Neutron and
 dynamic credentials it will enable running tests which require a static network
 and it will additionally be used as a fallback for server creation. However,
 unlike accounts.yaml this should never be triggered.
 
 However, there is an option ``create_isolated_networks`` to disable dynamic
 credentials's automatic provisioning of network resources. If this option is set
-to False you will have to either rely on there only being a single/default
+to ``False`` you will have to either rely on there only being a single/default
 network available for the server creation, or use ``fixed_network_name`` to
 inform Tempest which network to use.
 
@@ -332,16 +335,16 @@
 
 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``.
+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
+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
+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
+``keypair``. With this set to ``keypair`` Tempest will create an ssh keypair
 and use that for authenticating against the created server.
 
 Configuring Available Services
@@ -355,8 +358,8 @@
 
 The ``service_available`` section of the config file is used to set which
 services are available. It contains a boolean option for each service (except
-for keystone which is a hard requirement) set it to True if the service is
-available or False if it is not.
+for Keystone which is a hard requirement) set it to ``True`` if the service is
+available or ``False`` if it is not.
 
 Service Catalog
 ^^^^^^^^^^^^^^^
@@ -378,17 +381,18 @@
 service you can set the ``region`` option in that service's section.
 
 It should also be noted that the default values for these options are set
-to what devstack uses (which is a de facto standard for service catalog
+to what DevStack uses (which is a de facto standard for service catalog
 entries). So often nothing actually needs to be set on these options to enable
 communication to a particular service. It is only if you are either not using
-the same ``catalog_type`` as devstack or you want Tempest to talk to a different
-endpoint type instead of publicURL for a service that these need to be changed.
+the same ``catalog_type`` as DevStack or you want Tempest to talk to a different
+endpoint type instead of ``publicURL`` for a service that these need to be
+changed.
 
 .. note::
 
     Tempest does not serve all kinds of fancy URLs in the service catalog.  The
     service catalog should be in a standard format (which is going to be
-    standardized at the keystone level).
+    standardized at the Keystone level).
     Tempest expects URLs in the Service catalog in the following format:
 
     * ``http://example.com:1234/<version-info>``
@@ -416,7 +420,7 @@
 configured as to which optional features are enabled. This is in order to
 prevent bugs in the discovery mechanisms from masking failures.
 
-The service feature-enabled config sections are how Tempest addresses the
+The service ``feature-enabled`` config sections are how Tempest addresses the
 optional feature question. Each service that has tests for optional features
 contains one of these sections. The only options in it are boolean options
 with the name of a feature which is used. If it is set to false any test which
@@ -427,7 +431,7 @@
 API Extensions
 ^^^^^^^^^^^^^^
 The service feature-enabled sections often contain an ``api-extensions`` option
-(or in the case of swift a ``discoverable_apis`` option). This is used to tell
+(or in the case of Swift a ``discoverable_apis`` option). This is used to tell
 Tempest which api extensions (or configurable middleware) is used in your
 deployment. It has two valid config states: either it contains a single value
 ``all`` (which is the default) which means that every api extension is assumed
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 896cd98..f562850 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -2,18 +2,16 @@
 Tempest Testing Project
 =======================
 
---------
 Overview
---------
+========
 
 .. toctree::
    :maxdepth: 2
 
    overview
 
-------------
 Field Guides
-------------
+============
 Tempest contains tests of many different types, the field guides
 attempt to explain these in a way that makes it easy to understand
 where your test contributions should go.
@@ -26,11 +24,9 @@
    field_guide/scenario
    field_guide/unit_tests
 
-=========
-For users
-=========
+Users Guide
+===========
 
----------------------------
 Tempest Configuration Guide
 ---------------------------
 
@@ -40,7 +36,6 @@
    configuration
    sampleconf
 
----------------------
 Command Documentation
 ---------------------
 
@@ -53,11 +48,9 @@
    workspace
    run
 
-==============
-For developers
-==============
+Developers Guide
+================
 
------------
 Development
 -----------
 
@@ -67,9 +60,9 @@
    HACKING
    REVIEWING
    microversion_testing
-   test-removal
+   test_removal
+   write_tests
 
--------
 Plugins
 -------
 
@@ -79,7 +72,6 @@
    plugin
    plugin-registry
 
--------
 Library
 -------
 
@@ -88,7 +80,6 @@
 
    library
 
-==================
 Indices and tables
 ==================
 
diff --git a/doc/source/library.rst b/doc/source/library.rst
index 29248d1..a461a0f 100644
--- a/doc/source/library.rst
+++ b/doc/source/library.rst
@@ -68,3 +68,4 @@
    library/api_microversion_testing
    library/auth
    library/clients
+   library/credential_providers
diff --git a/doc/source/library/clients.rst b/doc/source/library/clients.rst
index 086cfc9..0f4ba4c 100644
--- a/doc/source/library/clients.rst
+++ b/doc/source/library/clients.rst
@@ -16,9 +16,18 @@
 The ``ServiceClients`` class provides a convenient way to get access to all
 available service clients initialized with a provided set of credentials.
 
-------------------
-The clients module
-------------------
+-----------------------------
+The clients management module
+-----------------------------
 
 .. automodule:: tempest.lib.services.clients
    :members:
+
+------------------------------
+Compute service client modules
+------------------------------
+
+.. toctree::
+   :maxdepth: 2
+
+   service_clients/compute_clients
diff --git a/doc/source/library/credential_providers.rst b/doc/source/library/credential_providers.rst
new file mode 100644
index 0000000..f4eb37d
--- /dev/null
+++ b/doc/source/library/credential_providers.rst
@@ -0,0 +1,148 @@
+.. _cred_providers:
+
+Credential Providers
+====================
+
+These library interfaces are used to deal with allocating credentials on demand
+either dynamically by calling keystone to allocate new credentials, or from
+a list of preprovisioned credentials. These 2 modules are implementations of
+the same abstract credential providers class and can be used interchangeably.
+However, each implementation has some additional parameters that are used to
+influence the behavior of the modules. The API reference at the bottom of this
+doc shows the interface definitions for both modules, however that may be a bit
+opaque. You can see some examples of how to leverage this interface below.
+
+Initialization Example
+----------------------
+This example is from Tempest itself (from tempest/common/credentials_factory.py
+just modified slightly) and is how it initializes the credential provider based
+on config::
+
+  from tempest import config
+  from tempest.lib.common import dynamic_creds
+  from tempest.lib.common import preprov_creds
+
+  CONF = config.CONF
+
+  def get_credentials_provider(name, network_resources=None,
+                               force_tenant_isolation=False,
+                               identity_version=None):
+      # If a test requires a new account to work, it can have it via forcing
+      # dynamic credentials. A new account will be produced only for that test.
+      # In case admin credentials are not available for the account creation,
+      # the test should be skipped else it would fail.
+      identity_version = identity_version or CONF.identity.auth_version
+      if CONF.auth.use_dynamic_credentials or force_tenant_isolation:
+          admin_creds = get_configured_admin_credentials(
+              fill_in=True, identity_version=identity_version)
+          return dynamic_creds.DynamicCredentialProvider(
+              name=name,
+              network_resources=network_resources,
+              identity_version=identity_version,
+              admin_creds=admin_creds,
+              identity_admin_domain_scope=CONF.identity.admin_domain_scope,
+              identity_admin_role=CONF.identity.admin_role,
+              extra_roles=CONF.auth.tempest_roles,
+              neutron_available=CONF.service_available.neutron,
+              project_network_cidr=CONF.network.project_network_cidr,
+              project_network_mask_bits=CONF.network.project_network_mask_bits,
+              public_network_id=CONF.network.public_network_id,
+              create_networks=(CONF.auth.create_isolated_networks and not
+                               CONF.network.shared_physical_network),
+              resource_prefix=CONF.resources_prefix,
+              credentials_domain=CONF.auth.default_credentials_domain_name,
+              admin_role=CONF.identity.admin_role,
+              identity_uri=CONF.identity.uri_v3,
+              identity_admin_endpoint_type=CONF.identity.v3_endpoint_type)
+      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,
+                  accounts_lock_dir=lockutils.get_lock_path(CONF),
+                  test_accounts_file=CONF.auth.test_accounts_file,
+                  object_storage_operator_role=CONF.object_storage.operator_role,
+                  object_storage_reseller_admin_role=reseller_admin_role,
+                  credentials_domain=CONF.auth.default_credentials_domain_name,
+                  admin_role=CONF.identity.admin_role,
+                  identity_uri=CONF.identity.uri_v3,
+                  identity_admin_endpoint_type=CONF.identity.v3_endpoint_type)
+          else:
+              raise exceptions.InvalidConfiguration(
+                  'A valid credential provider is needed')
+
+This function just returns an initialized credential provider class based on the
+config file. The consumer of this function treats the output as the same
+regardless of whether it's a dynamic or preprovisioned provider object.
+
+Dealing with Credentials
+------------------------
+
+Once you have a credential provider object created the access patterns for
+allocating and removing credentials are the same across both the dynamic
+and preprovisioned credentials. These are defined in the abstract
+CredentialProvider class. At a high level the credentials provider enables
+you to get 3 basic types of credentials at once (per object): a primary, alt,
+and admin. You're also able to allocate a credential by role. These credentials
+are tracked by the provider object and delete must manually be called otherwise
+the created resources will not be deleted (or returned to the pool in the case
+of preprovisioned creds)
+
+Examples
+''''''''
+
+Continuing from the example above, to allocate credentials by the 3 basic types
+you can do the following::
+
+  provider = get_credentials_provider('my_tests')
+  primary_creds = provider.get_primary_creds()
+  alt_creds = provider.get_alt_creds()
+  admin_creds = provider.get_admin_creds()
+  # Make sure to delete the credentials when you're finished
+  provider.clear_creds()
+
+To create and interact with credentials by role you can do the following::
+
+  provider = get_credentials_provider('my_tests')
+  my_role_creds = provider.get_creds_by_role({'roles': ['my_role']})
+  # provider.clear_creds() will clear all creds including those allocated by
+  # role
+  provider.clear_creds()
+
+When multiple roles are specified a set of creds with all the roles assigned
+will be allocated::
+
+  provider = get_credentials_provider('my_tests')
+  my_role_creds = provider.get_creds_by_role({'roles': ['my_role',
+                                                        'my_other_role']})
+  # provider.clear_creds() will clear all creds including those allocated by
+  # role
+  provider.clear_creds()
+
+If you need multiple sets of credentials with the same roles you can also do
+this by leveraging the ``force_new`` kwarg::
+
+  provider = get_credentials_provider('my_tests')
+  my_role_creds = provider.get_creds_by_role({'roles': ['my_role']})
+  my_role_other_creds = provider.get_creds_by_role({'roles': ['my_role']},
+                                                   force_new=True)
+  # provider.clear_creds() will clear all creds including those allocated by
+  # role
+  provider.clear_creds()
+
+API Reference
+=============
+
+------------------------------
+The dynamic credentials module
+------------------------------
+
+.. automodule:: tempest.lib.common.dynamic_creds
+   :members:
+
+--------------------------------------
+The pre-provisioned credentials module
+--------------------------------------
+
+.. automodule:: tempest.lib.common.preprov_creds
+   :members:
diff --git a/doc/source/library/service_clients/compute_clients.rst b/doc/source/library/service_clients/compute_clients.rst
new file mode 100644
index 0000000..4ca55d4
--- /dev/null
+++ b/doc/source/library/service_clients/compute_clients.rst
@@ -0,0 +1,7 @@
+.. _servers_client:
+
+Compute Client Usage
+====================
+
+.. automodule:: tempest.lib.services.compute.servers_client
+   :members:
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 572d425..307eb07 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -1,13 +1,83 @@
-===================================
-How To Implement Microversion Tests
-===================================
+=================================
+Microversion Testing With Tempest
+=================================
 
-Tempest provides stable interfaces to test API Microversion.
-For Details, see: `API Microversion testing Framework`_
-This document explains how to implement Microversion tests using those
-interfaces.
+Many OpenStack Services provide their APIs with `microversion`_
+support and want to test them in Tempest.
 
-.. _API Microversion testing Framework: http://docs.openstack.org/developer/tempest/library/api_microversion_testing.html
+.. _microversion: http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html
+
+This document covers how to test microversions for each project and
+whether tests should live in Tempest or on project side.
+
+Tempest Scope For Microversion Testing
+""""""""""""""""""""""""""""""""""""""
+APIs microversions for any OpenStack service grow rapidly and
+testing each and every microversion in Tempest is not feasible and
+efficient way.
+Also not every API microversion changes the complete system behavior
+and many of them only change the API or DB layer to accept and return more
+data on API.
+
+Tempest is an integration test suite, but not all API microversion testing fall under this category.
+As a result, Tempest mainly covers integration test cases for microversions, Other testing coverage
+for microversion should be hosted on project side as functional tests or via Tempest plugin as per
+project guidelines.
+
+.. note:: Integration tests are those tests which involve more than one service to
+     verify the expected behavior by single or combination of API requests.
+     If a test is just to verify the API behavior as success and failure cases
+     or verify its expected response object, then it does not fall under integration
+     tests.
+
+Tempest will cover only integration testing of applicable microversions with
+below exceptions:
+
+  #. Test covers a feature which is important for interoperability. This covers tests requirement
+     from Defcore.
+  #. Test needed to fill Schema gaps.
+     Tempest validates API responses with defined JSON schema. API responses can be different on
+     each microversion and the JSON schemas need to be defined separately for the microversion.
+     While implementing new integration tests for a specific microversion, there
+     may be a gap in the JSON schemas (caused by previous microversions) implemented
+     in Tempest.
+     Filling that gap while implementing the new integration test cases is not efficient due to
+     many reasons:
+
+     * Hard to review
+     * Sync between multiple integration tests patches which try to fill the same schema gap at same
+       time
+     * Might delay the microversion change on project side where project team wants Tempest
+       tests to verify the results.
+
+     Tempest will allow to fill the schema gaps at the end of each cycle, or more
+     often if required.
+     Schema gap can be filled with testing those with a minimal set of tests. Those
+     tests might not be integration tests and might be already covered on project
+     side also.
+     This exception is needed because:
+
+     * Allow to create microversion response schema in Tempest at the same time that projects are
+       implementing their API microversions. This will make implementation easier for adding
+       required tests before a new microversion change can be merged in the corresponding project
+       and hence accelerate the development of microversions.
+     * New schema must be verified by at least one test case which exercises such schema.
+
+     For example:
+        If any projects implemented 4 API microversion say- v2.3, v2.4, v2.5, v2.6
+        Assume microversion v2.3, v2.4, v2.6 change the API Response which means Tempest
+        needs to add JSON schema for v2.3, v2.4, v2.6.
+        In that case if only 1 or 2 tests can verify all new schemas then we do not need
+        separate tests for each new schemas. In worst case, we have to add 3 separate tests.
+  #. Test covers service behavior at large scale with involvement of more deep layer like hypervisor
+     etc not just API/DB layer. This type of tests will be added case by case basis and
+     with project team consultation about why it cannot be covered on project side and worth to test
+     in Tempest.
+
+Project Scope For Microversion Testing
+""""""""""""""""""""""""""""""""""""""
+All microversions testing which are not covered under Tempest as per above section, should be
+tested on project side as functional tests or as Tempest plugin as per project decision.
 
 
 Configuration options for Microversion
@@ -36,6 +106,14 @@
 How To Implement Microversion Tests
 """""""""""""""""""""""""""""""""""
 
+Tempest provides stable interfaces to test API Microversion.
+For Details, see: `API Microversion testing Framework`_
+This document explains how to implement Microversion tests using those
+interfaces.
+
+.. _API Microversion testing Framework: https://docs.openstack.org/tempest/latest/library/api_microversion_testing.html
+
+
 Step1: Add skip logic based on configured Microversion range
 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
 
@@ -218,32 +296,46 @@
 
  * `2.1`_
 
- .. _2.1:  http://docs.openstack.org/developer/nova/api_microversion_history.html#id1
+ .. _2.1:  https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id1
 
  * `2.2`_
 
- .. _2.2: http://docs.openstack.org/developer/nova/api_microversion_history.html#id2
+ .. _2.2: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id2
 
  * `2.10`_
 
- .. _2.10: http://docs.openstack.org/developer/nova/api_microversion_history.html#id9
+ .. _2.10: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id9
 
  * `2.20`_
 
- .. _2.20: http://docs.openstack.org/developer/nova/api_microversion_history.html#id18
+ .. _2.20: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id18
 
  * `2.25`_
 
- .. _2.25: http://docs.openstack.org/developer/nova/api_microversion_history.html#maximum-in-mitaka
+ .. _2.25: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-mitaka
 
  * `2.32`_
 
- .. _2.32: http://docs.openstack.org/developer/nova/api_microversion_history.html#id29
+ .. _2.32: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id29
 
  * `2.37`_
 
- .. _2.37: http://docs.openstack.org/developer/nova/api_microversion_history.html#id34
+ .. _2.37: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id34
 
  * `2.42`_
 
- .. _2.42: http://docs.openstack.org/developer/nova/api_microversion_history.html#maximum-in-ocata
+ .. _2.42: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-ocata
+
+ * `2.47`_
+
+ .. _2.47: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id42
+
+ * `2.48`_
+
+ .. _2.48: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id43
+
+* Volume
+
+ * `3.3`_
+
+ .. _3.3:  https://docs.openstack.org/cinder/latest/contributor/api_microversion_history.html#id4
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index 6b30825..77ef9ed 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -1,3 +1,5 @@
+.. _tempest_plugin:
+
 =============================
 Tempest Test Plugin Interface
 =============================
@@ -26,6 +28,7 @@
 * tempest.lib.*
 * tempest.config
 * tempest.test_discover.plugins
+* tempest.common.credentials_factory
 
 If there is an interface from tempest that you need to rely on in your plugin
 which is not listed above, it likely needs to be migrated to tempest.lib. In
diff --git a/doc/source/test-removal.rst b/doc/source/test_removal.rst
similarity index 85%
rename from doc/source/test-removal.rst
rename to doc/source/test_removal.rst
index 79a5846..07c3046 100644
--- a/doc/source/test-removal.rst
+++ b/doc/source/test_removal.rst
@@ -38,8 +38,10 @@
  #. 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
+
+    .. _`prong #3`:
+ #. 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
@@ -62,7 +64,7 @@
 
 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)
+setUpClass or tearDownClass failures)
 
 You can access the infra mysql subunit2sql db w/ read-only permissions with:
 
@@ -80,7 +82,7 @@
  #. 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)
+    catch failures in setUpClass and tearDownClass)
  #. paste the output table with numbers and the mysql command you ran to
     generate it into the etherpad.
 
@@ -133,6 +135,10 @@
  #. 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)
+ #. Tests that would become out of scope as a consequence of an API change,
+    as described in `API Compatibility`_.
+    Such tests cannot live in Tempest because of the branchless nature of
+    Tempest. Such test must still honor `prong #3`_.
 
 For the first exception type the only types of testing in tree which have been
 declared out of scope at this point are:
@@ -149,7 +155,7 @@
 Tempest Scope
 ^^^^^^^^^^^^^
 
-Also starting in the liberty cycle tempest has defined a set of projects which
+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:
 
@@ -165,4 +171,18 @@
 `tempest plugin mechanism`_
 to maintain continuity after migrating the tests out of tempest.
 
-.. _tempest plugin mechanism: http://docs.openstack.org/developer/tempest/plugin.html
+.. _tempest plugin mechanism: https://docs.openstack.org/tempest/latest/plugin.html
+
+API Compatibility
+"""""""""""""""""
+
+If an API introduces a non-discoverable, backward incompatible change, and
+such change is not backported to all versions supported by Tempest, tests for
+that API cannot live in Tempest anymore.
+This is because tests would not be able to know or control which API response
+to expect, and thus would not be able to enforce a specific behavior.
+
+If a test exists in Tempest that would meet this criteria as consequence of a
+change, the test must be removed according to the procedure discussed into
+this document. The API change should not be merged until all conditions
+required for test removal can be met.
diff --git a/doc/source/write_tests.rst b/doc/source/write_tests.rst
new file mode 100644
index 0000000..aec55e9
--- /dev/null
+++ b/doc/source/write_tests.rst
@@ -0,0 +1,311 @@
+.. _tempest_test_writing:
+
+Tempest Test Writing Guide
+##########################
+
+This guide serves as a starting point for developers working on writing new
+Tempest tests. At a high level tests in Tempest are just tests that conform to
+the standard python `unit test`_ framework. But there are several aspects of
+that are unique to Tempest and its role as an integration test suite running
+against a real cloud.
+
+.. _unit test: https://docs.python.org/3.6/library/unittest.html
+
+.. note:: This guide is for writing tests in the Tempest repository. While many
+          parts of this guide are also applicable to Tempest plugins, not all
+          the APIs mentioned are considered stable or recommended for use in
+          plugins. Please refer to :ref:`tempest_plugin` for details about
+          writing plugins
+
+
+Adding a New TestCase
+=====================
+
+The base unit of testing in Tempest is the `TestCase`_ (also called the test
+class). Each TestCase contains test methods which are the individual tests that
+will be executed by the test runner. But, the TestCase is the smallest self
+contained unit for tests from the Tempest perspective. It's also the level at
+which Tempest is parallel safe. In other words, multiple TestCases can be
+executed in parallel, but individual test methods in the same TestCase can not.
+Also, all test methods within a TestCase are assumed to be executed serially. As
+such you can use the test case to store variables that are shared between
+methods.
+
+.. _TestCase: https://docs.python.org/3.6/library/unittest.html#unittest.TestCase
+
+In standard unittest the lifecycle of a TestCase can be described in the
+following phases:
+
+ #. setUpClass
+ #. setUp
+ #. Test Execution
+ #. tearDown
+ #. doCleanups
+ #. tearDownClass
+
+setUpClass
+----------
+
+The setUpClass phase is the first phase executed by the test runner and is used
+to perform any setup required for all the test methods to be executed. In
+Tempest this is a very important step and will automatically do the necessary
+setup for interacting with the configured cloud.
+
+To accomplish this you do **not** define a setUpClass function, instead there
+are a number of predefined phases to setUpClass that are used. The phases are:
+
+ * skip_checks
+ * setup_credentials
+ * setup_clients
+ * resource_setup
+
+which is executed in that order. An example of a TestCase which defines all
+of these would be::
+
+  from tempest import config
+  from tempest import test
+
+  CONF = config.CONF
+
+
+  class TestExampleCase(test.BaseTestCase):
+
+    @classmethod
+    def skip_checks(cls):
+        """This section is used to evaluate config early and skip all test
+           methods based on these checks
+        """
+        super(TestExampleCase, cls).skip_checks()
+        if not CONF.section.foo
+            cls.skip('A helpful message')
+
+    @classmethod
+    def setup_credentials(cls):
+        """This section is used to do any manual credential allocation and also
+           in the case of dynamic credentials to override the default network
+           resource creation/auto allocation
+        """
+        # This call is used to tell the credential allocator to not create any
+        # network resources for this test case. It also enables selective
+        # creation of other neutron resources. NOTE: it must go before the
+        # super call
+        cls.set_network_resources()
+        super(TestExampleCase, cls).setup_credentials()
+
+    @classmethod
+    def setup_clients(cls):
+        """This section is used to setup client aliases from the manager object
+           or to initialize any additional clients. Except in a few very
+           specific situations you should not need to use this.
+        """
+        super(TestExampleCase, cls).setup_clients()
+        cls.servers_client = cls.os_primary.servers_client
+
+    @classmethod
+    def resource_setup(cls):
+        """This section is used to create any resources or objects which are
+           going to be used and shared by **all** test methods in the
+           TestCase. Note then anything created in this section must also be
+           destroyed in the corresponding resource_cleanup() method (which will
+           be run during tearDownClass())
+        """
+        super(TestExampleCase, cls).resource_setup()
+        cls.shared_server = cls.servers_client.create_server(...)
+
+.. _credentials:
+
+Allocating Credentials
+''''''''''''''''''''''
+
+Since Tempest tests are all about testing a running cloud, every test will need
+credentials to be able to make API requests against the cloud. Since this is
+critical to operation and, when running in parallel, easy to make a mistake,
+the base TestCase class will automatically allocate a regular user for each
+TestCase during the setup_credentials() phase. During this process it will also
+initialize a client manager object using those credentials, which will be your
+entry point into interacting with the cloud. For more details on how credentials
+are allocated the :ref:`tempest_cred_provider_conf` section of the Tempest
+Configuration Guide provides more details on the operation of this.
+
+There are some cases when you need more than a single set of credentials, or
+credentials with a more specialized set of roles. To accomplish this you have
+to set a class variable ``credentials`` on the TestCase directly. For example::
+
+    from tempest import test
+
+    class TestExampleAdmin(test.BaseTestCase):
+
+        credentials = ['primary', 'admin']
+
+    @classmethod
+    def skip_checks(cls):
+    ...
+
+In this example the ``TestExampleAdmin`` TestCase will allocate 2 sets of
+credentials, one regular user and one admin user. The corresponding manager
+objects will be set as class variables ``cls.os_primary`` and ``cls.os_admin``
+respectively. You can also allocate a second user by putting **'alt'** in the
+list too. A set of alt credentials are the same as primary but can be used
+for tests cases that need a second user/project.
+
+You can also specify credentials with specific roles assigned. This is useful
+for cases where there are specific RBAC requirements hard coded into an API.
+The canonical example of this are swift tests which often want to test swift's
+concepts of operator and reseller_admin. An actual example from Tempest on how
+to do this is::
+
+    class PublicObjectTest(base.BaseObjectTest):
+
+        credentials = [['operator', CONF.object_storage.operator_role],
+                       ['operator_alt', CONF.object_storage.operator_role]]
+
+        @classmethod
+        def setup_credentials(cls):
+            super(PublicObjectTest, cls).setup_credentials()
+            ...
+
+In this case the manager objects will be set to ``cls.os_roles_operator`` and
+``cls.os_roles_operator_alt`` respectively.
+
+
+There is no limit to how many credentials you can allocate in this manner,
+however in almost every case you should **not** need more than 3 sets of
+credentials per test case.
+
+To figure out the mapping of manager objects set on the TestCase and the
+requested credentials you can reference:
+
++-------------------+---------------------+
+| Credentials Entry | Manager Variable    |
++===================+=====================+
+| primary           | cls.os_primary      |
++-------------------+---------------------+
+| admin             | cls.os_admin        |
++-------------------+---------------------+
+| alt               | cls.os_alt          |
++-------------------+---------------------+
+| [$label, $role]   | cls.os_roles_$label |
++-------------------+---------------------+
+
+By default cls.os_primary is available since it is allocated in the base Tempest test
+class (located in tempest/test.py). If your TestCase inherits from a different
+direct parent class (it'll still inherit from the BaseTestCase, just not
+directly) be sure to check if that class overrides allocated credentials.
+
+Dealing with Network Allocation
+'''''''''''''''''''''''''''''''
+
+When Neutron is enabled and a testing requires networking this isn't normally
+automatically setup when a tenant is created. Since Tempest needs isolated
+tenants to function properly it also needs to handle network allocation. By
+default the base test class will allocate a network, subnet, and router
+automatically (this depends on the configured credential provider, for more
+details see: :ref:`tempest_conf_network_allocation`). However, there are
+situations where you do no need all of these resources allocated (or your
+TestCase inherits from a class that overrides the default in tempest/test.py).
+There is a class level mechanism to override this allocation and specify which
+resources you need. To do this you need to call `cls.set_network_resources()`
+in the `setup_credentials()` method before the `super()`. For example::
+
+  from tempest import test
+
+
+  class TestExampleCase(test.BaseTestCase):
+
+  @classmethod
+  def setup_credentials(cls):
+      cls.set_network_resources(network=True, subnet=True, router=False)
+      super(TestExampleCase, cls).setup_credentials()
+
+There are 2 quirks with the usage here. First for the set_network_resources
+function to work properly it **must be called before super()**. This is so
+that children classes' settings are always used instead of a parent classes'.
+The other quirk here is that if you do not want to allocate any network
+resources for your test class simply call `set_network_resources()` without
+any arguments. For example::
+
+  from tempest import test
+
+
+  class TestExampleCase(test.BaseTestCase):
+
+  @classmethod
+  def setup_credentials(cls):
+      cls.set_network_resources()
+      super(TestExampleCase, cls).setup_credentials()
+
+This will not allocate any networking resources. This is because by default all
+the arguments default to False.
+
+It's also worth pointing out that it is common for base test classes for
+different services (and scenario tests) to override this setting. When
+inheriting from classes other than the base TestCase in tempest/test.py it is
+worth checking the immediate parent for what is set to determine if your
+class needs to override that setting.
+
+Interacting with Credentials and Clients
+========================================
+
+Once you have your basic TestCase setup you'll want to start writing tests. To
+do that you need to interact with an OpenStack deployment. This section will
+cover how credentials and clients are used inside of Tempest tests.
+
+
+Manager Objects
+---------------
+
+The primary interface with which you interact with both credentials and
+API clients is the client manager object. These objects are created
+automatically by the base test class as part of credential setup (for more
+details see the previous :ref:`credentials` section). Each manager object is
+initialized with a set of credentials and has each client object already setup
+to use that set of credentials for making all the API requests. Each client is
+accessible as a top level attribute on the manager object. So to start making
+API requests you just access the client's method for making that call and the
+credentials are already setup for you. For example if you wanted to make an API
+call to create a server in Nova::
+
+  from tempest import test
+
+
+  class TestExampleCase(test.BaseTestCase):
+    def test_example_create_server(self):
+      self.os_primary.servers_client.create_server(...)
+
+is all you need to do. As described previously, in the above example the
+``self.os_primary`` is created automatically because the base test class sets the
+``credentials`` attribute to allocate a primary credential set and initializes
+the client manager as ``self.os_primary``. This same access pattern can be used
+for all of the clients in Tempest.
+
+Credentials Objects
+-------------------
+
+In certain cases you need direct access to the credentials (the most common
+use case would be an API request that takes a user or project id in the request
+body). If you're in a situation where you need to access this you'll need to
+access the ``credentials`` object which is allocated from the configured
+credential provider in the base test class. This is accessible from the manager
+object via the manager's ``credentials`` attribute. For example::
+
+  from tempest import test
+
+
+  class TestExampleCase(test.BaseTestCase):
+    def test_example_create_server(self):
+      credentials = self.os_primary.credentials
+
+The credentials object provides access to all of the credential information you
+would need to make API requests. For example, building off the previous
+example::
+
+  from tempest import test
+
+
+  class TestExampleCase(test.BaseTestCase):
+    def test_example_create_server(self):
+      credentials = self.os_primary.credentials
+      username = credentials.username
+      user_id = credentials.user_id
+      password = credentials.password
+      tenant_id = credentials.tenant_id
diff --git a/releasenotes/notes/10.0-supported-openstack-releases-b88db468695348f6.yaml b/releasenotes/notes/10/10.0-supported-openstack-releases-b88db468695348f6.yaml
similarity index 100%
rename from releasenotes/notes/10.0-supported-openstack-releases-b88db468695348f6.yaml
rename to releasenotes/notes/10/10.0-supported-openstack-releases-b88db468695348f6.yaml
diff --git a/releasenotes/notes/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml b/releasenotes/notes/10/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
similarity index 75%
rename from releasenotes/notes/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
rename to releasenotes/notes/10/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
index 0ed3130..c1edd63 100644
--- a/releasenotes/notes/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
+++ b/releasenotes/notes/10/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
@@ -5,7 +5,7 @@
     it lives directly in the tempest project. For more information refer to
     the `library docs`_.
 
-    .. _library docs: http://docs.openstack.org/developer/tempest/library.html#library
+    .. _library docs: https://docs.openstack.org/tempest/latest/library.html#current-library-apis
 
 features:
   - Tempest library interface
diff --git a/releasenotes/notes/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml b/releasenotes/notes/10/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml
similarity index 100%
rename from releasenotes/notes/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml
rename to releasenotes/notes/10/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml
diff --git a/releasenotes/notes/11.0.0-api-microversion-testing-support-2ceddd2255670932.yaml b/releasenotes/notes/11/11.0.0-api-microversion-testing-support-2ceddd2255670932.yaml
similarity index 100%
rename from releasenotes/notes/11.0.0-api-microversion-testing-support-2ceddd2255670932.yaml
rename to releasenotes/notes/11/11.0.0-api-microversion-testing-support-2ceddd2255670932.yaml
diff --git a/releasenotes/notes/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml b/releasenotes/notes/11/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml
similarity index 100%
rename from releasenotes/notes/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml
rename to releasenotes/notes/11/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml
diff --git a/releasenotes/notes/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml b/releasenotes/notes/11/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml
similarity index 100%
rename from releasenotes/notes/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml
rename to releasenotes/notes/11/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml
diff --git a/releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml b/releasenotes/notes/12/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
similarity index 100%
rename from releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
rename to releasenotes/notes/12/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
diff --git a/releasenotes/notes/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml b/releasenotes/notes/12/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml
rename to releasenotes/notes/12/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml
diff --git a/releasenotes/notes/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml b/releasenotes/notes/12/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml
rename to releasenotes/notes/12/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml
diff --git a/releasenotes/notes/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml b/releasenotes/notes/12/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml
rename to releasenotes/notes/12/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml
diff --git a/releasenotes/notes/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml b/releasenotes/notes/12/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml
rename to releasenotes/notes/12/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml
diff --git a/releasenotes/notes/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml b/releasenotes/notes/12/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml
rename to releasenotes/notes/12/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml
diff --git a/releasenotes/notes/12.1.0-bug-1486834-7ebca15836ae27a9.yaml b/releasenotes/notes/12/12.1.0-bug-1486834-7ebca15836ae27a9.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-bug-1486834-7ebca15836ae27a9.yaml
rename to releasenotes/notes/12/12.1.0-bug-1486834-7ebca15836ae27a9.yaml
diff --git a/releasenotes/notes/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml b/releasenotes/notes/12/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml
rename to releasenotes/notes/12/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml
diff --git a/releasenotes/notes/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml b/releasenotes/notes/12/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml
rename to releasenotes/notes/12/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml
diff --git a/releasenotes/notes/12.1.0-new-test-utils-module-adf34468c4d52719.yaml b/releasenotes/notes/12/12.1.0-new-test-utils-module-adf34468c4d52719.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-new-test-utils-module-adf34468c4d52719.yaml
rename to releasenotes/notes/12/12.1.0-new-test-utils-module-adf34468c4d52719.yaml
diff --git a/releasenotes/notes/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml b/releasenotes/notes/12/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml
rename to releasenotes/notes/12/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml
diff --git a/releasenotes/notes/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml b/releasenotes/notes/12/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml
rename to releasenotes/notes/12/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml
diff --git a/releasenotes/notes/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml b/releasenotes/notes/12/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
rename to releasenotes/notes/12/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
diff --git a/releasenotes/notes/12.1.0-remove-trove-tests-666522e9113549f9.yaml b/releasenotes/notes/12/12.1.0-remove-trove-tests-666522e9113549f9.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-remove-trove-tests-666522e9113549f9.yaml
rename to releasenotes/notes/12/12.1.0-remove-trove-tests-666522e9113549f9.yaml
diff --git a/releasenotes/notes/12.1.0-routers-client-as-library-25a363379da351f6.yaml b/releasenotes/notes/12/12.1.0-routers-client-as-library-25a363379da351f6.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-routers-client-as-library-25a363379da351f6.yaml
rename to releasenotes/notes/12/12.1.0-routers-client-as-library-25a363379da351f6.yaml
diff --git a/releasenotes/notes/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml b/releasenotes/notes/12/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml
rename to releasenotes/notes/12/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml
diff --git a/releasenotes/notes/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml b/releasenotes/notes/12/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
rename to releasenotes/notes/12/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
diff --git a/releasenotes/notes/12.2.0-add-httptimeout-in-restclient-ax78061900e3f3d7.yaml b/releasenotes/notes/12/12.2.0-add-httptimeout-in-restclient-ax78061900e3f3d7.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-add-httptimeout-in-restclient-ax78061900e3f3d7.yaml
rename to releasenotes/notes/12/12.2.0-add-httptimeout-in-restclient-ax78061900e3f3d7.yaml
diff --git a/releasenotes/notes/12.2.0-add-new-identity-clients-3c3afd674a395bde.yaml b/releasenotes/notes/12/12.2.0-add-new-identity-clients-3c3afd674a395bde.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-add-new-identity-clients-3c3afd674a395bde.yaml
rename to releasenotes/notes/12/12.2.0-add-new-identity-clients-3c3afd674a395bde.yaml
diff --git a/releasenotes/notes/12.2.0-clients_module-16f3025f515bf9ec.yaml b/releasenotes/notes/12/12.2.0-clients_module-16f3025f515bf9ec.yaml
similarity index 93%
rename from releasenotes/notes/12.2.0-clients_module-16f3025f515bf9ec.yaml
rename to releasenotes/notes/12/12.2.0-clients_module-16f3025f515bf9ec.yaml
index 484d543..d07448a 100644
--- a/releasenotes/notes/12.2.0-clients_module-16f3025f515bf9ec.yaml
+++ b/releasenotes/notes/12/12.2.0-clients_module-16f3025f515bf9ec.yaml
@@ -4,7 +4,7 @@
     plugins to declare and automatically register any service client defined
     in the plugin.
   - tempest.lib exposes a new stable interface, the clients module and
-    ServiceClients class, which provides a convinient way for plugin tests to
+    ServiceClients class, which provides a convenient way for plugin tests to
     access service clients defined in Tempest as well as service clients
     defined in all loaded plugins.
     The new ServiceClients class only exposes for now the service clients
diff --git a/releasenotes/notes/12.2.0-nova_cert_default-90eb7c1e3cde624a.yaml b/releasenotes/notes/12/12.2.0-nova_cert_default-90eb7c1e3cde624a.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-nova_cert_default-90eb7c1e3cde624a.yaml
rename to releasenotes/notes/12/12.2.0-nova_cert_default-90eb7c1e3cde624a.yaml
diff --git a/releasenotes/notes/12.2.0-plugin-service-client-registration-00b19a2dd4935ba0.yaml b/releasenotes/notes/12/12.2.0-plugin-service-client-registration-00b19a2dd4935ba0.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-plugin-service-client-registration-00b19a2dd4935ba0.yaml
rename to releasenotes/notes/12/12.2.0-plugin-service-client-registration-00b19a2dd4935ba0.yaml
diff --git a/releasenotes/notes/12.2.0-remove-javelin-276f62d04f7e4a1d.yaml b/releasenotes/notes/12/12.2.0-remove-javelin-276f62d04f7e4a1d.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-remove-javelin-276f62d04f7e4a1d.yaml
rename to releasenotes/notes/12/12.2.0-remove-javelin-276f62d04f7e4a1d.yaml
diff --git a/releasenotes/notes/12.2.0-service_client_config-8a1d7b4de769c633.yaml b/releasenotes/notes/12/12.2.0-service_client_config-8a1d7b4de769c633.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-service_client_config-8a1d7b4de769c633.yaml
rename to releasenotes/notes/12/12.2.0-service_client_config-8a1d7b4de769c633.yaml
diff --git a/releasenotes/notes/12.2.0-volume-clients-as-library-9a3444dd63c134b3.yaml b/releasenotes/notes/12/12.2.0-volume-clients-as-library-9a3444dd63c134b3.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-volume-clients-as-library-9a3444dd63c134b3.yaml
rename to releasenotes/notes/12/12.2.0-volume-clients-as-library-9a3444dd63c134b3.yaml
diff --git a/releasenotes/notes/13.0.0-add-new-identity-clients-as-library-5f7ndha733nwdsn9.yaml b/releasenotes/notes/13/13.0.0-add-new-identity-clients-as-library-5f7ndha733nwdsn9.yaml
similarity index 100%
rename from releasenotes/notes/13.0.0-add-new-identity-clients-as-library-5f7ndha733nwdsn9.yaml
rename to releasenotes/notes/13/13.0.0-add-new-identity-clients-as-library-5f7ndha733nwdsn9.yaml
diff --git a/releasenotes/notes/13.0.0-add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml b/releasenotes/notes/13/13.0.0-add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml
similarity index 100%
rename from releasenotes/notes/13.0.0-add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml
rename to releasenotes/notes/13/13.0.0-add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml
diff --git a/releasenotes/notes/13.0.0-deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml b/releasenotes/notes/13/13.0.0-deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml
similarity index 100%
rename from releasenotes/notes/13.0.0-deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml
rename to releasenotes/notes/13/13.0.0-deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml
diff --git a/releasenotes/notes/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml b/releasenotes/notes/13/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml
similarity index 79%
rename from releasenotes/notes/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml
rename to releasenotes/notes/13/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml
index 543cf7b..52c04af 100644
--- a/releasenotes/notes/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml
+++ b/releasenotes/notes/13/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml
@@ -2,4 +2,4 @@
 deprecations:
   - The ``call_until_true`` function is moved from the ``tempest.test`` module
    to the ``tempest.lib.common.utils.test_utils`` module. Backward
-   compatibilty is preserved until Ocata.
+   compatibility is preserved until Ocata.
diff --git a/releasenotes/notes/13.0.0-start-of-newton-support-3ebb274f300f28eb.yaml b/releasenotes/notes/13/13.0.0-start-of-newton-support-3ebb274f300f28eb.yaml
similarity index 100%
rename from releasenotes/notes/13.0.0-start-of-newton-support-3ebb274f300f28eb.yaml
rename to releasenotes/notes/13/13.0.0-start-of-newton-support-3ebb274f300f28eb.yaml
diff --git a/releasenotes/notes/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml b/releasenotes/notes/13/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml
similarity index 69%
rename from releasenotes/notes/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml
rename to releasenotes/notes/13/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml
index 20f310d..813e47f 100644
--- a/releasenotes/notes/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml
+++ b/releasenotes/notes/13/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml
@@ -1,5 +1,5 @@
 ---
 upgrade:
-  - the already depreacted tempest-cleanup standalone command has been
+  - the already deprecated tempest-cleanup standalone command has been
     removed. The corresponding functionalities can be accessed through
     the unified `tempest` command (`tempest cleanup`).
diff --git a/releasenotes/notes/13.0.0-volume-clients-as-library-660811011be29d1a.yaml b/releasenotes/notes/13/13.0.0-volume-clients-as-library-660811011be29d1a.yaml
similarity index 100%
rename from releasenotes/notes/13.0.0-volume-clients-as-library-660811011be29d1a.yaml
rename to releasenotes/notes/13/13.0.0-volume-clients-as-library-660811011be29d1a.yaml
diff --git a/releasenotes/notes/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml b/releasenotes/notes/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml
deleted file mode 100644
index aa3a78e..0000000
--- a/releasenotes/notes/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-upgrade:
-  - The Stress tests framework and all the stress tests have been removed.
-
diff --git a/releasenotes/notes/14.0.0-add-cred-provider-abstract-class-to-lib-70ff513221f8a871.yaml b/releasenotes/notes/14/14.0.0-add-cred-provider-abstract-class-to-lib-70ff513221f8a871.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-cred-provider-abstract-class-to-lib-70ff513221f8a871.yaml
rename to releasenotes/notes/14/14.0.0-add-cred-provider-abstract-class-to-lib-70ff513221f8a871.yaml
diff --git a/releasenotes/notes/14.0.0-add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml b/releasenotes/notes/14/14.0.0-add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml
rename to releasenotes/notes/14/14.0.0-add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml
diff --git a/releasenotes/notes/14.0.0-add-error-code-translation-to-versions-clients-acbc78292e24b014.yaml b/releasenotes/notes/14/14.0.0-add-error-code-translation-to-versions-clients-acbc78292e24b014.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-error-code-translation-to-versions-clients-acbc78292e24b014.yaml
rename to releasenotes/notes/14/14.0.0-add-error-code-translation-to-versions-clients-acbc78292e24b014.yaml
diff --git a/releasenotes/notes/14.0.0-add-image-clients-af94564fb34ddca6.yaml b/releasenotes/notes/14/14.0.0-add-image-clients-af94564fb34ddca6.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-image-clients-af94564fb34ddca6.yaml
rename to releasenotes/notes/14/14.0.0-add-image-clients-af94564fb34ddca6.yaml
diff --git a/releasenotes/notes/14.0.0-add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml b/releasenotes/notes/14/14.0.0-add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
rename to releasenotes/notes/14/14.0.0-add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
diff --git a/releasenotes/notes/14.0.0-add-service-provider-client-cbba77d424a30dd3.yaml b/releasenotes/notes/14/14.0.0-add-service-provider-client-cbba77d424a30dd3.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-service-provider-client-cbba77d424a30dd3.yaml
rename to releasenotes/notes/14/14.0.0-add-service-provider-client-cbba77d424a30dd3.yaml
diff --git a/releasenotes/notes/14.0.0-add-ssh-port-parameter-to-client-6d16c374ac4456c1.yaml b/releasenotes/notes/14/14.0.0-add-ssh-port-parameter-to-client-6d16c374ac4456c1.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-ssh-port-parameter-to-client-6d16c374ac4456c1.yaml
rename to releasenotes/notes/14/14.0.0-add-ssh-port-parameter-to-client-6d16c374ac4456c1.yaml
diff --git a/releasenotes/notes/14.0.0-deprecate-nova-api-extensions-df16b02485dae203.yaml b/releasenotes/notes/14/14.0.0-deprecate-nova-api-extensions-df16b02485dae203.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-deprecate-nova-api-extensions-df16b02485dae203.yaml
rename to releasenotes/notes/14/14.0.0-deprecate-nova-api-extensions-df16b02485dae203.yaml
diff --git a/releasenotes/notes/14.0.0-move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml b/releasenotes/notes/14/14.0.0-move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml
rename to releasenotes/notes/14/14.0.0-move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml
diff --git a/releasenotes/notes/14.0.0-new-volume-limit-client-517c17d9090f4df4.yaml b/releasenotes/notes/14/14.0.0-new-volume-limit-client-517c17d9090f4df4.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-new-volume-limit-client-517c17d9090f4df4.yaml
rename to releasenotes/notes/14/14.0.0-new-volume-limit-client-517c17d9090f4df4.yaml
diff --git a/releasenotes/notes/14/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml b/releasenotes/notes/14/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml
new file mode 100644
index 0000000..389b29f
--- /dev/null
+++ b/releasenotes/notes/14/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml
@@ -0,0 +1,13 @@
+---
+prelude: >
+  This release is marking the end of Liberty release support in Tempest
+upgrade:
+  - The Stress tests framework and all the stress tests have been removed.
+other:
+  - |
+    OpenStack releases supported at this time are **Mitaka** and **Newton**.
+
+    The release under current development as of this tag is Ocata, meaning that
+    every Tempest commit is also tested against master during the Ocata cycle.
+    However, this does not necessarily mean that using Tempest as of this tag
+    will work against a Ocata (or future releases) cloud.
diff --git a/releasenotes/notes/14.0.0-remove-baremetal-tests-65186d9e15d5b8fb.yaml b/releasenotes/notes/14/14.0.0-remove-baremetal-tests-65186d9e15d5b8fb.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-remove-baremetal-tests-65186d9e15d5b8fb.yaml
rename to releasenotes/notes/14/14.0.0-remove-baremetal-tests-65186d9e15d5b8fb.yaml
diff --git a/releasenotes/notes/14.0.0-remove-bootable-option-024f8944c056a3e0.yaml b/releasenotes/notes/14/14.0.0-remove-bootable-option-024f8944c056a3e0.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-remove-bootable-option-024f8944c056a3e0.yaml
rename to releasenotes/notes/14/14.0.0-remove-bootable-option-024f8944c056a3e0.yaml
diff --git a/releasenotes/notes/14.0.0-remove-negative-test-generator-1653f4c0f86ccf75.yaml b/releasenotes/notes/14/14.0.0-remove-negative-test-generator-1653f4c0f86ccf75.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-remove-negative-test-generator-1653f4c0f86ccf75.yaml
rename to releasenotes/notes/14/14.0.0-remove-negative-test-generator-1653f4c0f86ccf75.yaml
diff --git a/releasenotes/notes/14.0.0-remove-sahara-tests-1532c47c7df80e3a.yaml b/releasenotes/notes/14/14.0.0-remove-sahara-tests-1532c47c7df80e3a.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-remove-sahara-tests-1532c47c7df80e3a.yaml
rename to releasenotes/notes/14/14.0.0-remove-sahara-tests-1532c47c7df80e3a.yaml
diff --git a/releasenotes/notes/14.0.0-volume-clients-as-library-309030c7a16e62ab.yaml b/releasenotes/notes/14/14.0.0-volume-clients-as-library-309030c7a16e62ab.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-volume-clients-as-library-309030c7a16e62ab.yaml
rename to releasenotes/notes/14/14.0.0-volume-clients-as-library-309030c7a16e62ab.yaml
diff --git a/releasenotes/notes/add-identity-v3-clients-as-a-library-d34b4fdf376984ad.yaml b/releasenotes/notes/15/15.0.0-add-identity-v3-clients-as-a-library-d34b4fdf376984ad.yaml
similarity index 100%
rename from releasenotes/notes/add-identity-v3-clients-as-a-library-d34b4fdf376984ad.yaml
rename to releasenotes/notes/15/15.0.0-add-identity-v3-clients-as-a-library-d34b4fdf376984ad.yaml
diff --git a/releasenotes/notes/add-image-clients-tests-49dbc0a0a4281a77.yaml b/releasenotes/notes/15/15.0.0-add-image-clients-tests-49dbc0a0a4281a77.yaml
similarity index 100%
rename from releasenotes/notes/add-image-clients-tests-49dbc0a0a4281a77.yaml
rename to releasenotes/notes/15/15.0.0-add-image-clients-tests-49dbc0a0a4281a77.yaml
diff --git a/releasenotes/notes/add-implied-roles-to-roles-client-library-edf96408ad9ba82e.yaml b/releasenotes/notes/15/15.0.0-add-implied-roles-to-roles-client-library-edf96408ad9ba82e.yaml
similarity index 100%
rename from releasenotes/notes/add-implied-roles-to-roles-client-library-edf96408ad9ba82e.yaml
rename to releasenotes/notes/15/15.0.0-add-implied-roles-to-roles-client-library-edf96408ad9ba82e.yaml
diff --git a/releasenotes/notes/add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml b/releasenotes/notes/15/15.0.0-add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml
similarity index 100%
rename from releasenotes/notes/add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml
rename to releasenotes/notes/15/15.0.0-add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml
diff --git a/releasenotes/notes/deprecate-allow_port_security_disabled-option-2d3d87f6bd11d03a.yaml b/releasenotes/notes/15/15.0.0-deprecate-allow_port_security_disabled-option-2d3d87f6bd11d03a.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-allow_port_security_disabled-option-2d3d87f6bd11d03a.yaml
rename to releasenotes/notes/15/15.0.0-deprecate-allow_port_security_disabled-option-2d3d87f6bd11d03a.yaml
diff --git a/releasenotes/notes/deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml b/releasenotes/notes/15/15.0.0-deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml
rename to releasenotes/notes/15/15.0.0-deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml
diff --git a/releasenotes/notes/deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml b/releasenotes/notes/15/15.0.0-deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml
rename to releasenotes/notes/15/15.0.0-deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml
diff --git a/releasenotes/notes/jsonschema-validator-2377ba131e12d3c7.yaml b/releasenotes/notes/15/15.0.0-jsonschema-validator-2377ba131e12d3c7.yaml
similarity index 100%
rename from releasenotes/notes/jsonschema-validator-2377ba131e12d3c7.yaml
rename to releasenotes/notes/15/15.0.0-jsonschema-validator-2377ba131e12d3c7.yaml
diff --git a/releasenotes/notes/15/15.0.0-remove-deprecated-compute-microversion-config-options-eaee6a7d2f8390a8.yaml b/releasenotes/notes/15/15.0.0-remove-deprecated-compute-microversion-config-options-eaee6a7d2f8390a8.yaml
new file mode 100644
index 0000000..b1c0c62
--- /dev/null
+++ b/releasenotes/notes/15/15.0.0-remove-deprecated-compute-microversion-config-options-eaee6a7d2f8390a8.yaml
@@ -0,0 +1,9 @@
+---
+upgrade:
+  - The deprecated compute microversion config options from
+    'compute-feature-enabled' group have been removed. Those config options
+    are available under 'compute' group to configure the min and max
+    microversion for compute service.
+
+    * CONF.compute.min_microversion
+    * CONF.compute.max_microversion
diff --git a/releasenotes/notes/15/15.0.0-remove-deprecated-compute-validation-config-options-e3d1b89ce074d71c.yaml b/releasenotes/notes/15/15.0.0-remove-deprecated-compute-validation-config-options-e3d1b89ce074d71c.yaml
new file mode 100644
index 0000000..104bf27
--- /dev/null
+++ b/releasenotes/notes/15/15.0.0-remove-deprecated-compute-validation-config-options-e3d1b89ce074d71c.yaml
@@ -0,0 +1,25 @@
+---
+prelude: >
+    This release is marking the start of Ocata release support in Tempest
+upgrade:
+  - |
+    Below deprecated config options from compute group have been removed.
+    Corresponding config options already been available in validation group.
+
+    - ``compute.use_floatingip_for_ssh`` (available as ``validation.connect_method``)
+    - ``compute.ssh_auth_method`` (available as ``validation.auth_method``)
+    - ``compute.image_ssh_password`` (available as ``validation.image_ssh_password``)
+    - ``compute.ssh_shell_prologue`` (available as ``validation.ssh_shell_prologue``)
+    - ``compute.ping_size `` (available as ``validation.ping_size``)
+    - ``compute.ping_count `` (available as ``validation.ping_count``)
+    - ``compute.floating_ip_range `` (available as ``validation.floating_ip_range``)
+other:
+  - |
+    OpenStack releases supported at this time are **Mitaka**, **Newton**,
+    and **Ocata**.
+
+    The release under current development as of this tag is Pike,
+    meaning that every Tempest commit is also tested against master during
+    the Pike cycle. However, this does not necessarily mean that using
+    Tempest as of this tag will work against a Pike (or future releases)
+    cloud.
diff --git a/releasenotes/notes/15/15.0.0-remove-deprecated-input-scenario-config-options-414e0c5442e967e9.yaml b/releasenotes/notes/15/15.0.0-remove-deprecated-input-scenario-config-options-414e0c5442e967e9.yaml
new file mode 100644
index 0000000..371c061
--- /dev/null
+++ b/releasenotes/notes/15/15.0.0-remove-deprecated-input-scenario-config-options-414e0c5442e967e9.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - The deprecated input-scenario config options and group
+    have been removed.
+    The input scenarios functionality already being removed from tempest
+    and from this release, their corresponding config options too.
diff --git a/releasenotes/notes/15/15.0.0-remove-deprecated-network-config-options-f9ce276231578fe6.yaml b/releasenotes/notes/15/15.0.0-remove-deprecated-network-config-options-f9ce276231578fe6.yaml
new file mode 100644
index 0000000..e445fb3
--- /dev/null
+++ b/releasenotes/notes/15/15.0.0-remove-deprecated-network-config-options-f9ce276231578fe6.yaml
@@ -0,0 +1,11 @@
+---
+upgrade:
+  - |
+    Below deprecated network config options have been removed.
+    Those config options already been renamed to below meaningful names.
+
+    - ``tenant_network_cidr`` (removed) -> ``project_network_cidr``
+    - ``tenant_network_mask_bits`` (removed) -> ``project_network_mask_bits``
+    - ``tenant_network_v6_cidr`` (removed) -> ``project_network_v6_cidr``
+    - ``tenant_network_v6_mask_bits`` (removed) -> ``project_network_v6_mask_bits``
+    - ``tenant_networks_reachable`` (removed) -> ``project_networks_reachable``
diff --git a/releasenotes/notes/16/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml b/releasenotes/notes/16/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
new file mode 100644
index 0000000..6b45666
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
@@ -0,0 +1,3 @@
+---
+features:
+  - Add a new client to handle the OAUTH consumers feature from the identity API.
diff --git a/releasenotes/notes/16/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml b/releasenotes/notes/16/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
new file mode 100644
index 0000000..01136c6
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Add missing API call, list all role inference rules,
+    to the roles_client library. This feature enables the
+    possibility of listing all role inference rules in the
+    system.
diff --git a/releasenotes/notes/16/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml b/releasenotes/notes/16/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml
new file mode 100644
index 0000000..6801858
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Add cascade parameter to volumes_client.
+    This option provides the ability to delete a volume and have Cinder
+    handle deletion of snapshots associated with that volume by passing
+    an additional argument to volume delete, "cascade=True".
diff --git a/releasenotes/notes/16/16.0.0-add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml b/releasenotes/notes/16/16.0.0-add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
new file mode 100644
index 0000000..848a21b
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
@@ -0,0 +1,3 @@
+---
+features:
+    - Define the compute server evacuate client method in the servers_client library.
diff --git a/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml b/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
new file mode 100644
index 0000000..0075a36
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
@@ -0,0 +1,9 @@
+---
+upgrade:
+  - The ``JSON_ENC`` and ``TXT_ENC`` option in the ``_error_checker``
+    section have been added with additional content-type which are
+    defined in RFC7231 but missing in the currnt rest_client.py file.
+    The lack of these additional content-type will cause defcore test
+    to fail for OpenStack public cloud which uses tomcat module in the
+    api gateway. The additions are ``application/json;charset=utf-8``,
+    ``text/html;charset=utf-8``,``text/plain;charset=utf-8``
\ No newline at end of file
diff --git a/releasenotes/notes/16/16.0.0-add-list-auth-project-client-5905076d914a3943.yaml b/releasenotes/notes/16/16.0.0-add-list-auth-project-client-5905076d914a3943.yaml
new file mode 100644
index 0000000..471f8f0
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-list-auth-project-client-5905076d914a3943.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add the list auth projects API to the identity client library. This feature
+    enables the possibility to list projects that are available to be scoped
+    to based on the X-Auth-Token provided in the request.
diff --git a/releasenotes/notes/16/16.0.0-add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml b/releasenotes/notes/16/16.0.0-add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
new file mode 100644
index 0000000..acc7a41
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add versions_client module for image service.
+    This new module provides list_versions() method which shows API versions
+    from Image service.
diff --git a/releasenotes/notes/16/16.0.0-add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml b/releasenotes/notes/16/16.0.0-add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml
new file mode 100644
index 0000000..67f9541
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add the list security groups by server API to the servers_client
+    library. This feature enables the possibility to list security
+    groups for a server instance.
diff --git a/releasenotes/notes/16/16.0.0-add-list-version-to-identity-client-944cb7396088a575.yaml b/releasenotes/notes/16/16.0.0-add-list-version-to-identity-client-944cb7396088a575.yaml
new file mode 100644
index 0000000..dd66ff5
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-list-version-to-identity-client-944cb7396088a575.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add versions_client module for identity service.
+    This new module provides list_versions() method which shows API versions
+    from Identity service.
diff --git a/releasenotes/notes/16/16.0.0-add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml b/releasenotes/notes/16/16.0.0-add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
new file mode 100644
index 0000000..233cc28
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add versions_client module for volume service.
+    This new module provides list_versions() method which shows API versions
+    from Volume service.
diff --git a/releasenotes/notes/16/16.0.0-add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml b/releasenotes/notes/16/16.0.0-add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
new file mode 100644
index 0000000..06f4fcd
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Interface show_quota_set of compute quotas_client has been extended to include the
+    argument "detail", which allows for detailed quota set information for a project to be
+    retrieved, if set to True.
diff --git a/releasenotes/notes/16/16.0.0-add-tempest-lib-remote-client-adbeb3f42a36910b.yaml b/releasenotes/notes/16/16.0.0-add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
new file mode 100644
index 0000000..1b8cda2
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
@@ -0,0 +1,11 @@
+---
+features:
+  - |
+    Add remote_client under tempest.lib.
+    This remote_client under tempest.lib is defined as stable
+    interface, and now this module provides the following
+    essential methods.
+
+    - exec_command
+    - validate_authentication
+    - ping_host
diff --git a/releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml b/releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
new file mode 100644
index 0000000..73900ca
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Adds a new cli option to tempest run, --combine, which is used to indicate
+    you want the subunit stream output combined with the previous run's in
+    the testr repository
diff --git a/releasenotes/notes/16/16.0.0-add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml b/releasenotes/notes/16/16.0.0-add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
new file mode 100644
index 0000000..c95e77c
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add update encryption type API to the v2 encryption_types_client library.
+    This feature enables the possibility to update an encryption type for an
+    existing volume type.
diff --git a/releasenotes/notes/16/16.0.0-add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml b/releasenotes/notes/16/16.0.0-add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
new file mode 100644
index 0000000..3453050
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    Add the unmanage volume API service method in v2 volumes_client library.
+    Define v2 volume_manage_client client for the volume service as library
+    interfaces, allowing other projects to use this module as stable libraries
+    without maintenance changes.
+
+    * volume_manage_client(v2)
diff --git a/releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml b/releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
new file mode 100644
index 0000000..9927971
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Add server tags APIs to the servers_client library.
+    This feature enables the possibility of upating, deleting
+    and checking existence of a tag on a server, as well
+    as updating and deleting all tags on a server.
+
diff --git a/releasenotes/notes/16/16.0.0-deprecate-deactivate_image-config-7a282c471937bbcb.yaml b/releasenotes/notes/16/16.0.0-deprecate-deactivate_image-config-7a282c471937bbcb.yaml
new file mode 100644
index 0000000..69c6bb6
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-deprecate-deactivate_image-config-7a282c471937bbcb.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+  - |
+    The ``deactivate_image`` configuration switch from the ``config`` module
+    is deprecated. It was added to support the older-than-kilo releases
+    which we don't support anymore.
diff --git a/releasenotes/notes/16/16.0.0-deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml b/releasenotes/notes/16/16.0.0-deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
new file mode 100644
index 0000000..c3e43ee
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
@@ -0,0 +1,7 @@
+---
+deprecations:
+  - |
+    The ``dvr_extra_resources`` configuration switch from the ``config`` module
+    is deprecated. It was added to support the Liberty Release which we don't
+    support anymore.
+
diff --git a/releasenotes/notes/16/16.0.0-deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml b/releasenotes/notes/16/16.0.0-deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
new file mode 100644
index 0000000..788bc95
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
@@ -0,0 +1,10 @@
+---
+deprecations:
+  - |
+    Glance v1 APIs are deprecated and v2 are current.
+    Tempest should tests only v2 APIs.
+    Below API version selection config options
+    for glance have been deprecated and will be removed in future.
+
+    * CONF.image_feature_enabled.api_v2
+    * CONF.image_feature_enabled.api_v1
diff --git a/releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml b/releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
new file mode 100644
index 0000000..f679208
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
@@ -0,0 +1,10 @@
+---
+upgrade:
+  - The default value of rand_name()'s prefix argument is changed
+    to 'tempest' from None to identify resources are created by
+    Tempest.
+deprecations:
+  - The resources_prefix is marked as deprecated because it is
+    enough to set 'tempest' as the prefix on rand_name() to
+    ideintify resources which are created by Tempest and no
+    projects set this option on OpenStack dev community.
diff --git a/releasenotes/notes/16/16.0.0-deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml b/releasenotes/notes/16/16.0.0-deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml
new file mode 100644
index 0000000..4d8b941
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml
@@ -0,0 +1,5 @@
+---
+deprecations:
+  - The ``skip_unless_attr`` decorator in lib/decorators.py has been deprecated,
+    please use the standard ``testtools.skipUnless`` and ``testtools.skipIf``
+    decorators.
diff --git a/releasenotes/notes/16/16.0.0-deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml b/releasenotes/notes/16/16.0.0-deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml
new file mode 100644
index 0000000..6285ea6
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml
@@ -0,0 +1,5 @@
+---
+deprecations:
+  - The ``skip_unless_config`` and ``skip_if_config`` decorators in the
+    ``config`` module have been deprecated and will be removed in the Queens
+    dev cycle. Use the ``testtools.skipUnless`` (or a variation of) instead.
diff --git a/releasenotes/notes/16/16.0.0-deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml b/releasenotes/notes/16/16.0.0-deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
new file mode 100644
index 0000000..0660d9c
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
@@ -0,0 +1,12 @@
+---
+deprecations:
+  - |
+    Volume v1 API is deprecated and the v3 are CURRENT.
+    Tempest doesn't need to test the v1 API as the default.
+    The volume config option 'api_v1' has been marked as
+    deprecated.
+upgrade:
+  - |
+    The volume config option 'api_v1' default is changed to
+    ``False`` because the volume v1 API has been deprecated
+    since Juno release.
diff --git a/releasenotes/notes/16/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml b/releasenotes/notes/16/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml
new file mode 100644
index 0000000..4081f6a
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+  - |
+    Deprecate the client_parameters argument in
+    `tempest.lib.services.clients.ServiceClients`. The parameter is actually
+    not honoured already - see https://bugs.launchpad.net/tempest/+bug/1680915
diff --git a/releasenotes/notes/16/16.0.0-fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml b/releasenotes/notes/16/16.0.0-fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
new file mode 100644
index 0000000..6d31576
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
@@ -0,0 +1,7 @@
+---
+fixes:
+  - |
+    Fix below volume v2 service clients to make v2 API call: Bug#1667354
+
+    - SchedulerStatsClient
+    - CapabilitiesClient
diff --git a/releasenotes/notes/16/16.0.0-mitaka-eol-88ff8355fff81b55.yaml b/releasenotes/notes/16/16.0.0-mitaka-eol-88ff8355fff81b55.yaml
new file mode 100644
index 0000000..24ec512
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-mitaka-eol-88ff8355fff81b55.yaml
@@ -0,0 +1,13 @@
+---
+prelude: >
+    This release indicates end of support for Mitaka in Tempest.
+other:
+  - |
+    OpenStack Releases Supported after this release are **Newton**
+    and **Ocata**
+
+    The release under current development as of this tag is Pike,
+    meaning that every Tempest commit is also tested against master branch
+    during the Pike cycle. However, this does not necessarily mean that
+    using Tempest as of this tag will work against Pike (or future
+    releases) cloud.
diff --git a/releasenotes/notes/16/16.0.0-remove-call_until_true-of-test-de9c13bc8f969921.yaml b/releasenotes/notes/16/16.0.0-remove-call_until_true-of-test-de9c13bc8f969921.yaml
new file mode 100644
index 0000000..5670821
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-remove-call_until_true-of-test-de9c13bc8f969921.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - The *call_until_true* of *test* module is removed because it was marked
+    as deprecated and Tempest provides it from *test_utils* as a stable
+    interface instead. Please switch to use *test_utils.call_until_true* if
+    necessary.
diff --git a/releasenotes/notes/16/16.0.0-remove-cinder-v1-api-tests-71e266b8d55d475f.yaml b/releasenotes/notes/16/16.0.0-remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
new file mode 100644
index 0000000..710ad9e
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
@@ -0,0 +1,5 @@
+upgrade:
+  - Remove Cinder v1 API tests.
+    Cinder v1 API has been deprecated since Juno release, and Juno is
+    not supported by current Tempest. Then Cinder v1 API tests are
+    removed from Tempest.
diff --git a/releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml b/releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
new file mode 100644
index 0000000..9d7102f
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+  - |
+    The deprecated config option 'allow_port_security_disabled' from compute_feature_enabled
+    group has been removed.
diff --git a/releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml b/releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
new file mode 100644
index 0000000..b4e4dd1
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
@@ -0,0 +1,11 @@
+---
+upgrade:
+  - |
+    Below deprecated config options from compute group have been removed.
+    Corresponding config options already been available in validation group.
+
+    - ``compute.image_ssh_user`` (available as ``validation.image_ssh_user``)
+    - ``compute.ssh_user`` (available as ``validation.image_ssh_user``)
+    - ``scenario.ssh_user`` (available as ``validation.image_ssh_user``)
+    - ``compute.network_for_ssh`` (available as ``validation.network_for_ssh``)
+    - ``compute.ping_timeout `` (available as ``validation.ping_timeout``)
diff --git a/releasenotes/notes/16/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml b/releasenotes/notes/16/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
new file mode 100644
index 0000000..889e862
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+  - |
+    The deprecated config option 'dvr_extra_resources' from network group has been removed.
+    This option was for extra resources which were provisioned to bind a router to Neutron
+    L3 agent. The extra resources need to be provisioned in Liberty release or older,
+    and are not required since Mitaka release. Current Tempest doesn't support Liberty, so
+    this option has been removed from Tempest.
diff --git a/releasenotes/notes/16/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml b/releasenotes/notes/16/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml
new file mode 100644
index 0000000..8085694
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+  - |
+    The deprecated config option 'reseller' from identity_feature_enabled group has been removed.
diff --git a/releasenotes/notes/16/16.0.0-remove-sahara-service-available-44a642aa9c634ab4.yaml b/releasenotes/notes/16/16.0.0-remove-sahara-service-available-44a642aa9c634ab4.yaml
new file mode 100644
index 0000000..c0dc7d7
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-remove-sahara-service-available-44a642aa9c634ab4.yaml
@@ -0,0 +1,7 @@
+---
+fixes:
+  - |
+    The 'sahara' config option in the 'service-available' group has been moved to the sahara plugin
+    (openstack/sahara-tests) along with tests and service client during the Ocata timeframe.
+    A 'sahara' config option was left over on Tempest side, and it's removed now.
+    As long as the sahara plugin is installed, this change as no impact on users of sahara tests.
diff --git a/releasenotes/notes/16/16.0.0-remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml b/releasenotes/notes/16/16.0.0-remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml
new file mode 100644
index 0000000..fc15995
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+  - |
+    The deprecated ``volume_services`` option in the ``volume_feature_enabled``
+    section has now been removed.
diff --git a/releasenotes/notes/16/16.0.0-use-keystone-v3-api-935860d30ddbb8e9.yaml b/releasenotes/notes/16/16.0.0-use-keystone-v3-api-935860d30ddbb8e9.yaml
new file mode 100644
index 0000000..dd6e924
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-use-keystone-v3-api-935860d30ddbb8e9.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+  - Tempest now defaults to using Keystone v3 API for the
+    authentication, because Keystone v3 API is CURRENT and
+    the v2 API is deprecated.
diff --git a/releasenotes/notes/16/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml b/releasenotes/notes/16/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml
new file mode 100644
index 0000000..e5e479b
--- /dev/null
+++ b/releasenotes/notes/16/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml
@@ -0,0 +1,18 @@
+---
+features:
+  - |
+    Define volume transfers service clients as libraries.
+    The following volume transfers service clients are defined as library interface.
+
+    * transfers_client(v2)
+deprecations:
+  - |
+    Deprecate volume v2 transfers resource methods from volumes_client(v2) libraries.
+    Same methods are available in new transfers service client: transfers_client(v2)
+    The following methods of volume v2 volumes_clients have been deprecated:
+
+    * create_volume_transfer (v2.volumes_client)
+    * show_volume_transfer (v2.volumes_client)
+    * list_volume_transfers (v2.volumes_client)
+    * delete_volume_transfer (v2.volumes_client)
+    * accept_volume_transfer (v2.volumes_client)
diff --git a/releasenotes/notes/add-OAUTH-Token-Client-tempest-tests-6351eda451b95a86.yaml b/releasenotes/notes/add-OAUTH-Token-Client-tempest-tests-6351eda451b95a86.yaml
new file mode 100644
index 0000000..9115f03
--- /dev/null
+++ b/releasenotes/notes/add-OAUTH-Token-Client-tempest-tests-6351eda451b95a86.yaml
@@ -0,0 +1,3 @@
+---
+features:
+  - Add a new client to handle the OAUTH token feature from the identity API.
diff --git a/releasenotes/notes/add-compute-feature-serial-console-45583c4341e34fc9.yaml b/releasenotes/notes/add-compute-feature-serial-console-45583c4341e34fc9.yaml
new file mode 100644
index 0000000..18fd5ad
--- /dev/null
+++ b/releasenotes/notes/add-compute-feature-serial-console-45583c4341e34fc9.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    A new boolean config option ``serial_console`` is added to the section
+    ``compute-feature-enabled``. If enabled, tests, which validate the
+    behavior of Nova's *serial console* feature (an alternative to VNC,
+    RDP, SPICE) can be executed.
diff --git a/releasenotes/notes/add-create-group-from-src-tempest-tests-9eb8b0b4b5c52055.yaml b/releasenotes/notes/add-create-group-from-src-tempest-tests-9eb8b0b4b5c52055.yaml
new file mode 100644
index 0000000..dec4a27
--- /dev/null
+++ b/releasenotes/notes/add-create-group-from-src-tempest-tests-9eb8b0b4b5c52055.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Add create_group_from_source to groups_client in the volume service library.
diff --git a/releasenotes/notes/add-domain-configuration-client-tempest-tests-e383efabdbb9ad03.yaml b/releasenotes/notes/add-domain-configuration-client-tempest-tests-e383efabdbb9ad03.yaml
new file mode 100644
index 0000000..5653681
--- /dev/null
+++ b/releasenotes/notes/add-domain-configuration-client-tempest-tests-e383efabdbb9ad03.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Add a new client to handle the domain configuration feature from the
+    identity v3 API.
diff --git a/releasenotes/notes/add-floating-ip-config-option-e5774bf77702ce9f.yaml b/releasenotes/notes/add-floating-ip-config-option-e5774bf77702ce9f.yaml
new file mode 100644
index 0000000..8221d78
--- /dev/null
+++ b/releasenotes/notes/add-floating-ip-config-option-e5774bf77702ce9f.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - A new config option in the network-feature-enabled section, floating_ips,
+    to specify whether floating ips are available in the cloud under test. By
+    default this is set to True.
diff --git a/releasenotes/notes/add-force-detach-volume-to-volumes-client-library-b2071f2954f8e8b1.yaml b/releasenotes/notes/add-force-detach-volume-to-volumes-client-library-b2071f2954f8e8b1.yaml
new file mode 100644
index 0000000..a0156a0
--- /dev/null
+++ b/releasenotes/notes/add-force-detach-volume-to-volumes-client-library-b2071f2954f8e8b1.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add force detach volume feature API to v2 volumes_client library.
+    This feature enables the possibility to force a volume to detach, and
+    roll back an unsuccessful detach operation after you disconnect the volume.
diff --git a/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-endpoint-groups-3518a90bbb731d0f.yaml b/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-endpoint-groups-3518a90bbb731d0f.yaml
new file mode 100644
index 0000000..1dc33aa
--- /dev/null
+++ b/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-endpoint-groups-3518a90bbb731d0f.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Defines the identity v3 OS-EP-FILTER EndPoint Groups API client.
+    This client manages Create, Get, Update, Check, List, and Delete
+    of EndPoint Group.
+
diff --git a/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-extensions-9cfd217fd2c6a61f.yaml b/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-extensions-9cfd217fd2c6a61f.yaml
new file mode 100644
index 0000000..69320fb
--- /dev/null
+++ b/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-extensions-9cfd217fd2c6a61f.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Defines the identity v3 OS-EP-FILTER extension API client.
+    This client manages associations between endpoints, projects
+    along with groups.
diff --git a/releasenotes/notes/add-ip-version-check-in-addresses-x491ac6d9abaxa12.yaml b/releasenotes/notes/add-ip-version-check-in-addresses-x491ac6d9abaxa12.yaml
new file mode 100644
index 0000000..957e903
--- /dev/null
+++ b/releasenotes/notes/add-ip-version-check-in-addresses-x491ac6d9abaxa12.yaml
@@ -0,0 +1,4 @@
+---
+fixes:
+  Add more accurate ip version check in addresses schema which will
+  limit the ip version value in [4, 6].
diff --git a/releasenotes/notes/add-is-resource-deleted-sg-client-f4a7a7a54ff024d7.yaml b/releasenotes/notes/add-is-resource-deleted-sg-client-f4a7a7a54ff024d7.yaml
new file mode 100644
index 0000000..e046326
--- /dev/null
+++ b/releasenotes/notes/add-is-resource-deleted-sg-client-f4a7a7a54ff024d7.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Implement the `rest_client` method `is_resource_deleted` in the network
+    security group client.
diff --git a/releasenotes/notes/add-kwargs-to-delete-vol-of-vol-client-1ecde75beb62933c.yaml b/releasenotes/notes/add-kwargs-to-delete-vol-of-vol-client-1ecde75beb62933c.yaml
new file mode 100644
index 0000000..b8c9dfc
--- /dev/null
+++ b/releasenotes/notes/add-kwargs-to-delete-vol-of-vol-client-1ecde75beb62933c.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    The ``delete_volume`` method of the ``VolumesClient`` class
+    now has an additional ``**params`` argument that enables passing
+    additional information in the query string of the HTTP request.
+
diff --git a/releasenotes/notes/add-list-volume-transfers-with-detail-to-transfers-client-80169bf78cf4fa66.yaml b/releasenotes/notes/add-list-volume-transfers-with-detail-to-transfers-client-80169bf78cf4fa66.yaml
new file mode 100644
index 0000000..8e85d3a
--- /dev/null
+++ b/releasenotes/notes/add-list-volume-transfers-with-detail-to-transfers-client-80169bf78cf4fa66.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Add list volume transfers with details API to v2 transfers_client library.
+    This feature enables the possibility to list volume transfers with details.
diff --git a/releasenotes/notes/add-manage-snapshot-ref-config-option-67efd04897335b67.yaml b/releasenotes/notes/add-manage-snapshot-ref-config-option-67efd04897335b67.yaml
new file mode 100644
index 0000000..bc7bcc8
--- /dev/null
+++ b/releasenotes/notes/add-manage-snapshot-ref-config-option-67efd04897335b67.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    A new config option 'manage_snapshot_ref' is added in the volume section,
+    to specify snapshot ref parameter for different storage backend drivers
+    when managing an existing snapshot. By default it is set to fit the LVM
+    driver.
diff --git a/releasenotes/notes/add-params-to-identity-v3-list-endpoints-958a155be4e17e5b.yaml b/releasenotes/notes/add-params-to-identity-v3-list-endpoints-958a155be4e17e5b.yaml
new file mode 100644
index 0000000..46f3b49
--- /dev/null
+++ b/releasenotes/notes/add-params-to-identity-v3-list-endpoints-958a155be4e17e5b.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    The ``list_endpoints`` method of the v3 ``EndPointsClient`` class now has
+    an additional ``**params`` argument that enables passing additional
+    information in the query string of the HTTP request.
diff --git a/releasenotes/notes/add-params-to-v2-list-backups-api-c088d2b4bfe90247.yaml b/releasenotes/notes/add-params-to-v2-list-backups-api-c088d2b4bfe90247.yaml
new file mode 100644
index 0000000..cee2d76
--- /dev/null
+++ b/releasenotes/notes/add-params-to-v2-list-backups-api-c088d2b4bfe90247.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    The ``list_backups`` method of the v2 ``BackupsClient`` class now has
+    an additional ``**params`` argument that enables passing additional
+    information in the query string of the HTTP request.
diff --git a/releasenotes/notes/add-return-value-to-retype-volume-a401aa619aaa2457.yaml b/releasenotes/notes/add-return-value-to-retype-volume-a401aa619aaa2457.yaml
new file mode 100644
index 0000000..ca42014
--- /dev/null
+++ b/releasenotes/notes/add-return-value-to-retype-volume-a401aa619aaa2457.yaml
@@ -0,0 +1,7 @@
+---
+fixes:
+  - |
+    Add a missing return statement to the retype_volume API in the v2
+    volumes_client library: Bug#1703997
+
+    This changes the response body from None to an empty dictionary.
diff --git a/releasenotes/notes/add-save-state-option-5ea67858cbaca969.yaml b/releasenotes/notes/add-save-state-option-5ea67858cbaca969.yaml
new file mode 100644
index 0000000..8fdf4f0
--- /dev/null
+++ b/releasenotes/notes/add-save-state-option-5ea67858cbaca969.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Add --save-state option to allow saving state of cloud before tempest run.
diff --git a/releasenotes/notes/add-server-diagnostics-validation-schema-b5a3c55b45aa718a.yaml b/releasenotes/notes/add-server-diagnostics-validation-schema-b5a3c55b45aa718a.yaml
new file mode 100644
index 0000000..e0ac87c
--- /dev/null
+++ b/releasenotes/notes/add-server-diagnostics-validation-schema-b5a3c55b45aa718a.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Add validation schema for Nova server diagnostics API
\ No newline at end of file
diff --git a/releasenotes/notes/add-show-host-to-hosts-client-library-c60c4eb49d139480.yaml b/releasenotes/notes/add-show-host-to-hosts-client-library-c60c4eb49d139480.yaml
new file mode 100644
index 0000000..0de1803
--- /dev/null
+++ b/releasenotes/notes/add-show-host-to-hosts-client-library-c60c4eb49d139480.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Add show host API to the volume v2 hosts_client library.
+    This feature enables the possibility to show details for a host.
diff --git a/releasenotes/notes/add-show-snapshot-metadata-item-api-to-v2-snapshots-client-bd3cbab3c7f0e0b3.yaml b/releasenotes/notes/add-show-snapshot-metadata-item-api-to-v2-snapshots-client-bd3cbab3c7f0e0b3.yaml
new file mode 100644
index 0000000..140df60
--- /dev/null
+++ b/releasenotes/notes/add-show-snapshot-metadata-item-api-to-v2-snapshots-client-bd3cbab3c7f0e0b3.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add show snapshot metadata item API to v2 snapshots_client library.
+    This feature enables the possibility to show a snapshot's metadata for
+    a specific key.
diff --git a/releasenotes/notes/add-show-volume-image-metadata-api-to-v2-volumes-client-ee3c027f35276561.yaml b/releasenotes/notes/add-show-volume-image-metadata-api-to-v2-volumes-client-ee3c027f35276561.yaml
new file mode 100644
index 0000000..ac7c74e
--- /dev/null
+++ b/releasenotes/notes/add-show-volume-image-metadata-api-to-v2-volumes-client-ee3c027f35276561.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Add show volume image metadata API to v2 volumes_client library.
+    This feature enables the possibility to show volume's image metadata.
diff --git a/releasenotes/notes/add-show-volume-metadata-item-api-to-v2-volumes-client-47d59ecd999ca9df.yaml b/releasenotes/notes/add-show-volume-metadata-item-api-to-v2-volumes-client-47d59ecd999ca9df.yaml
new file mode 100644
index 0000000..49a935c
--- /dev/null
+++ b/releasenotes/notes/add-show-volume-metadata-item-api-to-v2-volumes-client-47d59ecd999ca9df.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add show volume metadata item API to v2 volumes_client library.
+    This feature enables the possibility to show a volume's metadata for
+    a specific key.
diff --git a/releasenotes/notes/add-show-volume-summary-api-to-v3-volumes-client-96e7b01abdb5c9c3.yaml b/releasenotes/notes/add-show-volume-summary-api-to-v3-volumes-client-96e7b01abdb5c9c3.yaml
new file mode 100644
index 0000000..361e387
--- /dev/null
+++ b/releasenotes/notes/add-show-volume-summary-api-to-v3-volumes-client-96e7b01abdb5c9c3.yaml
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    Define v3 volumes_client for the volume service as a library interface,
+    allowing other projects to use this module as a stable library without
+    maintenance changes.
+    Add show volume summary API to v3 volumes_client library, min_microversion
+    of this API is 3.12.
+
+    * volumes_client(v3)
diff --git a/releasenotes/notes/add-update-backup-api-to-v3-backups-client-e8465b2b66617dc0.yaml b/releasenotes/notes/add-update-backup-api-to-v3-backups-client-e8465b2b66617dc0.yaml
new file mode 100644
index 0000000..7cd6887
--- /dev/null
+++ b/releasenotes/notes/add-update-backup-api-to-v3-backups-client-e8465b2b66617dc0.yaml
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    Define v3 backups_client for the volume service as a library interface,
+    allowing other projects to use this module as a stable library without
+    maintenance changes.
+    Add update backup API to v3 backups_client library, min_microversion
+    of this API is 3.9.
+
+    * backups_client(v3)
diff --git a/releasenotes/notes/add-update-group-tempest-tests-72f8ec19b2809849.yaml b/releasenotes/notes/add-update-group-tempest-tests-72f8ec19b2809849.yaml
new file mode 100644
index 0000000..23c30af
--- /dev/null
+++ b/releasenotes/notes/add-update-group-tempest-tests-72f8ec19b2809849.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Add update_group to groups_client in the volume service library.
diff --git a/releasenotes/notes/add-volume-group-snapshots-tempest-tests-840df3da26590f5e.yaml b/releasenotes/notes/add-volume-group-snapshots-tempest-tests-840df3da26590f5e.yaml
new file mode 100644
index 0000000..2ca6e5a
--- /dev/null
+++ b/releasenotes/notes/add-volume-group-snapshots-tempest-tests-840df3da26590f5e.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add group_snapshots client for the volume service as library.
+    Add tempest tests for create group snapshot, delete group snapshot, show
+    group snapshot, and list group snapshots volume APIs.
diff --git a/releasenotes/notes/add-volume-group-types-tempest-tests-1298ab8cb4fe8b7b.yaml b/releasenotes/notes/add-volume-group-types-tempest-tests-1298ab8cb4fe8b7b.yaml
new file mode 100644
index 0000000..4fd3bee
--- /dev/null
+++ b/releasenotes/notes/add-volume-group-types-tempest-tests-1298ab8cb4fe8b7b.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Add list_group_type and show_group_type in the group_types client for
+    the volume service. Add tests for create/delete/show/list group types.
diff --git a/releasenotes/notes/add-volume-groups-tempest-tests-dd7b2abfe2b48427.yaml b/releasenotes/notes/add-volume-groups-tempest-tests-dd7b2abfe2b48427.yaml
new file mode 100644
index 0000000..898d366
--- /dev/null
+++ b/releasenotes/notes/add-volume-groups-tempest-tests-dd7b2abfe2b48427.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add groups and group_types clients for the volume service as library.
+    Add tempest tests for create group, delete group, show group, and
+    list group volume APIs.
diff --git a/releasenotes/notes/add-volume-quota-class-client-as-library-c4c2b22c36ff807e.yaml b/releasenotes/notes/add-volume-quota-class-client-as-library-c4c2b22c36ff807e.yaml
new file mode 100644
index 0000000..e6847eb
--- /dev/null
+++ b/releasenotes/notes/add-volume-quota-class-client-as-library-c4c2b22c36ff807e.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Define v2 quota_classes_client for the volume service as library
+    interfaces, allowing other projects to use this module as stable libraries
+    without maintenance changes.
+
+    * quota_classes_client(v2)
diff --git a/releasenotes/notes/api_v2_admin_flag-dea5ca9bc2ce63bc.yaml b/releasenotes/notes/api_v2_admin_flag-dea5ca9bc2ce63bc.yaml
new file mode 100644
index 0000000..0c33b69
--- /dev/null
+++ b/releasenotes/notes/api_v2_admin_flag-dea5ca9bc2ce63bc.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    A new configuration flag api_v2_admin is introduced in the identity
+    feature flag group to allow for enabling/disabling all identity v2
+    admin tests. The new flag only applies when the existing api_v2 flag
+    is set to True
diff --git a/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml b/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml
new file mode 100644
index 0000000..305e756
--- /dev/null
+++ b/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml
@@ -0,0 +1,15 @@
+---
+features:
+  - |
+    Add a new function called ``compare_version_header_to_response`` to
+    ``tempest.lib.common.api_version_utils``, which compares the API
+    micoversion in the response header to another microversion using the
+    comparators defined in
+    ``tempest.lib.common.api_version_request.APIVersionRequest``.
+
+    It is now possible to determine how to retrieve an attribute from a
+    response body of an API call, depending on the returned microversion.
+
+    Add a new exception type called ``InvalidParam`` to
+    ``tempest.lib.exceptions``, allowing the possibility of raising an
+    exception if an invalid parameter is passed to a library function.
diff --git a/releasenotes/notes/credentials-factory-stable-c8037bd9ae642482.yaml b/releasenotes/notes/credentials-factory-stable-c8037bd9ae642482.yaml
new file mode 100644
index 0000000..6faa536
--- /dev/null
+++ b/releasenotes/notes/credentials-factory-stable-c8037bd9ae642482.yaml
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    The credentials_factory.py module is now marked as stable for Tempest
+    plugins. It provides helpers that can be used by Tempest plugins to
+    obtain test credentials for their test cases in a format that honors the
+    Tempest configuration in use.
+    Credentials may be provisioned on the fly during the test run, or they
+    can be setup in advance and fed to test via a YAML file; they can be
+    setup for identity v2 or identity v3.
diff --git a/releasenotes/notes/deprecate-compute-images-client-in-volume-tests-92b6dd55fcaba620.yaml b/releasenotes/notes/deprecate-compute-images-client-in-volume-tests-92b6dd55fcaba620.yaml
new file mode 100644
index 0000000..1ae251c
--- /dev/null
+++ b/releasenotes/notes/deprecate-compute-images-client-in-volume-tests-92b6dd55fcaba620.yaml
@@ -0,0 +1,10 @@
+---
+deprecations:
+  - |
+    Image APIs in compute are deprecated, Image native APIs are recommended.
+    And Glance v1 APIs are deprecated and v2 APIs are current. Image client
+    compute_images_client and Glance v1 APIs are removed in volume tests.
+upgrade:
+  - |
+    Switch to use Glance v2 APIs in volume tests, by adding the Glance v2
+    client images_client.
diff --git a/releasenotes/notes/deprecate-config-forbid_global_implied_dsr-e64cfa66e6e3ded5.yaml b/releasenotes/notes/deprecate-config-forbid_global_implied_dsr-e64cfa66e6e3ded5.yaml
new file mode 100644
index 0000000..2b63402
--- /dev/null
+++ b/releasenotes/notes/deprecate-config-forbid_global_implied_dsr-e64cfa66e6e3ded5.yaml
@@ -0,0 +1,5 @@
+---
+deprecations:
+  - The config option ``forbid_global_implied_dsr`` from the ``IdentityFeature``
+    group is now deprecated. This feature flag was introduced to support
+    testing of old OpenStack versions which are not supported anymore.
diff --git a/releasenotes/notes/deprecate-default-value-for-v3_endpoint_type-fb9e47c5ba1c719d.yaml b/releasenotes/notes/deprecate-default-value-for-v3_endpoint_type-fb9e47c5ba1c719d.yaml
new file mode 100644
index 0000000..c88522e
--- /dev/null
+++ b/releasenotes/notes/deprecate-default-value-for-v3_endpoint_type-fb9e47c5ba1c719d.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+  - |
+    Deprecate default value for configuration parameter v3_endpoint_type
+    of identity section in OpenStack Pike and modify the default value to
+    publicURL in OpenStack Q release.
diff --git a/releasenotes/notes/extra-compute-services-tests-92b6c0618972e02f.yaml b/releasenotes/notes/extra-compute-services-tests-92b6c0618972e02f.yaml
new file mode 100644
index 0000000..414adf1
--- /dev/null
+++ b/releasenotes/notes/extra-compute-services-tests-92b6c0618972e02f.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add the ``disable_log_reason`` and the ``update_forced_down`` API endpoints
+    to the compute ``services_client``.
+    Add '2.11' compute validation schema for compute services API.
diff --git a/releasenotes/notes/fix-remoteclient-default-ssh-shell-prologue-33e99343d086f601.yaml b/releasenotes/notes/fix-remoteclient-default-ssh-shell-prologue-33e99343d086f601.yaml
new file mode 100644
index 0000000..5063fd5
--- /dev/null
+++ b/releasenotes/notes/fix-remoteclient-default-ssh-shell-prologue-33e99343d086f601.yaml
@@ -0,0 +1,7 @@
+---
+fixes:
+  - |
+    Fix RemoteClient default ssh_shell_prologue: Bug#1707478
+
+    The default ssh_shell_proloque has been modified from
+    specifying erroneous PATH=$$PATH:/sbin to PATH=$PATH:/sbin.
diff --git a/releasenotes/notes/identity-tests-domain-drivers-76235f6672221e45.yaml b/releasenotes/notes/identity-tests-domain-drivers-76235f6672221e45.yaml
new file mode 100644
index 0000000..7ed3081
--- /dev/null
+++ b/releasenotes/notes/identity-tests-domain-drivers-76235f6672221e45.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    A new boolean config option ``domain_specific_drivers``
+    is added to the section ``identity-feature-enabled``.
+    This option must be enabled when testing an environment that
+    is configured to use domain-specific identity drivers.
diff --git a/releasenotes/notes/identity-token-client-8aaef74b1d61090a.yaml b/releasenotes/notes/identity-token-client-8aaef74b1d61090a.yaml
new file mode 100644
index 0000000..d94de3e
--- /dev/null
+++ b/releasenotes/notes/identity-token-client-8aaef74b1d61090a.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add additional API endpoints to the identity v2 client token API:
+    -  list_endpoints_for_token
+    -  check_token_existence
diff --git a/releasenotes/notes/identity_client-635275d43abbb807.yaml b/releasenotes/notes/identity_client-635275d43abbb807.yaml
new file mode 100644
index 0000000..6f984b7
--- /dev/null
+++ b/releasenotes/notes/identity_client-635275d43abbb807.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Enhances the v3 identity client with the ``check_token_existence``
+    endpoint, allowing users to check the existence of tokens
diff --git a/releasenotes/notes/intermediate-pike-release-2ce492432ff8f012.yaml b/releasenotes/notes/intermediate-pike-release-2ce492432ff8f012.yaml
new file mode 100644
index 0000000..bfebcd9
--- /dev/null
+++ b/releasenotes/notes/intermediate-pike-release-2ce492432ff8f012.yaml
@@ -0,0 +1,4 @@
+---
+prelude: >
+    This is an intermediate release during the Pike development cycle to
+    make new functionality available to plugins and other consumers.
diff --git a/releasenotes/notes/migrate-dynamic-creds-ecebb47528080761.yaml b/releasenotes/notes/migrate-dynamic-creds-ecebb47528080761.yaml
new file mode 100644
index 0000000..c20cbc6
--- /dev/null
+++ b/releasenotes/notes/migrate-dynamic-creds-ecebb47528080761.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    The tempest module tempest.common.dynamic creds which is used for
+    dynamically allocating credentials has been migrated into tempest lib.
diff --git a/releasenotes/notes/migrate-object-storage-as-stable-interface-42014c7b43ecb254.yaml b/releasenotes/notes/migrate-object-storage-as-stable-interface-42014c7b43ecb254.yaml
new file mode 100644
index 0000000..72b8e26
--- /dev/null
+++ b/releasenotes/notes/migrate-object-storage-as-stable-interface-42014c7b43ecb254.yaml
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    Define below object storage service clients as libraries.
+    Add new service clients to the library interface so the
+    other projects can use these modules as stable libraries
+    without any maintenance changes.
+
+      * bulk_middleware_client
+      * capabilities_client
diff --git a/releasenotes/notes/migrate-preprov-creds-ef61a046ee1ec604.yaml b/releasenotes/notes/migrate-preprov-creds-ef61a046ee1ec604.yaml
new file mode 100644
index 0000000..aa5f71a
--- /dev/null
+++ b/releasenotes/notes/migrate-preprov-creds-ef61a046ee1ec604.yaml
@@ -0,0 +1,11 @@
+---
+features:
+  - The tempest module tempest.common.preprov_creds which is used to provide
+    credentials from a list of preprovisioned resources has been migrated into
+    tempest lib at tempest.lib.common.preprov_creds.
+  - The InvalidTestResource exception class from tempest.exceptions has been
+    migrated into tempest.lib.exceptions
+  - The tempest module tempest.common.fixed_network which provided utilities for
+    finding fixed networks by and helpers for picking the network to use when
+    multiple tenant networks are available has been migrated into tempest lib
+    at tempest.lib.common.fixed_network.
diff --git a/releasenotes/notes/move-attr-decorator-to-lib-a1e80c42ba9c5392.yaml b/releasenotes/notes/move-attr-decorator-to-lib-a1e80c42ba9c5392.yaml
new file mode 100644
index 0000000..58d45cc
--- /dev/null
+++ b/releasenotes/notes/move-attr-decorator-to-lib-a1e80c42ba9c5392.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    A new ``attr`` decorator has been added in the ``tempest.lib.decorators``
+    module. For example, use it to tag specific tests, which could be leveraged
+    by test runners to run only a subset of Tempest tests.
diff --git a/releasenotes/notes/move-related_bug-decorator-to-lib-dbfd5c543bbb2805.yaml b/releasenotes/notes/move-related_bug-decorator-to-lib-dbfd5c543bbb2805.yaml
new file mode 100644
index 0000000..8c420c8
--- /dev/null
+++ b/releasenotes/notes/move-related_bug-decorator-to-lib-dbfd5c543bbb2805.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    A new ``related_bug`` decorator has been added to
+    ``tempest.lib.decorators``. Use it to decorate and tag a test that was
+    added in relation to a launchpad bug report.
diff --git a/releasenotes/notes/move-volume-v3-base_client-to-volume-1edbz0f207c3b283.yaml b/releasenotes/notes/move-volume-v3-base_client-to-volume-1edbz0f207c3b283.yaml
new file mode 100644
index 0000000..ec81dc5
--- /dev/null
+++ b/releasenotes/notes/move-volume-v3-base_client-to-volume-1edbz0f207c3b283.yaml
@@ -0,0 +1,15 @@
+features:
+  - |
+    Move base_client from tempest.lib.services.volume.v3 to
+    tempest.lib.services.volume, so if we want to add new
+    interfaces based on a v2 client, we can make that v2
+    client inherit from volume.base_client.BaseClient to
+    get microversion support, and then to make the new v3
+    client inherit from the v2 client, thus to avoid the
+    multiple inheritance.
+deprecations:
+  - |
+    Deprecate class BaseClient from volume.v3.base_client
+    and move it to volume.base_client.
+    ``tempest.lib.services.volume.v3.base_client.BaseClient``
+    (new ``tempest.lib.services.volume.base_client.BaseClient``)
diff --git a/releasenotes/notes/network-tag-client-f4614029af7927f0.yaml b/releasenotes/notes/network-tag-client-f4614029af7927f0.yaml
new file mode 100644
index 0000000..9af57b1
--- /dev/null
+++ b/releasenotes/notes/network-tag-client-f4614029af7927f0.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Define v2.0 ``tags_client`` for the network service as a library
+    interface, allowing other projects to use this module as a stable
+    library without maintenance changes.
+
+    * tags_client(v2.0)
diff --git a/releasenotes/notes/pause_teardown-45c9d60ffa889f7f.yaml b/releasenotes/notes/pause_teardown-45c9d60ffa889f7f.yaml
new file mode 100644
index 0000000..a540c7d
--- /dev/null
+++ b/releasenotes/notes/pause_teardown-45c9d60ffa889f7f.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    Pause teardown
+    When pause_teardown flag in tempest.conf is set to True a pdb breakpoint
+    is added to tearDown and tearDownClass methods in test.py.
+    This allows to pause cleaning resources process, so that used resources
+    can be examined. Closer examination of used resources may lead to faster
+    debugging.
diff --git a/releasenotes/notes/plugin-client-registration-enhancements-e09131742391225b.yaml b/releasenotes/notes/plugin-client-registration-enhancements-e09131742391225b.yaml
new file mode 100644
index 0000000..b6391b6
--- /dev/null
+++ b/releasenotes/notes/plugin-client-registration-enhancements-e09131742391225b.yaml
@@ -0,0 +1,12 @@
+---
+features:
+  - |
+    When registering service clients from installed plugins, all registrations
+    are now processed, even if one or more fails. All exceptions encountered
+    during the registration process are recorded.  If at least one exception
+    was encountered, the registration process fails and all interim errors are
+    reported.
+  - |
+    The __repr__ method is now implemented for the base `tempest.Exception`
+    class, its implementation is identical to __str__: it reports the error
+    message merged with input parameters.
diff --git a/releasenotes/notes/prevent-error-in-parse-resp-when-nullable-list-9898cd0f22180986.yaml b/releasenotes/notes/prevent-error-in-parse-resp-when-nullable-list-9898cd0f22180986.yaml
new file mode 100644
index 0000000..afb6006
--- /dev/null
+++ b/releasenotes/notes/prevent-error-in-parse-resp-when-nullable-list-9898cd0f22180986.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+  - |
+    When receiving nullable list as a response body, tempest.lib
+    rest_client module raised an exception without valid json
+    deserialization. A new release fixes this bug.
diff --git a/releasenotes/notes/raise-exception-when-error-deleting-on-volume-18d0d0c5886212dd.yaml b/releasenotes/notes/raise-exception-when-error-deleting-on-volume-18d0d0c5886212dd.yaml
new file mode 100644
index 0000000..194dbc1
--- /dev/null
+++ b/releasenotes/notes/raise-exception-when-error-deleting-on-volume-18d0d0c5886212dd.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+  - |
+    Tempest checks a volume delete by waiting for NotFound(404) on
+    show_volume(). Sometime a volume delete fails and the volume status
+    becomes error_deleting which means the delete is failed.
+    So Tempest doesn't need to wait anymore. A new release of Tempest
+    raises an exception DeleteErrorException instead of waiting.
diff --git a/releasenotes/notes/remove-heat-tests-9efb42cac3e0b306.yaml b/releasenotes/notes/remove-heat-tests-9efb42cac3e0b306.yaml
new file mode 100644
index 0000000..4d0f3ce
--- /dev/null
+++ b/releasenotes/notes/remove-heat-tests-9efb42cac3e0b306.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+  - The Heat API tests have been removed from tempest, they were unmaintained.
+    The future direction of api test for heat is their in-tree Gabbi tests
diff --git a/releasenotes/notes/remove-support-of-py34-7d59fdb431fefe24.yaml b/releasenotes/notes/remove-support-of-py34-7d59fdb431fefe24.yaml
new file mode 100644
index 0000000..093228a
--- /dev/null
+++ b/releasenotes/notes/remove-support-of-py34-7d59fdb431fefe24.yaml
@@ -0,0 +1,5 @@
+---
+deprecations:
+  - |
+    Remove the support of python3.4, because in Ubuntu Xenial only
+    python3.5 is available (python3.4 is restricted to <= Mitaka).
diff --git a/releasenotes/notes/set-cinder-api-v3-option-true-1b3e61e3129b7c00.yaml b/releasenotes/notes/set-cinder-api-v3-option-true-1b3e61e3129b7c00.yaml
new file mode 100644
index 0000000..6959ca7
--- /dev/null
+++ b/releasenotes/notes/set-cinder-api-v3-option-true-1b3e61e3129b7c00.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+  - |
+    The volume config option 'api_v3' default is changed to
+    ``True`` because the volume v3 API is CURRENT.
diff --git a/releasenotes/notes/start-of-pike-support-f2a1b7ea8e8b0311.yaml b/releasenotes/notes/start-of-pike-support-f2a1b7ea8e8b0311.yaml
new file mode 100644
index 0000000..0787821
--- /dev/null
+++ b/releasenotes/notes/start-of-pike-support-f2a1b7ea8e8b0311.yaml
@@ -0,0 +1,11 @@
+---
+prelude: >
+    This release marks the start of support for the Pike release in Tempest.
+other:
+    - OpenStack Releases supported after this release are **Pike**, **Ocata**,
+      and **Newton**.
+
+      The release under current development of this tag is Queens, meaning
+      that every Tempest commit is also tested against master during the Queens
+      cycle. However, this does not necessarily mean that using Tempest as of
+      this tag will work against a Queens (or future release) cloud.
diff --git a/releasenotes/notes/tempest-identity-catalog-client-f5c8589a9d7c1eb5.yaml b/releasenotes/notes/tempest-identity-catalog-client-f5c8589a9d7c1eb5.yaml
new file mode 100644
index 0000000..dcaaceb
--- /dev/null
+++ b/releasenotes/notes/tempest-identity-catalog-client-f5c8589a9d7c1eb5.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - Add a new identity catalog client. At this point, the new client
+    contains a single functionality, "show_catalog", which returns a
+    catalog object.
diff --git a/releasenotes/notes/tempest-workspace-delete-directory-feature-74d6d157a5a05561.yaml b/releasenotes/notes/tempest-workspace-delete-directory-feature-74d6d157a5a05561.yaml
new file mode 100644
index 0000000..ec21098
--- /dev/null
+++ b/releasenotes/notes/tempest-workspace-delete-directory-feature-74d6d157a5a05561.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Added tempest workspace remove --name <workspace_name> --rmdir
+    feature to delete the workspace directory as well as entry.
diff --git a/releasenotes/notes/use-cinder-v3-client-for-verify_tempest_config-2bf3d817b0070064.yaml b/releasenotes/notes/use-cinder-v3-client-for-verify_tempest_config-2bf3d817b0070064.yaml
new file mode 100644
index 0000000..1c8fa77
--- /dev/null
+++ b/releasenotes/notes/use-cinder-v3-client-for-verify_tempest_config-2bf3d817b0070064.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - verify_tempest_config command starts using extension_client of
+    cinder v2 API only, because cinder v3 API is current and v2 and
+    v1 are deprecated and v3 extension API is the same as v2. Then
+    we can reuse the v2 client for v3 API also.
diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py
index eec42cd..ae3dca1 100644
--- a/releasenotes/source/conf.py
+++ b/releasenotes/source/conf.py
@@ -11,7 +11,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# tempest Release Notes documentation build configuration file, created by
+# Tempest Release Notes documentation build configuration file, created by
 # sphinx-quickstart on Tue Nov  3 17:40:50 2015.
 #
 # This file is execfile()d with the current directory set to its
@@ -37,10 +37,18 @@
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
 extensions = [
-    'oslosphinx',
+    'openstackdocstheme',
     'reno.sphinxext',
 ]
 
+# openstackdocstheme options
+repository_name = 'openstack/tempest'
+bug_project = 'tempest'
+bug_tag = ''
+
+# Must set this variable to include year, month, day, hours, and minutes.
+html_last_updated_fmt = '%Y-%m-%d %H:%M'
+
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 
@@ -111,7 +119,7 @@
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'default'
+html_theme = 'openstackdocs'
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
@@ -119,7 +127,6 @@
 # html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-# html_theme_path = []
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
@@ -151,10 +158,6 @@
 # using the given strftime format.
 # html_last_updated_fmt = '%b %d, %Y'
 
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-# html_use_smartypants = True
-
 # Custom sidebar templates, maps document names to template names.
 # html_sidebars = {}
 
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 8eac1d0..df1de46 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -1,11 +1,16 @@
 ===========================
- tempest Release Notes
+ Tempest Release Notes
 ===========================
 
  .. toctree::
     :maxdepth: 1
 
     unreleased
+    v17.0.0
+    v16.1.0
+    v16.0.0
+    v15.0.0
+    v14.0.0
     v13.0.0
     v12.0.0
     v11.0.0
diff --git a/releasenotes/source/v14.0.0.rst b/releasenotes/source/v14.0.0.rst
new file mode 100644
index 0000000..440c85b
--- /dev/null
+++ b/releasenotes/source/v14.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v14.0.0 Release Notes
+=====================
+
+.. release-notes:: 14.0.0 Release Notes
+   :version: 14.0.0
diff --git a/releasenotes/source/v15.0.0.rst b/releasenotes/source/v15.0.0.rst
new file mode 100644
index 0000000..2ee1894
--- /dev/null
+++ b/releasenotes/source/v15.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v15.0.0 Release Notes
+=====================
+
+.. release-notes:: 15.0.0 Release Notes
+   :version: 15.0.0
diff --git a/releasenotes/source/v16.0.0.rst b/releasenotes/source/v16.0.0.rst
new file mode 100644
index 0000000..ae29599
--- /dev/null
+++ b/releasenotes/source/v16.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v16.0.0 Release Notes
+=====================
+
+.. release-notes:: 16.0.0 Release Notes
+   :version: 16.0.0
\ No newline at end of file
diff --git a/releasenotes/source/v16.1.0.rst b/releasenotes/source/v16.1.0.rst
new file mode 100644
index 0000000..e24a70f
--- /dev/null
+++ b/releasenotes/source/v16.1.0.rst
@@ -0,0 +1,6 @@
+=====================
+v16.1.0 Release Notes
+=====================
+
+.. release-notes:: 16.1.0 Release Notes
+   :version: 16.1.0
diff --git a/releasenotes/source/v17.0.0.rst b/releasenotes/source/v17.0.0.rst
new file mode 100644
index 0000000..3f50f11
--- /dev/null
+++ b/releasenotes/source/v17.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v17.0.0 Release Notes
+=====================
+
+.. release-notes:: 17.0.0 Release Notes
+   :version: 17.0.0
diff --git a/requirements.txt b/requirements.txt
index d9a9ebb..36b9efa 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,25 +1,25 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-pbr>=1.8 # Apache-2.0
-cliff>=2.3.0 # Apache-2.0
+pbr!=2.1.0,>=2.0.0 # Apache-2.0
+cliff>=2.8.0 # Apache-2.0
 jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
 testtools>=1.4.0 # MIT
-paramiko>=2.0 # LGPLv2.1+
+paramiko>=2.0.0 # LGPLv2.1+
 netaddr!=0.7.16,>=0.7.13 # BSD
 testrepository>=0.0.18 # Apache-2.0/BSD
 oslo.concurrency>=3.8.0 # Apache-2.0
-oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
-oslo.log>=3.11.0 # Apache-2.0
-oslo.serialization>=1.10.0 # Apache-2.0
-oslo.utils>=3.18.0 # Apache-2.0
+oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
+oslo.log>=3.30.0 # Apache-2.0
+oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0
+oslo.utils>=3.20.0 # Apache-2.0
 six>=1.9.0 # MIT
 fixtures>=3.0.0 # Apache-2.0/BSD
-PyYAML>=3.10.0 # MIT
+PyYAML>=3.10 # MIT
 python-subunit>=0.0.18 # Apache-2.0/BSD
-stevedore>=1.17.1 # Apache-2.0
+stevedore>=1.20.0 # Apache-2.0
 PrettyTable<0.8,>=0.7.1 # BSD
 os-testr>=0.8.0 # Apache-2.0
-urllib3>=1.15.1 # MIT
+urllib3>=1.21.1 # MIT
 debtcollector>=1.2.0 # Apache-2.0
 unittest2 # BSD
diff --git a/run_tempest.sh b/run_tempest.sh
deleted file mode 100755
index 414146b..0000000
--- a/run_tempest.sh
+++ /dev/null
@@ -1,135 +0,0 @@
-#!/usr/bin/env bash
-
-echo "WARNING: This script is deprecated and will be removed in the near future. Please migrate to tempest run or another method of launching a test runner"
-
-function usage {
-  echo "Usage: $0 [OPTION]..."
-  echo "Run Tempest test suite"
-  echo ""
-  echo "  -V, --virtual-env        Always use virtualenv.  Install automatically if not present"
-  echo "  -N, --no-virtual-env     Don't use virtualenv.  Run tests in local environment"
-  echo "  -n, --no-site-packages   Isolate the virtualenv from the global Python environment"
-  echo "  -f, --force              Force a clean re-build of the virtual environment. Useful when dependencies have been added."
-  echo "  -u, --update             Update the virtual environment with any newer package versions"
-  echo "  -s, --smoke              Only run smoke tests"
-  echo "  -t, --serial             Run testr serially"
-  echo "  -C, --config             Config file location"
-  echo "  -h, --help               Print this usage message"
-  echo "  -d, --debug              Run tests with testtools instead of testr. This allows you to use PDB"
-  echo "  -- [TESTROPTIONS]        After the first '--' you can pass arbitrary arguments to testr "
-}
-
-testrargs=""
-venv=${VENV:-.venv}
-with_venv=tools/with_venv.sh
-serial=0
-always_venv=0
-never_venv=0
-no_site_packages=0
-debug=0
-force=0
-wrapper=""
-config_file=""
-update=0
-
-if ! options=$(getopt -o VNnfusthdC:lL: -l virtual-env,no-virtual-env,no-site-packages,force,update,smoke,serial,help,debug,config: -- "$@")
-then
-    # parse error
-    usage
-    exit 1
-fi
-
-eval set -- $options
-first_uu=yes
-while [ $# -gt 0 ]; do
-  case "$1" in
-    -h|--help) usage; exit;;
-    -V|--virtual-env) always_venv=1; never_venv=0;;
-    -N|--no-virtual-env) always_venv=0; never_venv=1;;
-    -n|--no-site-packages) no_site_packages=1;;
-    -f|--force) force=1;;
-    -u|--update) update=1;;
-    -d|--debug) debug=1;;
-    -C|--config) config_file=$2; shift;;
-    -s|--smoke) testrargs+="smoke";;
-    -t|--serial) serial=1;;
-    --) [ "yes" == "$first_uu" ] || testrargs="$testrargs $1"; first_uu=no  ;;
-    *) testrargs="$testrargs $1";;
-  esac
-  shift
-done
-
-if [ -n "$config_file" ]; then
-    config_file=`readlink -f "$config_file"`
-    export TEMPEST_CONFIG_DIR=`dirname "$config_file"`
-    export TEMPEST_CONFIG=`basename "$config_file"`
-fi
-
-cd `dirname "$0"`
-
-if [ $no_site_packages -eq 1 ]; then
-  installvenvopts="--no-site-packages"
-fi
-
-function testr_init {
-  if [ ! -d .testrepository ]; then
-      ${wrapper} testr init
-  fi
-}
-
-function run_tests {
-  testr_init
-  ${wrapper} find . -type f -name "*.pyc" -delete
-  export OS_TEST_PATH=./tempest/test_discover
-  if [ $debug -eq 1 ]; then
-      if [ "$testrargs" = "" ]; then
-           testrargs="discover ./tempest/test_discover"
-      fi
-      ${wrapper} python -m testtools.run $testrargs
-      return $?
-  fi
-
-  if [ $serial -eq 1 ]; then
-      ${wrapper} testr run --subunit $testrargs | ${wrapper} subunit-trace -n -f
-  else
-      ${wrapper} testr run --parallel --subunit $testrargs | ${wrapper} subunit-trace -n -f
-  fi
-}
-
-if [ $never_venv -eq 0 ]
-then
-  # Remove the virtual environment if --force used
-  if [ $force -eq 1 ]; then
-    echo "Cleaning virtualenv..."
-    rm -rf ${venv}
-  fi
-  if [ $update -eq 1 ]; then
-      echo "Updating virtualenv..."
-      virtualenv $installvenvopts $venv
-      $venv/bin/pip install -U -r requirements.txt
-  fi
-  if [ -e ${venv} ]; then
-    wrapper="${with_venv}"
-  else
-    if [ $always_venv -eq 1 ]; then
-      # Automatically install the virtualenv
-      virtualenv $installvenvopts $venv
-      wrapper="${with_venv}"
-      ${wrapper} pip install -U -r requirements.txt
-    else
-      echo -e "No virtual environment found...create one? (Y/n) \c"
-      read use_ve
-      if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
-        # Install the virtualenv and run the test suite in it
-        virtualenv $installvenvopts $venv
-        wrapper=${with_venv}
-        ${wrapper} pip install -U -r requirements.txt
-      fi
-    fi
-  fi
-fi
-
-run_tests
-retval=$?
-
-exit $retval
diff --git a/run_tests.sh b/run_tests.sh
deleted file mode 100755
index a856bb4..0000000
--- a/run_tests.sh
+++ /dev/null
@@ -1,193 +0,0 @@
-#!/usr/bin/env bash
-
-function usage {
-  echo "Usage: $0 [OPTION]..."
-  echo "Run Tempest unit tests"
-  echo ""
-  echo "  -V, --virtual-env        Always use virtualenv.  Install automatically if not present"
-  echo "  -N, --no-virtual-env     Don't use virtualenv.  Run tests in local environment"
-  echo "  -n, --no-site-packages   Isolate the virtualenv from the global Python environment"
-  echo "  -f, --force              Force a clean re-build of the virtual environment. Useful when dependencies have been added."
-  echo "  -u, --update             Update the virtual environment with any newer package versions"
-  echo "  -t, --serial             Run testr serially"
-  echo "  -p, --pep8               Just run pep8"
-  echo "  -c, --coverage           Generate coverage report"
-  echo "  -h, --help               Print this usage message"
-  echo "  -d, --debug              Run tests with testtools instead of testr. This allows you to use PDB"
-  echo "  -- [TESTROPTIONS]        After the first '--' you can pass arbitrary arguments to testr "
-}
-
-function deprecation_warning {
-  cat <<EOF
--------------------------------------------------------------------------
-WARNING: run_tests.sh is deprecated and this script will be removed after
-the Newton release. All tests should be run through testr/ostestr or tox.
-
-To run style checks:
-
- tox -e pep8
-
-To run python 2.7 unit tests
-
- tox -e py27
-
-To run unit tests and generate coverage report
-
- tox -e cover
-
-To run a subset of any of these tests:
-
- tox -e py27 someregex
-
- i.e.: tox -e py27 test_servers
-
-Additional tox targets are available in tox.ini. For more information
-see:
-http://docs.openstack.org/project-team-guide/project-setup/python.html
-
-NOTE: if you want to use testr to run tests, you can instead use:
-
- OS_TEST_PATH=./tempest/tests testr run
-
-Documentation on using testr directly can be found at
-http://testrepository.readthedocs.org/en/latest/MANUAL.html
--------------------------------------------------------------------------
-EOF
-}
-
-testrargs=""
-just_pep8=0
-venv=${VENV:-.venv}
-with_venv=tools/with_venv.sh
-serial=0
-always_venv=0
-never_venv=0
-no_site_packages=0
-debug=0
-force=0
-coverage=0
-wrapper=""
-config_file=""
-update=0
-
-deprecation_warning
-
-if ! options=$(getopt -o VNnfuctphd -l virtual-env,no-virtual-env,no-site-packages,force,update,serial,coverage,pep8,help,debug -- "$@")
-then
-    # parse error
-    usage
-    exit 1
-fi
-
-eval set -- $options
-first_uu=yes
-while [ $# -gt 0 ]; do
-  case "$1" in
-    -h|--help) usage; exit;;
-    -V|--virtual-env) always_venv=1; never_venv=0;;
-    -N|--no-virtual-env) always_venv=0; never_venv=1;;
-    -n|--no-site-packages) no_site_packages=1;;
-    -f|--force) force=1;;
-    -u|--update) update=1;;
-    -d|--debug) debug=1;;
-    -p|--pep8) let just_pep8=1;;
-    -c|--coverage) coverage=1;;
-    -t|--serial) serial=1;;
-    --) [ "yes" == "$first_uu" ] || testrargs="$testrargs $1"; first_uu=no  ;;
-    *) testrargs="$testrargs $1";;
-  esac
-  shift
-done
-
-
-cd `dirname "$0"`
-
-if [ $no_site_packages -eq 1 ]; then
-  installvenvopts="--no-site-packages"
-fi
-
-function testr_init {
-  if [ ! -d .testrepository ]; then
-      ${wrapper} testr init
-  fi
-}
-
-function run_tests {
-  testr_init
-  ${wrapper} find . -type f -name "*.pyc" -delete
-  export OS_TEST_PATH=./tempest/tests
-  if [ $debug -eq 1 ]; then
-      if [ "$testrargs" = "" ]; then
-          testrargs="discover ./tempest/tests"
-      fi
-      ${wrapper} python -m testtools.run $testrargs
-      return $?
-  fi
-
-  if [ $coverage -eq 1 ]; then
-      ${wrapper} python setup.py test --coverage
-      return $?
-  fi
-
-  if [ $serial -eq 1 ]; then
-      ${wrapper} testr run --subunit $testrargs | ${wrapper} subunit-trace -n -f
-  else
-      ${wrapper} testr run --parallel --subunit $testrargs | ${wrapper} subunit-trace -n -f
-  fi
-}
-
-function run_pep8 {
-  echo "Running flake8 ..."
-  if [ $never_venv -eq 1 ]; then
-      echo "**WARNING**:" >&2
-      echo "Running flake8 without virtual env may miss OpenStack HACKING detection" >&2
-  fi
-  ${wrapper} flake8
-}
-
-if [ $never_venv -eq 0 ]
-then
-  # Remove the virtual environment if --force used
-  if [ $force -eq 1 ]; then
-    echo "Cleaning virtualenv..."
-    rm -rf ${venv}
-  fi
-  if [ $update -eq 1 ]; then
-      echo "Updating virtualenv..."
-      virtualenv $installvenvopts $venv
-      $venv/bin/pip install -U -r requirements.txt -r test-requirements.txt
-  fi
-  if [ -e ${venv} ]; then
-    wrapper="${with_venv}"
-  else
-    if [ $always_venv -eq 1 ]; then
-      # Automatically install the virtualenv
-      virtualenv $installvenvopts $venv
-      wrapper="${with_venv}"
-      ${wrapper} pip install -U -r requirements.txt -r test-requirements.txt
-    else
-      echo -e "No virtual environment found...create one? (Y/n) \c"
-      read use_ve
-      if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
-        # Install the virtualenv and run the test suite in it
-        virtualenv $installvenvopts $venv
-        wrapper=${with_venv}
-        ${wrapper} pip install -U -r requirements.txt -r test-requirements.txt
-      fi
-    fi
-  fi
-fi
-
-if [ $just_pep8 -eq 1 ]; then
-    run_pep8
-    exit
-fi
-
-run_tests
-retval=$?
-
-if [ -z "$testrargs" ]; then
-    run_pep8
-fi
-
-exit $retval
diff --git a/setup.cfg b/setup.cfg
index 96313fd..04bb29f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@
     README.rst
 author = OpenStack
 author-email = openstack-dev@lists.openstack.org
-home-page = http://docs.openstack.org/developer/tempest/
+home-page = https://docs.openstack.org/tempest/latest/
 classifier =
     Intended Audience :: Information Technology
     Intended Audience :: System Administrators
@@ -16,7 +16,6 @@
     Programming Language :: Python :: 2
     Programming Language :: Python :: 2.7
     Programming Language :: Python :: 3
-    Programming Language :: Python :: 3.4
     Programming Language :: Python :: 3.5
 
 [files]
@@ -39,18 +38,20 @@
     cleanup = tempest.cmd.cleanup:TempestCleanup
     list-plugins = tempest.cmd.list_plugins:TempestListPlugins
     verify-config = tempest.cmd.verify_tempest_config:TempestVerifyConfig
-    workspace = tempest.cmd.workspace:TempestWorkspace
+    workspace_register = tempest.cmd.workspace:TempestWorkspaceRegister
+    workspace_rename = tempest.cmd.workspace:TempestWorkspaceRename
+    workspace_move = tempest.cmd.workspace:TempestWorkspaceMove
+    workspace_remove = tempest.cmd.workspace:TempestWorkspaceRemove
+    workspace_list = tempest.cmd.workspace:TempestWorkspaceList
     run = tempest.cmd.run:TempestRun
 oslo.config.opts =
     tempest.config = tempest.config:list_opts
 
 [build_sphinx]
-all_files = 1
+all-files = 1
+warning-is-error = 1
 build-dir = doc/build
 source-dir = doc/source
 
-[pbr]
-warnerrors = True
-
 [wheel]
 universal = 1
diff --git a/setup.py b/setup.py
index 782bb21..566d844 100644
--- a/setup.py
+++ b/setup.py
@@ -25,5 +25,5 @@
     pass
 
 setuptools.setup(
-    setup_requires=['pbr>=1.8'],
+    setup_requires=['pbr>=2.0.0'],
     pbr=True)
diff --git a/tempest/README.rst b/tempest/README.rst
index 0feec41..663653e 100644
--- a/tempest/README.rst
+++ b/tempest/README.rst
@@ -9,12 +9,13 @@
 OpenStack clouds.
 
 As such Tempest tests come in many flavors, each with their own rules
-and guidelines. Below is the proposed Havana restructuring for Tempest
+and guidelines. Below is the overview of the Tempest respository structure
 to make this clear.
 
 | tempest/
 |    api/ - API tests
 |    scenario/ - complex scenario tests
+|    tests/ - unit tests for Tempest internals
 
 Each of these directories contains different types of tests. What
 belongs in each directory, the rules and examples for good tests, are
@@ -24,8 +25,8 @@
 ----------------------
 
 API tests are validation tests for the OpenStack API. They should not
-use the existing python clients for OpenStack, but should instead use
-the tempest implementations of clients. Having raw clients let us
+use the existing Python clients for OpenStack, but should instead use
+the Tempest implementations of clients. Having raw clients let us
 pass invalid JSON to the APIs and see the results, something we could
 not get with the native clients.
 
@@ -41,14 +42,14 @@
 functionality. They are typically a series of steps where complicated
 state requiring multiple services is set up exercised, and torn down.
 
-Scenario tests should not use the existing python clients for OpenStack,
-but should instead use the tempest implementations of clients.
+Scenario tests should not use the existing Python clients for OpenStack,
+but should instead use the Tempest implementations of clients.
 
 
 :ref:`unit_tests_field_guide`
 -----------------------------
 
 Unit tests are the self checks for Tempest. They provide functional
-verification and regression checking for the internal components of tempest.
-They should be used to just verify that the individual pieces of tempest are
+verification and regression checking for the internal components of Tempest.
+They should be used to just verify that the individual pieces of Tempest are
 working as expected.
diff --git a/tempest/api/README.rst b/tempest/api/README.rst
index 91e6ad6..a796922 100644
--- a/tempest/api/README.rst
+++ b/tempest/api/README.rst
@@ -13,7 +13,8 @@
 
 It's also important to test not only the expected positive path on
 APIs, but also to provide them with invalid data to ensure they fail
-in expected and documented ways. Over the course of the OpenStack
+in expected and documented ways. The latter type of tests is called
+``negative tests`` in Tempest source code. Over the course of the OpenStack
 project Tempest has discovered many fundamental bugs by doing just
 this.
 
@@ -22,7 +23,7 @@
 spinning up a server, image, etc, then operating on it.
 
 
-Why are these tests in tempest?
+Why are these tests in Tempest?
 -------------------------------
 
 This is one of the core missions for the Tempest project, and where it
diff --git a/tempest/api/compute/admin/test_agents.py b/tempest/api/compute/admin/test_agents.py
index 118ad60..0901374 100644
--- a/tempest/api/compute/admin/test_agents.py
+++ b/tempest/api/compute/admin/test_agents.py
@@ -13,7 +13,7 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 
@@ -23,7 +23,7 @@
     @classmethod
     def setup_clients(cls):
         super(AgentsAdminTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.agents_client
+        cls.client = cls.os_admin.agents_client
 
     @classmethod
     def resource_setup(cls):
@@ -86,8 +86,7 @@
         body = self.client.create_agent(**self.params_agent)['agent']
         self.addCleanup(self.client.delete_agent, body['agent_id'])
         agents = self.client.list_agents()['agents']
-        self.assertGreater(len(agents), 0,
-                           'Cannot get any agents.(%s)' % agents)
+        self.assertNotEmpty(agents, 'Cannot get any agents.')
         self.assertIn(body['agent_id'], map(lambda x: x['agent_id'], agents))
 
     @decorators.idempotent_id('eabadde4-3cd7-4ec4-a4b5-5a936d2d4408')
@@ -105,8 +104,7 @@
         agent_id_xen = agent_xen['agent_id']
         agents = (self.client.list_agents(hypervisor=agent_xen['hypervisor'])
                   ['agents'])
-        self.assertGreater(len(agents), 0,
-                           'Cannot get any agents.(%s)' % agents)
+        self.assertNotEmpty(agents, 'Cannot get any agents.')
         self.assertIn(agent_id_xen, map(lambda x: x['agent_id'], agents))
         self.assertNotIn(body['agent_id'], map(lambda x: x['agent_id'],
                                                agents))
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 36ce975..57d3983 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -17,8 +17,8 @@
 
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
@@ -31,7 +31,7 @@
     @classmethod
     def setup_clients(cls):
         super(AggregatesAdminTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.aggregates_client
+        cls.client = cls.os_admin.aggregates_client
 
     @classmethod
     def resource_setup(cls):
@@ -40,7 +40,7 @@
         cls.az_name_prefix = 'test_az'
 
         cls.host = None
-        hypers = cls.os_adm.hypervisor_client.list_hypervisors(
+        hypers = cls.os_admin.hypervisor_client.list_hypervisors(
             detail=True)['hypervisors']
 
         if CONF.compute.hypervisor_type:
@@ -59,15 +59,20 @@
                 msg += " for hypervisor_type %s" % CONF.compute.hypervisor_type
             raise testtools.TestCase.failureException(msg)
 
+    def _create_test_aggregate(self, **kwargs):
+        if 'name' not in kwargs:
+            kwargs['name'] = data_utils.rand_name(self.aggregate_name_prefix)
+        aggregate = self.client.create_aggregate(**kwargs)['aggregate']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_aggregate, aggregate['id'])
+        self.assertEqual(kwargs['name'], aggregate['name'])
+
+        return aggregate
+
     @decorators.idempotent_id('0d148aa3-d54c-4317-aa8d-42040a475e20')
     def test_aggregate_create_delete(self):
         # Create and delete an aggregate.
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.client.delete_aggregate, aggregate['id'])
-        self.assertEqual(aggregate_name, aggregate['name'])
+        aggregate = self._create_test_aggregate()
         self.assertIsNone(aggregate['availability_zone'])
 
         self.client.delete_aggregate(aggregate['id'])
@@ -76,13 +81,8 @@
     @decorators.idempotent_id('5873a6f8-671a-43ff-8838-7ce430bb6d0b')
     def test_aggregate_create_delete_with_az(self):
         # Create and delete an aggregate.
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
         az_name = data_utils.rand_name(self.az_name_prefix)
-        aggregate = self.client.create_aggregate(
-            name=aggregate_name, availability_zone=az_name)['aggregate']
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.client.delete_aggregate, aggregate['id'])
-        self.assertEqual(aggregate_name, aggregate['name'])
+        aggregate = self._create_test_aggregate(availability_zone=az_name)
         self.assertEqual(az_name, aggregate['availability_zone'])
 
         self.client.delete_aggregate(aggregate['id'])
@@ -91,11 +91,7 @@
     @decorators.idempotent_id('68089c38-04b1-4758-bdf0-cf0daec4defd')
     def test_aggregate_create_verify_entry_in_list(self):
         # Create an aggregate and ensure it is listed.
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
+        aggregate = self._create_test_aggregate()
         aggregates = self.client.list_aggregates()['aggregates']
         self.assertIn((aggregate['id'], aggregate['availability_zone']),
                       map(lambda x: (x['id'], x['availability_zone']),
@@ -104,11 +100,7 @@
     @decorators.idempotent_id('36ec92ca-7a73-43bc-b920-7531809e8540')
     def test_aggregate_create_update_metadata_get_details(self):
         # Create an aggregate and ensure its details are returned.
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
+        aggregate = self._create_test_aggregate()
         body = self.client.show_aggregate(aggregate['id'])['aggregate']
         self.assertEqual(aggregate['name'], body['name'])
         self.assertEqual(aggregate['availability_zone'],
@@ -129,13 +121,10 @@
         # Update an aggregate and ensure properties are updated correctly
         aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
         az_name = data_utils.rand_name(self.az_name_prefix)
-        aggregate = self.client.create_aggregate(
-            name=aggregate_name, availability_zone=az_name)['aggregate']
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+        aggregate = self._create_test_aggregate(
+            name=aggregate_name, availability_zone=az_name)
 
-        self.assertEqual(aggregate_name, aggregate['name'])
         self.assertEqual(az_name, aggregate['availability_zone'])
-        self.assertIsNotNone(aggregate['id'])
 
         aggregate_id = aggregate['id']
         new_aggregate_name = aggregate_name + '_new'
@@ -159,9 +148,7 @@
         # Add a host to the given aggregate and remove.
         self.useFixture(fixtures.LockFixture('availability_zone'))
         aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+        aggregate = self._create_test_aggregate(name=aggregate_name)
 
         body = (self.client.add_host(aggregate['id'], host=self.host)
                 ['aggregate'])
@@ -182,9 +169,8 @@
         # Add a host to the given aggregate and list.
         self.useFixture(fixtures.LockFixture('availability_zone'))
         aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+        aggregate = self._create_test_aggregate(name=aggregate_name)
+
         self.client.add_host(aggregate['id'], host=self.host)
         self.addCleanup(self.client.remove_host, aggregate['id'],
                         host=self.host)
@@ -202,9 +188,8 @@
         # Add a host to the given aggregate and get details.
         self.useFixture(fixtures.LockFixture('availability_zone'))
         aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        aggregate = (self.client.create_aggregate(name=aggregate_name)
-                     ['aggregate'])
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+        aggregate = self._create_test_aggregate(name=aggregate_name)
+
         self.client.add_host(aggregate['id'], host=self.host)
         self.addCleanup(self.client.remove_host, aggregate['id'],
                         host=self.host)
@@ -218,15 +203,13 @@
     def test_aggregate_add_host_create_server_with_az(self):
         # Add a host to the given aggregate and create a server.
         self.useFixture(fixtures.LockFixture('availability_zone'))
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
         az_name = data_utils.rand_name(self.az_name_prefix)
-        aggregate = self.client.create_aggregate(
-            name=aggregate_name, availability_zone=az_name)['aggregate']
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+        aggregate = self._create_test_aggregate(availability_zone=az_name)
+
         self.client.add_host(aggregate['id'], host=self.host)
         self.addCleanup(self.client.remove_host, aggregate['id'],
                         host=self.host)
-        admin_servers_client = self.os_adm.servers_client
+        admin_servers_client = self.os_admin.servers_client
         server = self.create_test_server(availability_zone=az_name,
                                          wait_until='ACTIVE')
         body = admin_servers_client.show_server(server['id'])['server']
diff --git a/tempest/api/compute/admin/test_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
index deded73..41be620 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -15,10 +15,9 @@
 
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class AggregatesAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -27,16 +26,15 @@
     @classmethod
     def setup_clients(cls):
         super(AggregatesAdminNegativeTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.aggregates_client
+        cls.client = cls.os_admin.aggregates_client
         cls.user_client = cls.aggregates_client
 
     @classmethod
     def resource_setup(cls):
         super(AggregatesAdminNegativeTestJSON, cls).resource_setup()
         cls.aggregate_name_prefix = 'test_aggregate'
-        cls.az_name_prefix = 'test_az'
 
-        hosts_all = cls.os_adm.hosts_client.list_hosts()['hosts']
+        hosts_all = cls.os_admin.hosts_client.list_hosts()['hosts']
         hosts = ([host['host_name']
                  for host in hosts_all if host['service'] == 'compute'])
         cls.host = hosts[0]
@@ -48,7 +46,7 @@
         self.addCleanup(self.client.delete_aggregate, aggregate['id'])
         return aggregate
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('86a1cb14-da37-4a70-b056-903fd56dfe29')
     def test_aggregate_create_as_user(self):
         # Regular user is not allowed to create an aggregate.
@@ -57,7 +55,7 @@
                           self.user_client.create_aggregate,
                           name=aggregate_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('3b8a1929-3793-4e92-bcb4-dfa572ee6c1d')
     def test_aggregate_create_aggregate_name_length_less_than_1(self):
         # the length of aggregate name should >= 1 and <=255
@@ -65,7 +63,7 @@
                           self.client.create_aggregate,
                           name='')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4c194563-543b-4e70-a719-557bbe947fac')
     def test_aggregate_create_aggregate_name_length_exceeds_255(self):
         # the length of aggregate name should >= 1 and <=255
@@ -74,7 +72,7 @@
                           self.client.create_aggregate,
                           name=aggregate_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9c23a291-b0b1-487b-b464-132e061151b3')
     def test_aggregate_create_with_existent_aggregate_name(self):
         # creating an aggregate with existent aggregate name is forbidden
@@ -83,7 +81,7 @@
                           self.client.create_aggregate,
                           name=aggregate['name'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('cd6de795-c15d-45f1-8d9e-813c6bb72a3d')
     def test_aggregate_delete_as_user(self):
         # Regular user is not allowed to delete an aggregate.
@@ -92,14 +90,14 @@
                           self.user_client.delete_aggregate,
                           aggregate['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b7d475a6-5dcd-4ff4-b70a-cd9de66a6672')
     def test_aggregate_list_as_user(self):
         # Regular user is not allowed to list aggregates.
         self.assertRaises(lib_exc.Forbidden,
                           self.user_client.list_aggregates)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('557cad12-34c9-4ff4-95f0-22f0dfbaf7dc')
     def test_aggregate_get_details_as_user(self):
         # Regular user is not allowed to get aggregate details.
@@ -108,25 +106,25 @@
                           self.user_client.show_aggregate,
                           aggregate['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c74f4bf1-4708-4ff2-95a0-f49eaca951bd')
     def test_aggregate_delete_with_invalid_id(self):
         # Delete an aggregate with invalid id should raise exceptions.
         self.assertRaises(lib_exc.NotFound,
                           self.client.delete_aggregate, -1)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('3c916244-2c46-49a4-9b55-b20bb0ae512c')
     def test_aggregate_get_details_with_invalid_id(self):
         # Get aggregate details with invalid id should raise exceptions.
         self.assertRaises(lib_exc.NotFound,
                           self.client.show_aggregate, -1)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0ef07828-12b4-45ba-87cc-41425faf5711')
     def test_aggregate_add_non_exist_host(self):
         # Adding a non-exist host to an aggregate should raise exceptions.
-        hosts_all = self.os_adm.hosts_client.list_hosts()['hosts']
+        hosts_all = self.os_admin.hosts_client.list_hosts()['hosts']
         hosts = map(lambda x: x['host_name'], hosts_all)
         while True:
             non_exist_host = data_utils.rand_name('nonexist_host')
@@ -136,7 +134,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.add_host,
                           aggregate['id'], host=non_exist_host)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7324c334-bd13-4c93-8521-5877322c3d51')
     def test_aggregate_add_host_as_user(self):
         # Regular user is not allowed to add a host to an aggregate.
@@ -145,7 +143,7 @@
                           self.user_client.add_host,
                           aggregate['id'], host=self.host)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('19dd44e1-c435-4ee1-a402-88c4f90b5950')
     def test_aggregate_add_existent_host(self):
         self.useFixture(fixtures.LockFixture('availability_zone'))
@@ -158,7 +156,7 @@
         self.assertRaises(lib_exc.Conflict, self.client.add_host,
                           aggregate['id'], host=self.host)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7a53af20-137a-4e44-a4ae-e19260e626d9')
     def test_aggregate_remove_host_as_user(self):
         # Regular user is not allowed to remove a host from an aggregate.
@@ -173,7 +171,7 @@
                           self.user_client.remove_host,
                           aggregate['id'], host=self.host)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('95d6a6fa-8da9-4426-84d0-eec0329f2e4d')
     def test_aggregate_remove_nonexistent_host(self):
         aggregate = self._create_test_aggregate()
diff --git a/tempest/api/compute/admin/test_auto_allocate_network.py b/tempest/api/compute/admin/test_auto_allocate_network.py
index 7fda732..0c80252 100644
--- a/tempest/api/compute/admin/test_auto_allocate_network.py
+++ b/tempest/api/compute/admin/test_auto_allocate_network.py
@@ -16,12 +16,11 @@
 
 from tempest.api.compute import base
 from tempest.common import compute
-from tempest.common import credentials_factory as credentials
+from tempest.common import utils
 from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_excs
-from tempest import test
 
 CONF = config.CONF
 LOG = log.getLogger(__name__)
@@ -46,14 +45,10 @@
     @classmethod
     def skip_checks(cls):
         super(AutoAllocateNetworkTest, cls).skip_checks()
-        identity_version = cls.get_identity_version()
-        if not credentials.is_admin_available(
-                identity_version=identity_version):
-            msg = "Missing Identity Admin API credentials in configuration."
-            raise cls.skipException(msg)
         if not CONF.service_available.neutron:
             raise cls.skipException('Neutron is required')
-        if not test.is_extension_enabled('auto-allocated-topology', 'network'):
+        if not utils.is_extension_enabled('auto-allocated-topology',
+                                          'network'):
             raise cls.skipException(
                 'auto-allocated-topology extension is not available')
 
@@ -66,10 +61,10 @@
     @classmethod
     def setup_clients(cls):
         super(AutoAllocateNetworkTest, cls).setup_clients()
-        cls.networks_client = cls.os.networks_client
-        cls.routers_client = cls.os.routers_client
-        cls.subnets_client = cls.os.subnets_client
-        cls.ports_client = cls.os.ports_client
+        cls.networks_client = cls.os_primary.networks_client
+        cls.routers_client = cls.os_primary.routers_client
+        cls.subnets_client = cls.os_primary.subnets_client
+        cls.ports_client = cls.os_primary.ports_client
 
     @classmethod
     def resource_setup(cls):
@@ -111,10 +106,12 @@
             LOG.info('(%s) Found more than one router for tenant.',
                      test_utils.find_test_caller())
 
-        # Let's just blindly remove any networks, duplicate or otherwise, that
-        # the test might have created even though Neutron will cleanup
-        # duplicate resources automatically (so ignore 404s).
-        networks = cls.networks_client.list_networks().get('networks', [])
+        # Remove any networks, duplicate or otherwise, that these tests
+        # created. All such networks will be in the current tenant. Neutron
+        # will cleanup duplicate resources automatically, so ignore 404s.
+        search_opts = {'tenant_id': cls.networks_client.tenant_id}
+        networks = cls.networks_client.list_networks(
+            **search_opts).get('networks', [])
 
         for router in routers:
             # Disassociate the subnets from the router. Because of the race
@@ -151,7 +148,7 @@
         """Tests that no networking is allocated for the server."""
         # create the server with no networking
         server, _ = compute.create_test_server(
-            self.os, networks='none', wait_until='ACTIVE')
+            self.os_primary, networks='none', wait_until='ACTIVE')
         self.addCleanup(self.delete_server, server['id'])
         # get the server ips
         addresses = self.servers_client.list_addresses(
@@ -176,7 +173,7 @@
         # - Third request sees net1 and net2 for the tenant and fails with a
         #   NetworkAmbiguous 400 error.
         _, servers = compute.create_test_server(
-            self.os, networks='auto', wait_until='ACTIVE',
+            self.os_primary, networks='auto', wait_until='ACTIVE',
             min_count=3)
         server_nets = set()
         for server in servers:
diff --git a/tempest/api/compute/admin/test_availability_zone.py b/tempest/api/compute/admin/test_availability_zone.py
index 50dec28..bbd39b6 100644
--- a/tempest/api/compute/admin/test_availability_zone.py
+++ b/tempest/api/compute/admin/test_availability_zone.py
@@ -29,10 +29,10 @@
     def test_get_availability_zone_list(self):
         # List of availability zone
         availability_zone = self.client.list_availability_zones()
-        self.assertGreater(len(availability_zone['availabilityZoneInfo']), 0)
+        self.assertNotEmpty(availability_zone['availabilityZoneInfo'])
 
     @decorators.idempotent_id('ef726c58-530f-44c2-968c-c7bed22d5b8c')
     def test_get_availability_zone_list_detail(self):
         # List of availability zones and available services
         availability_zone = self.client.list_availability_zones(detail=True)
-        self.assertGreater(len(availability_zone['availabilityZoneInfo']), 0)
+        self.assertNotEmpty(availability_zone['availabilityZoneInfo'])
diff --git a/tempest/api/compute/admin/test_availability_zone_negative.py b/tempest/api/compute/admin/test_availability_zone_negative.py
index faeb811..a58c22c 100644
--- a/tempest/api/compute/admin/test_availability_zone_negative.py
+++ b/tempest/api/compute/admin/test_availability_zone_negative.py
@@ -15,7 +15,6 @@
 from tempest.api.compute import base
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class AZAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -26,7 +25,7 @@
         super(AZAdminNegativeTestJSON, cls).setup_clients()
         cls.non_adm_client = cls.availability_zone_client
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('bf34dca2-fdc3-4073-9c02-7648d9eae0d7')
     def test_get_availability_zone_list_detail_with_non_admin_user(self):
         # List of availability zones and available services with
diff --git a/tempest/api/compute/admin/test_baremetal_nodes.py b/tempest/api/compute/admin/test_baremetal_nodes.py
deleted file mode 100644
index 7726ed4..0000000
--- a/tempest/api/compute/admin/test_baremetal_nodes.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2015 NEC Corporation.  All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.compute import base
-from tempest import config
-from tempest.lib import decorators
-from tempest import test
-
-CONF = config.CONF
-
-
-class BaremetalNodesAdminTestJSON(base.BaseV2ComputeAdminTest):
-    """Tests Baremetal API"""
-
-    @classmethod
-    def resource_setup(cls):
-        super(BaremetalNodesAdminTestJSON, cls).resource_setup()
-        if not getattr(CONF.service_available, 'ironic', False):
-            skip_msg = ('%s skipped as Ironic is not available' % cls.__name__)
-            raise cls.skipException(skip_msg)
-        cls.client = cls.os_adm.baremetal_nodes_client
-        cls.ironic_client = cls.os_adm.baremetal_client
-
-    @test.attr(type=['baremetal'])
-    @decorators.idempotent_id('e475aa6e-416d-4fa4-b3af-28d5e84250fb')
-    def test_list_get_baremetal_nodes(self):
-        # Create some test nodes in Ironic directly
-        test_nodes = []
-        for _ in range(0, 3):
-            _, node = self.ironic_client.create_node()
-            test_nodes.append(node)
-            self.addCleanup(self.ironic_client.delete_node, node['uuid'])
-
-        # List all baremetal nodes and ensure our created test nodes are
-        # listed
-        bm_node_ids = set([n['id'] for n in
-                           self.client.list_baremetal_nodes()['nodes']])
-        test_node_ids = set([n['uuid'] for n in test_nodes])
-        self.assertTrue(test_node_ids.issubset(bm_node_ids))
-
-        # Test getting each individually
-        for node in test_nodes:
-            baremetal_node = self.client.show_baremetal_node(node['uuid'])
-            self.assertEqual(node['uuid'], baremetal_node['node']['id'])
diff --git a/tempest/api/compute/admin/test_create_server.py b/tempest/api/compute/admin/test_create_server.py
new file mode 100644
index 0000000..66bedd9
--- /dev/null
+++ b/tempest/api/compute/admin/test_create_server.py
@@ -0,0 +1,107 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import testtools
+
+from tempest.api.compute import base
+from tempest.common.utils.linux import remote_client
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class ServersWithSpecificFlavorTestJSON(base.BaseV2ComputeAdminTest):
+    @classmethod
+    def setup_credentials(cls):
+        cls.prepare_instance_network()
+        super(ServersWithSpecificFlavorTestJSON, cls).setup_credentials()
+
+    @classmethod
+    def setup_clients(cls):
+        super(ServersWithSpecificFlavorTestJSON, cls).setup_clients()
+        cls.client = cls.servers_client
+
+    @classmethod
+    def resource_setup(cls):
+        cls.set_validation_resources()
+
+        super(ServersWithSpecificFlavorTestJSON, cls).resource_setup()
+
+    @decorators.idempotent_id('b3c7bcfc-bb5b-4e22-b517-c7f686b802ca')
+    @testtools.skipUnless(CONF.validation.run_validation,
+                          'Instance validation tests are disabled.')
+    def test_verify_created_server_ephemeral_disk(self):
+        # Verify that the ephemeral disk is created when creating server
+        flavor_base = self.flavors_client.show_flavor(
+            self.flavor_ref)['flavor']
+
+        def create_flavor_with_ephemeral(ephem_disk):
+            name = 'flavor_with_ephemeral_%s' % ephem_disk
+            flavor_name = data_utils.rand_name(name)
+
+            ram = flavor_base['ram']
+            vcpus = flavor_base['vcpus']
+            disk = flavor_base['disk']
+
+            # Create a flavor with ephemeral disk
+            flavor = self.create_flavor(name=flavor_name, ram=ram, vcpus=vcpus,
+                                        disk=disk, ephemeral=ephem_disk)
+            return flavor['id']
+
+        flavor_with_eph_disk_id = create_flavor_with_ephemeral(ephem_disk=1)
+        flavor_no_eph_disk_id = create_flavor_with_ephemeral(ephem_disk=0)
+
+        admin_pass = self.image_ssh_password
+
+        server_no_eph_disk = self.create_test_server(
+            validatable=True,
+            wait_until='ACTIVE',
+            adminPass=admin_pass,
+            flavor=flavor_no_eph_disk_id)
+
+        # Get partition number of server without ephemeral disk.
+        server_no_eph_disk = self.client.show_server(
+            server_no_eph_disk['id'])['server']
+        linux_client = remote_client.RemoteClient(
+            self.get_server_ip(server_no_eph_disk),
+            self.ssh_user,
+            admin_pass,
+            self.validation_resources['keypair']['private_key'],
+            server=server_no_eph_disk,
+            servers_client=self.client)
+        disks_num = len(linux_client.get_disks().split('\n'))
+
+        # Explicit server deletion necessary for Juno compatibility
+        self.client.delete_server(server_no_eph_disk['id'])
+
+        server_with_eph_disk = self.create_test_server(
+            validatable=True,
+            wait_until='ACTIVE',
+            adminPass=admin_pass,
+            flavor=flavor_with_eph_disk_id)
+
+        server_with_eph_disk = self.client.show_server(
+            server_with_eph_disk['id'])['server']
+        linux_client = remote_client.RemoteClient(
+            self.get_server_ip(server_with_eph_disk),
+            self.ssh_user,
+            admin_pass,
+            self.validation_resources['keypair']['private_key'],
+            server=server_with_eph_disk,
+            servers_client=self.client)
+        disks_num_eph = len(linux_client.get_disks().split('\n'))
+        self.assertEqual(disks_num + 1, disks_num_eph)
diff --git a/tempest/api/compute/admin/test_delete_server.py b/tempest/api/compute/admin/test_delete_server.py
new file mode 100644
index 0000000..83444b9
--- /dev/null
+++ b/tempest/api/compute/admin/test_delete_server.py
@@ -0,0 +1,52 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.compute import base
+from tempest.common import waiters
+from tempest import config
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class DeleteServersAdminTestJSON(base.BaseV2ComputeAdminTest):
+    # NOTE: Server creations of each test class should be under 10
+    # for preventing "Quota exceeded for instances".
+
+    @classmethod
+    def setup_clients(cls):
+        super(DeleteServersAdminTestJSON, cls).setup_clients()
+        cls.non_admin_client = cls.servers_client
+        cls.admin_client = cls.os_admin.servers_client
+
+    @decorators.idempotent_id('99774678-e072-49d1-9d2a-49a59bc56063')
+    def test_delete_server_while_in_error_state(self):
+        # Delete a server while it's VM state is error
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.admin_client.reset_state(server['id'], state='error')
+        # Verify server's state
+        server = self.non_admin_client.show_server(server['id'])['server']
+        self.assertEqual(server['status'], 'ERROR')
+        self.non_admin_client.delete_server(server['id'])
+        waiters.wait_for_server_termination(self.servers_client,
+                                            server['id'],
+                                            ignore_error=True)
+
+    @decorators.idempotent_id('73177903-6737-4f27-a60c-379e8ae8cf48')
+    def test_admin_delete_servers_of_others(self):
+        # Administrator can delete servers of others
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.admin_client.delete_server(server['id'])
+        waiters.wait_for_server_termination(self.servers_client, server['id'])
diff --git a/tempest/api/compute/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index 10ed519..ebba73c 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -29,11 +29,13 @@
         if CONF.service_available.neutron:
             msg = ("%s skipped as neutron is available" % cls.__name__)
             raise cls.skipException(msg)
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
 
     @classmethod
     def setup_clients(cls):
         super(FixedIPsTestJson, cls).setup_clients()
-        cls.client = cls.os_adm.fixed_ips_client
+        cls.client = cls.os_admin.fixed_ips_client
 
     @classmethod
     def resource_setup(cls):
@@ -49,17 +51,14 @@
                 break
 
     @decorators.idempotent_id('16b7d848-2f7c-4709-85a3-2dfb4576cc52')
-    @test.services('network')
     def test_list_fixed_ip_details(self):
         fixed_ip = self.client.show_fixed_ip(self.ip)
         self.assertEqual(fixed_ip['fixed_ip']['address'], self.ip)
 
     @decorators.idempotent_id('5485077b-7e46-4cec-b402-91dc3173433b')
-    @test.services('network')
     def test_set_reserve(self):
         self.client.reserve_fixed_ip(self.ip, reserve="None")
 
     @decorators.idempotent_id('7476e322-b9ff-4710-bf82-49d51bac6e2e')
-    @test.services('network')
     def test_set_unreserve(self):
         self.client.reserve_fixed_ip(self.ip, unreserve="None")
diff --git a/tempest/api/compute/admin/test_fixed_ips_negative.py b/tempest/api/compute/admin/test_fixed_ips_negative.py
index 073f152..a5deb3c 100644
--- a/tempest/api/compute/admin/test_fixed_ips_negative.py
+++ b/tempest/api/compute/admin/test_fixed_ips_negative.py
@@ -13,10 +13,10 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -29,11 +29,13 @@
         if CONF.service_available.neutron:
             msg = ("%s skipped as neutron is available" % cls.__name__)
             raise cls.skipException(msg)
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
 
     @classmethod
     def setup_clients(cls):
         super(FixedIPsNegativeTestJson, cls).setup_clients()
-        cls.client = cls.os_adm.fixed_ips_client
+        cls.client = cls.os_admin.fixed_ips_client
         cls.non_admin_client = cls.fixed_ips_client
 
     @classmethod
@@ -49,32 +51,28 @@
             if cls.ip:
                 break
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9f17f47d-daad-4adc-986e-12370c93e407')
-    @test.services('network')
     def test_list_fixed_ip_details_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.show_fixed_ip, self.ip)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ce60042c-fa60-4836-8d43-1c8e3359dc47')
-    @test.services('network')
     def test_set_reserve_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.reserve_fixed_ip,
                           self.ip, reserve="None")
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f1f7a35b-0390-48c5-9803-5f27461439db')
-    @test.services('network')
     def test_set_unreserve_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.reserve_fixed_ip,
                           self.ip, unreserve="None")
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f51cf464-7fc5-4352-bc3e-e75cfa2cb717')
-    @test.services('network')
     def test_set_reserve_with_invalid_ip(self):
         # NOTE(maurosr): since this exercises the same code snippet, we do it
         # only for reserve action
@@ -85,9 +83,8 @@
                           self.client.reserve_fixed_ip,
                           "my.invalid.ip", reserve="None")
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fd26ef50-f135-4232-9d32-281aab3f9176')
-    @test.services('network')
     def test_fixed_ip_with_invalid_action(self):
         self.assertRaises(lib_exc.BadRequest,
                           self.client.reserve_fixed_ip,
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 3fd1612..1483c2e 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -16,10 +16,10 @@
 import uuid
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class FlavorsAdminTestJSON(base.BaseV2ComputeAdminTest):
@@ -28,17 +28,11 @@
     @classmethod
     def skip_checks(cls):
         super(FlavorsAdminTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
+        if not utils.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
             msg = "OS-FLV-EXT-DATA extension not enabled."
             raise cls.skipException(msg)
 
     @classmethod
-    def setup_clients(cls):
-        super(FlavorsAdminTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.flavors_client
-        cls.user_client = cls.os.flavors_client
-
-    @classmethod
     def resource_setup(cls):
         super(FlavorsAdminTestJSON, cls).resource_setup()
 
@@ -50,50 +44,22 @@
         cls.swap = 1024
         cls.rxtx = 2
 
-    def flavor_clean_up(self, flavor_id):
-        self.client.delete_flavor(flavor_id)
-        self.client.wait_for_resource_deletion(flavor_id)
-
-    def _create_flavor(self, flavor_id):
-        # Create a flavor and ensure it is listed
-        # This operation requires the user to have 'admin' role
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-
-        # Create the flavor
-        flavor = self.client.create_flavor(name=flavor_name,
-                                           ram=self.ram, vcpus=self.vcpus,
-                                           disk=self.disk,
-                                           id=flavor_id,
-                                           ephemeral=self.ephemeral,
-                                           swap=self.swap,
-                                           rxtx_factor=self.rxtx)['flavor']
-        self.addCleanup(self.flavor_clean_up, flavor['id'])
-        self.assertEqual(flavor['name'], flavor_name)
-        self.assertEqual(flavor['vcpus'], self.vcpus)
-        self.assertEqual(flavor['disk'], self.disk)
-        self.assertEqual(flavor['ram'], self.ram)
-        self.assertEqual(flavor['swap'], self.swap)
-        self.assertEqual(flavor['rxtx_factor'], self.rxtx)
-        self.assertEqual(flavor['OS-FLV-EXT-DATA:ephemeral'],
-                         self.ephemeral)
-        self.assertEqual(flavor['os-flavor-access:is_public'], True)
-
-        # Verify flavor is retrieved
-        flavor = self.client.show_flavor(flavor['id'])['flavor']
-        self.assertEqual(flavor['name'], flavor_name)
-
-        return flavor['id']
-
     @decorators.idempotent_id('8b4330e1-12c4-4554-9390-e6639971f086')
     def test_create_flavor_with_int_id(self):
         flavor_id = data_utils.rand_int_id(start=1000)
-        new_flavor_id = self._create_flavor(flavor_id)
+        new_flavor_id = self.create_flavor(ram=self.ram,
+                                           vcpus=self.vcpus,
+                                           disk=self.disk,
+                                           id=flavor_id)['id']
         self.assertEqual(new_flavor_id, str(flavor_id))
 
     @decorators.idempotent_id('94c9bb4e-2c2a-4f3c-bb1f-5f0daf918e6d')
     def test_create_flavor_with_uuid_id(self):
         flavor_id = data_utils.rand_uuid()
-        new_flavor_id = self._create_flavor(flavor_id)
+        new_flavor_id = self.create_flavor(ram=self.ram,
+                                           vcpus=self.vcpus,
+                                           disk=self.disk,
+                                           id=flavor_id)['id']
         self.assertEqual(new_flavor_id, flavor_id)
 
     @decorators.idempotent_id('f83fe669-6758-448a-a85e-32d351f36fe0')
@@ -101,7 +67,10 @@
         # If nova receives a request with None as flavor_id,
         # nova generates flavor_id of uuid.
         flavor_id = None
-        new_flavor_id = self._create_flavor(flavor_id)
+        new_flavor_id = self.create_flavor(ram=self.ram,
+                                           vcpus=self.vcpus,
+                                           disk=self.disk,
+                                           id=flavor_id)['id']
         self.assertEqual(new_flavor_id, str(uuid.UUID(new_flavor_id)))
 
     @decorators.idempotent_id('8261d7b0-be58-43ec-a2e5-300573c3f6c5')
@@ -109,24 +78,19 @@
         # Create a flavor and ensure it's details are listed
         # This operation requires the user to have 'admin' role
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
 
         # Create the flavor
-        flavor = self.client.create_flavor(name=flavor_name,
-                                           ram=self.ram, vcpus=self.vcpus,
-                                           disk=self.disk,
-                                           id=new_flavor_id,
-                                           ephemeral=self.ephemeral,
-                                           swap=self.swap,
-                                           rxtx_factor=self.rxtx)['flavor']
-        self.addCleanup(self.flavor_clean_up, flavor['id'])
-        flag = False
-        # Verify flavor is retrieved
-        flavors = self.client.list_flavors(detail=True)['flavors']
-        for flavor in flavors:
-            if flavor['name'] == flavor_name:
-                flag = True
-        self.assertTrue(flag)
+        self.create_flavor(name=flavor_name,
+                           ram=self.ram, vcpus=self.vcpus,
+                           disk=self.disk,
+                           ephemeral=self.ephemeral,
+                           swap=self.swap,
+                           rxtx_factor=self.rxtx)
+
+        # Check if flavor is present in list
+        flavors_list = self.admin_flavors_client.list_flavors(
+            detail=True)['flavors']
+        self.assertIn(flavor_name, [f['name'] for f in flavors_list])
 
     @decorators.idempotent_id('63dc64e6-2e79-4fdf-868f-85500d308d66')
     def test_create_list_flavor_without_extra_data(self):
@@ -137,18 +101,17 @@
             # check some extensions for the flavor create/show/detail response
             self.assertEqual(flavor['swap'], '')
             self.assertEqual(int(flavor['rxtx_factor']), 1)
-            self.assertEqual(int(flavor['OS-FLV-EXT-DATA:ephemeral']), 0)
+            self.assertEqual(flavor['OS-FLV-EXT-DATA:ephemeral'], 0)
             self.assertEqual(flavor['os-flavor-access:is_public'], True)
 
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
         new_flavor_id = data_utils.rand_int_id(start=1000)
 
         # Create the flavor
-        flavor = self.client.create_flavor(name=flavor_name,
-                                           ram=self.ram, vcpus=self.vcpus,
-                                           disk=self.disk,
-                                           id=new_flavor_id)['flavor']
-        self.addCleanup(self.flavor_clean_up, flavor['id'])
+        flavor = self.create_flavor(name=flavor_name,
+                                    ram=self.ram, vcpus=self.vcpus,
+                                    disk=self.disk,
+                                    id=new_flavor_id)
         self.assertEqual(flavor['name'], flavor_name)
         self.assertEqual(flavor['ram'], self.ram)
         self.assertEqual(flavor['vcpus'], self.vcpus)
@@ -157,18 +120,17 @@
         verify_flavor_response_extension(flavor)
 
         # Verify flavor is retrieved
-        flavor = self.client.show_flavor(new_flavor_id)['flavor']
+        flavor = self.admin_flavors_client.show_flavor(new_flavor_id)['flavor']
         self.assertEqual(flavor['name'], flavor_name)
         verify_flavor_response_extension(flavor)
 
         # Check if flavor is present in list
-        flag = False
-        flavors = self.user_client.list_flavors(detail=True)['flavors']
-        for flavor in flavors:
-            if flavor['name'] == flavor_name:
-                verify_flavor_response_extension(flavor)
-                flag = True
-        self.assertTrue(flag)
+        flavors_list = [
+            f for f in self.flavors_client.list_flavors(detail=True)['flavors']
+            if f['name'] == flavor_name
+        ]
+        self.assertNotEmpty(flavors_list)
+        verify_flavor_response_extension(flavors_list[0])
 
     @decorators.idempotent_id('be6cc18c-7c5d-48c0-ac16-17eaf03c54eb')
     def test_list_non_public_flavor(self):
@@ -177,48 +139,31 @@
         # tenant is not automatically added access list.
         # This operation requires the user to have 'admin' role
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
 
         # Create the flavor
-        flavor = self.client.create_flavor(name=flavor_name,
-                                           ram=self.ram, vcpus=self.vcpus,
-                                           disk=self.disk,
-                                           id=new_flavor_id,
-                                           is_public="False")['flavor']
-        self.addCleanup(self.flavor_clean_up, flavor['id'])
-        # Verify flavor is retrieved
-        flag = False
-        flavors = self.client.list_flavors(detail=True)['flavors']
-        for flavor in flavors:
-            if flavor['name'] == flavor_name:
-                flag = True
-        self.assertFalse(flag)
+        self.create_flavor(name=flavor_name,
+                           ram=self.ram, vcpus=self.vcpus,
+                           disk=self.disk,
+                           is_public="False")
+        # Verify flavor is not retrieved
+        flavors_list = self.admin_flavors_client.list_flavors(
+            detail=True)['flavors']
+        self.assertNotIn(flavor_name, [f['name'] for f in flavors_list])
 
         # Verify flavor is not retrieved with other user
-        flag = False
-        flavors = self.user_client.list_flavors(detail=True)['flavors']
-        for flavor in flavors:
-            if flavor['name'] == flavor_name:
-                flag = True
-        self.assertFalse(flag)
+        flavors_list = self.flavors_client.list_flavors(detail=True)['flavors']
+        self.assertNotIn(flavor_name, [f['name'] for f in flavors_list])
 
     @decorators.idempotent_id('bcc418ef-799b-47cc-baa1-ce01368b8987')
     def test_create_server_with_non_public_flavor(self):
         # Create a flavor with os-flavor-access:is_public false
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
-
-        # Create the flavor
-        flavor = self.client.create_flavor(name=flavor_name,
-                                           ram=self.ram, vcpus=self.vcpus,
-                                           disk=self.disk,
-                                           id=new_flavor_id,
-                                           is_public="False")['flavor']
-        self.addCleanup(self.flavor_clean_up, flavor['id'])
+        flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
+                                    disk=self.disk,
+                                    is_public="False")
 
         # Verify flavor is not used by other user
         self.assertRaises(lib_exc.BadRequest,
-                          self.os.servers_client.create_server,
+                          self.os_primary.servers_client.create_server,
                           name='test', imageRef=self.image_ref,
                           flavorRef=flavor['id'])
 
@@ -227,60 +172,40 @@
         # Create a Flavor with public access.
         # Try to List/Get flavor with another user
         flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
 
         # Create the flavor
-        flavor = self.client.create_flavor(name=flavor_name,
-                                           ram=self.ram, vcpus=self.vcpus,
-                                           disk=self.disk,
-                                           id=new_flavor_id,
-                                           is_public="True")['flavor']
-        self.addCleanup(self.flavor_clean_up, flavor['id'])
-        flag = False
-        self.new_client = self.flavors_client
+        self.create_flavor(name=flavor_name,
+                           ram=self.ram, vcpus=self.vcpus,
+                           disk=self.disk,
+                           is_public="True")
         # Verify flavor is retrieved with new user
-        flavors = self.new_client.list_flavors(detail=True)['flavors']
-        for flavor in flavors:
-            if flavor['name'] == flavor_name:
-                flag = True
-        self.assertTrue(flag)
+        flavors_list = self.flavors_client.list_flavors(detail=True)['flavors']
+        self.assertIn(flavor_name, [f['name'] for f in flavors_list])
 
     @decorators.idempotent_id('fb9cbde6-3a0e-41f2-a983-bdb0a823c44e')
     def test_is_public_string_variations(self):
-        flavor_id_not_public = data_utils.rand_int_id(start=1000)
         flavor_name_not_public = data_utils.rand_name(self.flavor_name_prefix)
-        flavor_id_public = data_utils.rand_int_id(start=1000)
         flavor_name_public = data_utils.rand_name(self.flavor_name_prefix)
 
         # Create a non public flavor
-        flavor = self.client.create_flavor(name=flavor_name_not_public,
-                                           ram=self.ram, vcpus=self.vcpus,
-                                           disk=self.disk,
-                                           id=flavor_id_not_public,
-                                           is_public="False")['flavor']
-        self.addCleanup(self.flavor_clean_up, flavor['id'])
+        self.create_flavor(name=flavor_name_not_public,
+                           ram=self.ram, vcpus=self.vcpus,
+                           disk=self.disk,
+                           is_public="False")
 
         # Create a public flavor
-        flavor = self.client.create_flavor(name=flavor_name_public,
-                                           ram=self.ram, vcpus=self.vcpus,
-                                           disk=self.disk,
-                                           id=flavor_id_public,
-                                           is_public="True")['flavor']
-        self.addCleanup(self.flavor_clean_up, flavor['id'])
-
-        def _flavor_lookup(flavors, flavor_name):
-            for flavor in flavors:
-                if flavor['name'] == flavor_name:
-                    return flavor
-            return None
+        self.create_flavor(name=flavor_name_public,
+                           ram=self.ram, vcpus=self.vcpus,
+                           disk=self.disk,
+                           is_public="True")
 
         def _test_string_variations(variations, flavor_name):
             for string in variations:
                 params = {'is_public': string}
-                flavors = (self.client.list_flavors(detail=True, **params)
+                flavors = (self.admin_flavors_client.list_flavors(detail=True,
+                                                                  **params)
                            ['flavors'])
-                flavor = _flavor_lookup(flavors, flavor_name)
-                self.assertIsNotNone(flavor)
+                self.assertIn(flavor_name, [f['name'] for f in flavors])
 
         _test_string_variations(['f', 'false', 'no', '0'],
                                 flavor_name_not_public)
@@ -290,17 +215,11 @@
 
     @decorators.idempotent_id('3b541a2e-2ac2-4b42-8b8d-ba6e22fcd4da')
     def test_create_flavor_using_string_ram(self):
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
         new_flavor_id = data_utils.rand_int_id(start=1000)
 
         ram = "1024"
-        flavor = self.client.create_flavor(name=flavor_name,
-                                           ram=ram, vcpus=self.vcpus,
-                                           disk=self.disk,
-                                           id=new_flavor_id)['flavor']
-        self.addCleanup(self.flavor_clean_up, flavor['id'])
-        self.assertEqual(flavor['name'], flavor_name)
-        self.assertEqual(flavor['vcpus'], self.vcpus)
-        self.assertEqual(flavor['disk'], self.disk)
+        flavor = self.create_flavor(ram=ram, vcpus=self.vcpus,
+                                    disk=self.disk,
+                                    id=new_flavor_id)
         self.assertEqual(flavor['ram'], int(ram))
         self.assertEqual(int(flavor['id']), new_flavor_id)
diff --git a/tempest/api/compute/admin/test_flavors_access.py b/tempest/api/compute/admin/test_flavors_access.py
index 38ff4c0..b8e2b42 100644
--- a/tempest/api/compute/admin/test_flavors_access.py
+++ b/tempest/api/compute/admin/test_flavors_access.py
@@ -14,9 +14,8 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class FlavorsAccessTestJSON(base.BaseV2ComputeAdminTest):
@@ -28,22 +27,16 @@
     @classmethod
     def skip_checks(cls):
         super(FlavorsAccessTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
+        if not utils.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
             msg = "OS-FLV-EXT-DATA extension not enabled."
             raise cls.skipException(msg)
 
     @classmethod
-    def setup_clients(cls):
-        super(FlavorsAccessTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.flavors_client
-
-    @classmethod
     def resource_setup(cls):
         super(FlavorsAccessTestJSON, cls).resource_setup()
 
         # Non admin tenant ID
         cls.tenant_id = cls.flavors_client.tenant_id
-        cls.flavor_name_prefix = 'test_flavor_access_'
         cls.ram = 512
         cls.vcpus = 1
         cls.disk = 10
@@ -52,49 +45,37 @@
     def test_flavor_access_list_with_private_flavor(self):
         # Test to make sure that list flavor access on a newly created
         # private flavor will return an empty access list
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
-        new_flavor = self.client.create_flavor(name=flavor_name,
-                                               ram=self.ram, vcpus=self.vcpus,
-                                               disk=self.disk,
-                                               id=new_flavor_id,
-                                               is_public='False')['flavor']
-        self.addCleanup(self.client.delete_flavor, new_flavor['id'])
-        flavor_access = (self.client.list_flavor_access(new_flavor_id)
-                         ['flavor_access'])
-        self.assertEqual(len(flavor_access), 0, str(flavor_access))
+        flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
+                                    disk=self.disk, is_public='False')
+
+        flavor_access = (self.admin_flavors_client.list_flavor_access(
+                         flavor['id'])['flavor_access'])
+        self.assertEmpty(flavor_access)
 
     @decorators.idempotent_id('59e622f6-bdf6-45e3-8ba8-fedad905a6b4')
     def test_flavor_access_add_remove(self):
         # Test to add and remove flavor access to a given tenant.
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
-        new_flavor = self.client.create_flavor(name=flavor_name,
-                                               ram=self.ram, vcpus=self.vcpus,
-                                               disk=self.disk,
-                                               id=new_flavor_id,
-                                               is_public='False')['flavor']
-        self.addCleanup(self.client.delete_flavor, new_flavor['id'])
+        flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
+                                    disk=self.disk, is_public='False')
+
         # Add flavor access to a tenant.
         resp_body = {
             "tenant_id": str(self.tenant_id),
-            "flavor_id": str(new_flavor['id']),
+            "flavor_id": str(flavor['id']),
         }
-        add_body = (self.client.add_flavor_access(new_flavor['id'],
-                                                  self.tenant_id)
-                    ['flavor_access'])
+        add_body = (self.admin_flavors_client.add_flavor_access(
+            flavor['id'], self.tenant_id)['flavor_access'])
         self.assertIn(resp_body, add_body)
 
         # The flavor is present in list.
         flavors = self.flavors_client.list_flavors(detail=True)['flavors']
-        self.assertIn(new_flavor['id'], map(lambda x: x['id'], flavors))
+        self.assertIn(flavor['id'], map(lambda x: x['id'], flavors))
 
         # Remove flavor access from a tenant.
-        remove_body = (self.client.remove_flavor_access(new_flavor['id'],
-                                                        self.tenant_id)
-                       ['flavor_access'])
+        remove_body = (self.admin_flavors_client.remove_flavor_access(
+            flavor['id'], self.tenant_id)['flavor_access'])
         self.assertNotIn(resp_body, remove_body)
 
         # The flavor is not present in list.
         flavors = self.flavors_client.list_flavors(detail=True)['flavors']
-        self.assertNotIn(new_flavor['id'], map(lambda x: x['id'], flavors))
+        self.assertNotIn(flavor['id'], map(lambda x: x['id'], flavors))
diff --git a/tempest/api/compute/admin/test_flavors_access_negative.py b/tempest/api/compute/admin/test_flavors_access_negative.py
index 2719cc4..45ca10a 100644
--- a/tempest/api/compute/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/admin/test_flavors_access_negative.py
@@ -14,10 +14,9 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class FlavorsAccessNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -26,122 +25,91 @@
     Add and remove Flavor Access require admin privileges.
     """
 
+    credentials = ['primary', 'admin', 'alt']
+
     @classmethod
     def skip_checks(cls):
         super(FlavorsAccessNegativeTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
+        if not utils.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
             msg = "OS-FLV-EXT-DATA extension not enabled."
             raise cls.skipException(msg)
 
     @classmethod
-    def setup_clients(cls):
-        super(FlavorsAccessNegativeTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.flavors_client
-
-    @classmethod
     def resource_setup(cls):
         super(FlavorsAccessNegativeTestJSON, cls).resource_setup()
 
         cls.tenant_id = cls.flavors_client.tenant_id
-        cls.flavor_name_prefix = 'test_flavor_access_'
         cls.ram = 512
         cls.vcpus = 1
         cls.disk = 10
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0621c53e-d45d-40e7-951d-43e5e257b272')
     def test_flavor_access_list_with_public_flavor(self):
         # Test to list flavor access with exceptions by querying public flavor
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
-        new_flavor = self.client.create_flavor(name=flavor_name,
-                                               ram=self.ram, vcpus=self.vcpus,
-                                               disk=self.disk,
-                                               id=new_flavor_id,
-                                               is_public='True')['flavor']
-        self.addCleanup(self.client.delete_flavor, new_flavor['id'])
+        flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
+                                    disk=self.disk, is_public='True')
         self.assertRaises(lib_exc.NotFound,
-                          self.client.list_flavor_access,
-                          new_flavor_id)
+                          self.admin_flavors_client.list_flavor_access,
+                          flavor['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('41eaaade-6d37-4f28-9c74-f21b46ca67bd')
     def test_flavor_non_admin_add(self):
         # Test to add flavor access as a user without admin privileges.
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
-        new_flavor = self.client.create_flavor(name=flavor_name,
-                                               ram=self.ram, vcpus=self.vcpus,
-                                               disk=self.disk,
-                                               id=new_flavor_id,
-                                               is_public='False')['flavor']
-        self.addCleanup(self.client.delete_flavor, new_flavor['id'])
+        flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
+                                    disk=self.disk, is_public='False')
         self.assertRaises(lib_exc.Forbidden,
                           self.flavors_client.add_flavor_access,
-                          new_flavor['id'],
+                          flavor['id'],
                           self.tenant_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('073e79a6-c311-4525-82dc-6083d919cb3a')
     def test_flavor_non_admin_remove(self):
         # Test to remove flavor access as a user without admin privileges.
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
-        new_flavor = self.client.create_flavor(name=flavor_name,
-                                               ram=self.ram, vcpus=self.vcpus,
-                                               disk=self.disk,
-                                               id=new_flavor_id,
-                                               is_public='False')['flavor']
-        self.addCleanup(self.client.delete_flavor, new_flavor['id'])
+        flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
+                                    disk=self.disk, is_public='False')
+
         # Add flavor access to a tenant.
-        self.client.add_flavor_access(new_flavor['id'], self.tenant_id)
-        self.addCleanup(self.client.remove_flavor_access,
-                        new_flavor['id'], self.tenant_id)
+        self.admin_flavors_client.add_flavor_access(flavor['id'],
+                                                    self.tenant_id)
+        self.addCleanup(self.admin_flavors_client.remove_flavor_access,
+                        flavor['id'], self.tenant_id)
         self.assertRaises(lib_exc.Forbidden,
                           self.flavors_client.remove_flavor_access,
-                          new_flavor['id'],
+                          flavor['id'],
                           self.tenant_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f3592cc0-0306-483c-b210-9a7b5346eddc')
     def test_add_flavor_access_duplicate(self):
         # Create a new flavor.
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
-        new_flavor = self.client.create_flavor(name=flavor_name,
-                                               ram=self.ram, vcpus=self.vcpus,
-                                               disk=self.disk,
-                                               id=new_flavor_id,
-                                               is_public='False')['flavor']
-        self.addCleanup(self.client.delete_flavor, new_flavor['id'])
+        flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
+                                    disk=self.disk, is_public='False')
 
         # Add flavor access to a tenant.
-        self.client.add_flavor_access(new_flavor['id'], self.tenant_id)
-        self.addCleanup(self.client.remove_flavor_access,
-                        new_flavor['id'], self.tenant_id)
+        self.admin_flavors_client.add_flavor_access(flavor['id'],
+                                                    self.tenant_id)
+        self.addCleanup(self.admin_flavors_client.remove_flavor_access,
+                        flavor['id'], self.tenant_id)
 
         # An exception should be raised when adding flavor access to the same
         # tenant
         self.assertRaises(lib_exc.Conflict,
-                          self.client.add_flavor_access,
-                          new_flavor['id'],
+                          self.admin_flavors_client.add_flavor_access,
+                          flavor['id'],
                           self.tenant_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1f710927-3bc7-4381-9f82-0ca6e42644b7')
     def test_remove_flavor_access_not_found(self):
         # Create a new flavor.
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
-        new_flavor = self.client.create_flavor(name=flavor_name,
-                                               ram=self.ram, vcpus=self.vcpus,
-                                               disk=self.disk,
-                                               id=new_flavor_id,
-                                               is_public='False')['flavor']
-        self.addCleanup(self.client.delete_flavor, new_flavor['id'])
+        flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
+                                    disk=self.disk, is_public='False')
 
         # An exception should be raised when flavor access is not found
         self.assertRaises(lib_exc.NotFound,
-                          self.client.remove_flavor_access,
-                          new_flavor['id'],
-                          data_utils.rand_uuid())
+                          self.admin_flavors_client.remove_flavor_access,
+                          flavor['id'],
+                          self.os_alt.servers_client.tenant_id)
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs.py b/tempest/api/compute/admin/test_flavors_extra_specs.py
index 70e662c..d4e7bd5 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class FlavorsExtraSpecsTestJSON(base.BaseV2ComputeAdminTest):
@@ -29,16 +29,11 @@
     @classmethod
     def skip_checks(cls):
         super(FlavorsExtraSpecsTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
+        if not utils.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
             msg = "OS-FLV-EXT-DATA extension not enabled."
             raise cls.skipException(msg)
 
     @classmethod
-    def setup_clients(cls):
-        super(FlavorsExtraSpecsTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.flavors_client
-
-    @classmethod
     def resource_setup(cls):
         super(FlavorsExtraSpecsTestJSON, cls).resource_setup()
         flavor_name = data_utils.rand_name('test_flavor')
@@ -46,22 +41,23 @@
         vcpus = 1
         disk = 10
         ephemeral = 10
-        cls.new_flavor_id = data_utils.rand_int_id(start=1000)
+        new_flavor_id = data_utils.rand_int_id(start=1000)
         swap = 1024
         rxtx = 1
         # Create a flavor so as to set/get/unset extra specs
-        cls.flavor = cls.client.create_flavor(name=flavor_name,
-                                              ram=ram, vcpus=vcpus,
-                                              disk=disk,
-                                              id=cls.new_flavor_id,
-                                              ephemeral=ephemeral,
-                                              swap=swap,
-                                              rxtx_factor=rxtx)['flavor']
+        cls.flavor = cls.admin_flavors_client.create_flavor(
+            name=flavor_name,
+            ram=ram, vcpus=vcpus,
+            disk=disk,
+            id=new_flavor_id,
+            ephemeral=ephemeral,
+            swap=swap,
+            rxtx_factor=rxtx)['flavor']
 
     @classmethod
     def resource_cleanup(cls):
-        cls.client.delete_flavor(cls.flavor['id'])
-        cls.client.wait_for_resource_deletion(cls.flavor['id'])
+        cls.admin_flavors_client.delete_flavor(cls.flavor['id'])
+        cls.admin_flavors_client.wait_for_resource_deletion(cls.flavor['id'])
         super(FlavorsExtraSpecsTestJSON, cls).resource_cleanup()
 
     @decorators.idempotent_id('0b2f9d4b-1ca2-4b99-bb40-165d4bb94208')
@@ -71,46 +67,50 @@
         # Assigning extra specs values that are to be set
         specs = {"key1": "value1", "key2": "value2"}
         # SET extra specs to the flavor created in setUp
-        set_body = self.client.set_flavor_extra_spec(self.flavor['id'],
-                                                     **specs)['extra_specs']
+        set_body = self.admin_flavors_client.set_flavor_extra_spec(
+            self.flavor['id'], **specs)['extra_specs']
         self.assertEqual(set_body, specs)
         # GET extra specs and verify
-        get_body = (self.client.list_flavor_extra_specs(self.flavor['id'])
-                    ['extra_specs'])
+        get_body = (self.admin_flavors_client.list_flavor_extra_specs(
+            self.flavor['id'])['extra_specs'])
         self.assertEqual(get_body, specs)
 
         # UPDATE the value of the extra specs key1
         update_body = \
-            self.client.update_flavor_extra_spec(self.flavor['id'],
-                                                 "key1",
-                                                 key1="value")
+            self.admin_flavors_client.update_flavor_extra_spec(
+                self.flavor['id'], "key1", key1="value")
         self.assertEqual({"key1": "value"}, update_body)
 
         # GET extra specs and verify the value of the key2
         # is the same as before
-        get_body = (self.client.list_flavor_extra_specs(self.flavor['id'])
-                    ['extra_specs'])
+        get_body = self.admin_flavors_client.list_flavor_extra_specs(
+            self.flavor['id'])['extra_specs']
         self.assertEqual(get_body, {"key1": "value", "key2": "value2"})
 
         # UNSET extra specs that were set in this test
-        self.client.unset_flavor_extra_spec(self.flavor['id'], "key1")
-        self.client.unset_flavor_extra_spec(self.flavor['id'], "key2")
+        self.admin_flavors_client.unset_flavor_extra_spec(self.flavor['id'],
+                                                          "key1")
+        self.admin_flavors_client.unset_flavor_extra_spec(self.flavor['id'],
+                                                          "key2")
+        get_body = self.admin_flavors_client.list_flavor_extra_specs(
+            self.flavor['id'])['extra_specs']
+        self.assertEmpty(get_body)
 
     @decorators.idempotent_id('a99dad88-ae1c-4fba-aeb4-32f898218bd0')
     def test_flavor_non_admin_get_all_keys(self):
         specs = {"key1": "value1", "key2": "value2"}
-        self.client.set_flavor_extra_spec(self.flavor['id'], **specs)
-        body = (self.flavors_client.list_flavor_extra_specs(self.flavor['id'])
-                ['extra_specs'])
+        self.admin_flavors_client.set_flavor_extra_spec(self.flavor['id'],
+                                                        **specs)
+        body = (self.flavors_client.list_flavor_extra_specs(
+            self.flavor['id'])['extra_specs'])
 
         for key in specs:
             self.assertEqual(body[key], specs[key])
 
     @decorators.idempotent_id('12805a7f-39a3-4042-b989-701d5cad9c90')
     def test_flavor_non_admin_get_specific_key(self):
-        body = self.client.set_flavor_extra_spec(self.flavor['id'],
-                                                 key1="value1",
-                                                 key2="value2")['extra_specs']
+        body = self.admin_flavors_client.set_flavor_extra_spec(
+            self.flavor['id'], key1="value1", key2="value2")['extra_specs']
         self.assertEqual(body['key1'], 'value1')
         self.assertIn('key2', body)
         body = self.flavors_client.show_flavor_extra_spec(
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
index 767a1ca..bc32346 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -15,10 +15,10 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class FlavorsExtraSpecsNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -30,16 +30,11 @@
     @classmethod
     def skip_checks(cls):
         super(FlavorsExtraSpecsNegativeTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
+        if not utils.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
             msg = "OS-FLV-EXT-DATA extension not enabled."
             raise cls.skipException(msg)
 
     @classmethod
-    def setup_clients(cls):
-        super(FlavorsExtraSpecsNegativeTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.flavors_client
-
-    @classmethod
     def resource_setup(cls):
         super(FlavorsExtraSpecsNegativeTestJSON, cls).resource_setup()
 
@@ -48,25 +43,26 @@
         vcpus = 1
         disk = 10
         ephemeral = 10
-        cls.new_flavor_id = data_utils.rand_int_id(start=1000)
+        new_flavor_id = data_utils.rand_int_id(start=1000)
         swap = 1024
         rxtx = 1
         # Create a flavor
-        cls.flavor = cls.client.create_flavor(name=flavor_name,
-                                              ram=ram, vcpus=vcpus,
-                                              disk=disk,
-                                              id=cls.new_flavor_id,
-                                              ephemeral=ephemeral,
-                                              swap=swap,
-                                              rxtx_factor=rxtx)['flavor']
+        cls.flavor = cls.admin_flavors_client.create_flavor(
+            name=flavor_name,
+            ram=ram, vcpus=vcpus,
+            disk=disk,
+            id=new_flavor_id,
+            ephemeral=ephemeral,
+            swap=swap,
+            rxtx_factor=rxtx)['flavor']
 
     @classmethod
     def resource_cleanup(cls):
-        cls.client.delete_flavor(cls.flavor['id'])
-        cls.client.wait_for_resource_deletion(cls.flavor['id'])
+        cls.admin_flavors_client.delete_flavor(cls.flavor['id'])
+        cls.admin_flavors_client.wait_for_resource_deletion(cls.flavor['id'])
         super(FlavorsExtraSpecsNegativeTestJSON, cls).resource_cleanup()
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a00a3b81-5641-45a8-ab2b-4a8ec41e1d7d')
     def test_flavor_non_admin_set_keys(self):
         # Test to SET flavor extra spec as a user without admin privileges.
@@ -75,11 +71,11 @@
                           self.flavor['id'],
                           key1="value1", key2="value2")
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1ebf4ef8-759e-48fe-a801-d451d80476fb')
     def test_flavor_non_admin_update_specific_key(self):
         # non admin user is not allowed to update flavor extra spec
-        body = self.client.set_flavor_extra_spec(
+        body = self.admin_flavors_client.set_flavor_extra_spec(
             self.flavor['id'], key1="value1", key2="value2")['extra_specs']
         self.assertEqual(body['key1'], 'value1')
         self.assertRaises(lib_exc.Forbidden,
@@ -89,26 +85,26 @@
                           'key1',
                           key1='value1_new')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('28f12249-27c7-44c1-8810-1f382f316b11')
     def test_flavor_non_admin_unset_keys(self):
-        self.client.set_flavor_extra_spec(self.flavor['id'],
-                                          key1="value1", key2="value2")
+        self.admin_flavors_client.set_flavor_extra_spec(
+            self.flavor['id'], key1="value1", key2="value2")
 
         self.assertRaises(lib_exc.Forbidden,
                           self.flavors_client.unset_flavor_extra_spec,
                           self.flavor['id'],
                           'key1')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('440b9f3f-3c7f-4293-a106-0ceda350f8de')
     def test_flavor_unset_nonexistent_key(self):
         self.assertRaises(lib_exc.NotFound,
-                          self.client.unset_flavor_extra_spec,
+                          self.admin_flavors_client.unset_flavor_extra_spec,
                           self.flavor['id'],
                           'nonexistent_key')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('329a7be3-54b2-48be-8052-bf2ce4afd898')
     def test_flavor_get_nonexistent_key(self):
         self.assertRaises(lib_exc.NotFound,
@@ -116,22 +112,22 @@
                           self.flavor['id'],
                           "nonexistent_key")
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('25b822b8-9f49-44f6-80de-d99f0482e5cb')
     def test_flavor_update_mismatch_key(self):
         # the key will be updated should be match the key in the body
         self.assertRaises(lib_exc.BadRequest,
-                          self.client.update_flavor_extra_spec,
+                          self.admin_flavors_client.update_flavor_extra_spec,
                           self.flavor['id'],
                           "key2",
                           key1="value")
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f5889590-bf66-41cc-b4b1-6e6370cfd93f')
     def test_flavor_update_more_key(self):
         # there should be just one item in the request body
         self.assertRaises(lib_exc.BadRequest,
-                          self.client.update_flavor_extra_spec,
+                          self.admin_flavors_client.update_flavor_extra_spec,
                           self.flavor['id'],
                           "key1",
                           key1="value",
diff --git a/tempest/api/compute/admin/test_floating_ips_bulk.py b/tempest/api/compute/admin/test_floating_ips_bulk.py
index f38af56..ba19937 100644
--- a/tempest/api/compute/admin/test_floating_ips_bulk.py
+++ b/tempest/api/compute/admin/test_floating_ips_bulk.py
@@ -16,11 +16,11 @@
 import netaddr
 
 from tempest.api.compute import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions
-from tempest import test
 
 CONF = config.CONF
 
@@ -35,7 +35,7 @@
     @classmethod
     def setup_clients(cls):
         super(FloatingIPsBulkAdminTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.floating_ips_bulk_client
+        cls.client = cls.os_admin.floating_ips_bulk_client
 
     @classmethod
     def resource_setup(cls):
@@ -57,7 +57,7 @@
         return
 
     @decorators.idempotent_id('2c8f145f-8012-4cb8-ac7e-95a587f0e4ab')
-    @test.services('network')
+    @utils.services('network')
     def test_create_list_delete_floating_ips_bulk(self):
         # Create, List  and delete the Floating IPs Bulk
         pool = 'test_pool'
@@ -73,7 +73,7 @@
                         self.client.delete_floating_ips_bulk, self.ip_range)
         self.assertEqual(self.ip_range, body['ip_range'])
         ips_list = self.client.list_floating_ips_bulk()['floating_ip_info']
-        self.assertNotEqual(0, len(ips_list))
+        self.assertNotEmpty(ips_list)
         for ip in netaddr.IPNetwork(self.ip_range).iter_hosts():
             self.assertIn(str(ip), map(lambda x: x['address'], ips_list))
         body = (self.client.delete_floating_ips_bulk(self.ip_range)
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index b664955..00f3256 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -23,7 +23,7 @@
     @classmethod
     def setup_clients(cls):
         super(HostsAdminTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.hosts_client
+        cls.client = cls.os_admin.hosts_client
 
     @decorators.idempotent_id('9bfaf98d-e2cb-44b0-a07e-2558b2821e4f')
     def test_list_hosts(self):
@@ -36,7 +36,7 @@
         hosts = self.client.list_hosts()['hosts']
         host = hosts[0]
         hosts = self.client.list_hosts(zone=host['zone'])['hosts']
-        self.assertGreaterEqual(len(hosts), 1)
+        self.assertNotEmpty(hosts)
         self.assertIn(host, hosts)
 
     @decorators.idempotent_id('9af3c171-fbf4-4150-a624-22109733c2a6')
@@ -44,30 +44,25 @@
         # If send the request with a blank zone, the request will be successful
         # and it will return all the hosts list
         hosts = self.client.list_hosts(zone='')['hosts']
-        self.assertNotEqual(0, len(hosts))
+        self.assertNotEmpty(hosts)
 
     @decorators.idempotent_id('c6ddbadb-c94e-4500-b12f-8ffc43843ff8')
     def test_list_hosts_with_nonexistent_zone(self):
         # If send the request with a nonexistent zone, the request will be
         # successful and no hosts will be returned
         hosts = self.client.list_hosts(zone='xxx')['hosts']
-        self.assertEqual(0, len(hosts))
+        self.assertEmpty(hosts)
 
     @decorators.idempotent_id('38adbb12-aee2-4498-8aec-329c72423aa4')
     def test_show_host_detail(self):
         hosts = self.client.list_hosts()['hosts']
 
         hosts = [host for host in hosts if host['service'] == 'compute']
-        self.assertGreaterEqual(len(hosts), 1)
+        self.assertNotEmpty(hosts)
 
         for host in hosts:
             hostname = host['host_name']
             resources = self.client.show_host(hostname)['host']
-            self.assertGreaterEqual(len(resources), 1)
+            self.assertNotEmpty(resources)
             host_resource = resources[0]['resource']
-            self.assertIsNotNone(host_resource)
-            self.assertIsNotNone(host_resource['cpu'])
-            self.assertIsNotNone(host_resource['disk_gb'])
-            self.assertIsNotNone(host_resource['memory_mb'])
-            self.assertIsNotNone(host_resource['project'])
             self.assertEqual(hostname, host_resource['host'])
diff --git a/tempest/api/compute/admin/test_hosts_negative.py b/tempest/api/compute/admin/test_hosts_negative.py
index 07ab8c5..5bd8104 100644
--- a/tempest/api/compute/admin/test_hosts_negative.py
+++ b/tempest/api/compute/admin/test_hosts_negative.py
@@ -15,7 +15,6 @@
 from tempest.api.compute import base
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class HostsAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -24,8 +23,8 @@
     @classmethod
     def setup_clients(cls):
         super(HostsAdminNegativeTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.hosts_client
-        cls.non_admin_client = cls.os.hosts_client
+        cls.client = cls.os_admin.hosts_client
+        cls.non_admin_client = cls.os_primary.hosts_client
 
     @classmethod
     def resource_setup(cls):
@@ -35,26 +34,26 @@
             raise lib_exc.NotFound("no host found")
         cls.hostname = hosts[0]['host_name']
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('dd032027-0210-4d9c-860e-69b1b8deed5f')
     def test_list_hosts_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.list_hosts)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e75b0a1a-041f-47a1-8b4a-b72a6ff36d3f')
     def test_show_host_detail_with_nonexistent_hostname(self):
         self.assertRaises(lib_exc.NotFound,
                           self.client.show_host, 'nonexistent_hostname')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('19ebe09c-bfd4-4b7c-81a2-e2e0710f59cc')
     def test_show_host_detail_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.show_host,
                           self.hostname)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e40c72b1-0239-4ed6-ba21-81a184df1f7c')
     def test_update_host_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
@@ -63,7 +62,7 @@
                           status='enable',
                           maintenance_mode='enable')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fbe2bf3e-3246-4a95-a59f-94e4e298ec77')
     def test_update_host_with_invalid_status(self):
         # 'status' can only be 'enable' or 'disable'
@@ -73,7 +72,7 @@
                           status='invalid',
                           maintenance_mode='enable')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ab1e230e-5e22-41a9-8699-82b9947915d4')
     def test_update_host_with_invalid_maintenance_mode(self):
         # 'maintenance_mode' can only be 'enable' or 'disable'
@@ -83,7 +82,7 @@
                           status='enable',
                           maintenance_mode='invalid')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0cd85f75-6992-4a4a-b1bd-d11e37fd0eee')
     def test_update_host_without_param(self):
         # 'status' or 'maintenance_mode' needed for host update
@@ -91,7 +90,7 @@
                           self.client.update_host,
                           self.hostname)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('23c92146-2100-4d68-b2d6-c7ade970c9c1')
     def test_update_nonexistent_host(self):
         self.assertRaises(lib_exc.NotFound,
@@ -100,42 +99,42 @@
                           status='enable',
                           maintenance_mode='enable')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0d981ac3-4320-4898-b674-82b61fbb60e4')
     def test_startup_nonexistent_host(self):
         self.assertRaises(lib_exc.NotFound,
                           self.client.startup_host,
                           'nonexistent_hostname')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9f4ebb7e-b2ae-4e5b-a38f-0fd1bb0ddfca')
     def test_startup_host_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.startup_host,
                           self.hostname)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9e637444-29cf-4244-88c8-831ae82c31b6')
     def test_shutdown_nonexistent_host(self):
         self.assertRaises(lib_exc.NotFound,
                           self.client.shutdown_host,
                           'nonexistent_hostname')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a803529c-7e3f-4d3c-a7d6-8e1c203d27f6')
     def test_shutdown_host_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.shutdown_host,
                           self.hostname)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f86bfd7b-0b13-4849-ae29-0322e83ee58b')
     def test_reboot_nonexistent_host(self):
         self.assertRaises(lib_exc.NotFound,
                           self.client.reboot_host,
                           'nonexistent_hostname')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('02d79bb9-eb57-4612-abf6-2cb38897d2f8')
     def test_reboot_host_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index 4637024..404fd94 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -23,36 +23,33 @@
     @classmethod
     def setup_clients(cls):
         super(HypervisorAdminTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.hypervisor_client
+        cls.client = cls.os_admin.hypervisor_client
 
     def _list_hypervisors(self):
         # List of hypervisors
         hypers = self.client.list_hypervisors()['hypervisors']
         return hypers
 
-    def assertHypervisors(self, hypers):
-        self.assertGreater(len(hypers), 0, "No hypervisors found: %s" % hypers)
-
     @decorators.idempotent_id('7f0ceacd-c64d-4e96-b8ee-d02943142cc5')
     def test_get_hypervisor_list(self):
         # List of hypervisor and available hypervisors hostname
         hypers = self._list_hypervisors()
-        self.assertHypervisors(hypers)
+        self.assertNotEmpty(hypers, "No hypervisors found.")
 
     @decorators.idempotent_id('1e7fdac2-b672-4ad1-97a4-bad0e3030118')
     def test_get_hypervisor_list_details(self):
         # Display the details of the all hypervisor
         hypers = self.client.list_hypervisors(detail=True)['hypervisors']
-        self.assertHypervisors(hypers)
+        self.assertNotEmpty(hypers, "No hypervisors found.")
 
     @decorators.idempotent_id('94ff9eae-a183-428e-9cdb-79fde71211cc')
     def test_get_hypervisor_show_details(self):
         # Display the details of the specified hypervisor
         hypers = self._list_hypervisors()
-        self.assertHypervisors(hypers)
+        self.assertNotEmpty(hypers, "No hypervisors found.")
 
         details = self.client.show_hypervisor(hypers[0]['id'])['hypervisor']
-        self.assertGreater(len(details), 0)
+        self.assertNotEmpty(details)
         self.assertEqual(details['hypervisor_hostname'],
                          hypers[0]['hypervisor_hostname'])
 
@@ -60,19 +57,19 @@
     def test_get_hypervisor_show_servers(self):
         # Show instances about the specific hypervisors
         hypers = self._list_hypervisors()
-        self.assertHypervisors(hypers)
+        self.assertNotEmpty(hypers, "No hypervisors found.")
 
         hostname = hypers[0]['hypervisor_hostname']
         hypervisors = (self.client.list_servers_on_hypervisor(hostname)
                        ['hypervisors'])
-        self.assertGreater(len(hypervisors), 0)
+        self.assertNotEmpty(hypervisors)
 
     @decorators.idempotent_id('797e4f28-b6e0-454d-a548-80cc77c00816')
     def test_get_hypervisor_stats(self):
         # Verify the stats of the all hypervisor
         stats = (self.client.show_hypervisor_statistics()
                  ['hypervisor_statistics'])
-        self.assertGreater(len(stats), 0)
+        self.assertNotEmpty(stats)
 
     @decorators.idempotent_id('91a50d7d-1c2b-4f24-b55a-a1fe20efca70')
     def test_get_hypervisor_uptime(self):
@@ -104,7 +101,7 @@
             try:
                 uptime = (self.client.show_hypervisor_uptime(hyper['id'])
                           ['hypervisor'])
-                if len(uptime) > 0:
+                if uptime:
                     has_valid_uptime = True
                     break
             except Exception:
@@ -116,7 +113,7 @@
     @decorators.idempotent_id('d7e1805b-3b14-4a3b-b6fd-50ec6d9f361f')
     def test_search_hypervisor(self):
         hypers = self._list_hypervisors()
-        self.assertHypervisors(hypers)
+        self.assertNotEmpty(hypers, "No hypervisors found.")
         hypers = self.client.search_hypervisor(
             hypers[0]['hypervisor_hostname'])['hypervisors']
-        self.assertHypervisors(hypers)
+        self.assertNotEmpty(hypers, "No hypervisors found.")
diff --git a/tempest/api/compute/admin/test_hypervisor_negative.py b/tempest/api/compute/admin/test_hypervisor_negative.py
index 1a37312..431e823 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -14,10 +14,9 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class HypervisorAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -26,7 +25,7 @@
     @classmethod
     def setup_clients(cls):
         super(HypervisorAdminNegativeTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.hypervisor_client
+        cls.client = cls.os_admin.hypervisor_client
         cls.non_adm_client = cls.hypervisor_client
 
     def _list_hypervisors(self):
@@ -34,7 +33,7 @@
         hypers = self.client.list_hypervisors()['hypervisors']
         return hypers
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c136086a-0f67-4b2b-bc61-8482bd68989f')
     def test_show_nonexistent_hypervisor(self):
         nonexistent_hypervisor_id = data_utils.rand_uuid()
@@ -44,29 +43,29 @@
             self.client.show_hypervisor,
             nonexistent_hypervisor_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('51e663d0-6b89-4817-a465-20aca0667d03')
     def test_show_hypervisor_with_non_admin_user(self):
         hypers = self._list_hypervisors()
-        self.assertGreater(len(hypers), 0)
+        self.assertNotEmpty(hypers)
 
         self.assertRaises(
             lib_exc.Forbidden,
             self.non_adm_client.show_hypervisor,
             hypers[0]['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('2a0a3938-832e-4859-95bf-1c57c236b924')
     def test_show_servers_with_non_admin_user(self):
         hypers = self._list_hypervisors()
-        self.assertGreater(len(hypers), 0)
+        self.assertNotEmpty(hypers)
 
         self.assertRaises(
             lib_exc.Forbidden,
             self.non_adm_client.list_servers_on_hypervisor,
             hypers[0]['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('02463d69-0ace-4d33-a4a8-93d7883a2bba')
     def test_show_servers_with_nonexistent_hypervisor(self):
         nonexistent_hypervisor_id = data_utils.rand_uuid()
@@ -76,14 +75,14 @@
             self.client.list_servers_on_hypervisor,
             nonexistent_hypervisor_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e2b061bb-13f9-40d8-9d6e-d5bf17595849')
     def test_get_hypervisor_stats_with_non_admin_user(self):
         self.assertRaises(
             lib_exc.Forbidden,
             self.non_adm_client.show_hypervisor_statistics)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f60aa680-9a3a-4c7d-90e1-fae3a4891303')
     def test_get_nonexistent_hypervisor_uptime(self):
         nonexistent_hypervisor_id = data_utils.rand_uuid()
@@ -93,18 +92,18 @@
             self.client.show_hypervisor_uptime,
             nonexistent_hypervisor_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6c3461f9-c04c-4e2a-bebb-71dc9cb47df2')
     def test_get_hypervisor_uptime_with_non_admin_user(self):
         hypers = self._list_hypervisors()
-        self.assertGreater(len(hypers), 0)
+        self.assertNotEmpty(hypers)
 
         self.assertRaises(
             lib_exc.Forbidden,
             self.non_adm_client.show_hypervisor_uptime,
             hypers[0]['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('51b3d536-9b14-409c-9bce-c6f7c794994e')
     def test_get_hypervisor_list_with_non_admin_user(self):
         # List of hypervisor and available services with non admin user
@@ -112,7 +111,7 @@
             lib_exc.Forbidden,
             self.non_adm_client.list_hypervisors)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('dc02db05-e801-4c5f-bc8e-d915290ab345')
     def test_get_hypervisor_list_details_with_non_admin_user(self):
         # List of hypervisor details and available services with non admin user
@@ -120,7 +119,7 @@
             lib_exc.Forbidden,
             self.non_adm_client.list_hypervisors, detail=True)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('19a45cc1-1000-4055-b6d2-28e8b2ec4faa')
     def test_search_nonexistent_hypervisor(self):
         self.assertRaises(
@@ -128,11 +127,11 @@
             self.client.search_hypervisor,
             'nonexistent_hypervisor_name')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5b6a6c79-5dc1-4fa5-9c58-9c8085948e74')
     def test_search_hypervisor_with_non_admin_user(self):
         hypers = self._list_hypervisors()
-        self.assertGreater(len(hypers), 0)
+        self.assertNotEmpty(hypers)
 
         self.assertRaises(
             lib_exc.Forbidden,
diff --git a/tempest/api/compute/admin/test_instance_usage_audit_log.py b/tempest/api/compute/admin/test_instance_usage_audit_log.py
index b613a26..e4a2ffd 100644
--- a/tempest/api/compute/admin/test_instance_usage_audit_log.py
+++ b/tempest/api/compute/admin/test_instance_usage_audit_log.py
@@ -26,7 +26,7 @@
     @classmethod
     def setup_clients(cls):
         super(InstanceUsageAuditLogTestJSON, cls).setup_clients()
-        cls.adm_client = cls.os_adm.instance_usages_audit_log_client
+        cls.adm_client = cls.os_admin.instance_usages_audit_log_client
 
     @decorators.idempotent_id('25319919-33d9-424f-9f99-2c203ee48b9d')
     def test_list_instance_usage_audit_logs(self):
diff --git a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
index 16c39c4..de8e221 100644
--- a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
+++ b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
@@ -20,7 +20,6 @@
 from tempest.api.compute import base
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class InstanceUsageAuditLogNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -28,9 +27,9 @@
     @classmethod
     def setup_clients(cls):
         super(InstanceUsageAuditLogNegativeTestJSON, cls).setup_clients()
-        cls.adm_client = cls.os_adm.instance_usages_audit_log_client
+        cls.adm_client = cls.os_admin.instance_usages_audit_log_client
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a9d33178-d2c9-4131-ad3b-f4ca8d0308a2')
     def test_instance_usage_audit_logs_with_nonadmin_user(self):
         # the instance_usage_audit_logs API just can be accessed by admin user
@@ -43,7 +42,7 @@
                           show_instance_usage_audit_log,
                           urllib.quote(now.strftime("%Y-%m-%d %H:%M:%S")))
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9b952047-3641-41c7-ba91-a809fc5974c8')
     def test_get_instance_usage_audit_logs_with_invalid_time(self):
         self.assertRaises(lib_exc.BadRequest,
diff --git a/tempest/api/compute/admin/test_keypairs_v210.py b/tempest/api/compute/admin/test_keypairs_v210.py
index 82d3bae..e24c7c1 100644
--- a/tempest/api/compute/admin/test_keypairs_v210.py
+++ b/tempest/api/compute/admin/test_keypairs_v210.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 from tempest.api.compute.keypairs import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 
@@ -25,8 +25,8 @@
     @classmethod
     def setup_clients(cls):
         super(KeyPairsV210TestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.keypairs_client
-        cls.non_admin_client = cls.os.keypairs_client
+        cls.client = cls.os_admin.keypairs_client
+        cls.non_admin_client = cls.os_primary.keypairs_client
 
     def _create_and_check_keypairs(self, user_id):
         key_list = list()
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index 39797f7..14be947 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -13,25 +13,29 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import time
 
+from oslo_log import log as logging
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import compute
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
-class LiveBlockMigrationTestJSON(base.BaseV2ComputeAdminTest):
+class LiveMigrationTest(base.BaseV2ComputeAdminTest):
     max_microversion = '2.24'
     block_migration = None
 
     @classmethod
     def skip_checks(cls):
-        super(LiveBlockMigrationTestJSON, cls).skip_checks()
+        super(LiveMigrationTest, cls).skip_checks()
 
         if not CONF.compute_feature_enabled.live_migration:
             skip_msg = ("%s skipped as live-migration is "
@@ -43,26 +47,8 @@
 
     @classmethod
     def setup_clients(cls):
-        super(LiveBlockMigrationTestJSON, cls).setup_clients()
-        cls.admin_hosts_client = cls.os_adm.hosts_client
-        cls.admin_servers_client = cls.os_adm.servers_client
-        cls.admin_migration_client = cls.os_adm.migrations_client
-
-    @classmethod
-    def _get_compute_hostnames(cls):
-        body = cls.admin_hosts_client.list_hosts()['hosts']
-        return [
-            host_record['host_name']
-            for host_record in body
-            if host_record['service'] == 'compute'
-        ]
-
-    def _get_server_details(self, server_id):
-        body = self.admin_servers_client.show_server(server_id)['server']
-        return body
-
-    def _get_host_for_server(self, server_id):
-        return self._get_server_details(server_id)['OS-EXT-SRV-ATTR:host']
+        super(LiveMigrationTest, cls).setup_clients()
+        cls.admin_migration_client = cls.os_admin.migrations_client
 
     def _migrate_server_to(self, server_id, dest_host, volume_backed=False):
         kwargs = dict()
@@ -76,10 +62,21 @@
             server_id, host=dest_host, block_migration=block_migration,
             **kwargs)
 
-    def _get_host_other_than(self, host):
-        for target_host in self._get_compute_hostnames():
-            if host != target_host:
-                return target_host
+    def _live_migrate(self, server_id, target_host, state,
+                      volume_backed=False):
+        self._migrate_server_to(server_id, target_host, volume_backed)
+        waiters.wait_for_server_status(self.servers_client, server_id, state)
+        migration_list = (self.admin_migration_client.list_migrations()
+                          ['migrations'])
+
+        msg = ("Live Migration failed. Migrations list for Instance "
+               "%s: [" % server_id)
+        for live_migration in migration_list:
+            if (live_migration['instance_uuid'] == server_id):
+                msg += "\n%s" % live_migration
+        msg += "]"
+        self.assertEqual(target_host, self.get_host_for_server(server_id),
+                         msg)
 
     def _test_live_migration(self, state='ACTIVE', volume_backed=False):
         """Tests live migration between two hosts.
@@ -95,27 +92,23 @@
         # Live migrate an instance to another host
         server_id = self.create_test_server(wait_until="ACTIVE",
                                             volume_backed=volume_backed)['id']
-        actual_host = self._get_host_for_server(server_id)
-        target_host = self._get_host_other_than(actual_host)
+        source_host = self.get_host_for_server(server_id)
+        destination_host = self.get_host_other_than(server_id)
 
         if state == 'PAUSED':
             self.admin_servers_client.pause_server(server_id)
             waiters.wait_for_server_status(self.admin_servers_client,
                                            server_id, state)
 
-        self._migrate_server_to(server_id, target_host, volume_backed)
-        waiters.wait_for_server_status(self.servers_client, server_id, state)
-        migration_list = (self.admin_migration_client.list_migrations()
-                          ['migrations'])
-
-        msg = ("Live Migration failed. Migrations list for Instance "
-               "%s: [" % server_id)
-        for live_migration in migration_list:
-            if (live_migration['instance_uuid'] == server_id):
-                msg += "\n%s" % live_migration
-        msg += "]"
-        self.assertEqual(target_host, self._get_host_for_server(server_id),
-                         msg)
+        LOG.info("Live migrate from source %s to destination %s",
+                 source_host, destination_host)
+        self._live_migrate(server_id, destination_host, state, volume_backed)
+        if CONF.compute_feature_enabled.live_migrate_back_and_forth:
+            # If live_migrate_back_and_forth is enabled it is a grenade job.
+            # Therefore test should validate whether LM is compatible in both
+            # ways, so live migrate VM back to the source host
+            LOG.info("Live migrate back to source %s", source_host)
+            self._live_migrate(server_id, source_host, state, volume_backed)
 
     @decorators.idempotent_id('1dce86b8-eb04-4c03-a9d8-9c1dc3ee0c7b')
     def test_live_block_migration(self):
@@ -129,7 +122,7 @@
 
     @decorators.skip_because(bug="1524898")
     @decorators.idempotent_id('5071cf17-3004-4257-ae61-73a84e28badd')
-    @test.services('volume')
+    @utils.services('volume')
     def test_volume_backed_live_migration(self):
         self._test_live_migration(volume_backed=True)
 
@@ -143,21 +136,100 @@
     def test_iscsi_volume(self):
         server = self.create_test_server(wait_until="ACTIVE")
         server_id = server['id']
-        actual_host = self._get_host_for_server(server_id)
-        target_host = self._get_host_other_than(actual_host)
+        target_host = self.get_host_other_than(server_id)
 
         volume = self.create_volume()
 
         # Attach the volume to the server
         self.attach_volume(server, volume, device='/dev/xvdb')
-
+        server = self.admin_servers_client.show_server(server_id)['server']
+        volume_id1 = server["os-extended-volumes:volumes_attached"][0]["id"]
         self._migrate_server_to(server_id, target_host)
         waiters.wait_for_server_status(self.servers_client,
                                        server_id, 'ACTIVE')
-        self.assertEqual(target_host, self._get_host_for_server(server_id))
+
+        server = self.admin_servers_client.show_server(server_id)['server']
+        volume_id2 = server["os-extended-volumes:volumes_attached"][0]["id"]
+
+        self.assertEqual(target_host, self.get_host_for_server(server_id))
+        self.assertEqual(volume_id1, volume_id2)
 
 
-class LiveAutoBlockMigrationV225TestJSON(LiveBlockMigrationTestJSON):
+class LiveMigrationRemoteConsolesV26Test(LiveMigrationTest):
+    min_microversion = '2.6'
+    max_microversion = 'latest'
+
+    @decorators.idempotent_id('6190af80-513e-4f0f-90f2-9714e84955d7')
+    @testtools.skipUnless(CONF.compute_feature_enabled.serial_console,
+                          'Serial console not supported.')
+    @testtools.skipUnless(
+        compute.is_scheduler_filter_enabled("DifferentHostFilter"),
+        'DifferentHostFilter is not available.')
+    def test_live_migration_serial_console(self):
+        """Test the live-migration of an instance which has a serial console
+
+        The serial console feature of an instance uses ports on the host.
+        These ports need to be updated when they are already in use by
+        another instance on the target host. This test checks if this
+        update behavior is correctly done, by connecting to the serial
+        consoles of the instances before and after the live migration.
+        """
+        server01_id = self.create_test_server(wait_until='ACTIVE')['id']
+        hints = {'different_host': server01_id}
+        server02_id = self.create_test_server(scheduler_hints=hints,
+                                              wait_until='ACTIVE')['id']
+        host01_id = self.get_host_for_server(server01_id)
+        host02_id = self.get_host_for_server(server02_id)
+        self.assertNotEqual(host01_id, host02_id)
+
+        # At this step we have 2 instances on different hosts, both with
+        # serial consoles, both with port 10000 (the default value).
+        # https://bugs.launchpad.net/nova/+bug/1455252 describes the issue
+        # when live-migrating in such a scenario.
+
+        self._verify_console_interaction(server01_id)
+        self._verify_console_interaction(server02_id)
+
+        self._migrate_server_to(server01_id, host02_id)
+        waiters.wait_for_server_status(self.servers_client,
+                                       server01_id, 'ACTIVE')
+        self.assertEqual(host02_id, self.get_host_for_server(server01_id))
+        self._verify_console_interaction(server01_id)
+        # At this point, both instances have a valid serial console
+        # connection, which means the ports got updated.
+
+    def _verify_console_interaction(self, server_id):
+        body = self.servers_client.get_remote_console(server_id,
+                                                      console_type='serial',
+                                                      protocol='serial')
+        console_url = body['remote_console']['url']
+        data = "test_live_migration_serial_console"
+        console_output = ''
+        t = 0.0
+        interval = 0.1
+
+        ws = compute.create_websocket(console_url)
+        try:
+            # NOTE (markus_z): It can take a long time until the terminal
+            # of the instance is available for interaction. Hence the
+            # long timeout value.
+            while data not in console_output and t <= 120.0:
+                try:
+                    ws.send_frame(data)
+                    recieved = ws.receive_frame()
+                    console_output += recieved
+                except Exception:
+                    # In case we had an issue with send/receive on the
+                    # websocket connection, we create a new one.
+                    ws = compute.create_websocket(console_url)
+                time.sleep(interval)
+                t += interval
+        finally:
+            ws.close()
+        self.assertIn(data, console_output)
+
+
+class LiveAutoBlockMigrationV225Test(LiveMigrationTest):
     min_microversion = '2.25'
     max_microversion = 'latest'
     block_migration = 'auto'
diff --git a/tempest/api/compute/test_live_block_migration_negative.py b/tempest/api/compute/admin/test_live_migration_negative.py
similarity index 69%
rename from tempest/api/compute/test_live_block_migration_negative.py
rename to tempest/api/compute/admin/test_live_migration_negative.py
index 40d0746..deabbc2 100644
--- a/tempest/api/compute/test_live_block_migration_negative.py
+++ b/tempest/api/compute/admin/test_live_migration_negative.py
@@ -14,35 +14,29 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
 
-class LiveBlockMigrationNegativeTestJSON(base.BaseV2ComputeAdminTest):
+class LiveMigrationNegativeTest(base.BaseV2ComputeAdminTest):
     @classmethod
     def skip_checks(cls):
-        super(LiveBlockMigrationNegativeTestJSON, cls).skip_checks()
+        super(LiveMigrationNegativeTest, cls).skip_checks()
         if not CONF.compute_feature_enabled.live_migration:
             raise cls.skipException("Live migration is not enabled")
 
-    @classmethod
-    def setup_clients(cls):
-        super(LiveBlockMigrationNegativeTestJSON, cls).setup_clients()
-        cls.admin_servers_client = cls.os_adm.servers_client
-
     def _migrate_server_to(self, server_id, dest_host):
         bmflm = CONF.compute_feature_enabled.block_migration_for_live_migration
         self.admin_servers_client.live_migrate_server(
             server_id, host=dest_host, block_migration=bmflm,
             disk_over_commit=False)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7fb7856e-ae92-44c9-861a-af62d7830bcb')
     def test_invalid_host_for_migration(self):
         # Migrating to an invalid host should not change the status
@@ -53,3 +47,17 @@
                           server['id'], target_host)
         waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'ACTIVE')
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('6e2f94f5-2ee8-4830-bef5-5bc95bb0795b')
+    def test_live_block_migration_suspended(self):
+        server = self.create_test_server(wait_until="ACTIVE")
+
+        self.admin_servers_client.suspend_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client,
+                                       server['id'], 'SUSPENDED')
+
+        destination_host = self.get_host_other_than(server['id'])
+
+        self.assertRaises(lib_exc.Conflict, self._migrate_server_to,
+                          server['id'], destination_host)
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index 21f5c68..a626ebb 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -15,9 +15,9 @@
 import testtools
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions
 
@@ -29,9 +29,7 @@
     @classmethod
     def setup_clients(cls):
         super(MigrationsAdminTest, cls).setup_clients()
-        cls.client = cls.os_adm.migrations_client
-        cls.flavors_admin_client = cls.os_adm.flavors_client
-        cls.admin_servers_client = cls.os_adm.servers_client
+        cls.client = cls.os_admin.migrations_client
 
     @decorators.idempotent_id('75c0b83d-72a0-4cf8-a153-631e83e7d53f')
     def test_list_migrations(self):
@@ -55,8 +53,8 @@
 
     def _flavor_clean_up(self, flavor_id):
         try:
-            self.flavors_admin_client.delete_flavor(flavor_id)
-            self.flavors_admin_client.wait_for_resource_deletion(flavor_id)
+            self.admin_flavors_client.delete_flavor(flavor_id)
+            self.admin_flavors_client.wait_for_resource_deletion(flavor_id)
         except exceptions.NotFound:
             pass
 
@@ -69,9 +67,9 @@
 
         # First we have to create a flavor that we can delete so make a copy
         # of the normal flavor from which we'd create a server.
-        flavor = self.flavors_admin_client.show_flavor(
+        flavor = self.admin_flavors_client.show_flavor(
             self.flavor_ref)['flavor']
-        flavor = self.flavors_admin_client.create_flavor(
+        flavor = self.admin_flavors_client.create_flavor(
             name=data_utils.rand_name('test_resize_flavor_'),
             ram=flavor['ram'],
             disk=flavor['disk'],
diff --git a/tempest/api/compute/admin/test_networks.py b/tempest/api/compute/admin/test_networks.py
index 12ae864..acb0d90 100644
--- a/tempest/api/compute/admin/test_networks.py
+++ b/tempest/api/compute/admin/test_networks.py
@@ -30,7 +30,7 @@
     @classmethod
     def setup_clients(cls):
         super(NetworksTest, cls).setup_clients()
-        cls.client = cls.os_adm.compute_networks_client
+        cls.client = cls.os_admin.compute_networks_client
 
     @decorators.idempotent_id('d206d211-8912-486f-86e2-a9d090d1f416')
     def test_get_network(self):
@@ -62,4 +62,4 @@
             self.assertIn(configured_network, [x['label'] for x in networks])
         else:
             network_labels = [x['label'] for x in networks]
-            self.assertGreaterEqual(len(network_labels), 1)
+            self.assertNotEmpty(network_labels)
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 3e9dc79..5894e80 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -18,7 +18,7 @@
 
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 LOG = logging.getLogger(__name__)
@@ -35,7 +35,7 @@
     @classmethod
     def setup_clients(cls):
         super(QuotasAdminTestJSON, cls).setup_clients()
-        cls.adm_client = cls.os_adm.quotas_client
+        cls.adm_client = cls.os_admin.quotas_client
 
     @classmethod
     def resource_setup(cls):
@@ -153,12 +153,11 @@
     @classmethod
     def resource_setup(cls):
         super(QuotaClassesAdminTestJSON, cls).resource_setup()
-        cls.adm_client = cls.os_adm.quota_classes_client
+        cls.adm_client = cls.os_admin.quota_classes_client
 
     def _restore_default_quotas(self, original_defaults):
         LOG.debug("restoring quota class defaults")
-        self.adm_client.update_quota_class_set(
-            'default', **original_defaults)['quota_class_set']
+        self.adm_client.update_quota_class_set('default', **original_defaults)
 
     # NOTE(sdague): this test is problematic as it changes
     # global state, and possibly needs to be part of a set of
@@ -169,7 +168,6 @@
         LOG.debug("get the current 'default' quota class values")
         body = (self.adm_client.show_quota_class_set('default')
                 ['quota_class_set'])
-        self.assertIn('id', body)
         self.assertEqual('default', body.pop('id'))
         # restore the defaults when the test is done
         self.addCleanup(self._restore_default_quotas, body.copy())
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index 0850205..5ef7ee4 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -13,11 +13,11 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -28,8 +28,8 @@
     @classmethod
     def setup_clients(cls):
         super(QuotasAdminNegativeTestJSON, cls).setup_clients()
-        cls.client = cls.os.quotas_client
-        cls.adm_client = cls.os_adm.quotas_client
+        cls.client = cls.os_primary.quotas_client
+        cls.adm_client = cls.os_admin.quotas_client
         cls.sg_client = cls.security_groups_client
         cls.sgr_client = cls.security_group_rules_client
 
@@ -51,7 +51,7 @@
         self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
                         **{quota_item: default_quota_value})
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('733abfe8-166e-47bb-8363-23dbd7ff3476')
     def test_update_quota_normal_user(self):
         self.assertRaises(lib_exc.Forbidden,
@@ -61,7 +61,7 @@
 
     # TODO(afazekas): Add dedicated tenant to the skipped quota tests.
     # It can be moved into the setUpClass as well.
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('91058876-9947-4807-9f22-f6eb17140d9b')
     def test_create_server_when_cpu_quota_is_full(self):
         # Disallow server creation when tenant's vcpu quota is full
@@ -69,7 +69,7 @@
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.create_test_server)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6fdd7012-584d-4327-a61c-49122e0d5864')
     def test_create_server_when_memory_quota_is_full(self):
         # Disallow server creation when tenant's memory quota is full
@@ -77,7 +77,7 @@
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.create_test_server)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7c6be468-0274-449a-81c3-ac1c32ee0161')
     def test_create_server_when_instances_quota_is_full(self):
         # Once instances quota limit is reached, disallow server creation
@@ -87,8 +87,9 @@
 
     @decorators.skip_because(bug="1186354",
                              condition=CONF.service_available.neutron)
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7c6c8f3b-2bf6-4918-b240-57b136a66aa0')
-    @test.services('network')
+    @utils.services('network')
     def test_security_groups_exceed_limit(self):
         # Negative test: Creation Security Groups over limit should FAIL
         # Set the quota to number of used security groups
@@ -105,9 +106,9 @@
 
     @decorators.skip_because(bug="1186354",
                              condition=CONF.service_available.neutron)
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6e9f436d-f1ed-4f8e-a493-7275dfaa4b4d')
-    @test.services('network')
+    @utils.services('network')
     def test_security_groups_rules_exceed_limit(self):
         # Negative test: Creation of Security Group Rules should FAIL
         # when we reach limit maxSecurityGroupRules
diff --git a/tempest/api/compute/admin/test_security_group_default_rules.py b/tempest/api/compute/admin/test_security_group_default_rules.py
index ab97bd4..f2f3b57 100644
--- a/tempest/api/compute/admin/test_security_group_default_rules.py
+++ b/tempest/api/compute/admin/test_security_group_default_rules.py
@@ -38,7 +38,7 @@
     @classmethod
     def setup_clients(cls):
         super(SecurityGroupDefaultRulesTest, cls).setup_clients()
-        cls.adm_client = cls.os_adm.security_group_default_rules_client
+        cls.adm_client = cls.os_admin.security_group_default_rules_client
 
     def _create_security_group_default_rules(self, ip_protocol='tcp',
                                              from_port=22, to_port=22,
@@ -111,7 +111,7 @@
                         rule['id'])
         rules = (self.adm_client.list_security_group_default_rules()
                  ['security_group_default_rules'])
-        self.assertNotEqual(0, len(rules))
+        self.assertNotEmpty(rules)
         self.assertIn(rule, rules)
 
     @decorators.idempotent_id('15cbb349-86b4-4f71-a048-04b7ef3f150b')
diff --git a/tempest/api/compute/admin/test_security_groups.py b/tempest/api/compute/admin/test_security_groups.py
index 2efe755..ff9caa3 100644
--- a/tempest/api/compute/admin/test_security_groups.py
+++ b/tempest/api/compute/admin/test_security_groups.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class SecurityGroupsTestAdminJSON(base.BaseV2ComputeAdminTest):
@@ -24,7 +24,7 @@
     @classmethod
     def setup_clients(cls):
         super(SecurityGroupsTestAdminJSON, cls).setup_clients()
-        cls.adm_client = cls.os_adm.compute_security_groups_client
+        cls.adm_client = cls.os_admin.compute_security_groups_client
         cls.client = cls.security_groups_client
 
     def _delete_security_group(self, securitygroup_id, admin=True):
@@ -34,7 +34,7 @@
             self.client.delete_security_group(securitygroup_id)
 
     @decorators.idempotent_id('49667619-5af9-4c63-ab5d-2cfdd1c8f7f1')
-    @test.services('network')
+    @utils.services('network')
     def test_list_security_groups_list_all_tenants_filter(self):
         # Admin can list security groups of all tenants
         # List of all security groups created
diff --git a/tempest/api/compute/admin/test_server_diagnostics.py b/tempest/api/compute/admin/test_server_diagnostics.py
new file mode 100644
index 0000000..005efdd
--- /dev/null
+++ b/tempest/api/compute/admin/test_server_diagnostics.py
@@ -0,0 +1,76 @@
+# Copyright 2017 Mirantis Inc.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import time
+
+from tempest.api.compute import base
+from tempest.lib import decorators
+
+
+class ServerDiagnosticsTest(base.BaseV2ComputeAdminTest):
+    min_microversion = None
+    max_microversion = '2.47'
+
+    @classmethod
+    def setup_clients(cls):
+        super(ServerDiagnosticsTest, cls).setup_clients()
+        cls.client = cls.os_admin.servers_client
+
+    @decorators.idempotent_id('31ff3486-b8a0-4f56-a6c0-aab460531db3')
+    def test_get_server_diagnostics(self):
+        server_id = self.create_test_server(wait_until='ACTIVE')['id']
+        diagnostics = self.client.show_server_diagnostics(server_id)
+
+        # NOTE(snikitin): Before microversion 2.48 response data from each
+        # hypervisor (libvirt, xen, vmware) was different. None of the fields
+        # were equal. As this test is common for libvirt, xen and vmware CI
+        # jobs we can't check any field in the response because all fields are
+        # different.
+        self.assertNotEmpty(diagnostics)
+
+
+class ServerDiagnosticsV248Test(base.BaseV2ComputeAdminTest):
+    min_microversion = '2.48'
+    max_microversion = 'latest'
+
+    @classmethod
+    def setup_clients(cls):
+        super(ServerDiagnosticsV248Test, cls).setup_clients()
+        cls.client = cls.os_admin.servers_client
+
+    @decorators.idempotent_id('64d0d48c-dff1-11e6-bf01-fe55135034f3')
+    def test_get_server_diagnostics(self):
+        server_id = self.create_test_server(wait_until='ACTIVE')['id']
+        # Response status and filed types will be checked by json schema
+        self.client.show_server_diagnostics(server_id)
+
+        # NOTE(snikitin): This is a special case for Xen hypervisor. In Xen
+        # case we're getting diagnostics stats from the RRDs which are updated
+        # every 5 seconds. It means that diagnostics information may be
+        # incomplete during first 5 seconds of VM life. In such cases methods
+        # which get diagnostics stats from Xen may raise exceptions or
+        # return `NaN` values. Such behavior must be handled correctly.
+        # Response must contain all diagnostics fields (may be with `None`
+        # values) and response status must be 200. Line above checks it by
+        # json schema.
+        time.sleep(10)
+        diagnostics = self.client.show_server_diagnostics(server_id)
+
+        # NOTE(snikitin): After 10 seconds diagnostics fields must contain
+        # not None values. But we will check only "memory_details.maximum"
+        # field because only this field meets all the following conditions:
+        # 1) This field may be unset because of Xen 5 seconds timeout.
+        # 2) This field is present in responses from all three supported
+        #    hypervisors (libvirt, xen, vmware).
+        self.assertIsNotNone(diagnostics['memory_details']['maximum'])
diff --git a/tempest/api/compute/admin/test_server_diagnostics_negative.py b/tempest/api/compute/admin/test_server_diagnostics_negative.py
new file mode 100644
index 0000000..d5b6674
--- /dev/null
+++ b/tempest/api/compute/admin/test_server_diagnostics_negative.py
@@ -0,0 +1,40 @@
+# Copyright 2017 Mirantis Inc.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.compute import base
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+
+class ServerDiagnosticsNegativeTest(base.BaseV2ComputeAdminTest):
+    min_microversion = None
+    max_microversion = '2.47'
+
+    @classmethod
+    def setup_clients(cls):
+        super(ServerDiagnosticsNegativeTest, cls).setup_clients()
+        cls.client = cls.servers_client
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('e84e2234-60d2-42fa-8b30-e2d3049724ac')
+    def test_get_server_diagnostics_by_non_admin(self):
+        # Non-admin user cannot view server diagnostics according to policy
+        server_id = self.create_test_server(wait_until='ACTIVE')['id']
+        self.assertRaises(lib_exc.Forbidden,
+                          self.client.show_server_diagnostics, server_id)
+
+
+class ServerDiagnosticsNegativeV248Test(ServerDiagnosticsNegativeTest):
+    min_microversion = '2.48'
+    max_microversion = 'latest'
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 9e1c0e9..3f06c4e 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -13,12 +13,9 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common import compute
-from tempest.common import fixed_network
-from tempest.common.utils import data_utils
 from tempest.common import waiters
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
@@ -27,7 +24,7 @@
     @classmethod
     def setup_clients(cls):
         super(ServersAdminTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.servers_client
+        cls.client = cls.os_admin.servers_client
         cls.non_admin_client = cls.servers_client
 
     @classmethod
@@ -35,14 +32,15 @@
         super(ServersAdminTestJSON, cls).resource_setup()
 
         cls.s1_name = data_utils.rand_name(cls.__name__ + '-server')
-        server = cls.create_test_server(name=cls.s1_name,
-                                        wait_until='ACTIVE')
+        server = cls.create_test_server(name=cls.s1_name)
         cls.s1_id = server['id']
 
         cls.s2_name = data_utils.rand_name(cls.__name__ + '-server')
         server = cls.create_test_server(name=cls.s2_name,
                                         wait_until='ACTIVE')
         cls.s2_id = server['id']
+        waiters.wait_for_server_status(cls.non_admin_client,
+                                       cls.s1_id, 'ACTIVE')
 
     @decorators.idempotent_id('06f960bb-15bb-48dc-873d-f96e89be7870')
     def test_list_servers_filter_by_error_status(self):
@@ -65,7 +63,7 @@
         params = {'status': 'invalid_status'}
         body = self.client.list_servers(detail=True, **params)
         servers = body['servers']
-        self.assertEqual([], servers)
+        self.assertEmpty(servers)
 
     @decorators.idempotent_id('51717b38-bdc1-458b-b636-1cf82d99f62f')
     def test_list_servers_by_admin(self):
@@ -92,7 +90,7 @@
         self.assertIn(self.s1_name, servers_name)
         self.assertIn(self.s2_name, servers_name)
 
-    @test.related_bug('1659811')
+    @decorators.related_bug('1659811')
     @decorators.idempotent_id('7e5d6b8f-454a-4ba1-8ae2-da857af8338b')
     def test_list_servers_by_admin_with_specified_tenant(self):
         # In nova v2, tenant_id is ignored unless all_tenants is specified
@@ -126,26 +124,17 @@
     @decorators.idempotent_id('86c7a8f7-50cf-43a9-9bac-5b985317134f')
     def test_list_servers_filter_by_exist_host(self):
         # Filter the list of servers by existent host
-        name = data_utils.rand_name(self.__class__.__name__ + '-server')
-        network = self.get_tenant_network()
-        network_kwargs = fixed_network.set_networks_kwarg(network)
-        # We need to create the server as an admin, so we can't use
-        # self.create_test_server() here as this method creates the server
-        # in the "primary" (i.e non-admin) tenant.
-        test_server, _ = compute.create_test_server(
-            self.os_adm, wait_until="ACTIVE", name=name, **network_kwargs)
-        self.addCleanup(self.client.delete_server, test_server['id'])
-        server = self.client.show_server(test_server['id'])['server']
-        self.assertEqual(server['status'], 'ACTIVE')
+        server = self.client.show_server(self.s1_id)['server']
         hostname = server['OS-EXT-SRV-ATTR:host']
-        params = {'host': hostname}
-        body = self.client.list_servers(**params)
-        servers = body['servers']
-        nonexistent_params = {'host': 'nonexistent_host'}
+        params = {'host': hostname, 'all_tenants': '1'}
+        servers = self.client.list_servers(**params)['servers']
+        self.assertIn(server['id'], map(lambda x: x['id'], servers))
+
+        nonexistent_params = {'host': 'nonexistent_host',
+                              'all_tenants': '1'}
         nonexistent_body = self.client.list_servers(**nonexistent_params)
         nonexistent_servers = nonexistent_body['servers']
-        self.assertIn(test_server['id'], map(lambda x: x['id'], servers))
-        self.assertNotIn(test_server['id'],
+        self.assertNotIn(server['id'],
                          map(lambda x: x['id'], nonexistent_servers))
 
     @decorators.idempotent_id('ee8ae470-db70-474d-b752-690b7892cab1')
@@ -164,17 +153,6 @@
         server = self.client.show_server(self.s1_id)['server']
         self.assertEqual(server['status'], 'ACTIVE')
 
-    @decorators.skip_because(bug="1240043")
-    @decorators.idempotent_id('31ff3486-b8a0-4f56-a6c0-aab460531db3')
-    def test_get_server_diagnostics_by_admin(self):
-        # Retrieve server diagnostics by admin user
-        diagnostic = self.client.show_server_diagnostics(self.s1_id)
-        basic_attrs = ['rx_packets', 'rx_errors', 'rx_drop',
-                       'tx_packets', 'tx_errors', 'tx_drop',
-                       'read_req', 'write_req', 'cpu', 'memory']
-        for key in basic_attrs:
-            self.assertIn(key, str(diagnostic.keys()))
-
     @decorators.idempotent_id('682cb127-e5bb-4f53-87ce-cb9003604442')
     def test_rebuild_server_in_error_state(self):
         # The server in error state should be rebuilt using the provided
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 1283629..3656770 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -16,12 +16,11 @@
 
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -32,54 +31,34 @@
     @classmethod
     def setup_clients(cls):
         super(ServersAdminNegativeTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.servers_client
-        cls.non_adm_client = cls.servers_client
-        cls.flavors_client = cls.os_adm.flavors_client
-        cls.quotas_client = cls.os_adm.quotas_client
+        cls.client = cls.os_admin.servers_client
+        cls.quotas_client = cls.os_admin.quotas_client
 
     @classmethod
     def resource_setup(cls):
         super(ServersAdminNegativeTestJSON, cls).resource_setup()
         cls.tenant_id = cls.client.tenant_id
 
-        cls.s1_name = data_utils.rand_name(cls.__name__ + '-server')
-        server = cls.create_test_server(name=cls.s1_name,
-                                        wait_until='ACTIVE')
+        server = cls.create_test_server(wait_until='ACTIVE')
         cls.s1_id = server['id']
 
-    def _get_unused_flavor_id(self):
-        flavor_id = data_utils.rand_int_id(start=1000)
-        while True:
-            try:
-                self.flavors_client.show_flavor(flavor_id)
-            except lib_exc.NotFound:
-                break
-            flavor_id = data_utils.rand_int_id(start=1000)
-        return flavor_id
-
     @decorators.idempotent_id('28dcec23-f807-49da-822c-56a92ea3c687')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_resize_server_using_overlimit_ram(self):
         # NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
         self.useFixture(fixtures.LockFixture('compute_quotas'))
-        flavor_name = data_utils.rand_name("flavor")
-        flavor_id = self._get_unused_flavor_id()
         quota_set = self.quotas_client.show_quota_set(
             self.tenant_id)['quota_set']
-        ram = int(quota_set['ram'])
+        ram = quota_set['ram']
         if ram == -1:
             raise self.skipException("ram quota set is -1,"
                                      " cannot test overlimit")
         ram += 1
         vcpus = 1
         disk = 5
-        flavor_ref = self.flavors_client.create_flavor(name=flavor_name,
-                                                       ram=ram, vcpus=vcpus,
-                                                       disk=disk,
-                                                       id=flavor_id)['flavor']
-        self.addCleanup(self.flavors_client.delete_flavor, flavor_id)
+        flavor_ref = self.create_flavor(ram=ram, vcpus=vcpus, disk=disk)
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.client.resize_server,
                           self.servers[0]['id'],
@@ -88,60 +67,46 @@
     @decorators.idempotent_id('7368a427-2f26-4ad9-9ba9-911a0ec2b0db')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_resize_server_using_overlimit_vcpus(self):
         # NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
         self.useFixture(fixtures.LockFixture('compute_quotas'))
-        flavor_name = data_utils.rand_name("flavor")
-        flavor_id = self._get_unused_flavor_id()
         quota_set = self.quotas_client.show_quota_set(
             self.tenant_id)['quota_set']
-        vcpus = int(quota_set['cores'])
+        vcpus = quota_set['cores']
         if vcpus == -1:
             raise self.skipException("cores quota set is -1,"
                                      " cannot test overlimit")
         vcpus += 1
         ram = 512
         disk = 5
-        flavor_ref = self.flavors_client.create_flavor(name=flavor_name,
-                                                       ram=ram, vcpus=vcpus,
-                                                       disk=disk,
-                                                       id=flavor_id)['flavor']
-        self.addCleanup(self.flavors_client.delete_flavor, flavor_id)
+        flavor_ref = self.create_flavor(ram=ram, vcpus=vcpus, disk=disk)
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.client.resize_server,
                           self.servers[0]['id'],
                           flavor_ref['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b0b4d8af-1256-41ef-9ee7-25f1c19dde80')
     def test_reset_state_server_invalid_state(self):
         self.assertRaises(lib_exc.BadRequest,
                           self.client.reset_state, self.s1_id,
                           state='invalid')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4cdcc984-fab0-4577-9a9d-6d558527ee9d')
     def test_reset_state_server_invalid_type(self):
         self.assertRaises(lib_exc.BadRequest,
                           self.client.reset_state, self.s1_id,
                           state=1)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e741298b-8df2-46f0-81cb-8f814ff2504c')
     def test_reset_state_server_nonexistent_server(self):
         self.assertRaises(lib_exc.NotFound,
                           self.client.reset_state, '999', state='error')
 
-    @test.attr(type=['negative'])
-    @decorators.idempotent_id('e84e2234-60d2-42fa-8b30-e2d3049724ac')
-    def test_get_server_diagnostics_by_non_admin(self):
-        # Non-admin user can not view server diagnostics according to policy
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_adm_client.show_server_diagnostics,
-                          self.s1_id)
-
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('46a4e1ca-87ae-4d28-987a-1b6b136a0221')
     def test_migrate_non_existent_server(self):
         # migrate a non existent server
@@ -150,11 +115,11 @@
                           data_utils.rand_uuid())
 
     @decorators.idempotent_id('b0b17f83-d14e-4fc4-8f31-bcc9f3cfa629')
-    @testtools.skipUnless(CONF.compute_feature_enabled.resize,
-                          'Resize not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
+                          'Cold migration not available.')
     @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
                           'Suspend is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_migrate_server_invalid_state(self):
         # create server.
         server = self.create_test_server(wait_until='ACTIVE')
diff --git a/tempest/api/compute/admin/test_servers_on_multinodes.py b/tempest/api/compute/admin/test_servers_on_multinodes.py
index 6b92273..2e7b07b 100644
--- a/tempest/api/compute/admin/test_servers_on_multinodes.py
+++ b/tempest/api/compute/admin/test_servers_on_multinodes.py
@@ -15,9 +15,9 @@
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import compute
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -25,6 +25,12 @@
 class ServersOnMultiNodesTest(base.BaseV2ComputeAdminTest):
 
     @classmethod
+    def resource_setup(cls):
+        super(ServersOnMultiNodesTest, cls).resource_setup()
+        cls.server01 = cls.create_test_server(wait_until='ACTIVE')['id']
+        cls.host01 = cls._get_host(cls.server01)
+
+    @classmethod
     def skip_checks(cls):
         super(ServersOnMultiNodesTest, cls).skip_checks()
 
@@ -32,49 +38,76 @@
             raise cls.skipException(
                 "Less than 2 compute nodes, skipping multi-nodes test.")
 
-    def _get_host(self, server_id):
-        return self.os_adm.servers_client.show_server(
+    @classmethod
+    def _get_host(cls, server_id):
+        return cls.os_admin.servers_client.show_server(
             server_id)['server']['OS-EXT-SRV-ATTR:host']
 
     @decorators.idempotent_id('26a9d5df-6890-45f2-abc4-a659290cb130')
     @testtools.skipUnless(
-        test.is_scheduler_filter_enabled("SameHostFilter"),
+        compute.is_scheduler_filter_enabled("SameHostFilter"),
         'SameHostFilter is not available.')
     def test_create_servers_on_same_host(self):
-        server01 = self.create_test_server(wait_until='ACTIVE')['id']
-
-        hints = {'same_host': server01}
+        hints = {'same_host': self.server01}
         server02 = self.create_test_server(scheduler_hints=hints,
                                            wait_until='ACTIVE')['id']
-        host01 = self._get_host(server01)
         host02 = self._get_host(server02)
-        self.assertEqual(host01, host02)
+        self.assertEqual(self.host01, host02)
 
     @decorators.idempotent_id('cc7ca884-6e3e-42a3-a92f-c522fcf25e8e')
     @testtools.skipUnless(
-        test.is_scheduler_filter_enabled("DifferentHostFilter"),
+        compute.is_scheduler_filter_enabled("DifferentHostFilter"),
         'DifferentHostFilter is not available.')
     def test_create_servers_on_different_hosts(self):
-        server01 = self.create_test_server(wait_until='ACTIVE')['id']
-
-        hints = {'different_host': server01}
+        hints = {'different_host': self.server01}
         server02 = self.create_test_server(scheduler_hints=hints,
                                            wait_until='ACTIVE')['id']
-        host01 = self._get_host(server01)
         host02 = self._get_host(server02)
-        self.assertNotEqual(host01, host02)
+        self.assertNotEqual(self.host01, host02)
 
     @decorators.idempotent_id('7869cc84-d661-4e14-9f00-c18cdc89cf57')
     @testtools.skipUnless(
-        test.is_scheduler_filter_enabled("DifferentHostFilter"),
+        compute.is_scheduler_filter_enabled("DifferentHostFilter"),
         'DifferentHostFilter is not available.')
     def test_create_servers_on_different_hosts_with_list_of_servers(self):
-        server01 = self.create_test_server(wait_until='ACTIVE')['id']
-
         # This scheduler-hint supports list of servers also.
-        hints = {'different_host': [server01]}
+        hints = {'different_host': [self.server01]}
         server02 = self.create_test_server(scheduler_hints=hints,
                                            wait_until='ACTIVE')['id']
-        host01 = self._get_host(server01)
         host02 = self._get_host(server02)
-        self.assertNotEqual(host01, host02)
+        self.assertNotEqual(self.host01, host02)
+
+    @decorators.idempotent_id('f8bd0867-e459-45f5-ba53-59134552fe04')
+    @testtools.skipUnless(
+        compute.is_scheduler_filter_enabled("ServerGroupAntiAffinityFilter"),
+        'ServerGroupAntiAffinityFilter is not available.')
+    def test_create_server_with_scheduler_hint_group_anti_affinity(self):
+        """Tests the ServerGroupAntiAffinityFilter
+
+        Creates two servers in an anti-affinity server group and
+        asserts the servers are in the group and on different hosts.
+        """
+        group_id = self.create_test_server_group(
+            policy=['anti-affinity'])['id']
+        hints = {'group': group_id}
+        reservation_id = self.create_test_server(
+            scheduler_hints=hints, wait_until='ACTIVE', min_count=2,
+            return_reservation_id=True)['reservation_id']
+
+        # Get the servers using the reservation_id.
+        servers = self.servers_client.list_servers(
+            detail=True, reservation_id=reservation_id)['servers']
+        self.assertEqual(2, len(servers))
+
+        # Assert the servers are in the group.
+        server_group = self.server_groups_client.show_server_group(
+            group_id)['server_group']
+        hosts = {}
+        for server in servers:
+            self.assertIn(server['id'], server_group['members'])
+            hosts[server['id']] = self._get_host(server['id'])
+
+        # Assert the servers are on different hosts.
+        hostnames = list(hosts.values())
+        self.assertNotEqual(hostnames[0], hostnames[1],
+                            'Servers are on the same host: %s' % hosts)
diff --git a/tempest/api/compute/admin/test_services.py b/tempest/api/compute/admin/test_services.py
index c1c1c82..f3eb597 100644
--- a/tempest/api/compute/admin/test_services.py
+++ b/tempest/api/compute/admin/test_services.py
@@ -24,18 +24,18 @@
     @classmethod
     def setup_clients(cls):
         super(ServicesAdminTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.services_client
+        cls.client = cls.os_admin.services_client
 
     @decorators.idempotent_id('5be41ef4-53d1-41cc-8839-5c2a48a1b283')
     def test_list_services(self):
         services = self.client.list_services()['services']
-        self.assertNotEqual(0, len(services))
+        self.assertNotEmpty(services)
 
     @decorators.idempotent_id('f345b1ec-bc6e-4c38-a527-3ca2bc00bef5')
     def test_get_service_by_service_binary_name(self):
         binary_name = 'nova-compute'
         services = self.client.list_services(binary=binary_name)['services']
-        self.assertNotEqual(0, len(services))
+        self.assertNotEmpty(services)
         for service in services:
             self.assertEqual(binary_name, service['binary'])
 
diff --git a/tempest/api/compute/admin/test_services_negative.py b/tempest/api/compute/admin/test_services_negative.py
index 9447a09..201670a 100644
--- a/tempest/api/compute/admin/test_services_negative.py
+++ b/tempest/api/compute/admin/test_services_negative.py
@@ -15,7 +15,6 @@
 from tempest.api.compute import base
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ServicesAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -24,16 +23,16 @@
     @classmethod
     def setup_clients(cls):
         super(ServicesAdminNegativeTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.services_client
+        cls.client = cls.os_admin.services_client
         cls.non_admin_client = cls.services_client
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1126d1f8-266e-485f-a687-adc547492646')
     def test_list_services_with_non_admin_user(self):
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.list_services)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d0884a69-f693-4e79-a9af-232d15643bf7')
     def test_get_service_by_invalid_params(self):
         # return all services if send the request with invalid parameter
@@ -42,20 +41,20 @@
                         ['services'])
         self.assertEqual(len(services), len(services_xxx))
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1e966d4a-226e-47c7-b601-0b18a27add54')
     def test_get_service_by_invalid_service_and_valid_host(self):
         services = self.client.list_services()['services']
         host_name = services[0]['host']
         services = self.client.list_services(host=host_name,
                                              binary='xxx')['services']
-        self.assertEqual(0, len(services))
+        self.assertEmpty(services)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('64e7e7fb-69e8-4cb6-a71d-8d5eb0c98655')
     def test_get_service_with_valid_service_and_invalid_host(self):
         services = self.client.list_services()['services']
         binary_name = services[0]['binary']
         services = self.client.list_services(host='xxx',
                                              binary=binary_name)['services']
-        self.assertEqual(0, len(services))
+        self.assertEmpty(services)
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
index ef55584..d4c60b3 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage.py
@@ -30,8 +30,8 @@
     @classmethod
     def setup_clients(cls):
         super(TenantUsagesTestJSON, cls).setup_clients()
-        cls.adm_client = cls.os_adm.tenant_usages_client
-        cls.client = cls.os.tenant_usages_client
+        cls.adm_client = cls.os_admin.tenant_usages_client
+        cls.client = cls.os_primary.tenant_usages_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
index 35762d6..cb60b8d 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
@@ -18,7 +18,6 @@
 from tempest.api.compute import base
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class TenantUsagesNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -26,8 +25,8 @@
     @classmethod
     def setup_clients(cls):
         super(TenantUsagesNegativeTestJSON, cls).setup_clients()
-        cls.adm_client = cls.os_adm.tenant_usages_client
-        cls.client = cls.os.tenant_usages_client
+        cls.adm_client = cls.os_admin.tenant_usages_client
+        cls.client = cls.os_primary.tenant_usages_client
 
     @classmethod
     def resource_setup(cls):
@@ -41,7 +40,7 @@
         # Returns formatted datetime
         return at.strftime('%Y-%m-%dT%H:%M:%S.%f')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8b21e135-d94b-4991-b6e9-87059609c8ed')
     def test_get_usage_tenant_with_empty_tenant_id(self):
         # Get usage for a specific tenant empty
@@ -51,7 +50,7 @@
                           self.adm_client.show_tenant_usage,
                           '', **params)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4079dd2a-9e8d-479f-869d-6fa985ce45b6')
     def test_get_usage_tenant_with_invalid_date(self):
         # Get usage for tenant with invalid date
@@ -61,7 +60,7 @@
                           self.adm_client.show_tenant_usage,
                           self.client.tenant_id, **params)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('bbe6fe2c-15d8-404c-a0a2-44fad0ad5cc7')
     def test_list_usage_all_tenants_with_non_admin_user(self):
         # Get usage for all tenants with non admin user
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index 5f2444a..d715a42 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -10,11 +10,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import time
+
 from tempest.api.compute import base
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
+from tempest.lib import exceptions as lib_exc
 
 CONF = config.CONF
 
@@ -30,6 +33,9 @@
     5. Swap volume from "volume1" to "volume2" as admin.
     6. Check the swap volume is successful and "volume2"
        is attached to "instance1" and "volume1" is in available state.
+    7. Swap volume from "volume2" to "volume1" as admin.
+    8. Check the swap volume is successful and "volume1"
+       is attached to "instance1" and "volume2" is in available state.
     """
 
     @classmethod
@@ -38,14 +44,43 @@
         if not CONF.compute_feature_enabled.swap_volume:
             raise cls.skipException("Swapping volumes is not supported.")
 
-    @classmethod
-    def setup_clients(cls):
-        super(TestVolumeSwap, cls).setup_clients()
-        # We need the admin client for performing the update (swap) volume call
-        cls.servers_admin_client = cls.os_adm.servers_client
+    def _wait_for_server_volume_swap(self, server_id, old_volume_id,
+                                     new_volume_id):
+        """Waits for a server to swap the old volume to a new one."""
+        volume_attachments = self.servers_client.list_volume_attachments(
+            server_id)['volumeAttachments']
+        attached_volume_ids = [attachment['volumeId']
+                               for attachment in volume_attachments]
+        start = int(time.time())
+
+        while (old_volume_id in attached_volume_ids) \
+                or (new_volume_id not in attached_volume_ids):
+            time.sleep(self.servers_client.build_interval)
+            volume_attachments = self.servers_client.list_volume_attachments(
+                server_id)['volumeAttachments']
+            attached_volume_ids = [attachment['volumeId']
+                                   for attachment in volume_attachments]
+
+            if int(time.time()) - start >= self.servers_client.build_timeout:
+                old_vol_bdm_status = 'in BDM' \
+                    if old_volume_id in attached_volume_ids else 'not in BDM'
+                new_vol_bdm_status = 'in BDM' \
+                    if new_volume_id in attached_volume_ids else 'not in BDM'
+                message = ('Failed to swap old volume %(old_volume_id)s '
+                           '(current %(old_vol_bdm_status)s) to new volume '
+                           '%(new_volume_id)s (current %(new_vol_bdm_status)s)'
+                           ' on server %(server_id)s within the required time '
+                           '(%(timeout)s s)' %
+                           {'old_volume_id': old_volume_id,
+                            'old_vol_bdm_status': old_vol_bdm_status,
+                            'new_volume_id': new_volume_id,
+                            'new_vol_bdm_status': new_vol_bdm_status,
+                            'server_id': server_id,
+                            'timeout': self.servers_client.build_timeout})
+                raise lib_exc.TimeoutException(message)
 
     @decorators.idempotent_id('1769f00d-a693-4d67-a631-6a3496773813')
-    @test.services('volume')
+    @utils.services('volume')
     def test_volume_swap(self):
         # Create two volumes.
         # NOTE(gmann): Volumes are created before server creation so that
@@ -58,19 +93,31 @@
         # Attach "volume1" to server
         self.attach_volume(server, volume1)
         # Swap volume from "volume1" to "volume2"
-        self.servers_admin_client.update_attached_volume(
+        self.admin_servers_client.update_attached_volume(
             server['id'], volume1['id'], volumeId=volume2['id'])
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume1['id'], 'available')
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume2['id'], 'in-use')
-        self.addCleanup(self.servers_client.detach_volume,
-                        server['id'], volume2['id'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume1['id'], 'available')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume2['id'], 'in-use')
+        self._wait_for_server_volume_swap(server['id'], volume1['id'],
+                                          volume2['id'])
         # Verify "volume2" is attached to the server
         vol_attachments = self.servers_client.list_volume_attachments(
             server['id'])['volumeAttachments']
         self.assertEqual(1, len(vol_attachments))
         self.assertIn(volume2['id'], vol_attachments[0]['volumeId'])
 
-        # TODO(mriedem): Test swapping back from volume2 to volume1 after
-        # nova bug 1490236 is fixed.
+        # Swap volume from "volume2" to "volume1"
+        self.admin_servers_client.update_attached_volume(
+            server['id'], volume2['id'], volumeId=volume1['id'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume2['id'], 'available')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume1['id'], 'in-use')
+        self._wait_for_server_volume_swap(server['id'], volume2['id'],
+                                          volume1['id'])
+        # Verify "volume1" is attached to the server
+        vol_attachments = self.servers_client.list_volume_attachments(
+            server['id'])['volumeAttachments']
+        self.assertEqual(1, len(vol_attachments))
+        self.assertIn(volume1['id'], vol_attachments[0]['volumeId'])
diff --git a/tempest/api/compute/admin/test_volumes_negative.py b/tempest/api/compute/admin/test_volumes_negative.py
index 1f85c18..4a7f36f 100644
--- a/tempest/api/compute/admin/test_volumes_negative.py
+++ b/tempest/api/compute/admin/test_volumes_negative.py
@@ -13,11 +13,10 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -32,25 +31,22 @@
             raise cls.skipException(skip_msg)
 
     @classmethod
-    def setup_clients(cls):
-        super(VolumesAdminNegativeTest, cls).setup_clients()
-        cls.servers_admin_client = cls.os_adm.servers_client
-
-    @classmethod
     def resource_setup(cls):
         super(VolumesAdminNegativeTest, cls).resource_setup()
         cls.server = cls.create_test_server(wait_until='ACTIVE')
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('309b5ecd-0585-4a7e-a36f-d2b2bf55259d')
     def test_update_attached_volume_with_nonexistent_volume_in_uri(self):
         volume = self.create_volume()
         nonexistent_volume = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound,
-                          self.servers_admin_client.update_attached_volume,
+                          self.admin_servers_client.update_attached_volume,
                           self.server['id'], nonexistent_volume,
                           volumeId=volume['id'])
 
-    @test.related_bug('1629110', status_code=400)
+    @decorators.related_bug('1629110', status_code=400)
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7dcac15a-b107-46d3-a5f6-cb863f4e454a')
     def test_update_attached_volume_with_nonexistent_volume_in_body(self):
         volume = self.create_volume()
@@ -58,6 +54,6 @@
 
         nonexistent_volume = data_utils.rand_uuid()
         self.assertRaises(lib_exc.BadRequest,
-                          self.servers_admin_client.update_attached_volume,
+                          self.admin_servers_client.update_attached_volume,
                           self.server['id'], volume['id'],
                           volumeId=nonexistent_volume)
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index c3c5460..746f83a 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -19,11 +19,12 @@
 
 from tempest.api.compute import api_microversion_fixture
 from tempest.common import compute
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
 from tempest import exceptions
+from tempest.lib.common import api_version_request
 from tempest.lib.common import api_version_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
 import tempest.test
@@ -63,41 +64,41 @@
     @classmethod
     def setup_clients(cls):
         super(BaseV2ComputeTest, cls).setup_clients()
-        cls.servers_client = cls.os.servers_client
-        cls.server_groups_client = cls.os.server_groups_client
-        cls.flavors_client = cls.os.flavors_client
-        cls.compute_images_client = cls.os.compute_images_client
-        cls.extensions_client = cls.os.extensions_client
-        cls.floating_ip_pools_client = cls.os.floating_ip_pools_client
-        cls.floating_ips_client = cls.os.compute_floating_ips_client
-        cls.keypairs_client = cls.os.keypairs_client
+        cls.servers_client = cls.os_primary.servers_client
+        cls.server_groups_client = cls.os_primary.server_groups_client
+        cls.flavors_client = cls.os_primary.flavors_client
+        cls.compute_images_client = cls.os_primary.compute_images_client
+        cls.extensions_client = cls.os_primary.extensions_client
+        cls.floating_ip_pools_client = cls.os_primary.floating_ip_pools_client
+        cls.floating_ips_client = cls.os_primary.compute_floating_ips_client
+        cls.keypairs_client = cls.os_primary.keypairs_client
         cls.security_group_rules_client = (
-            cls.os.compute_security_group_rules_client)
-        cls.security_groups_client = cls.os.compute_security_groups_client
-        cls.quotas_client = cls.os.quotas_client
-        cls.compute_networks_client = cls.os.compute_networks_client
-        cls.limits_client = cls.os.limits_client
-        cls.volumes_extensions_client = cls.os.volumes_extensions_client
-        cls.snapshots_extensions_client = cls.os.snapshots_extensions_client
-        cls.interfaces_client = cls.os.interfaces_client
-        cls.fixed_ips_client = cls.os.fixed_ips_client
-        cls.availability_zone_client = cls.os.availability_zone_client
-        cls.agents_client = cls.os.agents_client
-        cls.aggregates_client = cls.os.aggregates_client
-        cls.services_client = cls.os.services_client
+            cls.os_primary.compute_security_group_rules_client)
+        cls.security_groups_client =\
+            cls.os_primary.compute_security_groups_client
+        cls.quotas_client = cls.os_primary.quotas_client
+        cls.compute_networks_client = cls.os_primary.compute_networks_client
+        cls.limits_client = cls.os_primary.limits_client
+        cls.volumes_extensions_client =\
+            cls.os_primary.volumes_extensions_client
+        cls.snapshots_extensions_client =\
+            cls.os_primary.snapshots_extensions_client
+        cls.interfaces_client = cls.os_primary.interfaces_client
+        cls.fixed_ips_client = cls.os_primary.fixed_ips_client
+        cls.availability_zone_client = cls.os_primary.availability_zone_client
+        cls.agents_client = cls.os_primary.agents_client
+        cls.aggregates_client = cls.os_primary.aggregates_client
+        cls.services_client = cls.os_primary.services_client
         cls.instance_usages_audit_log_client = (
-            cls.os.instance_usages_audit_log_client)
-        cls.hypervisor_client = cls.os.hypervisor_client
-        cls.certificates_client = cls.os.certificates_client
-        cls.migrations_client = cls.os.migrations_client
+            cls.os_primary.instance_usages_audit_log_client)
+        cls.hypervisor_client = cls.os_primary.hypervisor_client
+        cls.certificates_client = cls.os_primary.certificates_client
+        cls.migrations_client = cls.os_primary.migrations_client
         cls.security_group_default_rules_client = (
-            cls.os.security_group_default_rules_client)
-        cls.versions_client = cls.os.compute_versions_client
-
-        if CONF.volume_feature_enabled.api_v1:
-            cls.volumes_client = cls.os.volumes_client
-        else:
-            cls.volumes_client = cls.os.volumes_v2_client
+            cls.os_primary.security_group_default_rules_client)
+        cls.versions_client = cls.os_primary.compute_versions_client
+        if CONF.service_available.cinder:
+            cls.volumes_client = cls.os_primary.volumes_client_latest
 
     @classmethod
     def resource_setup(cls):
@@ -123,10 +124,13 @@
 
     @classmethod
     def resource_cleanup(cls):
-        cls.clear_images()
+        cls.clear_resources('images', cls.images,
+                            cls.compute_images_client.delete_image)
         cls.clear_servers()
-        cls.clear_security_groups()
-        cls.clear_server_groups()
+        cls.clear_resources('security groups', cls.security_groups,
+                            cls.security_groups_client.delete_security_group)
+        cls.clear_resources('server groups', cls.server_groups,
+                            cls.server_groups_client.delete_server_group)
         cls.clear_volumes()
         super(BaseV2ComputeTest, cls).resource_cleanup()
 
@@ -172,42 +176,19 @@
                 raise
 
     @classmethod
-    def clear_images(cls):
-        LOG.debug('Clearing images: %s', ','.join(cls.images))
-        for image_id in cls.images:
+    def clear_resources(cls, resource_name, resources, resource_del_func):
+        LOG.debug('Clearing %s: %s', resource_name,
+                  ','.join(map(str, resources)))
+        for res_id in resources:
             try:
                 test_utils.call_and_ignore_notfound_exc(
-                    cls.compute_images_client.delete_image, image_id)
-            except Exception:
-                LOG.exception('Exception raised deleting image %s', image_id)
-
-    @classmethod
-    def clear_security_groups(cls):
-        LOG.debug('Clearing security groups: %s', ','.join(
-            str(sg['id']) for sg in cls.security_groups))
-        for sg in cls.security_groups:
-            try:
-                test_utils.call_and_ignore_notfound_exc(
-                    cls.security_groups_client.delete_security_group, sg['id'])
+                    resource_del_func, res_id)
             except Exception as exc:
-                LOG.info('Exception raised deleting security group %s',
-                         sg['id'])
+                LOG.exception('Exception raised deleting %s: %s',
+                              resource_name, res_id)
                 LOG.exception(exc)
 
     @classmethod
-    def clear_server_groups(cls):
-        LOG.debug('Clearing server groups: %s', ','.join(cls.server_groups))
-        for server_group_id in cls.server_groups:
-            try:
-                test_utils.call_and_ignore_notfound_exc(
-                    cls.server_groups_client.delete_server_group,
-                    server_group_id
-                )
-            except Exception:
-                LOG.exception('Exception raised deleting server-group %s',
-                              server_group_id)
-
-    @classmethod
     def create_test_server(cls, validatable=False, volume_backed=False,
                            **kwargs):
         """Wrapper utility that returns a test server.
@@ -222,9 +203,18 @@
         """
         if 'name' not in kwargs:
             kwargs['name'] = data_utils.rand_name(cls.__name__ + "-server")
+
+        request_version = api_version_request.APIVersionRequest(
+            cls.request_microversion)
+        v2_37_version = api_version_request.APIVersionRequest('2.37')
+
+        # NOTE(snikitin): since microversion v2.37 'networks' field is required
+        if request_version >= v2_37_version and 'networks' not in kwargs:
+            kwargs['networks'] = 'none'
+
         tenant_network = cls.get_tenant_network()
         body, servers = compute.create_test_server(
-            cls.os,
+            cls.os_primary,
             validatable,
             validation_resources=cls.validation_resources,
             tenant_network=tenant_network,
@@ -243,7 +233,7 @@
             description = data_utils.rand_name('description')
         body = cls.security_groups_client.create_security_group(
             name=name, description=description)['security_group']
-        cls.security_groups.append(body)
+        cls.security_groups.append(body['id'])
 
         return body
 
@@ -295,20 +285,22 @@
     @classmethod
     def create_image_from_server(cls, server_id, **kwargs):
         """Wrapper utility that returns an image created from the server."""
-        name = data_utils.rand_name(cls.__name__ + "-image")
-        if 'name' in kwargs:
-            name = kwargs.pop('name')
+        name = kwargs.pop('name',
+                          data_utils.rand_name(cls.__name__ + "-image"))
+        wait_until = kwargs.pop('wait_until', None)
+        wait_for_server = kwargs.pop('wait_for_server', True)
 
-        image = cls.compute_images_client.create_image(server_id, name=name)
+        image = cls.compute_images_client.create_image(server_id, name=name,
+                                                       **kwargs)
         image_id = data_utils.parse_image_id(image.response['location'])
         cls.images.append(image_id)
 
-        if 'wait_until' in kwargs:
+        if wait_until is not None:
             try:
                 waiters.wait_for_image_status(cls.compute_images_client,
-                                              image_id, kwargs['wait_until'])
+                                              image_id, wait_until)
             except lib_exc.NotFound:
-                if kwargs['wait_until'].upper() == 'ACTIVE':
+                if wait_until.upper() == 'ACTIVE':
                     # If the image is not found after create_image returned
                     # that means the snapshot failed in nova-compute and nova
                     # deleted the image. There should be a compute fault
@@ -326,6 +318,10 @@
                     raise
             image = cls.compute_images_client.show_image(image_id)['image']
 
+            if wait_until.upper() == 'ACTIVE':
+                if wait_for_server:
+                    waiters.wait_for_server_status(cls.servers_client,
+                                                   server_id, 'ACTIVE')
         return image
 
     @classmethod
@@ -364,7 +360,7 @@
     @classmethod
     def delete_volume(cls, volume_id):
         """Deletes the given volume and waits for it to be gone."""
-        cls._delete_volume(cls.volumes_extensions_client, volume_id)
+        cls._delete_volume(cls.volumes_client, volume_id)
 
     @classmethod
     def get_server_ip(cls, server):
@@ -406,8 +402,8 @@
             kwargs['imageRef'] = image_ref
         volume = cls.volumes_client.create_volume(**kwargs)['volume']
         cls.volumes.append(volume)
-        waiters.wait_for_volume_status(cls.volumes_client,
-                                       volume['id'], 'available')
+        waiters.wait_for_volume_resource_status(cls.volumes_client,
+                                                volume['id'], 'available')
         return volume
 
     @classmethod
@@ -428,7 +424,7 @@
                 LOG.exception('Waiting for deletion of volume %s failed',
                               volume['id'])
 
-    def attach_volume(self, server, volume, device=None):
+    def attach_volume(self, server, volume, device=None, check_reserved=False):
         """Attaches volume to server and waits for 'in-use' volume status.
 
         The volume will be detached when the test tears down.
@@ -437,24 +433,33 @@
         :param volume: The volume to attach.
         :param device: Optional mountpoint for the attached volume. Note that
             this is not guaranteed for all hypervisors and is not recommended.
+        :param check_reserved: Consider a status of reserved as valid for
+            completion. This is to handle new Cinder attach where we more
+            accurately use 'reserved' for things like attaching to a shelved
+            server.
         """
         attach_kwargs = dict(volumeId=volume['id'])
         if device:
             attach_kwargs['device'] = device
-        self.servers_client.attach_volume(
-            server['id'], **attach_kwargs)
+
+        attachment = self.servers_client.attach_volume(
+            server['id'], **attach_kwargs)['volumeAttachment']
         # On teardown detach the volume and wait for it to be available. This
         # is so we don't error out when trying to delete the volume during
         # teardown.
-        self.addCleanup(waiters.wait_for_volume_status,
+        self.addCleanup(waiters.wait_for_volume_resource_status,
                         self.volumes_client, volume['id'], 'available')
         # Ignore 404s on detach in case the server is deleted or the volume
         # is already detached.
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.servers_client.detach_volume,
                         server['id'], volume['id'])
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume['id'], 'in-use')
+        statuses = ['in-use']
+        if check_reserved:
+            statuses.append('reserved')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], statuses)
+        return attachment
 
 
 class BaseV2ComputeAdminTest(BaseV2ComputeTest):
@@ -466,4 +471,37 @@
     def setup_clients(cls):
         super(BaseV2ComputeAdminTest, cls).setup_clients()
         cls.availability_zone_admin_client = (
-            cls.os_adm.availability_zone_client)
+            cls.os_admin.availability_zone_client)
+        cls.admin_flavors_client = cls.os_admin.flavors_client
+        cls.admin_servers_client = cls.os_admin.servers_client
+
+    def create_flavor(self, ram, vcpus, disk, name=None,
+                      is_public='True', **kwargs):
+        if name is None:
+            name = data_utils.rand_name(self.__class__.__name__ + "-flavor")
+        id = kwargs.pop('id', data_utils.rand_int_id(start=1000))
+        client = self.admin_flavors_client
+        flavor = client.create_flavor(
+            ram=ram, vcpus=vcpus, disk=disk, name=name,
+            id=id, is_public=is_public, **kwargs)['flavor']
+        self.addCleanup(client.wait_for_resource_deletion, flavor['id'])
+        self.addCleanup(client.delete_flavor, flavor['id'])
+        return flavor
+
+    def get_host_for_server(self, server_id):
+        server_details = self.admin_servers_client.show_server(server_id)
+        return server_details['server']['OS-EXT-SRV-ATTR:host']
+
+    def get_host_other_than(self, server_id):
+        source_host = self.get_host_for_server(server_id)
+
+        list_hosts_resp = self.os_admin.hosts_client.list_hosts()['hosts']
+        hosts = [
+            host_record['host_name']
+            for host_record in list_hosts_resp
+            if host_record['service'] == 'compute'
+        ]
+
+        for target_host in hosts:
+            if source_host != target_host:
+                return target_host
diff --git a/tempest/api/compute/certificates/test_certificates.py b/tempest/api/compute/certificates/test_certificates.py
index a39fec9..0e6c016 100644
--- a/tempest/api/compute/certificates/test_certificates.py
+++ b/tempest/api/compute/certificates/test_certificates.py
@@ -31,14 +31,9 @@
     @decorators.idempotent_id('c070a441-b08e-447e-a733-905909535b1b')
     def test_create_root_certificate(self):
         # create certificates
-        body = self.certificates_client.create_certificate()['certificate']
-        self.assertIn('data', body)
-        self.assertIn('private_key', body)
+        self.certificates_client.create_certificate()
 
     @decorators.idempotent_id('3ac273d0-92d2-4632-bdfc-afbc21d4606c')
     def test_get_root_certificate(self):
         # get the root certificate
-        body = (self.certificates_client.show_certificate('root')
-                ['certificate'])
-        self.assertIn('data', body)
-        self.assertIn('private_key', body)
+        self.certificates_client.show_certificate('root')
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index 546667f..d5bb45a 100644
--- a/tempest/api/compute/flavors/test_flavors.py
+++ b/tempest/api/compute/flavors/test_flavors.py
@@ -15,24 +15,18 @@
 
 from tempest.api.compute import base
 from tempest.lib import decorators
-from tempest import test
 
 
 class FlavorsV2TestJSON(base.BaseV2ComputeTest):
     _min_disk = 'minDisk'
     _min_ram = 'minRam'
 
-    @classmethod
-    def setup_clients(cls):
-        super(FlavorsV2TestJSON, cls).setup_clients()
-        cls.client = cls.flavors_client
-
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('e36c0eaa-dff5-4082-ad1f-3f9a80aa3f59')
     def test_list_flavors(self):
         # List of all flavors should contain the expected flavor
-        flavors = self.client.list_flavors()['flavors']
-        flavor = self.client.show_flavor(self.flavor_ref)['flavor']
+        flavors = self.flavors_client.list_flavors()['flavors']
+        flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         flavor_min_detail = {'id': flavor['id'], 'links': flavor['links'],
                              'name': flavor['name']}
         self.assertIn(flavor_min_detail, flavors)
@@ -40,89 +34,93 @@
     @decorators.idempotent_id('6e85fde4-b3cd-4137-ab72-ed5f418e8c24')
     def test_list_flavors_with_detail(self):
         # Detailed list of all flavors should contain the expected flavor
-        flavors = self.client.list_flavors(detail=True)['flavors']
-        flavor = self.client.show_flavor(self.flavor_ref)['flavor']
+        flavors = self.flavors_client.list_flavors(detail=True)['flavors']
+        flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         self.assertIn(flavor, flavors)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('1f12046b-753d-40d2-abb6-d8eb8b30cb2f')
     def test_get_flavor(self):
         # The expected flavor details should be returned
-        flavor = self.client.show_flavor(self.flavor_ref)['flavor']
+        flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         self.assertEqual(self.flavor_ref, flavor['id'])
 
     @decorators.idempotent_id('8d7691b3-6ed4-411a-abc9-2839a765adab')
     def test_list_flavors_limit_results(self):
         # Only the expected number of flavors should be returned
         params = {'limit': 1}
-        flavors = self.client.list_flavors(**params)['flavors']
+        flavors = self.flavors_client.list_flavors(**params)['flavors']
         self.assertEqual(1, len(flavors))
 
     @decorators.idempotent_id('b26f6327-2886-467a-82be-cef7a27709cb')
     def test_list_flavors_detailed_limit_results(self):
         # Only the expected number of flavors (detailed) should be returned
         params = {'limit': 1}
-        flavors = self.client.list_flavors(detail=True, **params)['flavors']
+        flavors = self.flavors_client.list_flavors(detail=True,
+                                                   **params)['flavors']
         self.assertEqual(1, len(flavors))
 
     @decorators.idempotent_id('e800f879-9828-4bd0-8eae-4f17189951fb')
     def test_list_flavors_using_marker(self):
         # The list of flavors should start from the provided marker
-        flavor = self.client.show_flavor(self.flavor_ref)['flavor']
+        flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         flavor_id = flavor['id']
 
         params = {'marker': flavor_id}
-        flavors = self.client.list_flavors(**params)['flavors']
-        self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
+        flavors = self.flavors_client.list_flavors(**params)['flavors']
+        self.assertEmpty([i for i in flavors if i['id'] == flavor_id],
                          'The list of flavors did not start after the marker.')
 
     @decorators.idempotent_id('6db2f0c0-ddee-4162-9c84-0703d3dd1107')
     def test_list_flavors_detailed_using_marker(self):
         # The list of flavors should start from the provided marker
-        flavor = self.client.show_flavor(self.flavor_ref)['flavor']
+        flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         flavor_id = flavor['id']
 
         params = {'marker': flavor_id}
-        flavors = self.client.list_flavors(detail=True, **params)['flavors']
-        self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
+        flavors = self.flavors_client.list_flavors(detail=True,
+                                                   **params)['flavors']
+        self.assertEmpty([i for i in flavors if i['id'] == flavor_id],
                          'The list of flavors did not start after the marker.')
 
     @decorators.idempotent_id('3df2743e-3034-4e57-a4cb-b6527f6eac79')
     def test_list_flavors_detailed_filter_by_min_disk(self):
         # The detailed list of flavors should be filtered by disk space
-        flavor = self.client.show_flavor(self.flavor_ref)['flavor']
+        flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         flavor_id = flavor['id']
 
         params = {self._min_disk: flavor['disk'] + 1}
-        flavors = self.client.list_flavors(detail=True, **params)['flavors']
-        self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
+        flavors = self.flavors_client.list_flavors(detail=True,
+                                                   **params)['flavors']
+        self.assertEmpty([i for i in flavors if i['id'] == flavor_id])
 
     @decorators.idempotent_id('09fe7509-b4ee-4b34-bf8b-39532dc47292')
     def test_list_flavors_detailed_filter_by_min_ram(self):
         # The detailed list of flavors should be filtered by RAM
-        flavor = self.client.show_flavor(self.flavor_ref)['flavor']
+        flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         flavor_id = flavor['id']
 
         params = {self._min_ram: flavor['ram'] + 1}
-        flavors = self.client.list_flavors(detail=True, **params)['flavors']
-        self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
+        flavors = self.flavors_client.list_flavors(detail=True,
+                                                   **params)['flavors']
+        self.assertEmpty([i for i in flavors if i['id'] == flavor_id])
 
     @decorators.idempotent_id('10645a4d-96f5-443f-831b-730711e11dd4')
     def test_list_flavors_filter_by_min_disk(self):
         # The list of flavors should be filtered by disk space
-        flavor = self.client.show_flavor(self.flavor_ref)['flavor']
+        flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         flavor_id = flavor['id']
 
         params = {self._min_disk: flavor['disk'] + 1}
-        flavors = self.client.list_flavors(**params)['flavors']
-        self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
+        flavors = self.flavors_client.list_flavors(**params)['flavors']
+        self.assertEmpty([i for i in flavors if i['id'] == flavor_id])
 
     @decorators.idempotent_id('935cf550-e7c8-4da6-8002-00f92d5edfaa')
     def test_list_flavors_filter_by_min_ram(self):
         # The list of flavors should be filtered by RAM
-        flavor = self.client.show_flavor(self.flavor_ref)['flavor']
+        flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         flavor_id = flavor['id']
 
         params = {self._min_ram: flavor['ram'] + 1}
-        flavors = self.client.list_flavors(**params)['flavors']
-        self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
+        flavors = self.flavors_client.list_flavors(**params)['flavors']
+        self.assertEmpty([i for i in flavors if i['id'] == flavor_id])
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
new file mode 100644
index 0000000..efd4f0e
--- /dev/null
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -0,0 +1,90 @@
+# Copyright 2017 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 random
+
+import six
+
+from tempest.api.compute import base
+from tempest.common import image as common_image
+from tempest.common import utils
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+CONF = config.CONF
+
+
+class FlavorsV2NegativeTest(base.BaseV2ComputeTest):
+
+    @classmethod
+    def setup_clients(cls):
+        super(FlavorsV2NegativeTest, cls).setup_clients()
+        if CONF.image_feature_enabled.api_v1:
+            cls.images_client = cls.os_primary.image_client
+        elif CONF.image_feature_enabled.api_v2:
+            cls.images_client = cls.os_primary.image_client_v2
+        else:
+            raise lib_exc.InvalidConfiguration(
+                'Either api_v1 or api_v2 must be True in '
+                '[image-feature-enabled].')
+
+    @decorators.attr(type=['negative'])
+    @utils.services('image')
+    @decorators.idempotent_id('90f0d93a-91c1-450c-91e6-07d18172cefe')
+    def test_boot_with_low_ram(self):
+        """Try boot a vm with lower than min ram
+
+        Create an image with min_ram value
+        Try to create server with flavor of insufficient ram size from
+        that image
+        """
+        flavor = self.flavors_client.show_flavor(
+            CONF.compute.flavor_ref)['flavor']
+        min_img_ram = flavor['ram'] + 1
+        size = random.randint(1024, 4096)
+        image_file = six.BytesIO(data_utils.random_bytes(size))
+        params = {
+            'name': data_utils.rand_name('image'),
+            'container_format': CONF.image.container_formats[0],
+            'disk_format': CONF.image.disk_formats[0],
+            'min_ram': min_img_ram
+        }
+
+        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'})
+
+        image = self.images_client.create_image(**params)
+        image = image['image'] if 'image' in image else image
+        self.addCleanup(self.images_client.delete_image, image['id'])
+
+        if CONF.image_feature_enabled.api_v1:
+            self.images_client.update_image(image['id'], data=image_file)
+        else:
+            self.images_client.store_image_file(image['id'], data=image_file)
+
+        self.assertEqual(min_img_ram, image['min_ram'])
+
+        # Try to create server with flavor of insufficient ram size
+        self.assertRaisesRegex(lib_exc.BadRequest,
+                               "Flavor's memory is too small for "
+                               "requested image",
+                               self.create_test_server,
+                               image_id=image['id'],
+                               flavor=flavor['id'])
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index 4d8416f..9ee0ac9 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -13,12 +13,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute.floating_ips import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -28,6 +30,14 @@
     floating_ip = None
 
     @classmethod
+    def skip_checks(cls):
+        super(FloatingIPsTestJSON, cls).skip_checks()
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
+        if not CONF.network_feature_enabled.floating_ips:
+            raise cls.skipException("Floating ips are not available")
+
+    @classmethod
     def setup_clients(cls):
         super(FloatingIPsTestJSON, cls).setup_clients()
         cls.client = cls.floating_ips_client
@@ -54,7 +64,6 @@
         super(FloatingIPsTestJSON, cls).resource_cleanup()
 
     @decorators.idempotent_id('f7bfb946-297e-41b8-9e8c-aba8e9bb5194')
-    @test.services('network')
     def test_allocate_floating_ip(self):
         # Positive test:Allocation of a new floating IP to a project
         # should be successful
@@ -70,7 +79,6 @@
         self.assertIn(floating_ip_details, body)
 
     @decorators.idempotent_id('de45e989-b5ca-4a9b-916b-04a52e7bbb8b')
-    @test.services('network')
     def test_delete_floating_ip(self):
         # Positive test:Deletion of valid floating IP from project
         # should be successful
@@ -85,7 +93,8 @@
         self.client.wait_for_resource_deletion(floating_ip_body['id'])
 
     @decorators.idempotent_id('307efa27-dc6f-48a0-8cd2-162ce3ef0b52')
-    @test.services('network')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
     def test_associate_disassociate_floating_ip(self):
         # Positive test:Associate and disassociate the provided floating IP
         # to a specific server should be successful
@@ -106,7 +115,8 @@
             self.server_id)
 
     @decorators.idempotent_id('6edef4b2-aaf1-4abc-bbe3-993e2561e0fe')
-    @test.services('network')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
     def test_associate_already_associated_floating_ip(self):
         # positive test:Association of an already associated floating IP
         # to specific server should change the association of the Floating IP
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
index 5e47d18..c3d7816 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -13,12 +13,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute.floating_ips import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -26,6 +28,14 @@
 class FloatingIPsNegativeTestJSON(base.BaseFloatingIPsTest):
 
     @classmethod
+    def skip_checks(cls):
+        super(FloatingIPsNegativeTestJSON, cls).skip_checks()
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
+        if not CONF.network_feature_enabled.floating_ips:
+            raise cls.skipException("Floating ips are not available")
+
+    @classmethod
     def setup_clients(cls):
         super(FloatingIPsNegativeTestJSON, cls).setup_clients()
         cls.client = cls.floating_ips_client
@@ -48,9 +58,8 @@
             if cls.non_exist_id not in floating_ip_ids:
                 break
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6e0f059b-e4dd-48fb-8207-06e3bba5b074')
-    @test.services('network')
     def test_allocate_floating_ip_from_nonexistent_pool(self):
         # Negative test:Allocation of a new floating IP from a nonexistent_pool
         # to a project should fail
@@ -58,9 +67,8 @@
                           self.client.create_floating_ip,
                           pool="non_exist_pool")
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ae1c55a8-552b-44d4-bfb6-2a115a15d0ba')
-    @test.services('network')
     def test_delete_nonexistent_floating_ip(self):
         # Negative test:Deletion of a nonexistent floating IP
         # from project should fail
@@ -69,9 +77,8 @@
         self.assertRaises(lib_exc.NotFound, self.client.delete_floating_ip,
                           self.non_exist_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('595fa616-1a71-4670-9614-46564ac49a4c')
-    @test.services('network')
     def test_associate_nonexistent_floating_ip(self):
         # Negative test:Association of a non existent floating IP
         # to specific server should fail
@@ -80,9 +87,8 @@
                           self.client.associate_floating_ip_to_server,
                           "0.0.0.0", self.server_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0a081a66-e568-4e6b-aa62-9587a876dca8')
-    @test.services('network')
     def test_dissociate_nonexistent_floating_ip(self):
         # Negative test:Dissociation of a non existent floating IP should fail
         # Dissociating non existent floating IP
@@ -90,12 +96,34 @@
                           self.client.disassociate_floating_ip_from_server,
                           "0.0.0.0", self.server_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('804b4fcb-bbf5-412f-925d-896672b61eb3')
-    @test.services('network')
     def test_associate_ip_to_server_without_passing_floating_ip(self):
         # Negative test:Association of empty floating IP to specific server
         # should raise NotFound or BadRequest(In case of Nova V2.1) exception.
         self.assertRaises((lib_exc.NotFound, lib_exc.BadRequest),
                           self.client.associate_floating_ip_to_server,
                           '', self.server_id)
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('58a80596-ffb2-11e6-9393-fa163e4fa634')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    def test_associate_ip_to_server_with_floating_ip(self):
+        # The VM have one port
+        # Associate floating IP A to the VM
+        # Associate floating IP B which is from same pool with floating IP A
+        # to the VM, should raise BadRequest exception
+        body = self.client.create_floating_ip(
+            pool=CONF.network.public_network_id)['floating_ip']
+        self.addCleanup(self.client.delete_floating_ip, body['id'])
+        self.client.associate_floating_ip_to_server(body['ip'], self.server_id)
+        self.addCleanup(self.client.disassociate_floating_ip_from_server,
+                        body['ip'], self.server_id)
+
+        body = self.client.create_floating_ip(
+            pool=CONF.network.public_network_id)['floating_ip']
+        self.addCleanup(self.client.delete_floating_ip, body['id'])
+        self.assertRaises(lib_exc.BadRequest,
+                          self.client.associate_floating_ip_to_server,
+                          body['ip'], self.server_id)
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips.py b/tempest/api/compute/floating_ips/test_list_floating_ips.py
index 71f5f13..2314433 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -24,6 +24,14 @@
 class FloatingIPDetailsTestJSON(base.BaseV2ComputeTest):
 
     @classmethod
+    def skip_checks(cls):
+        super(FloatingIPDetailsTestJSON, cls).skip_checks()
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
+        if not CONF.network_feature_enabled.floating_ips:
+            raise cls.skipException("Floating ips are not available")
+
+    @classmethod
     def setup_clients(cls):
         super(FloatingIPDetailsTestJSON, cls).setup_clients()
         cls.client = cls.floating_ips_client
@@ -47,18 +55,16 @@
         super(FloatingIPDetailsTestJSON, cls).resource_cleanup()
 
     @decorators.idempotent_id('16db31c3-fb85-40c9-bbe2-8cf7b67ff99f')
-    @test.services('network')
     def test_list_floating_ips(self):
         # Positive test:Should return the list of floating IPs
         body = self.client.list_floating_ips()['floating_ips']
         floating_ips = body
-        self.assertNotEqual(0, len(floating_ips),
+        self.assertNotEmpty(floating_ips,
                             "Expected floating IPs. Got zero.")
         for i in range(3):
             self.assertIn(self.floating_ip[i], floating_ips)
 
     @decorators.idempotent_id('eef497e0-8ff7-43c8-85ef-558440574f84')
-    @test.services('network')
     def test_get_floating_ip_details(self):
         # Positive test:Should be able to GET the details of floatingIP
         # Creating a floating IP for which details are to be checked
@@ -80,9 +86,8 @@
         self.assertEqual(floating_ip_id, body['id'])
 
     @decorators.idempotent_id('df389fc8-56f5-43cc-b290-20eda39854d3')
-    @test.services('network')
     def test_list_floating_ip_pools(self):
         # Positive test:Should return the list of floating IP Pools
         floating_ip_pools = self.pools_client.list_floating_ip_pools()
-        self.assertNotEqual(0, len(floating_ip_pools['floating_ip_pools']),
+        self.assertNotEmpty(floating_ip_pools['floating_ip_pools'],
                             "Expected floating IP Pools. Got zero.")
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
index 00a4075..0ade872 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
@@ -14,11 +14,11 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -26,13 +26,20 @@
 class FloatingIPDetailsNegativeTestJSON(base.BaseV2ComputeTest):
 
     @classmethod
+    def skip_checks(cls):
+        super(FloatingIPDetailsNegativeTestJSON, cls).skip_checks()
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
+        if not CONF.network_feature_enabled.floating_ips:
+            raise cls.skipException("Floating ips are not available")
+
+    @classmethod
     def setup_clients(cls):
         super(FloatingIPDetailsNegativeTestJSON, cls).setup_clients()
         cls.client = cls.floating_ips_client
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7ab18834-4a4b-4f28-a2c5-440579866695')
-    @test.services('network')
     def test_get_nonexistent_floating_ip_details(self):
         # Negative test:Should not be able to GET the details
         # of non-existent floating IP
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index f131007..8d503dc 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -17,9 +17,9 @@
 
 from tempest.api.compute import base
 from tempest.common import image as common_image
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions
 
@@ -42,9 +42,9 @@
         # 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
+            cls.glance_client = cls.os_primary.image_client
         elif CONF.image_feature_enabled.api_v2:
-            cls.glance_client = cls.os.image_client_v2
+            cls.glance_client = cls.os_primary.image_client_v2
         else:
             raise exceptions.InvalidConfiguration(
                 'Either api_v1 or api_v2 must be True in '
diff --git a/tempest/api/compute/images/test_image_metadata_negative.py b/tempest/api/compute/images/test_image_metadata_negative.py
index 0f5b9d8..03d0789 100644
--- a/tempest/api/compute/images/test_image_metadata_negative.py
+++ b/tempest/api/compute/images/test_image_metadata_negative.py
@@ -14,10 +14,9 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ImagesMetadataNegativeTestJSON(base.BaseV2ComputeTest):
@@ -27,7 +26,7 @@
         super(ImagesMetadataNegativeTestJSON, cls).setup_clients()
         cls.client = cls.compute_images_client
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('94069db2-792f-4fa8-8bd3-2271a6e0c095')
     def test_list_nonexistent_image_metadata(self):
         # Negative test: List on nonexistent image
@@ -35,7 +34,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.list_image_metadata,
                           data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a403ef9e-9f95-427c-b70a-3ce3388796f1')
     def test_update_nonexistent_image_metadata(self):
         # Negative test:An update should not happen for a non-existent image
@@ -44,7 +43,7 @@
                           self.client.update_image_metadata,
                           data_utils.rand_uuid(), meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('41ae052c-6ee6-405c-985e-5712393a620d')
     def test_get_nonexistent_image_metadata_item(self):
         # Negative test: Get on non-existent image should not happen
@@ -52,7 +51,7 @@
                           self.client.show_image_metadata_item,
                           data_utils.rand_uuid(), 'os_version')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('dc64f2ce-77e8-45b0-88c8-e15041d08eaf')
     def test_set_nonexistent_image_metadata(self):
         # Negative test: Metadata should not be set to a non-existent image
@@ -60,7 +59,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.set_image_metadata,
                           data_utils.rand_uuid(), meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('2154fd03-ab54-457c-8874-e6e3eb56e9cf')
     def test_set_nonexistent_image_metadata_item(self):
         # Negative test: Metadata item should not be set to a
@@ -71,7 +70,7 @@
                           data_utils.rand_uuid(), 'os_distro',
                           meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('848e157f-6bcf-4b2e-a5dd-5124025a8518')
     def test_delete_nonexistent_image_metadata_item(self):
         # Negative test: Shouldn't be able to delete metadata
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index d9db0b5..29bd6da 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -13,10 +13,11 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
+import testtools
 
 CONF = config.CONF
 
@@ -60,6 +61,43 @@
         snapshot_name = data_utils.rand_name('test-snap')
         image = self.create_image_from_server(server['id'],
                                               name=snapshot_name,
-                                              wait_until='ACTIVE')
+                                              wait_until='ACTIVE',
+                                              wait_for_server=False)
+        self.addCleanup(self.client.delete_image, image['id'])
+        self.assertEqual(snapshot_name, image['name'])
+
+    @decorators.idempotent_id('71bcb732-0261-11e7-9086-fa163e4fa634')
+    @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+                          'Pause is not available.')
+    def test_create_image_from_paused_server(self):
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.servers_client.pause_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client,
+                                       server['id'], 'PAUSED')
+        self.addCleanup(self.servers_client.delete_server, server['id'])
+
+        snapshot_name = data_utils.rand_name('test-snap')
+        image = self.create_image_from_server(server['id'],
+                                              name=snapshot_name,
+                                              wait_until='ACTIVE',
+                                              wait_for_server=False)
+        self.addCleanup(self.client.delete_image, image['id'])
+        self.assertEqual(snapshot_name, image['name'])
+
+    @decorators.idempotent_id('8ca07fec-0262-11e7-907e-fa163e4fa634')
+    @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+                          'Suspend is not available.')
+    def test_create_image_from_suspended_server(self):
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.servers_client.suspend_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client,
+                                       server['id'], 'SUSPENDED')
+        self.addCleanup(self.servers_client.delete_server, server['id'])
+
+        snapshot_name = data_utils.rand_name('test-snap')
+        image = self.create_image_from_server(server['id'],
+                                              name=snapshot_name,
+                                              wait_until='ACTIVE',
+                                              wait_for_server=False)
         self.addCleanup(self.client.delete_image, image['id'])
         self.assertEqual(snapshot_name, image['name'])
diff --git a/tempest/api/compute/images/test_images_negative.py b/tempest/api/compute/images/test_images_negative.py
index 10f3c70..e292389 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/test_images_negative.py
@@ -13,12 +13,11 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -41,7 +40,7 @@
         super(ImagesNegativeTestJSON, cls).setup_clients()
         cls.client = cls.compute_images_client
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6cd5a89d-5b47-46a7-93bc-3916f0d84973')
     def test_create_image_from_deleted_server(self):
         # An image should not be created if the server instance is removed
@@ -54,18 +53,18 @@
         meta = {'image_type': 'test'}
         self.assertRaises(lib_exc.NotFound,
                           self.create_image_from_server,
-                          server['id'], meta=meta)
+                          server['id'], metadata=meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('82c5b0c4-9dbd-463c-872b-20c4755aae7f')
     def test_create_image_from_invalid_server(self):
         # An image should not be created with invalid server id
         # Create a new image with invalid server id
         meta = {'image_type': 'test'}
         self.assertRaises(lib_exc.NotFound, self.create_image_from_server,
-                          data_utils.rand_name('invalid'), meta=meta)
+                          data_utils.rand_name('invalid'), metadata=meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ec176029-73dc-4037-8d72-2e4ff60cf538')
     def test_create_image_specify_uuid_35_characters_or_less(self):
         # Return an error if Image ID passed is 35 characters or less
@@ -74,7 +73,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.create_image,
                           test_uuid, name=snapshot_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('36741560-510e-4cc2-8641-55fe4dfb2437')
     def test_create_image_specify_uuid_37_characters_or_more(self):
         # Return an error if Image ID passed is 37 characters or more
@@ -83,14 +82,14 @@
         self.assertRaises(lib_exc.NotFound, self.client.create_image,
                           test_uuid, name=snapshot_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('381acb65-785a-4942-94ce-d8f8c84f1f0f')
     def test_delete_image_with_invalid_image_id(self):
         # An image should not be deleted with invalid image id
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
                           data_utils.rand_name('invalid'))
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('137aef61-39f7-44a1-8ddf-0adf82511701')
     def test_delete_non_existent_image(self):
         # Return an error while trying to delete a non-existent image
@@ -99,13 +98,13 @@
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
                           non_existent_image_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e6e41425-af5c-4fe6-a4b5-7b7b963ffda5')
     def test_delete_image_blank_id(self):
         # Return an error while trying to delete an image with blank Id
         self.assertRaises(lib_exc.NotFound, self.client.delete_image, '')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('924540c3-f1f1-444c-8f58-718958b6724e')
     def test_delete_image_non_hex_string_id(self):
         # Return an error while trying to delete an image with non hex id
@@ -113,13 +112,13 @@
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
                           invalid_image_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('68e2c175-bd26-4407-ac0f-4ea9ce2139ea')
     def test_delete_image_negative_image_id(self):
         # Return an error while trying to delete an image with negative id
         self.assertRaises(lib_exc.NotFound, self.client.delete_image, -1)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b340030d-82cd-4066-a314-c72fb7c59277')
     def test_delete_image_with_id_over_character_limit(self):
         # Return an error while trying to delete image with id over limit
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index 7fff555..5987d39 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -14,10 +14,8 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest.common import waiters
 from tempest import config
-from tempest.lib.common.utils import test_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -26,6 +24,11 @@
 class ImagesOneServerTestJSON(base.BaseV2ComputeTest):
 
     @classmethod
+    def resource_setup(cls):
+        super(ImagesOneServerTestJSON, cls).resource_setup()
+        cls.server_id = cls.create_test_server(wait_until='ACTIVE')['id']
+
+    @classmethod
     def skip_checks(cls):
         super(ImagesOneServerTestJSON, cls).skip_checks()
         if not CONF.service_available.glance:
@@ -48,20 +51,14 @@
 
     @decorators.idempotent_id('3731d080-d4c5-4872-b41a-64d0d0021314')
     def test_create_delete_image(self):
-        server_id = self.create_test_server(wait_until='ACTIVE')['id']
-
         # Create a new image
         name = data_utils.rand_name('image')
         meta = {'image_type': 'test'}
-        body = self.client.create_image(server_id, name=name,
-                                        metadata=meta)
-        image_id = data_utils.parse_image_id(body.response['location'])
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.client.delete_image, image_id)
-        waiters.wait_for_image_status(self.client, image_id, 'ACTIVE')
+        image = self.create_image_from_server(self.server_id, name=name,
+                                              metadata=meta,
+                                              wait_until='ACTIVE')
 
         # Verify the image was created correctly
-        image = self.client.show_image(image_id)['image']
         self.assertEqual(name, image['name'])
         self.assertEqual('test', image['metadata']['image_type'])
 
@@ -76,20 +73,19 @@
                       (str(original_image['minDisk']), str(flavor_disk_size)))
 
         # Verify the image was deleted correctly
-        self.client.delete_image(image_id)
-        self.client.wait_for_resource_deletion(image_id)
+        self.client.delete_image(image['id'])
+        self.images.remove(image['id'])
+        self.client.wait_for_resource_deletion(image['id'])
 
     @decorators.idempotent_id('3b7c6fe4-dfe7-477c-9243-b06359db51e6')
     def test_create_image_specify_multibyte_character_image_name(self):
-        server_id = self.create_test_server(wait_until='ACTIVE')['id']
-
         # prefix character is:
-        # http://www.fileformat.info/info/unicode/char/1F4A9/index.htm
+        # http://unicode.org/cldr/utility/character.jsp?a=20A1
 
-        # We use a string with 3 byte utf-8 character due to bug
-        # #1370954 in glance which will 500 if mysql is used as the
-        # backend and it attempts to store a 4 byte utf-8 character
+        # We use a string with 3 byte utf-8 character due to nova/glance which
+        # will return 400(Bad Request) if we attempt to send a name which has
+        # 4 byte utf-8 character.
         utf8_name = data_utils.rand_name(b'\xe2\x82\xa1'.decode('utf-8'))
-        body = self.client.create_image(server_id, name=utf8_name)
+        body = self.client.create_image(self.server_id, name=utf8_name)
         image_id = data_utils.parse_image_id(body.response['location'])
         self.addCleanup(self.client.delete_image, image_id)
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 09bbfbe..cf32ba3 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -17,12 +17,11 @@
 from oslo_log import log as logging
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -33,9 +32,6 @@
 
     def tearDown(self):
         """Terminate test instances created after a test is executed."""
-        for image_id in self.image_ids:
-            self.client.delete_image(image_id)
-            self.image_ids.remove(image_id)
         self.server_check_teardown()
         super(ImagesOneServerNegativeTestJSON, self).tearDown()
 
@@ -80,44 +76,40 @@
         server = cls.create_test_server(wait_until='ACTIVE')
         cls.server_id = server['id']
 
-        cls.image_ids = []
-
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('55d1d38c-dd66-4933-9c8e-7d92aeb60ddc')
     def test_create_image_specify_invalid_metadata(self):
         # Return an error when creating image with invalid metadata
-        snapshot_name = data_utils.rand_name('test-snap')
         meta = {'': ''}
-        self.assertRaises(lib_exc.BadRequest, self.client.create_image,
-                          self.server_id, name=snapshot_name, metadata=meta)
+        self.assertRaises(lib_exc.BadRequest, self.create_image_from_server,
+                          self.server_id, metadata=meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('3d24d11f-5366-4536-bd28-cff32b748eca')
     def test_create_image_specify_metadata_over_limits(self):
         # Return an error when creating image with meta data over 255 chars
-        snapshot_name = data_utils.rand_name('test-snap')
         meta = {'a' * 256: 'b' * 256}
-        self.assertRaises(lib_exc.BadRequest, self.client.create_image,
-                          self.server_id, name=snapshot_name, metadata=meta)
+        self.assertRaises(lib_exc.BadRequest, self.create_image_from_server,
+                          self.server_id, metadata=meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0460efcf-ee88-4f94-acef-1bf658695456')
     def test_create_second_image_when_first_image_is_being_saved(self):
         # Disallow creating another image when first image is being saved
 
         # Create first snapshot
-        snapshot_name = data_utils.rand_name('test-snap')
-        body = self.client.create_image(self.server_id, name=snapshot_name)
-        image_id = data_utils.parse_image_id(body.response['location'])
-        self.image_ids.append(image_id)
+        image = self.create_image_from_server(self.server_id)
         self.addCleanup(self._reset_server)
 
         # Create second snapshot
-        alt_snapshot_name = data_utils.rand_name('test-snap')
-        self.assertRaises(lib_exc.Conflict, self.client.create_image,
-                          self.server_id, name=alt_snapshot_name)
+        self.assertRaises(lib_exc.Conflict, self.create_image_from_server,
+                          self.server_id)
 
-    @test.attr(type=['negative'])
+        image_id = data_utils.parse_image_id(image.response['location'])
+        self.client.delete_image(image_id)
+        self.images.remove(image_id)
+
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('084f0cbc-500a-4963-8a4e-312905862581')
     def test_create_image_specify_name_over_character_limit(self):
         # Return an error if snapshot name over 255 characters is passed
@@ -126,19 +118,18 @@
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
                           self.server_id, name=snapshot_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0894954d-2db2-4195-a45b-ffec0bc0187e')
     def test_delete_image_that_is_not_yet_active(self):
         # Return an error while trying to delete an image what is creating
 
-        snapshot_name = data_utils.rand_name('test-snap')
-        body = self.client.create_image(self.server_id, name=snapshot_name)
-        image_id = data_utils.parse_image_id(body.response['location'])
-        self.image_ids.append(image_id)
+        image = self.create_image_from_server(self.server_id)
+        image_id = data_utils.parse_image_id(image.response['location'])
+
         self.addCleanup(self._reset_server)
 
         # Do not wait, attempt to delete the image, ensure it's successful
         self.client.delete_image(image_id)
-        self.image_ids.remove(image_id)
-
-        self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
+        self.images.remove(image_id)
+        self.assertRaises(lib_exc.NotFound,
+                          self.client.show_image, image_id)
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index 9c9b8a1..acc8b3e 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -20,9 +20,9 @@
 
 from tempest.api.compute import base
 from tempest.common import image as common_image
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions
 
@@ -46,9 +46,9 @@
         # 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
+            cls.glance_client = cls.os_primary.image_client
         elif CONF.image_feature_enabled.api_v2:
-            cls.glance_client = cls.os.image_client_v2
+            cls.glance_client = cls.os_primary.image_client_v2
         else:
             raise exceptions.InvalidConfiguration(
                 'Either api_v1 or api_v2 must be True in '
@@ -129,9 +129,9 @@
         params = {'status': 'ACTIVE'}
         images = self.client.list_images(**params)['images']
 
-        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
-        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
-        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+        self.assertNotEmpty([i for i in images if i['id'] == self.image1_id])
+        self.assertNotEmpty([i for i in images if i['id'] == self.image2_id])
+        self.assertNotEmpty([i for i in images if i['id'] == self.image3_id])
 
     @decorators.idempotent_id('33163b73-79f5-4d07-a7ea-9213bcc468ff')
     def test_list_images_filter_by_name(self):
@@ -140,9 +140,9 @@
         params = {'name': self.image1['name']}
         images = self.client.list_images(**params)['images']
 
-        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
-        self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
-        self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
+        self.assertNotEmpty([i for i in images if i['id'] == self.image1_id])
+        self.assertEmpty([i for i in images if i['id'] == self.image2_id])
+        self.assertEmpty([i for i in images if i['id'] == self.image3_id])
 
     @decorators.idempotent_id('9f238683-c763-45aa-b848-232ec3ce3105')
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
@@ -152,14 +152,13 @@
         params = {'server': self.server1['id']}
         images = self.client.list_images(**params)['images']
 
-        self.assertTrue(any([i for i in images
-                             if i['id'] == self.snapshot1_id]),
-                        "Failed to find image %s in images. Got images %s" %
-                        (self.image1_id, images))
-        self.assertTrue(any([i for i in images
-                             if i['id'] == self.snapshot2_id]))
-        self.assertFalse(any([i for i in images
-                              if i['id'] == self.snapshot3_id]))
+        self.assertNotEmpty([i for i in images
+                             if i['id'] == self.snapshot1_id],
+                            "Failed to find image %s in images. "
+                            "Got images %s" % (self.image1_id, images))
+        self.assertNotEmpty([i for i in images
+                             if i['id'] == self.snapshot2_id])
+        self.assertEmpty([i for i in images if i['id'] == self.snapshot3_id])
 
     @decorators.idempotent_id('05a377b8-28cf-4734-a1e6-2ab5c38bf606')
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
@@ -173,12 +172,12 @@
             params = {'server': link['href']}
             images = self.client.list_images(**params)['images']
 
-            self.assertFalse(any([i for i in images
-                                  if i['id'] == self.snapshot1_id]))
-            self.assertFalse(any([i for i in images
-                                  if i['id'] == self.snapshot2_id]))
-            self.assertTrue(any([i for i in images
-                                 if i['id'] == self.snapshot3_id]))
+            self.assertEmpty([i for i in images
+                              if i['id'] == self.snapshot1_id])
+            self.assertEmpty([i for i in images
+                              if i['id'] == self.snapshot2_id])
+            self.assertNotEmpty([i for i in images
+                                 if i['id'] == self.snapshot3_id])
 
     @decorators.idempotent_id('e3356918-4d3e-4756-81d5-abc4524ba29f')
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
@@ -188,14 +187,13 @@
         params = {'type': 'snapshot'}
         images = self.client.list_images(**params)['images']
 
-        self.assertTrue(any([i for i in images
-                             if i['id'] == self.snapshot1_id]))
-        self.assertTrue(any([i for i in images
-                             if i['id'] == self.snapshot2_id]))
-        self.assertTrue(any([i for i in images
-                             if i['id'] == self.snapshot3_id]))
-        self.assertFalse(any([i for i in images
-                              if i['id'] == self.image_ref]))
+        self.assertNotEmpty([i for i in images
+                             if i['id'] == self.snapshot1_id])
+        self.assertNotEmpty([i for i in images
+                             if i['id'] == self.snapshot2_id])
+        self.assertNotEmpty([i for i in images
+                             if i['id'] == self.snapshot3_id])
+        self.assertEmpty([i for i in images if i['id'] == self.image_ref])
 
     @decorators.idempotent_id('3a484ca9-67ba-451e-b494-7fcf28d32d62')
     def test_list_images_limit_results(self):
@@ -212,8 +210,8 @@
         # Filter by the image's created time
         params = {'changes-since': self.image3['created']}
         images = self.client.list_images(**params)['images']
-        found = any([i for i in images if i['id'] == self.image3_id])
-        self.assertTrue(found)
+        found = [i for i in images if i['id'] == self.image3_id]
+        self.assertNotEmpty(found)
 
     @decorators.idempotent_id('9b0ea018-6185-4f71-948a-a123a107988e')
     def test_list_images_with_detail_filter_by_status(self):
@@ -222,9 +220,9 @@
         params = {'status': 'ACTIVE'}
         images = self.client.list_images(detail=True, **params)['images']
 
-        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
-        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
-        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+        self.assertNotEmpty([i for i in images if i['id'] == self.image1_id])
+        self.assertNotEmpty([i for i in images if i['id'] == self.image2_id])
+        self.assertNotEmpty([i for i in images if i['id'] == self.image3_id])
 
     @decorators.idempotent_id('644ea267-9bd9-4f3b-af9f-dffa02396a17')
     def test_list_images_with_detail_filter_by_name(self):
@@ -233,9 +231,9 @@
         params = {'name': self.image1['name']}
         images = self.client.list_images(detail=True, **params)['images']
 
-        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
-        self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
-        self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
+        self.assertNotEmpty([i for i in images if i['id'] == self.image1_id])
+        self.assertEmpty([i for i in images if i['id'] == self.image2_id])
+        self.assertEmpty([i for i in images if i['id'] == self.image3_id])
 
     @decorators.idempotent_id('ba2fa9a9-b672-47cc-b354-3b4c0600e2cb')
     def test_list_images_with_detail_limit_results(self):
@@ -257,12 +255,12 @@
             params = {'server': link['href']}
             images = self.client.list_images(detail=True, **params)['images']
 
-            self.assertFalse(any([i for i in images
-                                  if i['id'] == self.snapshot1_id]))
-            self.assertFalse(any([i for i in images
-                                  if i['id'] == self.snapshot2_id]))
-            self.assertTrue(any([i for i in images
-                                 if i['id'] == self.snapshot3_id]))
+            self.assertEmpty([i for i in images
+                              if i['id'] == self.snapshot1_id])
+            self.assertEmpty([i for i in images
+                              if i['id'] == self.snapshot2_id])
+            self.assertNotEmpty([i for i in images
+                                 if i['id'] == self.snapshot3_id])
 
     @decorators.idempotent_id('888c0cc0-7223-43c5-9db0-b125fd0a393b')
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
@@ -273,14 +271,13 @@
         images = self.client.list_images(detail=True, **params)['images']
         self.client.show_image(self.image_ref)
 
-        self.assertTrue(any([i for i in images
-                             if i['id'] == self.snapshot1_id]))
-        self.assertTrue(any([i for i in images
-                             if i['id'] == self.snapshot2_id]))
-        self.assertTrue(any([i for i in images
-                             if i['id'] == self.snapshot3_id]))
-        self.assertFalse(any([i for i in images
-                              if i['id'] == self.image_ref]))
+        self.assertNotEmpty([i for i in images
+                             if i['id'] == self.snapshot1_id])
+        self.assertNotEmpty([i for i in images
+                             if i['id'] == self.snapshot2_id])
+        self.assertNotEmpty([i for i in images
+                             if i['id'] == self.snapshot3_id])
+        self.assertEmpty([i for i in images if i['id'] == self.image_ref])
 
     @decorators.idempotent_id('7d439e18-ac2e-4827-b049-7e18004712c4')
     def test_list_images_with_detail_filter_by_changes_since(self):
@@ -290,4 +287,4 @@
         # Filter by the image's created time
         params = {'changes-since': self.image1['created']}
         images = self.client.list_images(detail=True, **params)['images']
-        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertNotEmpty([i for i in images if i['id'] == self.image1_id])
diff --git a/tempest/api/compute/images/test_list_image_filters_negative.py b/tempest/api/compute/images/test_list_image_filters_negative.py
index e04a57a..d37f8fc 100644
--- a/tempest/api/compute/images/test_list_image_filters_negative.py
+++ b/tempest/api/compute/images/test_list_image_filters_negative.py
@@ -13,11 +13,10 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -36,7 +35,7 @@
         super(ListImageFiltersNegativeTestJSON, cls).setup_clients()
         cls.client = cls.compute_images_client
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('391b0440-432c-4d4b-b5da-c5096aa247eb')
     def test_get_nonexistent_image(self):
         # Check raises a NotFound
diff --git a/tempest/api/compute/images/test_list_images.py b/tempest/api/compute/images/test_list_images.py
index 5d3cbf3..e2dbd72 100644
--- a/tempest/api/compute/images/test_list_images.py
+++ b/tempest/api/compute/images/test_list_images.py
@@ -44,12 +44,12 @@
     def test_list_images(self):
         # The list of all images should contain the image
         images = self.client.list_images()['images']
-        found = any([i for i in images if i['id'] == self.image_ref])
-        self.assertTrue(found)
+        found = [i for i in images if i['id'] == self.image_ref]
+        self.assertNotEmpty(found)
 
     @decorators.idempotent_id('9f94cb6b-7f10-48c5-b911-a0b84d7d4cd6')
     def test_list_images_with_detail(self):
         # Detailed list of all images should contain the expected images
         images = self.client.list_images(detail=True)['images']
-        found = any([i for i in images if i['id'] == self.image_ref])
-        self.assertTrue(found)
+        found = [i for i in images if i['id'] == self.image_ref]
+        self.assertNotEmpty(found)
diff --git a/tempest/api/compute/keypairs/base.py b/tempest/api/compute/keypairs/base.py
index 21f504b..0051810 100644
--- a/tempest/api/compute/keypairs/base.py
+++ b/tempest/api/compute/keypairs/base.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 
 
 class BaseKeypairTest(base.BaseV2ComputeTest):
diff --git a/tempest/api/compute/keypairs/test_keypairs.py b/tempest/api/compute/keypairs/test_keypairs.py
index 11e84e8..3a54d51 100644
--- a/tempest/api/compute/keypairs/test_keypairs.py
+++ b/tempest/api/compute/keypairs/test_keypairs.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 from tempest.api.compute.keypairs import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 
@@ -51,13 +51,10 @@
         # Keypair should be created, verified and deleted
         k_name = data_utils.rand_name('keypair')
         keypair = self.create_keypair(k_name)
-        private_key = keypair['private_key']
         key_name = keypair['name']
         self.assertEqual(key_name, k_name,
                          "The created keypair name is not equal "
                          "to the requested name")
-        self.assertIsNotNone(private_key,
-                             "Field private_key is empty or not found.")
 
     @decorators.idempotent_id('a4233d5d-52d8-47cc-9a25-e1864527e3df')
     def test_get_keypair_detail(self):
@@ -65,14 +62,9 @@
         k_name = data_utils.rand_name('keypair')
         self.create_keypair(k_name)
         keypair_detail = self.client.show_keypair(k_name)['keypair']
-        self.assertIn('name', keypair_detail)
-        self.assertIn('public_key', keypair_detail)
         self.assertEqual(keypair_detail['name'], k_name,
                          "The created keypair name is not equal "
                          "to requested name")
-        public_key = keypair_detail['public_key']
-        self.assertIsNotNone(public_key,
-                             "Field public_key is empty or not found.")
 
     @decorators.idempotent_id('39c90c6a-304a-49dd-95ec-2366129def05')
     def test_keypair_create_with_pub_key(self):
diff --git a/tempest/api/compute/keypairs/test_keypairs_negative.py b/tempest/api/compute/keypairs/test_keypairs_negative.py
index 863ce0d..205076c 100644
--- a/tempest/api/compute/keypairs/test_keypairs_negative.py
+++ b/tempest/api/compute/keypairs/test_keypairs_negative.py
@@ -15,14 +15,13 @@
 #    under the License.
 
 from tempest.api.compute.keypairs import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class KeyPairsNegativeTestJSON(base.BaseKeypairTest):
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('29cca892-46ae-4d48-bc32-8fe7e731eb81')
     def test_keypair_create_with_invalid_pub_key(self):
         # Keypair should not be created with a non RSA public key
@@ -30,7 +29,7 @@
         self.assertRaises(lib_exc.BadRequest,
                           self.create_keypair, pub_key=pub_key)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7cc32e47-4c42-489d-9623-c5e2cb5a2fa5')
     def test_keypair_delete_nonexistent_key(self):
         # Non-existent key deletion should throw a proper error
@@ -38,7 +37,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.delete_keypair,
                           k_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('dade320e-69ca-42a9-ba4a-345300f127e0')
     def test_create_keypair_with_empty_public_key(self):
         # Keypair should not be created with an empty public key
@@ -46,7 +45,7 @@
         self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           pub_key=pub_key)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fc100c19-2926-4b9c-8fdc-d0589ee2f9ff')
     def test_create_keypair_when_public_key_bits_exceeds_maximum(self):
         # Keypair should not be created when public key bits are too long
@@ -54,7 +53,7 @@
         self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           pub_key=pub_key)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0359a7f1-f002-4682-8073-0c91e4011b7c')
     def test_create_keypair_with_duplicate_name(self):
         # Keypairs with duplicate names should not be created
@@ -65,14 +64,14 @@
                           k_name)
         self.client.delete_keypair(k_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1398abe1-4a84-45fb-9294-89f514daff00')
     def test_create_keypair_with_empty_name_string(self):
         # Keypairs with name being an empty string should not be created
         self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           '')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('3faa916f-779f-4103-aca7-dc3538eee1b7')
     def test_create_keypair_with_long_keynames(self):
         # Keypairs with name longer than 255 chars should not be created
@@ -80,7 +79,7 @@
         self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           k_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('45fbe5e0-acb5-49aa-837a-ff8d0719db91')
     def test_create_keypair_invalid_name(self):
         # Keypairs with name being an invalid name should not be created
diff --git a/tempest/api/compute/keypairs/test_keypairs_v22.py b/tempest/api/compute/keypairs/test_keypairs_v22.py
index c893a4f..f39bb12 100644
--- a/tempest/api/compute/keypairs/test_keypairs_v22.py
+++ b/tempest/api/compute/keypairs/test_keypairs_v22.py
@@ -13,7 +13,7 @@
 #    under the License.
 
 from tempest.api.compute.keypairs import test_keypairs
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 
diff --git a/tempest/api/compute/limits/test_absolute_limits.py b/tempest/api/compute/limits/test_absolute_limits.py
index 58352bd..0585fec 100644
--- a/tempest/api/compute/limits/test_absolute_limits.py
+++ b/tempest/api/compute/limits/test_absolute_limits.py
@@ -42,6 +42,6 @@
         # check whether all expected elements exist
         missing_elements =\
             [ele for ele in expected_elements if ele not in absolute_limits]
-        self.assertEqual(0, len(missing_elements),
+        self.assertEmpty(missing_elements,
                          "Failed to find element %s in absolute limits list"
                          % ', '.join(ele for ele in missing_elements))
diff --git a/tempest/api/compute/limits/test_absolute_limits_negative.py b/tempest/api/compute/limits/test_absolute_limits_negative.py
index b9ae0c6..bef4eb5 100644
--- a/tempest/api/compute/limits/test_absolute_limits_negative.py
+++ b/tempest/api/compute/limits/test_absolute_limits_negative.py
@@ -17,7 +17,6 @@
 from tempest.common import tempest_fixtures as fixtures
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class AbsoluteLimitsNegativeTestJSON(base.BaseV2ComputeTest):
@@ -32,7 +31,7 @@
         super(AbsoluteLimitsNegativeTestJSON, cls).setup_clients()
         cls.client = cls.limits_client
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('215cd465-d8ae-49c9-bf33-9c911913a5c8')
     def test_max_image_meta_exceed_limit(self):
         # We should not create vm with image meta over maxImageMeta limit
@@ -41,11 +40,11 @@
         max_meta = limits['absolute']['maxImageMeta']
 
         # No point in running this test if there is no limit.
-        if int(max_meta) == -1:
+        if max_meta == -1:
             raise self.skipException('no limit for maxImageMeta')
 
         # Create server should fail, since we are passing > metadata Limit!
-        max_meta_data = int(max_meta) + 1
+        max_meta_data = max_meta + 1
 
         meta_data = {}
         for xx in range(max_meta_data):
diff --git a/tempest/api/compute/security_groups/base.py b/tempest/api/compute/security_groups/base.py
index cb18775..54a6da8 100644
--- a/tempest/api/compute/security_groups/base.py
+++ b/tempest/api/compute/security_groups/base.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest import config
-from tempest import test
+from tempest.lib.common.utils import data_utils
 
 CONF = config.CONF
 
@@ -24,6 +24,12 @@
 class BaseSecurityGroupsTest(base.BaseV2ComputeTest):
 
     @classmethod
+    def skip_checks(cls):
+        super(BaseSecurityGroupsTest, cls).skip_checks()
+        if not utils.get_service_list()['network']:
+            raise cls.skipException("network service not enabled.")
+
+    @classmethod
     def setup_credentials(cls):
         # A network and a subnet will be created for these tests
         cls.set_network_resources(network=True, subnet=True)
@@ -32,7 +38,7 @@
     @staticmethod
     def generate_random_security_group_id():
         if (CONF.service_available.neutron and
-            test.is_extension_enabled('security-group', 'network')):
+            utils.is_extension_enabled('security-group', 'network')):
             return data_utils.rand_uuid()
         else:
             return data_utils.rand_int_id(start=999)
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index 7658848..4c99ea6 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -14,11 +14,7 @@
 #    under the License.
 
 from tempest.api.compute.security_groups import base
-from tempest import config
 from tempest.lib import decorators
-from tempest import test
-
-CONF = config.CONF
 
 
 class SecurityGroupRulesTestJSON(base.BaseSecurityGroupsTest):
@@ -31,7 +27,6 @@
     @classmethod
     def resource_setup(cls):
         super(SecurityGroupRulesTestJSON, cls).resource_setup()
-        cls.neutron_available = CONF.service_available.neutron
         cls.ip_protocol = 'tcp'
         cls.from_port = 22
         cls.to_port = 22
@@ -57,9 +52,8 @@
             self.assertEqual(self.expected[key], actual_rule[key],
                              "Miss-matched key is %s" % key)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('850795d7-d4d3-4e55-b527-a774c0123d3a')
-    @test.services('network')
     def test_security_group_rules_create(self):
         # Positive test: Creation of Security Group rule
         # should be successful
@@ -77,7 +71,6 @@
         self._check_expected_response(rule)
 
     @decorators.idempotent_id('7a01873e-3c38-4f30-80be-31a043cfe2fd')
-    @test.services('network')
     def test_security_group_rules_create_with_optional_cidr(self):
         # Positive test: Creation of Security Group rule
         # with optional argument cidr
@@ -100,7 +93,6 @@
         self._check_expected_response(rule)
 
     @decorators.idempotent_id('7f5d2899-7705-4d4b-8458-4505188ffab6')
-    @test.services('network')
     def test_security_group_rules_create_with_optional_group_id(self):
         # Positive test: Creation of Security Group rule
         # with optional argument group_id
@@ -127,9 +119,8 @@
                                   'name': group_name}
         self._check_expected_response(rule)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('a6154130-5a55-4850-8be4-5e9e796dbf17')
-    @test.services('network')
     def test_security_group_rules_list(self):
         # Positive test: Created Security Group rules should be
         # in the list of all rules
@@ -163,11 +154,10 @@
         # Get rules of the created Security Group
         rules = self.security_groups_client.show_security_group(
             securitygroup_id)['security_group']['rules']
-        self.assertTrue(any([i for i in rules if i['id'] == rule1_id]))
-        self.assertTrue(any([i for i in rules if i['id'] == rule2_id]))
+        self.assertNotEmpty([i for i in rules if i['id'] == rule1_id])
+        self.assertNotEmpty([i for i in rules if i['id'] == rule2_id])
 
     @decorators.idempotent_id('fc5c5acf-2091-43a6-a6ae-e42760e9ffaf')
-    @test.services('network')
     def test_security_group_rules_delete_when_peer_group_deleted(self):
         # Positive test:rule will delete when peer group deleting
         # Creating a Security Group to add rules to it
@@ -182,7 +172,7 @@
             ip_protocol=self.ip_protocol,
             from_port=self.from_port,
             to_port=self.to_port,
-            group_id=sg2_id)['security_group_rule']
+            group_id=sg2_id)
 
         # Delete group2
         self.security_groups_client.delete_security_group(sg2_id)
@@ -190,4 +180,4 @@
         rules = (self.security_groups_client.show_security_group(sg1_id)
                  ['security_group']['rules'])
         # The group1 has no rules because group2 has deleted
-        self.assertEqual(0, len(rules))
+        self.assertEmpty(rules)
diff --git a/tempest/api/compute/security_groups/test_security_group_rules_negative.py b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
index 0312736..8283aae 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules_negative.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
@@ -14,10 +14,9 @@
 #    under the License.
 
 from tempest.api.compute.security_groups import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class SecurityGroupRulesNegativeTestJSON(base.BaseSecurityGroupsTest):
@@ -27,9 +26,8 @@
         super(SecurityGroupRulesNegativeTestJSON, cls).setup_clients()
         cls.rules_client = cls.security_group_rules_client
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1d507e98-7951-469b-82c3-23f1e6b8c254')
-    @test.services('network')
     def test_create_security_group_rule_with_non_existent_id(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with non existent Parent group id
@@ -44,9 +42,8 @@
                           ip_protocol=ip_protocol, from_port=from_port,
                           to_port=to_port)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('2244d7e4-adb7-4ecb-9930-2d77e123ce4f')
-    @test.services('network')
     def test_create_security_group_rule_with_invalid_id(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with Parent group id which is not integer
@@ -61,9 +58,8 @@
                           ip_protocol=ip_protocol, from_port=from_port,
                           to_port=to_port)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8bd56d02-3ffa-4d67-9933-b6b9a01d6089')
-    @test.services('network')
     def test_create_security_group_rule_duplicate(self):
         # Negative test: Create Security Group rule duplicate should fail
         # Creating a Security Group to add rule to it
@@ -86,9 +82,8 @@
                           ip_protocol=ip_protocol, from_port=from_port,
                           to_port=to_port)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('84c81249-9f6e-439c-9bbf-cbb0d2cddbdf')
-    @test.services('network')
     def test_create_security_group_rule_with_invalid_ip_protocol(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid ip_protocol
@@ -106,9 +101,8 @@
                           ip_protocol=ip_protocol, from_port=from_port,
                           to_port=to_port)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('12bbc875-1045-4f7a-be46-751277baedb9')
-    @test.services('network')
     def test_create_security_group_rule_with_invalid_from_port(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid from_port
@@ -125,9 +119,8 @@
                           ip_protocol=ip_protocol, from_port=from_port,
                           to_port=to_port)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ff88804d-144f-45d1-bf59-dd155838a43a')
-    @test.services('network')
     def test_create_security_group_rule_with_invalid_to_port(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid to_port
@@ -144,9 +137,8 @@
                           ip_protocol=ip_protocol, from_port=from_port,
                           to_port=to_port)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('00296fa9-0576-496a-ae15-fbab843189e0')
-    @test.services('network')
     def test_create_security_group_rule_with_invalid_port_range(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid port range.
@@ -163,9 +155,8 @@
                           ip_protocol=ip_protocol, from_port=from_port,
                           to_port=to_port)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('56fddcca-dbb8-4494-a0db-96e9f869527c')
-    @test.services('network')
     def test_delete_security_group_rule_with_non_existent_id(self):
         # Negative test: Deletion of Security Group rule should be FAIL
         # with non existent id
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index e070336..eeb423e 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -14,11 +14,10 @@
 #    under the License.
 
 from tempest.api.compute.security_groups import base
-from tempest.common.utils import data_utils
 from tempest.common import waiters
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class SecurityGroupsTestJSON(base.BaseSecurityGroupsTest):
@@ -28,9 +27,8 @@
         super(SecurityGroupsTestJSON, cls).setup_clients()
         cls.client = cls.security_groups_client
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('eb2b087d-633d-4d0d-a7bd-9e6ba35b32de')
-    @test.services('network')
     def test_security_groups_create_list_delete(self):
         # Positive test:Should return the list of Security Groups
         # Create 3 Security Groups
@@ -62,14 +60,12 @@
                                             for m_group in deleted_sgs))
 
     @decorators.idempotent_id('ecc0da4a-2117-48af-91af-993cca39a615')
-    @test.services('network')
     def test_security_group_create_get_delete(self):
         # Security Group should be created, fetched and deleted
         # with char space between name along with
         # leading and trailing spaces
         s_name = ' %s ' % data_utils.rand_name('securitygroup ')
         securitygroup = self.create_security_group(name=s_name)
-        self.assertIn('name', securitygroup)
         securitygroup_name = securitygroup['name']
         self.assertEqual(securitygroup_name, s_name,
                          "The created Security Group name is "
@@ -84,7 +80,6 @@
         self.client.wait_for_resource_deletion(securitygroup['id'])
 
     @decorators.idempotent_id('fe4abc0d-83f5-4c50-ad11-57a1127297a2')
-    @test.services('network')
     def test_server_security_groups(self):
         # Checks that security groups may be added and linked to a server
         # and not deleted if the server is active.
@@ -126,12 +121,10 @@
         self.client.delete_security_group(sg2['id'])
 
     @decorators.idempotent_id('7d4e1d3c-3209-4d6d-b020-986304ebad1f')
-    @test.services('network')
     def test_update_security_groups(self):
         # Update security group name and description
         # Create a security group
         securitygroup = self.create_security_group()
-        self.assertIn('id', securitygroup)
         securitygroup_id = securitygroup['id']
         # Update the name and description
         s_new_name = data_utils.rand_name('sg-hth')
@@ -144,3 +137,30 @@
                          ['security_group'])
         self.assertEqual(s_new_name, fetched_group['name'])
         self.assertEqual(s_new_des, fetched_group['description'])
+
+    @decorators.idempotent_id('79517d60-535a-438f-af3d-e6feab1cbea7')
+    def test_list_security_groups_by_server(self):
+        # Create a couple security groups that we will use
+        # for the server resource this test creates
+        sg = self.create_security_group()
+        sg2 = self.create_security_group()
+        assigned_security_groups_ids = [sg['id'], sg2['id']]
+        # Create server and add the security group created
+        # above to the server we just created
+        server_id = self.create_test_server(wait_until='ACTIVE')['id']
+        # add security groups to server
+        self.servers_client.add_security_group(server_id, name=sg['name'])
+        self.servers_client.add_security_group(server_id, name=sg2['name'])
+
+        # list security groups for a server
+        fetched_groups = (
+            self.servers_client.list_security_groups_by_server(
+                server_id)['security_groups'])
+        fetched_security_groups_ids = [i['id'] for i in fetched_groups]
+        # verifying the security groups ids in list
+        missing_security_groups =\
+            [p for p in assigned_security_groups_ids
+             if p not in fetched_security_groups_ids]
+        self.assertEmpty(missing_security_groups,
+                         "Failed to find security_groups %s in fetched list" %
+                         ', '.join(missing_security_groups))
diff --git a/tempest/api/compute/security_groups/test_security_groups_negative.py b/tempest/api/compute/security_groups/test_security_groups_negative.py
index ad18861..9c44bb2 100644
--- a/tempest/api/compute/security_groups/test_security_groups_negative.py
+++ b/tempest/api/compute/security_groups/test_security_groups_negative.py
@@ -16,11 +16,10 @@
 import testtools
 
 from tempest.api.compute.security_groups import base
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -32,14 +31,8 @@
         super(SecurityGroupsNegativeTestJSON, cls).setup_clients()
         cls.client = cls.security_groups_client
 
-    @classmethod
-    def resource_setup(cls):
-        super(SecurityGroupsNegativeTestJSON, cls).resource_setup()
-        cls.neutron_available = CONF.service_available.neutron
-
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('673eaec1-9b3e-48ed-bdf1-2786c1b9661c')
-    @test.services('network')
     def test_security_group_get_nonexistent_group(self):
         # Negative test:Should not be able to GET the details
         # of non-existent Security Group
@@ -49,9 +42,8 @@
 
     @decorators.skip_because(bug="1161411",
                              condition=CONF.service_available.neutron)
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1759c3cb-b0fc-44b7-86ce-c99236be911d')
-    @test.services('network')
     def test_security_group_create_with_invalid_group_name(self):
         # Negative test: Security Group should not be created with group name
         # as an empty string/with white spaces/chars more than 255
@@ -72,9 +64,8 @@
 
     @decorators.skip_because(bug="1161411",
                              condition=CONF.service_available.neutron)
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('777b6f14-aca9-4758-9e84-38783cfa58bc')
-    @test.services('network')
     def test_security_group_create_with_invalid_group_description(self):
         # Negative test: Security Group should not be created with description
         # longer than 255 chars. Empty description is allowed by the API
@@ -89,8 +80,7 @@
     @decorators.idempotent_id('9fdb4abc-6b66-4b27-b89c-eb215a956168')
     @testtools.skipIf(CONF.service_available.neutron,
                       "Neutron allows duplicate names for security groups")
-    @test.attr(type=['negative'])
-    @test.services('network')
+    @decorators.attr(type=['negative'])
     def test_security_group_create_with_duplicate_name(self):
         # Negative test:Security Group with duplicate name should not
         # be created
@@ -102,9 +92,8 @@
                           self.client.create_security_group,
                           name=s_name, description=s_description)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('36a1629f-c6da-4a26-b8b8-55e7e5d5cd58')
-    @test.services('network')
     def test_delete_the_default_security_group(self):
         # Negative test:Deletion of the "default" Security Group should Fail
         default_security_group_id = None
@@ -118,18 +107,16 @@
                           self.client.delete_security_group,
                           default_security_group_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6727c00b-214c-4f9e-9a52-017ac3e98411')
-    @test.services('network')
     def test_delete_nonexistent_security_group(self):
         # Negative test:Deletion of a non-existent Security Group should fail
         non_exist_id = self.generate_random_security_group_id()
         self.assertRaises(lib_exc.NotFound,
                           self.client.delete_security_group, non_exist_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1438f330-8fa4-4aeb-8a94-37c250106d7f')
-    @test.services('network')
     def test_delete_security_group_without_passing_id(self):
         # Negative test:Deletion of a Security Group with out passing ID
         # should Fail
@@ -139,8 +126,7 @@
     @decorators.idempotent_id('00579617-fe04-4e1c-9d08-ca7467d2e34b')
     @testtools.skipIf(CONF.service_available.neutron,
                       "Neutron does not check the security group ID")
-    @test.attr(type=['negative'])
-    @test.services('network')
+    @decorators.attr(type=['negative'])
     def test_update_security_group_with_invalid_sg_id(self):
         # Update security_group with invalid sg_id should fail
         s_name = data_utils.rand_name('sg')
@@ -154,12 +140,10 @@
     @decorators.idempotent_id('cda8d8b4-59f8-4087-821d-20cf5a03b3b1')
     @testtools.skipIf(CONF.service_available.neutron,
                       "Neutron does not check the security group name")
-    @test.attr(type=['negative'])
-    @test.services('network')
+    @decorators.attr(type=['negative'])
     def test_update_security_group_with_invalid_sg_name(self):
         # Update security_group with invalid sg_name should fail
         securitygroup = self.create_security_group()
-        self.assertIn('id', securitygroup)
         securitygroup_id = securitygroup['id']
         # Update Security Group with group name longer than 255 chars
         s_new_name = 'securitygroup-'.ljust(260, '0')
@@ -170,12 +154,10 @@
     @decorators.idempotent_id('97d12b1c-a610-4194-93f1-ba859e718b45')
     @testtools.skipIf(CONF.service_available.neutron,
                       "Neutron does not check the security group description")
-    @test.attr(type=['negative'])
-    @test.services('network')
+    @decorators.attr(type=['negative'])
     def test_update_security_group_with_invalid_sg_des(self):
         # Update security_group with invalid sg_des should fail
         securitygroup = self.create_security_group()
-        self.assertIn('id', securitygroup)
         securitygroup_id = securitygroup['id']
         # Update Security Group with group description longer than 255 chars
         s_new_des = 'des-'.ljust(260, '0')
@@ -183,9 +165,8 @@
                           self.client.update_security_group,
                           securitygroup_id, description=s_new_des)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('27edee9c-873d-4da6-a68a-3c256efebe8f')
-    @test.services('network')
     def test_update_non_existent_security_group(self):
         # Update a non-existent Security Group should Fail
         non_exist_id = self.generate_random_security_group_id()
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index c4bf582..e0a1d77 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -15,14 +15,16 @@
 
 import time
 
+import six
+
 from tempest.api.compute import base
 from tempest.common import compute
+from tempest.common import utils
 from tempest.common.utils import net_utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -46,10 +48,9 @@
     @classmethod
     def setup_clients(cls):
         super(AttachInterfacesTestJSON, cls).setup_clients()
-        cls.subnets_client = cls.os.subnets_client
-        cls.ports_client = cls.os.ports_client
+        cls.subnets_client = cls.os_primary.subnets_client
+        cls.ports_client = cls.os_primary.ports_client
 
-    # TODO(mriedem): move this into a common waiters utility module
     def wait_for_port_detach(self, port_id):
         """Waits for the port's device_id to be unset.
 
@@ -79,7 +80,6 @@
 
     def _check_interface(self, iface, port_id=None, network_id=None,
                          fixed_ip=None, mac_addr=None):
-        self.assertIn('port_state', iface)
         if port_id:
             self.assertEqual(iface['port_id'], port_id)
         if network_id:
@@ -185,7 +185,7 @@
         self.assertEqual(sorted(list1), sorted(list2))
 
     @decorators.idempotent_id('73fe8f02-590d-4bf1-b184-e9ca81065051')
-    @test.services('network')
+    @utils.services('network')
     def test_create_list_show_delete_interfaces(self):
         server, ifs = self._create_server_get_interfaces()
         interface_count = len(ifs)
@@ -197,7 +197,7 @@
         except lib_exc.BadRequest as e:
             msg = ('Multiple possible networks found, use a Network ID to be '
                    'more specific.')
-            if not CONF.compute.fixed_network_name and e.message == msg:
+            if not CONF.compute.fixed_network_name and six.text_type(e) == msg:
                 raise
         else:
             ifs.append(iface)
@@ -220,9 +220,9 @@
         _ifs = self._test_delete_interface(server, ifs)
         self.assertEqual(len(ifs) - 1, len(_ifs))
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9')
-    @test.services('network')
+    @utils.services('network')
     def test_add_remove_fixed_ip(self):
         # Add and Remove the fixed IP to server.
         server, ifs = self._create_server_get_interfaces()
@@ -232,7 +232,7 @@
         network_id = ifs[0]['net_id']
         self.servers_client.add_fixed_ip(server['id'], networkId=network_id)
         # Remove the fixed IP from server.
-        server_detail = self.os.servers_client.show_server(
+        server_detail = self.os_primary.servers_client.show_server(
             server['id'])['server']
         # Get the Fixed IP from server.
         fixed_ip = None
@@ -264,7 +264,8 @@
 
         # create two servers
         _, servers = compute.create_test_server(
-            self.os, tenant_network=network, wait_until='ACTIVE', min_count=2)
+            self.os_primary, tenant_network=network,
+            wait_until='ACTIVE', min_count=2)
         # add our cleanups for the servers since we bypassed the base class
         for server in servers:
             self.addCleanup(self.delete_server, server['id'])
diff --git a/tempest/api/compute/servers/test_availability_zone.py b/tempest/api/compute/servers/test_availability_zone.py
index 82e74ed..36828d6 100644
--- a/tempest/api/compute/servers/test_availability_zone.py
+++ b/tempest/api/compute/servers/test_availability_zone.py
@@ -29,4 +29,4 @@
     def test_get_availability_zone_list_with_non_admin_user(self):
         # List of availability zone with non-administrator user
         availability_zone = self.client.list_availability_zones()
-        self.assertGreater(len(availability_zone['availabilityZoneInfo']), 0)
+        self.assertNotEmpty(availability_zone['availabilityZoneInfo'])
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 5ddae5e..d8ce7ea 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -17,17 +17,18 @@
 import testtools
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest.common.utils.linux import remote_client
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
 
 class ServersTestJSON(base.BaseV2ComputeTest):
     disk_config = 'AUTO'
+    volume_backed = False
 
     @classmethod
     def setup_credentials(cls):
@@ -38,8 +39,6 @@
     def setup_clients(cls):
         super(ServersTestJSON, cls).setup_clients()
         cls.client = cls.servers_client
-        cls.networks_client = cls.os.networks_client
-        cls.subnets_client = cls.os.subnets_client
 
     @classmethod
     def resource_setup(cls):
@@ -51,7 +50,7 @@
         cls.name = data_utils.rand_name(cls.__name__ + '-server')
         cls.password = data_utils.rand_password()
         disk_config = cls.disk_config
-        cls.server_initial = cls.create_test_server(
+        server_initial = cls.create_test_server(
             validatable=True,
             wait_until='ACTIVE',
             name=cls.name,
@@ -59,25 +58,12 @@
             accessIPv4=cls.accessIPv4,
             accessIPv6=cls.accessIPv6,
             disk_config=disk_config,
-            adminPass=cls.password)
-        cls.server = (cls.client.show_server(cls.server_initial['id'])
+            adminPass=cls.password,
+            volume_backed=cls.volume_backed)
+        cls.server = (cls.client.show_server(server_initial['id'])
                       ['server'])
 
-    def _create_net_subnet_ret_net_from_cidr(self, cidr):
-        name_net = data_utils.rand_name(self.__class__.__name__)
-        net = self.networks_client.create_network(name=name_net)
-        self.addCleanup(self.networks_client.delete_network,
-                        net['network']['id'])
-
-        subnet = self.subnets_client.create_subnet(
-            network_id=net['network']['id'],
-            cidr=cidr,
-            ip_version=4)
-        self.addCleanup(self.subnets_client.delete_subnet,
-                        subnet['subnet']['id'])
-        return net
-
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('5de47127-9977-400a-936f-abcfbec1218f')
     def test_verify_server_details(self):
         # Verify the specified server attributes are set correctly
@@ -87,26 +73,30 @@
         self.assertEqual(self.server['accessIPv6'],
                          str(netaddr.IPAddress(self.accessIPv6)))
         self.assertEqual(self.name, self.server['name'])
-        self.assertEqual(self.image_ref, self.server['image']['id'])
+        if self.volume_backed:
+            # Image is an empty string as per documentation
+            self.assertEqual("", self.server['image'])
+        else:
+            self.assertEqual(self.image_ref, self.server['image']['id'])
         self.assertEqual(self.flavor_ref, self.server['flavor']['id'])
         self.assertEqual(self.meta, self.server['metadata'])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('9a438d88-10c6-4bcd-8b5b-5b6e25e1346f')
     def test_list_servers(self):
         # The created server should be in the list of all servers
         body = self.client.list_servers()
         servers = body['servers']
-        found = any([i for i in servers if i['id'] == self.server['id']])
-        self.assertTrue(found)
+        found = [i for i in servers if i['id'] == self.server['id']]
+        self.assertNotEmpty(found)
 
     @decorators.idempotent_id('585e934c-448e-43c4-acbf-d06a9b899997')
     def test_list_servers_with_detail(self):
         # The created server should be in the detailed list of all servers
         body = self.client.list_servers(detail=True)
         servers = body['servers']
-        found = any([i for i in servers if i['id'] == self.server['id']])
-        self.assertTrue(found)
+        found = [i for i in servers if i['id'] == self.server['id']]
+        self.assertNotEmpty(found)
 
     @decorators.idempotent_id('cbc0f52f-05aa-492b-bdc1-84b575ca294b')
     @testtools.skipUnless(CONF.validation.run_validation,
@@ -122,7 +112,8 @@
             self.validation_resources['keypair']['private_key'],
             server=self.server,
             servers_client=self.client)
-        self.assertEqual(flavor['vcpus'], linux_client.get_number_of_vcpus())
+        output = linux_client.exec_command('grep -c ^processor /proc/cpuinfo')
+        self.assertEqual(flavor['vcpus'], int(output))
 
     @decorators.idempotent_id('ac1ad47f-984b-4441-9274-c9079b7a0666')
     @testtools.skipUnless(CONF.validation.run_validation,
@@ -136,188 +127,11 @@
             self.validation_resources['keypair']['private_key'],
             server=self.server,
             servers_client=self.client)
-        hostname = linux_client.get_hostname()
+        hostname = linux_client.exec_command("hostname").rstrip()
         msg = ('Failed while verifying servername equals hostname. Expected '
                'hostname "%s" but got "%s".' % (self.name, hostname))
         self.assertEqual(self.name.lower(), hostname, msg)
 
-    @decorators.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
-    @testtools.skipUnless(
-        test.is_scheduler_filter_enabled("ServerGroupAffinityFilter"),
-        'ServerGroupAffinityFilter is not available.')
-    def test_create_server_with_scheduler_hint_group(self):
-        # Create a server with the scheduler hint "group".
-        group_id = self.create_test_server_group()['id']
-        hints = {'group': group_id}
-        server = self.create_test_server(scheduler_hints=hints,
-                                         wait_until='ACTIVE')
-
-        # Check a server is in the group
-        server_group = (self.server_groups_client.show_server_group(group_id)
-                        ['server_group'])
-        self.assertIn(server['id'], server_group['members'])
-
-    @decorators.idempotent_id('0578d144-ed74-43f8-8e57-ab10dbf9b3c2')
-    @testtools.skipUnless(CONF.service_available.neutron,
-                          'Neutron service must be available.')
-    def test_verify_multiple_nics_order(self):
-        # Verify that the networks order given at the server creation is
-        # preserved within the server.
-        net1 = self._create_net_subnet_ret_net_from_cidr('19.80.0.0/24')
-        net2 = self._create_net_subnet_ret_net_from_cidr('19.86.0.0/24')
-
-        networks = [{'uuid': net1['network']['id']},
-                    {'uuid': net2['network']['id']}]
-
-        server_multi_nics = self.create_test_server(
-            networks=networks, wait_until='ACTIVE')
-
-        # Cleanup server; this is needed in the test case because with the LIFO
-        # nature of the cleanups, if we don't delete the server first, the port
-        # will still be part of the subnet and we'll get a 409 from Neutron
-        # when trying to delete the subnet. The tear down in the base class
-        # will try to delete the server and get a 404 but it's ignored so
-        # we're OK.
-        self.addCleanup(self.delete_server, server_multi_nics['id'])
-
-        addresses = (self.client.list_addresses(server_multi_nics['id'])
-                     ['addresses'])
-
-        # We can't predict the ip addresses assigned to the server on networks.
-        # Sometimes the assigned addresses are ['19.80.0.2', '19.86.0.2'], at
-        # other times ['19.80.0.3', '19.86.0.3']. So we check if the first
-        # address is in first network, similarly second address is in second
-        # network.
-        addr = [addresses[net1['network']['name']][0]['addr'],
-                addresses[net2['network']['name']][0]['addr']]
-        networks = [netaddr.IPNetwork('19.80.0.0/24'),
-                    netaddr.IPNetwork('19.86.0.0/24')]
-        for address, network in zip(addr, networks):
-            self.assertIn(address, network)
-
-    @decorators.idempotent_id('1678d144-ed74-43f8-8e57-ab10dbf9b3c2')
-    @testtools.skipUnless(CONF.service_available.neutron,
-                          'Neutron service must be available.')
-    def test_verify_duplicate_network_nics(self):
-        # Verify that server creation does not fail when more than one nic
-        # is created on the same network.
-        net1 = self._create_net_subnet_ret_net_from_cidr('19.80.0.0/24')
-        net2 = self._create_net_subnet_ret_net_from_cidr('19.86.0.0/24')
-
-        networks = [{'uuid': net1['network']['id']},
-                    {'uuid': net2['network']['id']},
-                    {'uuid': net1['network']['id']}]
-
-        server_multi_nics = self.create_test_server(
-            networks=networks, wait_until='ACTIVE')
-        self.addCleanup(self.delete_server, server_multi_nics['id'])
-
-        addresses = (self.client.list_addresses(server_multi_nics['id'])
-                     ['addresses'])
-
-        addr = [addresses[net1['network']['name']][0]['addr'],
-                addresses[net2['network']['name']][0]['addr'],
-                addresses[net1['network']['name']][1]['addr']]
-        networks = [netaddr.IPNetwork('19.80.0.0/24'),
-                    netaddr.IPNetwork('19.86.0.0/24'),
-                    netaddr.IPNetwork('19.80.0.0/24')]
-        for address, network in zip(addr, networks):
-            self.assertIn(address, network)
-
-
-class ServersWithSpecificFlavorTestJSON(base.BaseV2ComputeAdminTest):
-    disk_config = 'AUTO'
-
-    @classmethod
-    def setup_credentials(cls):
-        cls.prepare_instance_network()
-        super(ServersWithSpecificFlavorTestJSON, cls).setup_credentials()
-
-    @classmethod
-    def setup_clients(cls):
-        super(ServersWithSpecificFlavorTestJSON, cls).setup_clients()
-        cls.flavor_client = cls.os_adm.flavors_client
-        cls.client = cls.servers_client
-
-    @classmethod
-    def resource_setup(cls):
-        cls.set_validation_resources()
-
-        super(ServersWithSpecificFlavorTestJSON, cls).resource_setup()
-
-    @decorators.idempotent_id('b3c7bcfc-bb5b-4e22-b517-c7f686b802ca')
-    @testtools.skipUnless(CONF.validation.run_validation,
-                          'Instance validation tests are disabled.')
-    def test_verify_created_server_ephemeral_disk(self):
-        # Verify that the ephemeral disk is created when creating server
-        flavor_base = self.flavors_client.show_flavor(
-            self.flavor_ref)['flavor']
-
-        def create_flavor_with_ephemeral(ephem_disk):
-            flavor_id = data_utils.rand_int_id(start=1000)
-            name = 'flavor_with_ephemeral_%s' % ephem_disk
-            flavor_name = data_utils.rand_name(name)
-
-            ram = flavor_base['ram']
-            vcpus = flavor_base['vcpus']
-            disk = flavor_base['disk']
-
-            # Create a flavor with ephemeral disk
-            flavor = self.flavor_client.create_flavor(
-                name=flavor_name, ram=ram, vcpus=vcpus, disk=disk,
-                id=flavor_id, ephemeral=ephem_disk)['flavor']
-            self.addCleanup(flavor_clean_up, flavor['id'])
-
-            return flavor['id']
-
-        def flavor_clean_up(flavor_id):
-            self.flavor_client.delete_flavor(flavor_id)
-            self.flavor_client.wait_for_resource_deletion(flavor_id)
-
-        flavor_with_eph_disk_id = create_flavor_with_ephemeral(ephem_disk=1)
-        flavor_no_eph_disk_id = create_flavor_with_ephemeral(ephem_disk=0)
-
-        admin_pass = self.image_ssh_password
-
-        server_no_eph_disk = self.create_test_server(
-            validatable=True,
-            wait_until='ACTIVE',
-            adminPass=admin_pass,
-            flavor=flavor_no_eph_disk_id)
-
-        # Get partition number of server without ephemeral disk.
-        server_no_eph_disk = self.client.show_server(
-            server_no_eph_disk['id'])['server']
-        linux_client = remote_client.RemoteClient(
-            self.get_server_ip(server_no_eph_disk),
-            self.ssh_user,
-            admin_pass,
-            self.validation_resources['keypair']['private_key'],
-            server=server_no_eph_disk,
-            servers_client=self.client)
-        disks_num = len(linux_client.get_disks().split('\n'))
-
-        # Explicit server deletion necessary for Juno compatibility
-        self.client.delete_server(server_no_eph_disk['id'])
-
-        server_with_eph_disk = self.create_test_server(
-            validatable=True,
-            wait_until='ACTIVE',
-            adminPass=admin_pass,
-            flavor=flavor_with_eph_disk_id)
-
-        server_with_eph_disk = self.client.show_server(
-            server_with_eph_disk['id'])['server']
-        linux_client = remote_client.RemoteClient(
-            self.get_server_ip(server_with_eph_disk),
-            self.ssh_user,
-            admin_pass,
-            self.validation_resources['keypair']['private_key'],
-            server=server_with_eph_disk,
-            servers_client=self.client)
-        disks_num_eph = len(linux_client.get_disks().split('\n'))
-        self.assertEqual(disks_num + 1, disks_num_eph)
-
 
 class ServersTestManualDisk(ServersTestJSON):
     disk_config = 'MANUAL'
@@ -328,3 +142,15 @@
         if not CONF.compute_feature_enabled.disk_config:
             msg = "DiskConfig extension not enabled."
             raise cls.skipException(msg)
+
+
+class ServersTestBootFromVolume(ServersTestJSON):
+    """Run the `ServersTestJSON` tests with a volume backed VM"""
+    volume_backed = True
+
+    @classmethod
+    def skip_checks(cls):
+        super(ServersTestBootFromVolume, cls).skip_checks()
+        if not utils.get_service_list()['volume']:
+            msg = "Volume service not enabled."
+            raise cls.skipException(msg)
diff --git a/tempest/api/compute/servers/test_create_server_multi_nic.py b/tempest/api/compute/servers/test_create_server_multi_nic.py
new file mode 100644
index 0000000..3447d85
--- /dev/null
+++ b/tempest/api/compute/servers/test_create_server_multi_nic.py
@@ -0,0 +1,120 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import netaddr
+import testtools
+
+from tempest.api.compute import base
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class ServersTestMultiNic(base.BaseV2ComputeTest):
+
+    @classmethod
+    def setup_credentials(cls):
+        cls.prepare_instance_network()
+        super(ServersTestMultiNic, cls).setup_credentials()
+
+    @classmethod
+    def setup_clients(cls):
+        super(ServersTestMultiNic, cls).setup_clients()
+        cls.client = cls.servers_client
+        cls.networks_client = cls.os_primary.networks_client
+        cls.subnets_client = cls.os_primary.subnets_client
+
+    def _create_net_subnet_ret_net_from_cidr(self, cidr):
+        name_net = data_utils.rand_name(self.__class__.__name__)
+        net = self.networks_client.create_network(name=name_net)
+        self.addCleanup(self.networks_client.delete_network,
+                        net['network']['id'])
+
+        subnet = self.subnets_client.create_subnet(
+            network_id=net['network']['id'],
+            cidr=cidr,
+            ip_version=4)
+        self.addCleanup(self.subnets_client.delete_subnet,
+                        subnet['subnet']['id'])
+        return net
+
+    @decorators.idempotent_id('0578d144-ed74-43f8-8e57-ab10dbf9b3c2')
+    @testtools.skipUnless(CONF.service_available.neutron,
+                          'Neutron service must be available.')
+    def test_verify_multiple_nics_order(self):
+        # Verify that the networks order given at the server creation is
+        # preserved within the server.
+        net1 = self._create_net_subnet_ret_net_from_cidr('19.80.0.0/24')
+        net2 = self._create_net_subnet_ret_net_from_cidr('19.86.0.0/24')
+
+        networks = [{'uuid': net1['network']['id']},
+                    {'uuid': net2['network']['id']}]
+
+        server_multi_nics = self.create_test_server(
+            networks=networks, wait_until='ACTIVE')
+
+        # Cleanup server; this is needed in the test case because with the LIFO
+        # nature of the cleanups, if we don't delete the server first, the port
+        # will still be part of the subnet and we'll get a 409 from Neutron
+        # when trying to delete the subnet. The tear down in the base class
+        # will try to delete the server and get a 404 but it's ignored so
+        # we're OK.
+        self.addCleanup(self.delete_server, server_multi_nics['id'])
+
+        addresses = (self.client.list_addresses(server_multi_nics['id'])
+                     ['addresses'])
+
+        # We can't predict the ip addresses assigned to the server on networks.
+        # Sometimes the assigned addresses are ['19.80.0.2', '19.86.0.2'], at
+        # other times ['19.80.0.3', '19.86.0.3']. So we check if the first
+        # address is in first network, similarly second address is in second
+        # network.
+        addr = [addresses[net1['network']['name']][0]['addr'],
+                addresses[net2['network']['name']][0]['addr']]
+        networks = [netaddr.IPNetwork('19.80.0.0/24'),
+                    netaddr.IPNetwork('19.86.0.0/24')]
+        for address, network in zip(addr, networks):
+            self.assertIn(address, network)
+
+    @decorators.idempotent_id('1678d144-ed74-43f8-8e57-ab10dbf9b3c2')
+    @testtools.skipUnless(CONF.service_available.neutron,
+                          'Neutron service must be available.')
+    def test_verify_duplicate_network_nics(self):
+        # Verify that server creation does not fail when more than one nic
+        # is created on the same network.
+        net1 = self._create_net_subnet_ret_net_from_cidr('19.80.0.0/24')
+        net2 = self._create_net_subnet_ret_net_from_cidr('19.86.0.0/24')
+
+        networks = [{'uuid': net1['network']['id']},
+                    {'uuid': net2['network']['id']},
+                    {'uuid': net1['network']['id']}]
+
+        server_multi_nics = self.create_test_server(
+            networks=networks, wait_until='ACTIVE')
+        self.addCleanup(self.delete_server, server_multi_nics['id'])
+
+        addresses = (self.client.list_addresses(server_multi_nics['id'])
+                     ['addresses'])
+
+        addr = [addresses[net1['network']['name']][0]['addr'],
+                addresses[net2['network']['name']][0]['addr'],
+                addresses[net1['network']['name']][1]['addr']]
+        networks = [netaddr.IPNetwork('19.80.0.0/24'),
+                    netaddr.IPNetwork('19.86.0.0/24'),
+                    netaddr.IPNetwork('19.80.0.0/24')]
+        for address, network in zip(addr, networks):
+            self.assertIn(address, network)
diff --git a/tempest/api/compute/servers/test_delete_server.py b/tempest/api/compute/servers/test_delete_server.py
index 83b2e1b..0093752 100644
--- a/tempest/api/compute/servers/test_delete_server.py
+++ b/tempest/api/compute/servers/test_delete_server.py
@@ -17,10 +17,10 @@
 
 from tempest.api.compute import base
 from tempest.common import compute
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -104,7 +104,7 @@
         waiters.wait_for_server_termination(self.client, server['id'])
 
     @decorators.idempotent_id('d0f3f0d6-d9b6-4a32-8da4-23015dcab23c')
-    @test.services('volume')
+    @utils.services('volume')
     def test_delete_server_while_in_attached_volume(self):
         # Delete a server while a volume is attached to it
         device = '/dev/%s' % CONF.compute.volume_device_name
@@ -115,36 +115,5 @@
 
         self.client.delete_server(server['id'])
         waiters.wait_for_server_termination(self.client, server['id'])
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume['id'], 'available')
-
-
-class DeleteServersAdminTestJSON(base.BaseV2ComputeAdminTest):
-    # NOTE: Server creations of each test class should be under 10
-    # for preventing "Quota exceeded for instances".
-
-    @classmethod
-    def setup_clients(cls):
-        super(DeleteServersAdminTestJSON, cls).setup_clients()
-        cls.non_admin_client = cls.servers_client
-        cls.admin_client = cls.os_adm.servers_client
-
-    @decorators.idempotent_id('99774678-e072-49d1-9d2a-49a59bc56063')
-    def test_delete_server_while_in_error_state(self):
-        # Delete a server while it's VM state is error
-        server = self.create_test_server(wait_until='ACTIVE')
-        self.admin_client.reset_state(server['id'], state='error')
-        # Verify server's state
-        server = self.non_admin_client.show_server(server['id'])['server']
-        self.assertEqual(server['status'], 'ERROR')
-        self.non_admin_client.delete_server(server['id'])
-        waiters.wait_for_server_termination(self.servers_client,
-                                            server['id'],
-                                            ignore_error=True)
-
-    @decorators.idempotent_id('73177903-6737-4f27-a60c-379e8ae8cf48')
-    def test_admin_delete_servers_of_others(self):
-        # Administrator can delete servers of others
-        server = self.create_test_server(wait_until='ACTIVE')
-        self.admin_client.delete_server(server['id'])
-        waiters.wait_for_server_termination(self.servers_client, server['id'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index d9e83a6..dbf6713 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -17,12 +17,13 @@
 from oslo_log import log as logging
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest.common.utils.linux import remote_client
 from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions
-from tempest import test
 
 
 CONF = config.CONF
@@ -54,10 +55,10 @@
     @classmethod
     def setup_clients(cls):
         super(DeviceTaggingTest, cls).setup_clients()
-        cls.networks_client = cls.os.networks_client
-        cls.ports_client = cls.os.ports_client
-        cls.subnets_client = cls.os.subnets_client
-        cls.interfaces_client = cls.os.interfaces_client
+        cls.networks_client = cls.os_primary.networks_client
+        cls.ports_client = cls.os_primary.ports_client
+        cls.subnets_client = cls.os_primary.subnets_client
+        cls.interfaces_client = cls.os_primary.interfaces_client
 
     @classmethod
     def setup_credentials(cls):
@@ -83,13 +84,17 @@
                 if d['mac'] == self.net_2_200_mac:
                     self.assertEqual(d['tags'], ['net-2-200'])
 
-        found_devices = [d['tags'][0] for d in md_dict['devices']]
-        self.assertItemsEqual(found_devices, ['port-1', 'port-2', 'net-1',
-                                              'net-2-100', 'net-2-200',
-                                              'boot', 'other'])
+            # A hypervisor may present multiple paths to a tagged disk, so
+            # there may be duplicated tags in the metadata, use set() to
+            # remove duplicated tags.
+            found_devices = [d['tags'][0] for d in md_dict['devices']]
+            self.assertEqual(set(found_devices), set(['port-1', 'port-2',
+                                                      'net-1', 'net-2-100',
+                                                      'net-2-200', 'boot',
+                                                      'other']))
 
     @decorators.idempotent_id('a2e65a6c-66f1-4442-aaa8-498c31778d96')
-    @test.services('network', 'volume', 'image')
+    @utils.services('network', 'volume', 'image')
     def test_device_tagging(self):
         # Create volumes
         # The create_volume methods waits for the volumes to be available and
@@ -249,9 +254,9 @@
                 self.verify_device_metadata(md_json)
                 return True
 
-            if not test.call_until_true(get_and_verify_metadata,
-                                        CONF.compute.build_timeout,
-                                        CONF.compute.build_interval):
+            if not test_utils.call_until_true(get_and_verify_metadata,
+                                              CONF.compute.build_timeout,
+                                              CONF.compute.build_interval):
                 raise exceptions.TimeoutException('Timeout while verifying '
                                                   'metadata on server.')
 
@@ -262,7 +267,17 @@
                      'the config drive.', server['id'])
             dev_name = self.ssh_client.exec_command(cmd_blkid)
             dev_name = dev_name.rstrip()
-            self.ssh_client.exec_command('sudo mount %s /mnt' % dev_name)
+            try:
+                self.ssh_client.exec_command('sudo mount %s /mnt' % dev_name)
+            except exceptions.SSHExecCommandFailed:
+                # So the command failed, let's try to know why and print some
+                # useful information.
+                lsblk = self.ssh_client.exec_command('sudo lsblk --fs --ascii')
+                LOG.error("Mounting %s on /mnt failed. Right after the "
+                          "failure 'lsblk' in the guest reported:\n%s",
+                          dev_name, lsblk)
+                raise
+
             cmd_md = 'sudo cat /mnt/openstack/latest/meta_data.json'
             md_json = self.ssh_client.exec_command(cmd_md)
             self.verify_device_metadata(md_json)
diff --git a/tempest/api/compute/servers/test_disk_config.py b/tempest/api/compute/servers/test_disk_config.py
index 4709180..bc48069 100644
--- a/tempest/api/compute/servers/test_disk_config.py
+++ b/tempest/api/compute/servers/test_disk_config.py
@@ -35,7 +35,7 @@
     @classmethod
     def setup_clients(cls):
         super(ServerDiskConfigTestJSON, cls).setup_clients()
-        cls.client = cls.os.servers_client
+        cls.client = cls.os_primary.servers_client
 
     def _update_server_with_disk_config(self, server_id, disk_config):
         server = self.client.show_server(server_id)['server']
diff --git a/tempest/api/compute/servers/test_instance_actions_negative.py b/tempest/api/compute/servers/test_instance_actions_negative.py
index 85e3281..1d3a790 100644
--- a/tempest/api/compute/servers/test_instance_actions_negative.py
+++ b/tempest/api/compute/servers/test_instance_actions_negative.py
@@ -14,10 +14,9 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class InstanceActionsNegativeTestJSON(base.BaseV2ComputeTest):
@@ -32,7 +31,7 @@
         super(InstanceActionsNegativeTestJSON, cls).resource_setup()
         cls.server = cls.create_test_server(wait_until='ACTIVE')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('67e1fce6-7ec2-45c6-92d4-0a8f1a632910')
     def test_list_instance_actions_non_existent_server(self):
         # List actions of the non-existent server id
@@ -41,7 +40,7 @@
                           self.client.list_instance_actions,
                           non_existent_server_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0269f40a-6f18-456c-b336-c03623c897f1')
     def test_get_instance_action_invalid_request(self):
         # Get the action details of the provided server with invalid request
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index c0a8eae..14aecfd 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -12,13 +12,17 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+import testtools
 
 from tempest.api.compute import base
-from tempest.common import fixed_network
-from tempest.common.utils import data_utils
 from tempest.common import waiters
+from tempest import config
+from tempest.lib.common import fixed_network
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
+
+
+CONF = config.CONF
 
 
 class ListServerFiltersTestJSON(base.BaseV2ComputeTest):
@@ -37,31 +41,6 @@
     def resource_setup(cls):
         super(ListServerFiltersTestJSON, cls).resource_setup()
 
-        # Check to see if the alternate image ref actually exists...
-        images_client = cls.compute_images_client
-        images = images_client.list_images()['images']
-
-        if cls.image_ref != cls.image_ref_alt and \
-            any([image for image in images
-                 if image['id'] == cls.image_ref_alt]):
-            cls.multiple_images = True
-        else:
-            cls.image_ref_alt = cls.image_ref
-
-        # Do some sanity checks here. If one of the images does
-        # not exist, fail early since the tests won't work...
-        try:
-            cls.compute_images_client.show_image(cls.image_ref)
-        except lib_exc.NotFound:
-            raise RuntimeError("Image %s (image_ref) was not found!" %
-                               cls.image_ref)
-
-        try:
-            cls.compute_images_client.show_image(cls.image_ref_alt)
-        except lib_exc.NotFound:
-            raise RuntimeError("Image %s (image_ref_alt) was not found!" %
-                               cls.image_ref_alt)
-
         network = cls.get_tenant_network()
         if network:
             cls.fixed_network_name = network.get('name')
@@ -69,22 +48,28 @@
             cls.fixed_network_name = None
         network_kwargs = fixed_network.set_networks_kwarg(network)
         cls.s1_name = data_utils.rand_name(cls.__name__ + '-instance')
-        cls.s1 = cls.create_test_server(name=cls.s1_name,
-                                        wait_until='ACTIVE',
-                                        **network_kwargs)
+        cls.s1 = cls.create_test_server(name=cls.s1_name, **network_kwargs)
 
         cls.s2_name = data_utils.rand_name(cls.__name__ + '-instance')
-        cls.s2 = cls.create_test_server(name=cls.s2_name,
-                                        image_id=cls.image_ref_alt,
-                                        wait_until='ACTIVE')
+        # If image_ref_alt is "" or None then we still want to boot a server
+        # but we rely on `testtools.skipUnless` decorator to actually skip
+        # the irrelevant tests.
+        cls.s2 = cls.create_test_server(
+            name=cls.s2_name, image_id=cls.image_ref_alt or cls.image_ref)
 
         cls.s3_name = data_utils.rand_name(cls.__name__ + '-instance')
         cls.s3 = cls.create_test_server(name=cls.s3_name,
                                         flavor=cls.flavor_ref_alt,
                                         wait_until='ACTIVE')
 
+        waiters.wait_for_server_status(cls.client, cls.s1['id'],
+                                       'ACTIVE')
+        waiters.wait_for_server_status(cls.client, cls.s2['id'],
+                                       'ACTIVE')
+
     @decorators.idempotent_id('05e8a8e7-9659-459a-989d-92c2f501f4ba')
-    @decorators.skip_unless_attr('multiple_images', 'Only one image found')
+    @testtools.skipUnless(CONF.compute.image_ref != CONF.compute.image_ref_alt,
+                          "Need distinct images to run this test")
     def test_list_servers_filter_by_image(self):
         # Filter the list of servers by image
         params = {'image': self.image_ref}
@@ -157,7 +142,7 @@
         # Verify only the expected number of servers are returned
         params = {'limit': 0}
         servers = self.client.list_servers(**params)
-        self.assertEqual(0, len(servers['servers']))
+        self.assertEmpty(servers['servers'])
 
     @decorators.idempotent_id('37791bbd-90c0-4de0-831e-5f38cba9c6b3')
     def test_list_servers_filter_by_exceed_limit(self):
@@ -169,7 +154,8 @@
                          len([x for x in servers['servers'] if 'id' in x]))
 
     @decorators.idempotent_id('b3304c3b-97df-46d2-8cd3-e2b6659724e7')
-    @decorators.skip_unless_attr('multiple_images', 'Only one image found')
+    @testtools.skipUnless(CONF.compute.image_ref != CONF.compute.image_ref_alt,
+                          "Need distinct images to run this test")
     def test_list_servers_detailed_filter_by_image(self):
         # Filter the detailed list of servers by image
         params = {'image': self.image_ref}
@@ -269,16 +255,34 @@
         if not self.fixed_network_name:
             msg = 'fixed_network_name needs to be configured to run this test'
             raise self.skipException(msg)
+
+        # list servers filter by ip is something "regexp match", i.e,
+        # filter by "10.1.1.1" will return both "10.1.1.1" and "10.1.1.10".
+        # so here look for the longest server ip, and filter by that ip,
+        # so as to ensure only one server is returned.
+        ip_list = {}
         self.s1 = self.client.show_server(self.s1['id'])['server']
-        # Get first ip address inspite of v4 or v6
-        addr_spec = self.s1['addresses'][self.fixed_network_name][0]
-        params = {'ip': addr_spec['addr']}
+        # Get first ip address in spite of v4 or v6
+        ip_addr = self.s1['addresses'][self.fixed_network_name][0]['addr']
+        ip_list[ip_addr] = self.s1['id']
+
+        self.s2 = self.client.show_server(self.s2['id'])['server']
+        ip_addr = self.s2['addresses'][self.fixed_network_name][0]['addr']
+        ip_list[ip_addr] = self.s2['id']
+
+        self.s3 = self.client.show_server(self.s3['id'])['server']
+        ip_addr = self.s3['addresses'][self.fixed_network_name][0]['addr']
+        ip_list[ip_addr] = self.s3['id']
+
+        longest_ip = max([[len(ip), ip] for ip in ip_list])[1]
+        params = {'ip': longest_ip}
         body = self.client.list_servers(**params)
         servers = body['servers']
 
-        self.assertIn(self.s1_name, map(lambda x: x['name'], servers))
-        self.assertNotIn(self.s2_name, map(lambda x: x['name'], servers))
-        self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
+        self.assertIn(ip_list[longest_ip], map(lambda x: x['id'], servers))
+        del ip_list[longest_ip]
+        for ip in ip_list:
+            self.assertNotIn(ip_list[ip], map(lambda x: x['id'], servers))
 
     @decorators.skip_because(bug="1540645")
     @decorators.idempotent_id('a905e287-c35e-42f2-b132-d02b09f3654a')
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index 594c5c9..6c9b287 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -17,7 +17,6 @@
 from tempest.common import waiters
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ListServersNegativeTestJSON(base.BaseV2ComputeTest):
@@ -35,68 +34,57 @@
         # by the test methods in this class. These
         # servers are cleaned up automatically in the
         # tearDownClass method of the super-class.
-        cls.existing_fixtures = []
-        cls.deleted_fixtures = []
-        for _ in range(2):
-            srv = cls.create_test_server(wait_until='ACTIVE')
-            cls.existing_fixtures.append(srv)
+        body = cls.create_test_server(wait_until='ACTIVE', min_count=3)
 
-        srv = cls.create_test_server(wait_until='ACTIVE')
-        cls.client.delete_server(srv['id'])
-        waiters.wait_for_server_termination(cls.client, srv['id'])
-        cls.deleted_fixtures.append(srv)
+        # delete one of the created servers
+        cls.deleted_id = body['server']['id']
+        cls.client.delete_server(cls.deleted_id)
+        waiters.wait_for_server_termination(cls.client, cls.deleted_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('24a26f1a-1ddc-4eea-b0d7-a90cc874ad8f')
     def test_list_servers_with_a_deleted_server(self):
         # Verify deleted servers do not show by default in list servers
         # List servers and verify server not returned
         body = self.client.list_servers()
         servers = body['servers']
-        deleted_ids = [s['id'] for s in self.deleted_fixtures]
         actual = [srv for srv in servers
-                  if srv['id'] in deleted_ids]
-        self.assertEqual([], actual)
+                  if srv['id'] == self.deleted_id]
+        self.assertEmpty(actual)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ff01387d-c7ad-47b4-ae9e-64fa214638fe')
     def test_list_servers_by_non_existing_image(self):
         # Listing servers for a non existing image returns empty list
         body = self.client.list_servers(image='non_existing_image')
         servers = body['servers']
-        self.assertEqual([], servers)
+        self.assertEmpty(servers)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5913660b-223b-44d4-a651-a0fbfd44ca75')
     def test_list_servers_by_non_existing_flavor(self):
         # Listing servers by non existing flavor returns empty list
         body = self.client.list_servers(flavor='non_existing_flavor')
         servers = body['servers']
-        self.assertEqual([], servers)
+        self.assertEmpty(servers)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e2c77c4a-000a-4af3-a0bd-629a328bde7c')
     def test_list_servers_by_non_existing_server_name(self):
         # Listing servers for a non existent server name returns empty list
         body = self.client.list_servers(name='non_existing_server_name')
         servers = body['servers']
-        self.assertEqual([], servers)
+        self.assertEmpty(servers)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fcdf192d-0f74-4d89-911f-1ec002b822c4')
     def test_list_servers_status_non_existing(self):
         # Return an empty list when invalid status is specified
         body = self.client.list_servers(status='non_existing_status')
         servers = body['servers']
-        self.assertEqual([], servers)
+        self.assertEmpty(servers)
 
-    @decorators.idempotent_id('12c80a9f-2dec-480e-882b-98ba15757659')
-    def test_list_servers_by_limits(self):
-        # List servers by specifying limits
-        body = self.client.list_servers(limit=1)
-        self.assertEqual(1, len([x for x in body['servers'] if 'id' in x]))
-
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.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
@@ -106,21 +94,21 @@
         body = self.client.list_servers(limit=limit)
         self.assertEqual(len(full_list), len(body['servers']))
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('679bc053-5e70-4514-9800-3dfab1a380a6')
     def test_list_servers_by_limits_pass_string(self):
         # Return an error if a string value is passed for limit
         self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
                           limit='testing')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('62610dd9-4713-4ee0-8beb-fd2c1aa7f950')
     def test_list_servers_by_limits_pass_negative_value(self):
         # Return an error if a negative value for limit is passed
         self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
                           limit=-1)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('87d12517-e20a-4c9c-97b6-dd1628d6d6c9')
     def test_list_servers_by_changes_since_invalid_date(self):
         # Return an error when invalid date format is passed
@@ -128,21 +116,20 @@
         self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
                           **params)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('74745ad8-b346-45b5-b9b8-509d7447fc1f')
     def test_list_servers_by_changes_since_future_date(self):
         # Return an empty list when a date in the future is passed
         changes_since = {'changes-since': '2051-01-01T12:34:00Z'}
         body = self.client.list_servers(**changes_since)
-        self.assertEqual(0, len(body['servers']))
+        self.assertEmpty(body['servers'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('93055106-2d34-46fe-af68-d9ddbf7ee570')
     def test_list_servers_detail_server_is_deleted(self):
         # Server details are not listed for a deleted server
-        deleted_ids = [s['id'] for s in self.deleted_fixtures]
         body = self.client.list_servers(detail=True)
         servers = body['servers']
         actual = [srv for srv in servers
-                  if srv['id'] in deleted_ids]
-        self.assertEqual([], actual)
+                  if srv['id'] == self.deleted_id]
+        self.assertEmpty(actual)
diff --git a/tempest/api/compute/servers/test_multiple_create.py b/tempest/api/compute/servers/test_multiple_create.py
index 7e55b12..059454d 100644
--- a/tempest/api/compute/servers/test_multiple_create.py
+++ b/tempest/api/compute/servers/test_multiple_create.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common import compute
 from tempest.lib import decorators
 
 
@@ -21,13 +22,19 @@
 
     @decorators.idempotent_id('61e03386-89c3-449c-9bb1-a06f423fd9d1')
     def test_multiple_create(self):
-        body = self.create_test_server(wait_until='ACTIVE',
-                                       min_count=1,
-                                       max_count=2)
+        tenant_network = self.get_tenant_network()
+        body, servers = compute.create_test_server(
+            self.os_primary,
+            wait_until='ACTIVE',
+            min_count=2,
+            tenant_network=tenant_network)
+        for server in servers:
+            self.addCleanup(self.servers_client.delete_server, server['id'])
         # NOTE(maurosr): do status response check and also make sure that
         # reservation_id is not in the response body when the request send
         # contains return_reservation_id=False
         self.assertNotIn('reservation_id', body)
+        self.assertEqual(2, len(servers))
 
     @decorators.idempotent_id('864777fb-2f1e-44e3-b5b9-3eb6fa84f2f7')
     def test_multiple_create_with_reservation_return(self):
diff --git a/tempest/api/compute/servers/test_multiple_create_negative.py b/tempest/api/compute/servers/test_multiple_create_negative.py
index 675cbee..422510f 100644
--- a/tempest/api/compute/servers/test_multiple_create_negative.py
+++ b/tempest/api/compute/servers/test_multiple_create_negative.py
@@ -16,40 +16,39 @@
 from tempest.api.compute import base
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class MultipleCreateNegativeTestJSON(base.BaseV2ComputeTest):
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('daf29d8d-e928-4a01-9a8c-b129603f3fc0')
     def test_min_count_less_than_one(self):
         invalid_min_count = 0
         self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           min_count=invalid_min_count)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('999aa722-d624-4423-b813-0d1ac9884d7a')
     def test_min_count_non_integer(self):
         invalid_min_count = 2.5
         self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           min_count=invalid_min_count)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a6f9c2ab-e060-4b82-b23c-4532cb9390ff')
     def test_max_count_less_than_one(self):
         invalid_max_count = 0
         self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           max_count=invalid_max_count)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9c5698d1-d7af-4c80-b971-9d403135eea2')
     def test_max_count_non_integer(self):
         invalid_max_count = 2.5
         self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           max_count=invalid_max_count)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('476da616-f1ef-4271-a9b1-b9fc87727cdf')
     def test_max_count_less_than_min_count(self):
         min_count = 3
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index d10f370..d9581e3 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -1,4 +1,4 @@
-# Copyright 2016 OpenStack Foundation
+# Copyright 2016-2017 OpenStack Foundation
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -13,19 +13,23 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import socket
 import struct
 
 import six
-from six.moves.urllib import parse as urlparse
 import urllib3
 
 from tempest.api.compute import base
+from tempest.common import compute
 from tempest import config
-from tempest import test
+from tempest.lib import decorators
 
 CONF = config.CONF
 
+if six.PY2:
+    ord_func = ord
+else:
+    ord_func = int
+
 
 class NoVNCConsoleTestJSON(base.BaseV2ComputeTest):
 
@@ -60,46 +64,70 @@
         resp = urllib3.PoolManager().request('GET', vnc_url)
         # Make sure that the GET request was accepted by the novncproxy
         self.assertEqual(resp.status, 200, 'Got a Bad HTTP Response on the '
-                         'initial call: ' + str(resp.status))
+                         'initial call: ' + six.text_type(resp.status))
         # Do some basic validation to make sure it is an expected HTML document
-        self.assertTrue('<html>' in resp.data and '</html>' in resp.data,
-                        'Not a valid html document in the response.')
+        resp_data = resp.data.decode()
+        self.assertIn('<html>', resp_data,
+                      'Not a valid html document in the response.')
+        self.assertIn('</html>', resp_data,
+                      'Not a valid html document in the response.')
         # Just try to make sure we got JavaScript back for noVNC, since we
         # won't actually use it since not inside of a browser
-        self.assertTrue('noVNC' in resp.data and '<script' in resp.data,
-                        'Not a valid noVNC javascript html document.')
+        self.assertIn('noVNC', resp_data,
+                      'Not a valid noVNC javascript html document.')
+        self.assertIn('<script', resp_data,
+                      'Not a valid noVNC javascript html document.')
 
     def _validate_rfb_negotiation(self):
         """Verify we can connect to novnc and do the websocket connection."""
         # Turn the Socket into a WebSocket to do the communication
         data = self._websocket.receive_frame()
-        self.assertFalse(data is None or len(data) == 0,
+        self.assertFalse(data is None or not data,
                          'Token must be invalid because the connection '
                          'closed.')
         # Parse the RFB version from the data to make sure it is valid
-        # and greater than or equal to 3.3
+        # and belong to the known supported RFB versions.
         version = float("%d.%d" % (int(data[4:7], base=10),
                                    int(data[8:11], base=10)))
-        self.assertTrue(version >= 3.3, 'Bad RFB Version: ' + str(version))
-        # Send our RFB version to the server, which we will just go with 3.3
-        self._websocket.send_frame(str(data))
+        # Add the max RFB versions supported
+        supported_versions = [3.3, 3.8]
+        self.assertIn(version, supported_versions,
+                      'Bad RFB Version: ' + str(version))
+        # Send our RFB version to the server
+        self._websocket.send_frame(data)
         # Get the sever authentication type and make sure None is supported
         data = self._websocket.receive_frame()
         self.assertIsNotNone(data, 'Expected authentication type None.')
-        self.assertGreaterEqual(
-            len(data), 2, 'Expected authentication type None.')
-        self.assertIn(
-            1, [ord(data[i + 1]) for i in range(ord(data[0]))],
-            'Expected authentication type None.')
-        # Send to the server that we only support authentication type None
-        self._websocket.send_frame(six.int2byte(1))
-        # The server should send 4 bytes of 0's if security handshake succeeded
-        data = self._websocket.receive_frame()
-        self.assertEqual(
-            len(data), 4, 'Server did not think security was successful.')
-        self.assertEqual(
-            [ord(i) for i in data], [0, 0, 0, 0],
-            'Server did not think security was successful.')
+        data_length = len(data)
+        if version == 3.3:
+            # For RFB 3.3: in the security handshake, rather than a two-way
+            # negotiation, the server decides the security type and sends a
+            # single word(4 bytes).
+            self.assertEqual(
+                data_length, 4, 'Expected authentication type None.')
+            self.assertIn(1, [ord_func(data[i]) for i in (0, 3)],
+                          'Expected authentication type None.')
+        else:
+            self.assertGreaterEqual(
+                len(data), 2, 'Expected authentication type None.')
+            self.assertIn(
+                1,
+                [ord_func(data[i + 1]) for i in range(ord_func(data[0]))],
+                'Expected authentication type None.')
+            # Send to the server that we only support authentication
+            # type None
+            self._websocket.send_frame(six.int2byte(1))
+
+            # The server should send 4 bytes of 0's if security
+            # handshake succeeded
+            data = self._websocket.receive_frame()
+            self.assertEqual(
+                len(data), 4,
+                'Server did not think security was successful.')
+            self.assertEqual(
+                [ord_func(i) for i in data], [0, 0, 0, 0],
+                'Server did not think security was successful.')
+
         # Say to leave the desktop as shared as part of client initialization
         self._websocket.send_frame(six.int2byte(1))
         # Get the server initialization packet back and make sure it is the
@@ -121,23 +149,15 @@
 
     def _validate_websocket_upgrade(self):
         self.assertTrue(
-            self._websocket.response.startswith('HTTP/1.1 101 Switching '
-                                                'Protocols\r\n'),
+            self._websocket.response.startswith(b'HTTP/1.1 101 Switching '
+                                                b'Protocols\r\n'),
             'Did not get the expected 101 on the websockify call: '
-            + str(len(self._websocket.response)))
+            + six.text_type(self._websocket.response))
         self.assertTrue(
-            self._websocket.response.find('Server: WebSockify') > 0,
+            self._websocket.response.find(b'Server: WebSockify') > 0,
             'Did not get the expected WebSocket HTTP Response.')
 
-    def _create_websocket(self, url):
-        url = urlparse.urlparse(url)
-        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        client_socket.connect((url.hostname, url.port))
-        # Turn the Socket into a WebSocket to do the communication
-        return _WebSocket(client_socket, url)
-
-    @test.idempotent_id('c640fdff-8ab4-45a4-a5d8-7e6146cbd0dc')
+    @decorators.idempotent_id('c640fdff-8ab4-45a4-a5d8-7e6146cbd0dc')
     def test_novnc(self):
         body = self.client.get_vnc_console(self.server['id'],
                                            type='novnc')['console']
@@ -145,97 +165,22 @@
         # Do the initial HTTP Request to novncproxy to get the NoVNC JavaScript
         self._validate_novnc_html(body['url'])
         # Do the WebSockify HTTP Request to novncproxy to do the RFB connection
-        self._websocket = self._create_websocket(body['url'])
-        # Validate that we succesfully connected and upgraded to Web Sockets
+        self._websocket = compute.create_websocket(body['url'])
+        # Validate that we successfully connected and upgraded to Web Sockets
         self._validate_websocket_upgrade()
         # Validate the RFB Negotiation to determine if a valid VNC session
         self._validate_rfb_negotiation()
 
-    @test.idempotent_id('f9c79937-addc-4aaa-9e0e-841eef02aeb7')
+    @decorators.idempotent_id('f9c79937-addc-4aaa-9e0e-841eef02aeb7')
     def test_novnc_bad_token(self):
         body = self.client.get_vnc_console(self.server['id'],
                                            type='novnc')['console']
         self.assertEqual('novnc', body['type'])
         # Do the WebSockify HTTP Request to novncproxy with a bad token
         url = body['url'].replace('token=', 'token=bad')
-        self._websocket = self._create_websocket(url)
+        self._websocket = compute.create_websocket(url)
         # Make sure the novncproxy rejected the connection and closed it
         data = self._websocket.receive_frame()
-        self.assertTrue(data is None or len(data) == 0,
+        self.assertTrue(data is None or not data,
                         "The novnc proxy actually sent us some data, but we "
                         "expected it to close the connection.")
-
-
-class _WebSocket(object):
-    def __init__(self, client_socket, url):
-        """Contructor for the WebSocket wrapper to the socket."""
-        self._socket = client_socket
-        # Upgrade the HTTP connection to a WebSocket
-        self._upgrade(url)
-
-    def receive_frame(self):
-        """Wrapper for receiving data to parse the WebSocket frame format"""
-        # We need to loop until we either get some bytes back in the frame
-        # or no data was received (meaning the socket was closed).  This is
-        # done to handle the case where we get back some empty frames
-        while True:
-            header = self._socket.recv(2)
-            # If we didn't receive any data, just return None
-            if len(header) == 0:
-                return None
-            # We will make the assumption that we are only dealing with
-            # frames less than 125 bytes here (for the negotiation) and
-            # that only the 2nd byte contains the length, and since the
-            # server doesn't do masking, we can just read the data length
-            if ord(header[1]) & 127 > 0:
-                return self._socket.recv(ord(header[1]) & 127)
-
-    def send_frame(self, data):
-        """Wrapper for sending data to add in the WebSocket frame format."""
-        frame_bytes = list()
-        # For the first byte, want to say we are sending binary data (130)
-        frame_bytes.append(130)
-        # Only sending negotiation data so don't need to worry about > 125
-        # We do need to add the bit that says we are masking the data
-        frame_bytes.append(len(data) | 128)
-        # We don't really care about providing a random mask for security
-        # So we will just hard-code a value since a test program
-        mask = [7, 2, 1, 9]
-        for i in range(len(mask)):
-            frame_bytes.append(mask[i])
-        # Mask each of the actual data bytes that we are going to send
-        for i in range(len(data)):
-            frame_bytes.append(ord(data[i]) ^ mask[i % 4])
-        # Convert our integer list to a binary array of bytes
-        frame_bytes = struct.pack('!%iB' % len(frame_bytes), * frame_bytes)
-        self._socket.sendall(frame_bytes)
-
-    def close(self):
-        """Helper method to close the connection."""
-        # Close down the real socket connection and exit the test program
-        if self._socket is not None:
-            self._socket.shutdown(1)
-            self._socket.close()
-            self._socket = None
-
-    def _upgrade(self, url):
-        """Upgrade the HTTP connection to a WebSocket and verify."""
-        # The real request goes to the /websockify URI always
-        reqdata = 'GET /websockify HTTP/1.1\r\n'
-        reqdata += 'Host: %s:%s\r\n' % (url.hostname, url.port)
-        # Tell the HTTP Server to Upgrade the connection to a WebSocket
-        reqdata += 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
-        # The token=xxx is sent as a Cookie not in the URI
-        reqdata += 'Cookie: %s\r\n' % url.query
-        # Use a hard-coded WebSocket key since a test program
-        reqdata += 'Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n'
-        reqdata += 'Sec-WebSocket-Version: 13\r\n'
-        # We are choosing to use binary even though browser may do Base64
-        reqdata += 'Sec-WebSocket-Protocol: binary\r\n\r\n'
-        # Send the HTTP GET request and get the response back
-        self._socket.sendall(reqdata)
-        self.response = data = self._socket.recv(4096)
-        # Loop through & concatenate all of the data in the response body
-        while len(data) > 0 and self.response.find('\r\n\r\n') < 0:
-            data = self._socket.recv(4096)
-            self.response += data
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 6160024..b5fc39c 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -19,13 +19,14 @@
 
 from tempest.api.compute import base
 from tempest.common import compute
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest.common.utils.linux import remote_client
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common import api_version_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -33,8 +34,6 @@
 
 
 class ServerActionsTestJSON(base.BaseV2ComputeTest):
-    run_ssh = CONF.validation.run_validation
-
     def setUp(self):
         # NOTE(afazekas): Normally we use the same server with all test cases,
         # but if it has an issue, we build a new one
@@ -133,7 +132,7 @@
             self.assertGreater(new_boot_time, boot_time,
                                '%s > %s' % (new_boot_time, boot_time))
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('2cb1baf6-ac8d-4429-bf0d-ba8a0ba53e32')
     def test_reboot_server_hard(self):
         # The server should be power cycled
@@ -145,6 +144,18 @@
         # The server should be signaled to reboot gracefully
         self._test_reboot_server('SOFT')
 
+    @decorators.idempotent_id('1d1c9104-1b0a-11e7-a3d4-fa163e65f5ce')
+    def test_remove_server_all_security_groups(self):
+        server = self.create_test_server(wait_until='ACTIVE')
+
+        # Remove all Security group
+        self.client.remove_security_group(
+            server['id'], name=server['security_groups'][0]['name'])
+
+        # Verify all Security group
+        server = self.client.show_server(server['id'])['server']
+        self.assertNotIn('security_groups', server)
+
     def _rebuild_server_and_check(self, image_ref):
         rebuilt_server = (self.client.rebuild_server(self.server_id, image_ref)
                           ['server'])
@@ -154,8 +165,10 @@
                .format(image_ref, rebuilt_server['image']['id']))
         self.assertEqual(image_ref, rebuilt_server['image']['id'], msg)
 
-    @decorators.idempotent_id('aaa6cdf3-55a7-461a-add9-1c8596b9a07c')
-    def test_rebuild_server(self):
+    def _test_rebuild_server(self):
+        # Get the IPs the server has before rebuilding it
+        original_addresses = (self.client.show_server(self.server_id)['server']
+                              ['addresses'])
         # The server should be rebuilt using the provided image and data
         meta = {'rebuild': 'server'}
         new_name = data_utils.rand_name(self.__class__.__name__ + '-server')
@@ -185,6 +198,7 @@
         rebuilt_image_id = server['image']['id']
         self.assertTrue(self.image_ref_alt.endswith(rebuilt_image_id))
         self.assertEqual(new_name, server['name'])
+        self.assertEqual(original_addresses, server['addresses'])
 
         if CONF.validation.run_validation:
             # Authentication is attempted in the following order of priority:
@@ -202,6 +216,10 @@
                 servers_client=self.client)
             linux_client.validate_authentication()
 
+    @decorators.idempotent_id('aaa6cdf3-55a7-461a-add9-1c8596b9a07c')
+    def test_rebuild_server(self):
+        self._test_rebuild_server()
+
     @decorators.idempotent_id('30449a88-5aff-4f9b-9866-6ee9b17f906d')
     def test_rebuild_server_in_stop_state(self):
         # The server in stop state  should be rebuilt using the provided
@@ -235,7 +253,7 @@
         self.client.start_server(self.server_id)
 
     @decorators.idempotent_id('b68bd8d6-855d-4212-b59b-2e704044dace')
-    @test.services('volume')
+    @utils.services('volume')
     def test_rebuild_server_with_volume_attached(self):
         # create a new volume and attach it to the server
         volume = self.create_volume()
@@ -244,7 +262,7 @@
         self.attach_volume(server, volume)
 
         # run general rebuild test
-        self.test_rebuild_server()
+        self._test_rebuild_server()
 
         # make sure the volume is attached to the instance after rebuild
         vol_after_rebuild = self.volumes_client.show_volume(volume['id'])
@@ -316,7 +334,7 @@
     @decorators.idempotent_id('b963d4f1-94b3-4c40-9e97-7b583f46e470')
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
                           'Snapshotting not available, backup not possible.')
-    @test.services('image')
+    @utils.services('image')
     def test_create_backup(self):
         # Positive test:create backup successfully and rotate backups correctly
         # create the first and the second backup
@@ -325,9 +343,9 @@
         # 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
+            glance_client = self.os_primary.image_client
         elif CONF.image_feature_enabled.api_v2:
-            glance_client = self.os.image_client_v2
+            glance_client = self.os_primary.image_client_v2
         else:
             raise lib_exc.InvalidConfiguration(
                 'Either api_v1 or api_v2 must be True in '
@@ -352,7 +370,11 @@
                                 "been successful as it should have been "
                                 "deleted during rotation.", oldest_backup)
 
-        image1_id = data_utils.parse_image_id(resp['location'])
+        if api_version_utils.compare_version_header_to_response(
+                "OpenStack-API-Version", "compute 2.45", resp, "lt"):
+            image1_id = resp['image_id']
+        else:
+            image1_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(_clean_oldest_backup, image1_id)
         waiters.wait_for_image_status(glance_client,
                                       image1_id, 'active')
@@ -363,7 +385,11 @@
                                          backup_type='daily',
                                          rotation=2,
                                          name=backup2).response
-        image2_id = data_utils.parse_image_id(resp['location'])
+        if api_version_utils.compare_version_header_to_response(
+                "OpenStack-API-Version", "compute 2.45", resp, "lt"):
+            image2_id = resp['image_id']
+        else:
+            image2_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(glance_client.delete_image, image2_id)
         waiters.wait_for_image_status(glance_client,
                                       image2_id, 'active')
@@ -402,7 +428,11 @@
                                          backup_type='daily',
                                          rotation=2,
                                          name=backup3).response
-        image3_id = data_utils.parse_image_id(resp['location'])
+        if api_version_utils.compare_version_header_to_response(
+                "OpenStack-API-Version", "compute 2.45", resp, "lt"):
+            image3_id = resp['image_id']
+        else:
+            image3_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(glance_client.delete_image, image3_id)
         # the first back up should be deleted
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
@@ -471,7 +501,7 @@
 
         # NOTE: SHUTOFF is irregular status. To avoid test instability,
         #       one server is created only for this test without using
-        #       the server that was created in setupClass.
+        #       the server that was created in setUpClass.
         server = self.create_test_server(wait_until='ACTIVE')
         temp_server_id = server['id']
 
@@ -501,19 +531,33 @@
     @decorators.idempotent_id('77eba8e0-036e-4635-944b-f7a8f3b78dc9')
     @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
                           'Shelve is not available.')
+    @utils.services('image')
     def test_shelve_unshelve_server(self):
+        if CONF.image_feature_enabled.api_v2:
+            glance_client = self.os_primary.image_client_v2
+        elif CONF.image_feature_enabled.api_v1:
+            glance_client = self.os_primary.image_client
+        else:
+            raise lib_exc.InvalidConfiguration(
+                'Either api_v1 or api_v2 must be True in '
+                '[image-feature-enabled].')
         compute.shelve_server(self.client, self.server_id,
                               force_shelve_offload=True)
 
         server = self.client.show_server(self.server_id)['server']
         image_name = server['name'] + '-shelved'
         params = {'name': image_name}
-        images = self.compute_images_client.list_images(**params)['images']
+        if CONF.image_feature_enabled.api_v2:
+            images = glance_client.list_images(params)['images']
+        elif CONF.image_feature_enabled.api_v1:
+            images = glance_client.list_images(
+                detail=True, **params)['images']
         self.assertEqual(1, len(images))
         self.assertEqual(image_name, images[0]['name'])
 
         self.client.unshelve_server(self.server_id)
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        glance_client.wait_for_resource_deletion(images[0]['id'])
 
     @decorators.idempotent_id('af8eafd4-38a7-4a4b-bdbc-75145a580560')
     def test_stop_start_server(self):
diff --git a/tempest/api/compute/servers/test_server_addresses.py b/tempest/api/compute/servers/test_server_addresses.py
index dfda51b..f79b05f 100644
--- a/tempest/api/compute/servers/test_server_addresses.py
+++ b/tempest/api/compute/servers/test_server_addresses.py
@@ -14,8 +14,8 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common import utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class ServerAddressesTestJSON(base.BaseV2ComputeTest):
@@ -37,9 +37,9 @@
 
         cls.server = cls.create_test_server(wait_until='ACTIVE')
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('6eb718c0-02d9-4d5e-acd1-4e0c269cef39')
-    @test.services('network')
+    @utils.services('network')
     def test_list_server_addresses(self):
         # All public and private addresses for
         # a server should be returned
@@ -48,16 +48,13 @@
 
         # We do not know the exact network configuration, but an instance
         # should at least have a single public or private address
-        self.assertGreaterEqual(len(addresses), 1)
-        for network_name, network_addresses in addresses.items():
-            self.assertGreaterEqual(len(network_addresses), 1)
-            for address in network_addresses:
-                self.assertTrue(address['addr'])
-                self.assertTrue(address['version'])
+        self.assertNotEmpty(addresses)
+        for network_addresses in addresses.values():
+            self.assertNotEmpty(network_addresses)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('87bbc374-5538-4f64-b673-2b0e4443cc30')
-    @test.services('network')
+    @utils.services('network')
     def test_list_server_addresses_by_network(self):
         # Providing a network type should filter
         # the addresses return by that type
diff --git a/tempest/api/compute/servers/test_server_addresses_negative.py b/tempest/api/compute/servers/test_server_addresses_negative.py
index 1884e4f..b2b3cc0 100644
--- a/tempest/api/compute/servers/test_server_addresses_negative.py
+++ b/tempest/api/compute/servers/test_server_addresses_negative.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common import utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ServerAddressesNegativeTestJSON(base.BaseV2ComputeTest):
@@ -36,17 +36,17 @@
         super(ServerAddressesNegativeTestJSON, cls).resource_setup()
         cls.server = cls.create_test_server(wait_until='ACTIVE')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('02c3f645-2d2e-4417-8525-68c0407d001b')
-    @test.services('network')
+    @utils.services('network')
     def test_list_server_addresses_invalid_server_id(self):
         # List addresses request should fail if server id not in system
         self.assertRaises(lib_exc.NotFound, self.client.list_addresses,
                           '999')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a2ab5144-78c0-4942-a0ed-cc8edccfd9ba')
-    @test.services('network')
+    @utils.services('network')
     def test_list_server_addresses_by_network_neg(self):
         # List addresses by network should fail if network name not valid
         self.assertRaises(lib_exc.NotFound,
diff --git a/tempest/api/compute/servers/test_server_group.py b/tempest/api/compute/servers/test_server_group.py
index 6679b58..5286c8f 100644
--- a/tempest/api/compute/servers/test_server_group.py
+++ b/tempest/api/compute/servers/test_server_group.py
@@ -13,10 +13,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import compute
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class ServerGroupTestJSON(base.BaseV2ComputeTest):
@@ -30,7 +33,7 @@
     @classmethod
     def skip_checks(cls):
         super(ServerGroupTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('os-server-groups', 'compute'):
+        if not utils.is_extension_enabled('os-server-groups', 'compute'):
             msg = "os-server-groups extension is not enabled."
             raise cls.skipException(msg)
 
@@ -106,3 +109,19 @@
         # List the server-group
         body = self.client.list_server_groups()['server_groups']
         self.assertIn(self.created_server_group, body)
+
+    @decorators.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
+    @testtools.skipUnless(
+        compute.is_scheduler_filter_enabled("ServerGroupAffinityFilter"),
+        'ServerGroupAffinityFilter is not available.')
+    def test_create_server_with_scheduler_hint_group(self):
+        # Create a server with the scheduler hint "group".
+        hints = {'group': self.created_server_group['id']}
+        server = self.create_test_server(scheduler_hints=hints,
+                                         wait_until='ACTIVE')
+        self.addCleanup(self.delete_server, server['id'])
+
+        # Check a server is in the group
+        server_group = (self.server_groups_client.show_server_group(
+            self.created_server_group['id'])['server_group'])
+        self.assertIn(server['id'], server_group['members'])
diff --git a/tempest/api/compute/servers/test_server_metadata.py b/tempest/api/compute/servers/test_server_metadata.py
index f77e7d3..fe95018 100644
--- a/tempest/api/compute/servers/test_server_metadata.py
+++ b/tempest/api/compute/servers/test_server_metadata.py
@@ -32,7 +32,7 @@
     def setUp(self):
         super(ServerMetadataTestJSON, self).setUp()
         meta = {'key1': 'value1', 'key2': 'value2'}
-        self.client.set_server_metadata(self.server['id'], meta)['metadata']
+        self.client.set_server_metadata(self.server['id'], meta)
 
     @decorators.idempotent_id('479da087-92b3-4dcf-aeb3-fd293b2d14ce')
     def test_list_server_metadata(self):
@@ -49,8 +49,7 @@
         # The server's metadata should be replaced with the provided values
         # Create a new set of metadata for the server
         req_metadata = {'meta2': 'data2', 'meta3': 'data3'}
-        self.client.set_server_metadata(self.server['id'],
-                                        req_metadata)['metadata']
+        self.client.set_server_metadata(self.server['id'], req_metadata)
 
         # Verify the expected values are correct, and that the
         # previous values have been removed
diff --git a/tempest/api/compute/servers/test_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index 92ffa86..482ba09 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -14,10 +14,9 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ServerMetadataNegativeTestJSON(base.BaseV2ComputeTest):
@@ -33,7 +32,7 @@
         cls.tenant_id = cls.client.tenant_id
         cls.server = cls.create_test_server(metadata={}, wait_until='ACTIVE')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fe114a8f-3a57-4eff-9ee2-4e14628df049')
     def test_server_create_metadata_key_too_long(self):
         # Attempt to start a server with a meta-data key that is > 255
@@ -49,7 +48,7 @@
 
         # no teardown - all creates should fail
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('92431555-4d8b-467c-b95b-b17daa5e57ff')
     def test_create_server_metadata_blank_key(self):
         # Blank key should trigger an error.
@@ -58,7 +57,7 @@
                           self.create_test_server,
                           metadata=meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4d9cd7a3-2010-4b41-b8fe-3bbf0b169466')
     def test_server_metadata_non_existent_server(self):
         # GET on a non-existent server should not succeed
@@ -68,7 +67,7 @@
                           non_existent_server_id,
                           'test2')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f408e78e-3066-4097-9299-3b0182da812e')
     def test_list_server_metadata_non_existent_server(self):
         # List metadata on a non-existent server should not succeed
@@ -77,7 +76,7 @@
                           self.client.list_server_metadata,
                           non_existent_server_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0025fbd6-a4ba-4cde-b8c2-96805dcfdabc')
     def test_wrong_key_passed_in_body(self):
         # Raise BadRequest if key in uri does not match
@@ -87,7 +86,7 @@
                           self.client.set_server_metadata_item,
                           self.server['id'], 'key', meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0df38c2a-3d4e-4db5-98d8-d4d9fa843a12')
     def test_set_metadata_non_existent_server(self):
         # Set metadata on a non-existent server should not succeed
@@ -98,7 +97,7 @@
                           non_existent_server_id,
                           meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('904b13dc-0ef2-4e4c-91cd-3b4a0f2f49d8')
     def test_update_metadata_non_existent_server(self):
         # An update should not happen for a non-existent server
@@ -109,7 +108,7 @@
                           non_existent_server_id,
                           meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a452f38c-05c2-4b47-bd44-a4f0bf5a5e48')
     def test_update_metadata_with_blank_key(self):
         # Blank key should trigger an error
@@ -118,7 +117,7 @@
                           self.client.update_server_metadata,
                           self.server['id'], meta=meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6bbd88e1-f8b3-424d-ba10-ae21c45ada8d')
     def test_delete_metadata_non_existent_server(self):
         # Should not be able to delete metadata item from a non-existent server
@@ -128,7 +127,7 @@
                           non_existent_server_id,
                           'd')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d8c0a210-a5c3-4664-be04-69d96746b547')
     def test_metadata_items_limit(self):
         # A 403 Forbidden or 413 Overlimit (old behaviour) exception
@@ -154,7 +153,7 @@
                           self.client.update_server_metadata,
                           self.server['id'], req_metadata)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('96100343-7fa9-40d8-80fa-d29ef588ce1c')
     def test_set_server_metadata_blank_key(self):
         # Raise a bad request error for blank key.
@@ -164,7 +163,7 @@
                           self.client.set_server_metadata,
                           self.server['id'], meta=meta)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('64a91aee-9723-4863-be44-4c9d9f1e7d0e')
     def test_set_server_metadata_missing_metadata(self):
         # Raise a bad request error for a missing metadata field
diff --git a/tempest/api/compute/servers/test_server_personality.py b/tempest/api/compute/servers/test_server_personality.py
index 957d24a..90b9da4 100644
--- a/tempest/api/compute/servers/test_server_personality.py
+++ b/tempest/api/compute/servers/test_server_personality.py
@@ -97,7 +97,7 @@
         max_file_limit = limits['absolute']['maxPersonality']
         if max_file_limit == -1:
             raise self.skipException("No limit for personality files")
-        for i in range(0, int(max_file_limit) + 1):
+        for i in range(0, max_file_limit + 1):
             path = 'etc/test' + str(i) + '.txt'
             personality.append({'path': path,
                                 'contents': base64.encode_as_text(
@@ -117,7 +117,7 @@
         if max_file_limit == -1:
             raise self.skipException("No limit for personality files")
         person = []
-        for i in range(0, int(max_file_limit)):
+        for i in range(0, max_file_limit):
             # NOTE(andreaf) The cirros disk image is blank before boot
             # so we can only inject safely to /
             path = '/test' + str(i) + '.txt'
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 5db7f4f..b0ef3bc 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -13,10 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -40,81 +42,52 @@
     def resource_setup(cls):
         super(ServerRescueTestJSON, cls).resource_setup()
 
-        # Floating IP creation
-        body = cls.floating_ips_client.create_floating_ip(
-            pool=CONF.network.floating_network_name)['floating_ip']
-        cls.floating_ip_id = str(body['id']).strip()
-        cls.floating_ip = str(body['ip']).strip()
-
-        # Security group creation
-        cls.sg_name = data_utils.rand_name('sg')
-        cls.sg_desc = data_utils.rand_name('sg-desc')
-        cls.sg = cls.security_groups_client.create_security_group(
-            name=cls.sg_name, description=cls.sg_desc)['security_group']
-        cls.sg_id = cls.sg['id']
-
-        cls.password = data_utils.rand_password()
-        # Server for positive tests
-        server = cls.create_test_server(adminPass=cls.password,
-                                        wait_until='BUILD')
-        cls.server_id = server['id']
-        waiters.wait_for_server_status(cls.servers_client, cls.server_id,
-                                       'ACTIVE')
-
-    @classmethod
-    def resource_cleanup(cls):
-        # Deleting the floating IP which is created in this method
-        cls.floating_ips_client.delete_floating_ip(cls.floating_ip_id)
-        cls.sg = cls.security_groups_client.delete_security_group(
-            cls.sg_id)
-        super(ServerRescueTestJSON, cls).resource_cleanup()
-
-    def _unrescue(self, server_id):
-        self.servers_client.unrescue_server(server_id)
-        waiters.wait_for_server_status(self.servers_client, server_id,
-                                       'ACTIVE')
+        password = data_utils.rand_password()
+        server = cls.create_test_server(adminPass=password,
+                                        wait_until='ACTIVE')
+        cls.servers_client.rescue_server(server['id'], adminPass=password)
+        waiters.wait_for_server_status(cls.servers_client, server['id'],
+                                       'RESCUE')
+        cls.rescued_server_id = server['id']
 
     @decorators.idempotent_id('fd032140-714c-42e4-a8fd-adcd8df06be6')
     def test_rescue_unrescue_instance(self):
-        self.servers_client.rescue_server(
-            self.server_id, adminPass=self.password)
-        waiters.wait_for_server_status(self.servers_client, self.server_id,
+        password = data_utils.rand_password()
+        server = self.create_test_server(adminPass=password,
+                                         wait_until='ACTIVE')
+        self.servers_client.rescue_server(server['id'], adminPass=password)
+        waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'RESCUE')
-        self.servers_client.unrescue_server(self.server_id)
-        waiters.wait_for_server_status(self.servers_client, self.server_id,
+        self.servers_client.unrescue_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'ACTIVE')
 
     @decorators.idempotent_id('4842e0cf-e87d-4d9d-b61f-f4791da3cacc')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    @testtools.skipUnless(CONF.network_feature_enabled.floating_ips,
+                          "Floating ips are not available")
     def test_rescued_vm_associate_dissociate_floating_ip(self):
-        # Rescue the server
-        self.servers_client.rescue_server(
-            self.server_id, adminPass=self.password)
-        waiters.wait_for_server_status(self.servers_client, self.server_id,
-                                       'RESCUE')
-        self.addCleanup(self._unrescue, self.server_id)
-
         # Association of floating IP to a rescued vm
-        client = self.floating_ips_client
-        client.associate_floating_ip_to_server(self.floating_ip,
-                                               self.server_id)
+        floating_ip_body = self.floating_ips_client.create_floating_ip(
+            pool=CONF.network.floating_network_name)['floating_ip']
+        self.addCleanup(self.floating_ips_client.delete_floating_ip,
+                        floating_ip_body['id'])
+
+        self.floating_ips_client.associate_floating_ip_to_server(
+            str(floating_ip_body['ip']).strip(), self.rescued_server_id)
 
         # Disassociation of floating IP that was associated in this method
-        client.disassociate_floating_ip_from_server(self.floating_ip,
-                                                    self.server_id)
+        self.floating_ips_client.disassociate_floating_ip_from_server(
+            str(floating_ip_body['ip']).strip(), self.rescued_server_id)
 
     @decorators.idempotent_id('affca41f-7195-492d-8065-e09eee245404')
     def test_rescued_vm_add_remove_security_group(self):
-        # Rescue the server
-        self.servers_client.rescue_server(
-            self.server_id, adminPass=self.password)
-        waiters.wait_for_server_status(self.servers_client, self.server_id,
-                                       'RESCUE')
-        self.addCleanup(self._unrescue, self.server_id)
-
         # Add Security group
-        self.servers_client.add_security_group(self.server_id,
-                                               name=self.sg_name)
+        sg = self.create_security_group()
+        self.servers_client.add_security_group(self.rescued_server_id,
+                                               name=sg['name'])
 
         # Delete Security group
-        self.servers_client.remove_security_group(self.server_id,
-                                                  name=self.sg_name)
+        self.servers_client.remove_security_group(self.rescued_server_id,
+                                                  name=sg['name'])
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index c3a8c36..1260c6b 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -16,12 +16,12 @@
 import testtools
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -74,7 +74,7 @@
     @decorators.idempotent_id('cc3a883f-43c0-4fb6-a9bb-5579d64984ed')
     @testtools.skipUnless(CONF.compute_feature_enabled.pause,
                           'Pause is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_rescue_paused_instance(self):
         # Rescue a paused server
         self.servers_client.pause_server(self.server_id)
@@ -85,13 +85,13 @@
                           self.servers_client.rescue_server,
                           self.server_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('db22b618-f157-4566-a317-1b6d467a8094')
     def test_rescued_vm_reboot(self):
         self.assertRaises(lib_exc.Conflict, self.servers_client.reboot_server,
                           self.rescue_id, type='HARD')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6dfc0a55-3a77-4564-a144-1587b7971dde')
     def test_rescue_non_existent_server(self):
         # Rescue a non-existing server
@@ -100,7 +100,7 @@
                           self.servers_client.rescue_server,
                           non_existent_server)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('70cdb8a1-89f8-437d-9448-8844fd82bf46')
     def test_rescued_vm_rebuild(self):
         self.assertRaises(lib_exc.Conflict,
@@ -109,8 +109,8 @@
                           self.image_ref_alt)
 
     @decorators.idempotent_id('d0ccac79-0091-4cf4-a1ce-26162d0cc55f')
-    @test.services('volume')
-    @test.attr(type=['negative'])
+    @utils.services('volume')
+    @decorators.attr(type=['negative'])
     def test_rescued_vm_attach_volume(self):
         volume = self.create_volume()
 
@@ -129,8 +129,8 @@
                           device='/dev/%s' % self.device)
 
     @decorators.idempotent_id('f56e465b-fe10-48bf-b75d-646cda3a8bc9')
-    @test.services('volume')
-    @test.attr(type=['negative'])
+    @utils.services('volume')
+    @decorators.attr(type=['negative'])
     def test_rescued_vm_detach_volume(self):
         volume = self.create_volume()
 
diff --git a/tempest/api/compute/servers/test_server_tags.py b/tempest/api/compute/servers/test_server_tags.py
new file mode 100644
index 0000000..8d0a4e3
--- /dev/null
+++ b/tempest/api/compute/servers/test_server_tags.py
@@ -0,0 +1,108 @@
+# Copyright 2017 AT&T 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 six
+
+from tempest.api.compute import base
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+
+class ServerTagsTestJSON(base.BaseV2ComputeTest):
+
+    min_microversion = '2.26'
+    max_microversion = 'latest'
+
+    @classmethod
+    def skip_checks(cls):
+        super(ServerTagsTestJSON, cls).skip_checks()
+        if not utils.is_extension_enabled('os-server-tags', 'compute'):
+            msg = "os-server-tags extension is not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
+    def setup_clients(cls):
+        super(ServerTagsTestJSON, cls).setup_clients()
+        cls.client = cls.servers_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(ServerTagsTestJSON, cls).resource_setup()
+        cls.server = cls.create_test_server(wait_until='ACTIVE')
+
+    def _update_server_tags(self, server_id, tags):
+        if not isinstance(tags, (list, tuple)):
+            tags = [tags]
+        for tag in tags:
+            self.client.update_tag(server_id, tag)
+        self.addCleanup(self.client.delete_all_tags, server_id)
+
+    @decorators.idempotent_id('8d95abe2-c658-4c42-9a44-c0258500306b')
+    def test_create_delete_tag(self):
+        # Check that no tags exist.
+        fetched_tags = self.client.list_tags(self.server['id'])['tags']
+        self.assertEmpty(fetched_tags)
+
+        # Add server tag to the server.
+        assigned_tag = data_utils.rand_name('tag')
+        self._update_server_tags(self.server['id'], assigned_tag)
+
+        # Check that added tag exists.
+        fetched_tags = self.client.list_tags(self.server['id'])['tags']
+        self.assertEqual([assigned_tag], fetched_tags)
+
+        # Remove assigned tag from server and check that it was removed.
+        self.client.delete_tag(self.server['id'], assigned_tag)
+        fetched_tags = self.client.list_tags(self.server['id'])['tags']
+        self.assertEmpty(fetched_tags)
+
+    @decorators.idempotent_id('a2c1af8c-127d-417d-974b-8115f7e3d831')
+    def test_update_all_tags(self):
+        # Add server tags to the server.
+        tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
+        self._update_server_tags(self.server['id'], tags)
+
+        # Replace tags with new tags and check that they are present.
+        new_tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
+        replaced_tags = self.client.update_all_tags(
+            self.server['id'], new_tags)['tags']
+        six.assertCountEqual(self, new_tags, replaced_tags)
+
+        # List the tags and check that the tags were replaced.
+        fetched_tags = self.client.list_tags(self.server['id'])['tags']
+        six.assertCountEqual(self, new_tags, fetched_tags)
+
+    @decorators.idempotent_id('a63b2a74-e918-4b7c-bcab-10c855f3a57e')
+    def test_delete_all_tags(self):
+        # Add server tags to the server.
+        assigned_tags = [data_utils.rand_name('tag'),
+                         data_utils.rand_name('tag')]
+        self._update_server_tags(self.server['id'], assigned_tags)
+
+        # Delete tags from the server and check that they were deleted.
+        self.client.delete_all_tags(self.server['id'])
+        fetched_tags = self.client.list_tags(self.server['id'])['tags']
+        self.assertEmpty(fetched_tags)
+
+    @decorators.idempotent_id('81279a66-61c3-4759-b830-a2dbe64cbe08')
+    def test_check_tag_existence(self):
+        # Add server tag to the server.
+        assigned_tag = data_utils.rand_name('tag')
+        self._update_server_tags(self.server['id'], assigned_tag)
+
+        # Check that added tag exists. Throws a 404 if not found, else a 204,
+        # which was already checked by the schema validation.
+        self.client.check_tag_existence(self.server['id'], assigned_tag)
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index b463726..7fd1dd1 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -16,9 +16,9 @@
 import testtools
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -102,14 +102,10 @@
         prefix_name = u'\u00CD\u00F1st\u00E1\u00F1c\u00E9'
         self._update_server_name(server['id'], 'ACTIVE', prefix_name)
 
-    @decorators.idempotent_id('6ac19cb1-27a3-40ec-b350-810bdc04c08e')
-    def test_update_server_name_in_stop_state(self):
-        # The server name should be changed to the provided value
-        server = self.create_test_server(wait_until='ACTIVE')
+        # stop server and check server name update again
         self.client.stop_server(server['id'])
         waiters.wait_for_server_status(self.client, server['id'], 'SHUTOFF')
         # Update instance name with non-ASCII characters
-        prefix_name = u'\u00CD\u00F1st\u00E1\u00F1c\u00E9'
         updated_server = self._update_server_name(server['id'],
                                                   'SHUTOFF',
                                                   prefix_name)
@@ -138,3 +134,14 @@
         waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
         server = self.client.show_server(server['id'])['server']
         self.assertEqual('2001:2001::3', server['accessIPv6'])
+
+
+class ServerShowV247Test(base.BaseV2ComputeTest):
+    min_microversion = '2.47'
+    max_microversion = 'latest'
+
+    @decorators.idempotent_id('88b0bdb2-494c-11e7-a919-92ebcb67fe33')
+    def test_show_server(self):
+        server = self.create_test_server()
+        # All fields will be checked by API schema
+        self.servers_client.show_server(server['id'])
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index b22a434..8170b28 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -19,12 +19,12 @@
 
 from tempest.api.compute import base
 from tempest.common import compute
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -54,7 +54,12 @@
         server = cls.create_test_server(wait_until='ACTIVE')
         cls.server_id = server['id']
 
-    @test.attr(type=['negative'])
+        server = cls.create_test_server()
+        cls.client.delete_server(server['id'])
+        waiters.wait_for_server_termination(cls.client, server['id'])
+        cls.deleted_server_id = server['id']
+
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('dbbfd247-c40c-449e-8f6c-d2aa7c7da7cf')
     def test_server_name_blank(self):
         # Create a server with name parameter empty
@@ -63,7 +68,7 @@
                           self.create_test_server,
                           name='')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b8a7235e-5246-4a8f-a08e-b34877c6586f')
     @testtools.skipUnless(CONF.compute_feature_enabled.personality,
                           'Nova personality feature disabled')
@@ -78,7 +83,7 @@
                           self.create_test_server,
                           personality=person)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fcba1052-0a50-4cf3-b1ac-fae241edf02f')
     def test_create_with_invalid_image(self):
         # Create a server with an unknown image
@@ -87,7 +92,7 @@
                           self.create_test_server,
                           image_id=-1)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('18f5227f-d155-4429-807c-ccb103887537')
     def test_create_with_invalid_flavor(self):
         # Create a server with an unknown flavor
@@ -96,7 +101,7 @@
                           self.create_test_server,
                           flavor=-1,)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7f70a4d1-608f-4794-9e56-cb182765972c')
     def test_invalid_access_ip_v4_address(self):
         # An access IPv4 address must match a valid address pattern
@@ -105,7 +110,7 @@
         self.assertRaises(lib_exc.BadRequest,
                           self.create_test_server, accessIPv4=IPv4)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5226dd80-1e9c-4d8a-b5f9-b26ca4763fd0')
     def test_invalid_ip_v6_address(self):
         # An access IPv6 address must match a valid address pattern
@@ -118,7 +123,7 @@
     @decorators.idempotent_id('7ea45b3e-e770-46fa-bfcc-9daaf6d987c0')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_resize_nonexistent_server(self):
         # Resize a non-existent server
         nonexistent_server = data_utils.rand_uuid()
@@ -129,7 +134,7 @@
     @decorators.idempotent_id('ced1a1d7-2ab6-45c9-b90f-b27d87b30efd')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_resize_server_with_non_existent_flavor(self):
         # Resize a server with non-existent flavor
         nonexistent_flavor = data_utils.rand_uuid()
@@ -139,13 +144,13 @@
     @decorators.idempotent_id('45436a7d-a388-4a35-a9d8-3adc5d0d940b')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_resize_server_with_null_flavor(self):
         # Resize a server with null flavor
         self.assertRaises(lib_exc.BadRequest, self.client.resize_server,
                           self.server_id, flavor_ref="")
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d4c023a0-9c55-4747-9dd5-413b820143c7')
     def test_reboot_non_existent_server(self):
         # Reboot a non existent server
@@ -156,7 +161,7 @@
     @decorators.idempotent_id('d1417e7f-a509-41b5-a102-d5eed8613369')
     @testtools.skipUnless(CONF.compute_feature_enabled.pause,
                           'Pause is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_pause_paused_server(self):
         # Pause a paused server.
         self.client.pause_server(self.server_id)
@@ -166,31 +171,23 @@
                           self.server_id)
         self.client.unpause_server(self.server_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('98fa0458-1485-440f-873b-fe7f0d714930')
     def test_rebuild_deleted_server(self):
         # Rebuild a deleted server
-        server = self.create_test_server()
-        self.client.delete_server(server['id'])
-        waiters.wait_for_server_termination(self.client, server['id'])
-
         self.assertRaises(lib_exc.NotFound,
                           self.client.rebuild_server,
-                          server['id'], self.image_ref_alt)
+                          self.deleted_server_id, self.image_ref)
 
-    @test.related_bug('1660878', status_code=409)
-    @test.attr(type=['negative'])
+    @decorators.related_bug('1660878', status_code=409)
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('581a397d-5eab-486f-9cf9-1014bbd4c984')
     def test_reboot_deleted_server(self):
         # Reboot a deleted server
-        server = self.create_test_server()
-        self.client.delete_server(server['id'])
-        waiters.wait_for_server_termination(self.client, server['id'])
-
         self.assertRaises(lib_exc.NotFound, self.client.reboot_server,
-                          server['id'], type='SOFT')
+                          self.deleted_server_id, type='SOFT')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d86141a7-906e-4731-b187-d64a2ea61422')
     def test_rebuild_non_existent_server(self):
         # Rebuild a non existent server
@@ -198,9 +195,9 @@
         self.assertRaises(lib_exc.NotFound,
                           self.client.rebuild_server,
                           nonexistent_server,
-                          self.image_ref_alt)
+                          self.image_ref)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fd57f159-68d6-4c2a-902b-03070828a87e')
     def test_create_numeric_server_name(self):
         server_name = 12345
@@ -208,7 +205,7 @@
                           self.create_test_server,
                           name=server_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c3e0fb12-07fc-4d76-a22e-37409887afe8')
     def test_create_server_name_length_exceeds_256(self):
         # Create a server with name length exceeding 255 characters
@@ -218,8 +215,9 @@
                           self.create_test_server,
                           name=server_name)
 
-    @test.attr(type=['negative'])
-    @test.related_bug('1651064', status_code=500)
+    @decorators.attr(type=['negative'])
+    @decorators.related_bug('1651064', status_code=500)
+    @utils.services('volume')
     @decorators.idempotent_id('12146ac1-d7df-4928-ad25-b1f99e5286cd')
     def test_create_server_invalid_bdm_in_2nd_dict(self):
         volume = self.create_volume()
@@ -238,7 +236,7 @@
                           image_id=self.image_ref,
                           block_device_mapping_v2=bdm)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4e72dc2d-44c5-4336-9667-f7972e95c402')
     def test_create_with_invalid_network_uuid(self):
         # Pass invalid network uuid while creating a server
@@ -249,7 +247,7 @@
                           self.create_test_server,
                           networks=networks)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7a2efc39-530c-47de-b875-2dd01c8d39bd')
     def test_create_with_non_existent_keypair(self):
         # Pass a non-existent keypair while creating a server
@@ -259,7 +257,7 @@
                           self.create_test_server,
                           key_name=key_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7fc74810-0bd2-4cd7-8244-4f33a9db865a')
     def test_create_server_metadata_exceeds_length_limit(self):
         # Pass really long metadata while creating a server
@@ -269,7 +267,7 @@
                           self.create_test_server,
                           metadata=metadata)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('aa8eed43-e2cb-4ebf-930b-da14f6a21d81')
     def test_update_name_of_non_existent_server(self):
         # Update name of a non-existent server
@@ -281,7 +279,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.update_server,
                           nonexistent_server, name=new_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('38204696-17c6-44da-9590-40f87fb5a899')
     def test_update_server_set_empty_name(self):
         # Update name of the server to an empty string
@@ -291,7 +289,7 @@
         self.assertRaises(lib_exc.BadRequest, self.client.update_server,
                           self.server_id, name=new_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5c8e244c-dada-4590-9944-749c455b431f')
     def test_update_server_name_length_exceeds_256(self):
         # Update name of server exceed the name length limit
@@ -302,7 +300,7 @@
                           self.server_id,
                           name=new_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1041b4e6-514b-4855-96a5-e974b60870a3')
     def test_delete_non_existent_server(self):
         # Delete a non existent server
@@ -311,14 +309,14 @@
         self.assertRaises(lib_exc.NotFound, self.client.delete_server,
                           nonexistent_server)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('75f79124-277c-45e6-a373-a1d6803f4cc4')
     def test_delete_server_pass_negative_id(self):
         # Pass an invalid string parameter to delete server
 
         self.assertRaises(lib_exc.NotFound, self.client.delete_server, -1)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f4d7279b-5fd2-4bf2-9ba4-ae35df0d18c5')
     def test_delete_server_pass_id_exceeding_length_limit(self):
         # Pass a server ID that exceeds length limit to delete server
@@ -326,7 +324,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.delete_server,
                           sys.maxsize + 1)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c5fa6041-80cd-483b-aa6d-4e45f19d093c')
     def test_create_with_nonexistent_security_group(self):
         # Create a server with a nonexistent security group
@@ -336,7 +334,7 @@
                           self.create_test_server,
                           security_groups=security_groups)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('3436b02f-1b1e-4f03-881e-c6a602327439')
     def test_get_non_existent_server(self):
         # Get a non existent server details
@@ -344,7 +342,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.show_server,
                           nonexistent_server)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a31460a9-49e1-42aa-82ee-06e0bb7c2d03')
     def test_stop_non_existent_server(self):
         # Stop a non existent server
@@ -355,7 +353,7 @@
     @decorators.idempotent_id('6a8dc0c6-6cd4-4c0a-9f32-413881828091')
     @testtools.skipUnless(CONF.compute_feature_enabled.pause,
                           'Pause is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_pause_non_existent_server(self):
         # pause a non existent server
         nonexistent_server = data_utils.rand_uuid()
@@ -365,7 +363,7 @@
     @decorators.idempotent_id('705b8e3a-e8a7-477c-a19b-6868fc24ac75')
     @testtools.skipUnless(CONF.compute_feature_enabled.pause,
                           'Pause is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_unpause_non_existent_server(self):
         # unpause a non existent server
         nonexistent_server = data_utils.rand_uuid()
@@ -375,7 +373,7 @@
     @decorators.idempotent_id('c8e639a7-ece8-42dd-a2e0-49615917ba4f')
     @testtools.skipUnless(CONF.compute_feature_enabled.pause,
                           'Pause is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_unpause_server_invalid_state(self):
         # unpause an active server.
         self.assertRaises(lib_exc.Conflict,
@@ -385,7 +383,7 @@
     @decorators.idempotent_id('d1f032d5-7b6e-48aa-b252-d5f16dd994ca')
     @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
                           'Suspend is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_suspend_non_existent_server(self):
         # suspend a non existent server
         nonexistent_server = data_utils.rand_uuid()
@@ -395,7 +393,7 @@
     @decorators.idempotent_id('7f323206-05a9-4bf8-996b-dd5b2036501b')
     @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
                           'Suspend is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_suspend_server_invalid_state(self):
         # suspend a suspended server.
         self.client.suspend_server(self.server_id)
@@ -409,7 +407,7 @@
     @decorators.idempotent_id('221cd282-bddb-4837-a683-89c2487389b6')
     @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
                           'Suspend is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_resume_non_existent_server(self):
         # resume a non existent server
         nonexistent_server = data_utils.rand_uuid()
@@ -419,14 +417,14 @@
     @decorators.idempotent_id('ccb6294d-c4c9-498f-8a43-554c098bfadb')
     @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
                           'Suspend is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_resume_server_invalid_state(self):
         # resume an active server.
         self.assertRaises(lib_exc.Conflict,
                           self.client.resume_server,
                           self.server_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7dd919e7-413f-4198-bebb-35e2a01b13e9')
     def test_get_console_output_of_non_existent_server(self):
         # get the console output for a non existent server
@@ -435,7 +433,7 @@
                           self.client.get_console_output,
                           nonexistent_server, length=10)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6f47992b-5144-4250-9f8b-f00aa33950f3')
     def test_force_delete_nonexistent_server_id(self):
         # force-delete a non existent server
@@ -444,7 +442,7 @@
                           self.client.force_delete_server,
                           nonexistent_server)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9c6d38cc-fcfb-437a-85b9-7b788af8bf01')
     def test_restore_nonexistent_server_id(self):
         # restore-delete a non existent server
@@ -453,7 +451,7 @@
                           self.client.restore_soft_deleted_server,
                           nonexistent_server)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7fcadfab-bd6a-4753-8db7-4a51e51aade9')
     def test_restore_server_invalid_state(self):
         # we can only restore-delete a server in 'soft-delete' state
@@ -464,7 +462,7 @@
     @decorators.idempotent_id('abca56e2-a892-48ea-b5e5-e07e69774816')
     @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
                           'Shelve is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_shelve_non_existent_server(self):
         # shelve a non existent server
         nonexistent_server = data_utils.rand_uuid()
@@ -474,7 +472,7 @@
     @decorators.idempotent_id('443e4f9b-e6bf-4389-b601-3a710f15fddd')
     @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
                           'Shelve is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_shelve_shelved_server(self):
         # shelve a shelved server.
         compute.shelve_server(self.client, self.server_id)
@@ -495,7 +493,7 @@
     @decorators.idempotent_id('23d23b37-afaf-40d7-aa5d-5726f82d8821')
     @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
                           'Shelve is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_unshelve_non_existent_server(self):
         # unshelve a non existent server
         nonexistent_server = data_utils.rand_uuid()
@@ -505,16 +503,16 @@
     @decorators.idempotent_id('8f198ded-1cca-4228-9e65-c6b449c54880')
     @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
                           'Shelve is not available.')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_unshelve_server_invalid_state(self):
         # unshelve an active server.
         self.assertRaises(lib_exc.Conflict,
                           self.client.unshelve_server,
                           self.server_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('74085be3-a370-4ca2-bc51-2d0e10e0f573')
-    @test.services('volume', 'image')
+    @utils.services('volume', 'image')
     def test_create_server_from_non_bootable_volume(self):
         # Create a volume
         volume = self.create_volume()
@@ -550,7 +548,7 @@
     def setUp(self):
         super(ServersNegativeTestMultiTenantJSON, self).setUp()
         try:
-            waiters.wait_for_server_status(self.client, self.server_id,
+            waiters.wait_for_server_status(self.servers_client, self.server_id,
                                            'ACTIVE')
         except Exception:
             self.__class__.server_id = self.rebuild_server(self.server_id)
@@ -566,7 +564,7 @@
         server = cls.create_test_server(wait_until='ACTIVE')
         cls.server_id = server['id']
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('543d84c1-dd2e-4c6d-8cb2-b9da0efaa384')
     def test_update_server_of_another_tenant(self):
         # Update name of a server that belongs to another tenant
@@ -576,7 +574,7 @@
                           self.alt_client.update_server, self.server_id,
                           name=new_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5c75009d-3eea-423e-bea3-61b09fd25f9c')
     def test_delete_a_server_of_another_tenant(self):
         # Delete a server that belongs to another tenant
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index 610121b..90f04ff 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -17,10 +17,10 @@
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions
-from tempest import test
 
 CONF = config.CONF
 
@@ -44,7 +44,7 @@
         cls.server = cls.create_test_server(wait_until='ACTIVE')
 
     @decorators.idempotent_id('96c4e2ef-5e4d-4d7f-87f5-fed6dca18016')
-    @test.services('network')
+    @utils.services('network')
     def test_list_virtual_interfaces(self):
         # Positive test:Should be able to GET the virtual interfaces list
         # for a given server_id
@@ -56,12 +56,11 @@
                 self.client.list_virtual_interfaces(self.server['id'])
         else:
             output = self.client.list_virtual_interfaces(self.server['id'])
-            self.assertIsNotNone(output)
-            virt_ifaces = output
-            self.assertNotEqual(0, len(virt_ifaces['virtual_interfaces']),
+            virt_ifaces = output['virtual_interfaces']
+            self.assertNotEmpty(virt_ifaces,
                                 'Expected virtual interfaces, got 0 '
                                 'interfaces.')
-            for virt_iface in virt_ifaces['virtual_interfaces']:
+            for virt_iface in virt_ifaces:
                 mac_address = virt_iface['mac_address']
                 self.assertTrue(netaddr.valid_mac(mac_address),
                                 "Invalid mac address detected. mac address: %s"
diff --git a/tempest/api/compute/servers/test_virtual_interfaces_negative.py b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
index b1374d2..20923a8 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces_negative.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
@@ -14,10 +14,10 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common import utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class VirtualInterfacesNegativeTestJSON(base.BaseV2ComputeTest):
@@ -33,9 +33,9 @@
         super(VirtualInterfacesNegativeTestJSON, cls).setup_clients()
         cls.client = cls.servers_client
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('64ebd03c-1089-4306-93fa-60f5eb5c803c')
-    @test.services('network')
+    @utils.services('network')
     def test_list_virtual_interfaces_invalid_server_id(self):
         # Negative test: Should not be able to GET virtual interfaces
         # for an invalid server_id
diff --git a/tempest/api/compute/test_extensions.py b/tempest/api/compute/test_extensions.py
index f87bf6d..34faf5f 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -16,9 +16,9 @@
 from oslo_log import log as logging
 
 from tempest.api.compute import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -26,27 +26,29 @@
 LOG = logging.getLogger(__name__)
 
 
-class ExtensionsTestJSON(base.BaseV2ComputeTest):
+class ExtensionsTest(base.BaseV2ComputeTest):
 
     @decorators.idempotent_id('3bb27738-b759-4e0d-a5fa-37d7a6df07d1')
     def test_list_extensions(self):
         # List of all extensions
-        if len(CONF.compute_feature_enabled.api_extensions) == 0:
+        if not CONF.compute_feature_enabled.api_extensions:
             raise self.skipException('There are not any extensions configured')
         extensions = self.extensions_client.list_extensions()['extensions']
         ext = CONF.compute_feature_enabled.api_extensions[0]
-        if ext == 'all':
-            self.assertIn('Hosts', map(lambda x: x['name'], extensions))
-        elif ext:
-            self.assertIn(ext, map(lambda x: x['alias'], extensions))
-        else:
-            raise self.skipException('There are not any extensions configured')
+
         # Log extensions list
         extension_list = map(lambda x: x['alias'], extensions)
         LOG.debug("Nova extensions: %s", ','.join(extension_list))
 
+        if ext == 'all':
+            self.assertIn('Hosts', map(lambda x: x['name'], extensions))
+        elif ext:
+            self.assertIn(ext, extension_list)
+        else:
+            raise self.skipException('There are not any extensions configured')
+
     @decorators.idempotent_id('05762f39-bdfa-4cdb-9b46-b78f8e78e2fd')
-    @test.requires_ext(extension='os-consoles', service='compute')
+    @utils.requires_ext(extension='os-consoles', service='compute')
     def test_get_extension(self):
         # get the specified extensions
         extension = self.extensions_client.show_extension('os-consoles')
diff --git a/tempest/api/compute/test_networks.py b/tempest/api/compute/test_networks.py
index 4d21fed..b8c79d7 100644
--- a/tempest/api/compute/test_networks.py
+++ b/tempest/api/compute/test_networks.py
@@ -29,7 +29,7 @@
     @classmethod
     def setup_clients(cls):
         super(ComputeNetworksTest, cls).setup_clients()
-        cls.client = cls.os.compute_networks_client
+        cls.client = cls.os_primary.compute_networks_client
 
     @decorators.idempotent_id('3fe07175-312e-49a5-a623-5f52eeada4c2')
     def test_list_networks(self):
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 0ad2df8..7cf90ae 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -15,8 +15,8 @@
 
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
+from tempest.common import utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class QuotasTestJSON(base.BaseV2ComputeTest):
@@ -24,7 +24,7 @@
     @classmethod
     def skip_checks(cls):
         super(QuotasTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('os-quota-sets', 'compute'):
+        if not utils.is_extension_enabled('os-quota-sets', 'compute'):
             msg = "quotas extension not enabled."
             raise cls.skipException(msg)
 
@@ -62,8 +62,8 @@
             self.assertIn(quota, quota_set.keys())
 
         # get the quota set using user id
-        quota_set = self.client.show_quota_set(self.tenant_id,
-                                               self.user_id)['quota_set']
+        quota_set = self.client.show_quota_set(
+            self.tenant_id, user_id=self.user_id)['quota_set']
         self.assertEqual(quota_set['id'], self.tenant_id)
         for quota in expected_quota_set:
             self.assertIn(quota, quota_set.keys())
diff --git a/tempest/api/compute/test_tenant_networks.py b/tempest/api/compute/test_tenant_networks.py
index b203c7e..b55e2c0 100644
--- a/tempest/api/compute/test_tenant_networks.py
+++ b/tempest/api/compute/test_tenant_networks.py
@@ -13,8 +13,8 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common import utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class ComputeTenantNetworksTest(base.BaseV2ComputeTest):
@@ -22,7 +22,7 @@
     @classmethod
     def resource_setup(cls):
         super(ComputeTenantNetworksTest, cls).resource_setup()
-        cls.client = cls.os.tenant_networks_client
+        cls.client = cls.os_primary.tenant_networks_client
         cls.network = cls.get_tenant_network()
 
     @classmethod
@@ -31,7 +31,7 @@
         super(ComputeTenantNetworksTest, cls).setup_credentials()
 
     @decorators.idempotent_id('edfea98e-bbe3-4c7a-9739-87b986baff26')
-    @test.services('network')
+    @utils.services('network')
     def test_list_show_tenant_networks(self):
         # Fetch all networks that are visible to the tenant: this may include
         # shared and external networks
diff --git a/tempest/api/compute/test_versions.py b/tempest/api/compute/test_versions.py
index c9f0724..6141553 100644
--- a/tempest/api/compute/test_versions.py
+++ b/tempest/api/compute/test_versions.py
@@ -19,6 +19,7 @@
 class TestVersions(base.BaseV2ComputeTest):
 
     @decorators.idempotent_id('6c0a0990-43b6-4529-9b61-5fd8daf7c55c')
+    @decorators.attr(type='smoke')
     def test_list_api_versions(self):
         """Test that a get of the unversioned url returns the choices doc.
 
@@ -37,6 +38,7 @@
                          "The first listed version should be v2.0")
 
     @decorators.idempotent_id('b953a29e-929c-4a8e-81be-ec3a7e03cb76')
+    @decorators.attr(type='smoke')
     def test_get_version_details(self):
         """Test individual version endpoints info works.
 
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index cbe7178..e0fed58 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -13,21 +13,15 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_log import log as logging
-import testtools
-
 from tempest.api.compute import base
 from tempest.common import compute
 from tempest.common.utils.linux import remote_client
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
 
 CONF = config.CONF
 
-LOG = logging.getLogger(__name__)
-
 
 class AttachVolumeTestJSON(base.BaseV2ComputeTest):
     max_microversion = '2.19'
@@ -56,43 +50,34 @@
             validatable=True,
             wait_until='ACTIVE',
             adminPass=self.image_ssh_password)
+        self.addCleanup(self.delete_server, server['id'])
         # Record addresses so that we can ssh later
         server['addresses'] = self.servers_client.list_addresses(
             server['id'])['addresses']
         return server
 
-    def _detach_volume(self, server_id, volume_id):
-        try:
-            self.servers_client.detach_volume(server_id, volume_id)
-            waiters.wait_for_volume_status(self.volumes_client,
-                                           volume_id, 'available')
-        except lib_exc.NotFound:
-            LOG.warning("Unable to detach volume %s from server %s "
-                        "possibly it was already detached", volume_id,
-                        server_id)
-
-    def _attach_volume(self, server_id, volume_id, device=None):
-        # Attach the volume to the server
-        kwargs = {'volumeId': volume_id}
-        if device:
-            kwargs.update({'device': '/dev/%s' % device})
-        attachment = self.servers_client.attach_volume(
-            server_id, **kwargs)['volumeAttachment']
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume_id, 'in-use')
-        self.addCleanup(self._detach_volume, server_id,
-                        volume_id)
-
-        return attachment
-
     @decorators.idempotent_id('52e9045a-e90d-4c0d-9087-79d657faffff')
     def test_attach_detach_volume(self):
         # Stop and Start a server with an attached volume, ensuring that
         # the volume remains attached.
         server = self._create_server()
+
+        # NOTE(andreaf) Create one remote client used throughout the test.
+        if CONF.validation.run_validation:
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(server),
+                self.image_ssh_user,
+                self.image_ssh_password,
+                self.validation_resources['keypair']['private_key'],
+                server=server,
+                servers_client=self.servers_client)
+            # NOTE(andreaf) We need to ensure the ssh key has been
+            # injected in the guest before we power cycle
+            linux_client.validate_authentication()
+
         volume = self.create_volume()
-        attachment = self._attach_volume(server['id'], volume['id'],
-                                         device=self.device)
+        attachment = self.attach_volume(server, volume,
+                                        device=('/dev/%s' % self.device))
 
         self.servers_client.stop_server(server['id'])
         waiters.wait_for_server_status(self.servers_client, server['id'],
@@ -103,19 +88,14 @@
                                        'ACTIVE')
 
         if CONF.validation.run_validation:
-            linux_client = remote_client.RemoteClient(
-                self.get_server_ip(server),
-                self.image_ssh_user,
-                self.image_ssh_password,
-                self.validation_resources['keypair']['private_key'],
-                server=server,
-                servers_client=self.servers_client)
-
             disks = linux_client.get_disks()
             device_name_to_match = '\n' + self.device + ' '
             self.assertIn(device_name_to_match, disks)
 
-        self._detach_volume(server['id'], attachment['volumeId'])
+        self.servers_client.detach_volume(server['id'], attachment['volumeId'])
+        waiters.wait_for_volume_resource_status(
+            self.volumes_client, attachment['volumeId'], 'available')
+
         self.servers_client.stop_server(server['id'])
         waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'SHUTOFF')
@@ -125,14 +105,6 @@
                                        'ACTIVE')
 
         if CONF.validation.run_validation:
-            linux_client = remote_client.RemoteClient(
-                self.get_server_ip(server),
-                self.image_ssh_user,
-                self.image_ssh_password,
-                self.validation_resources['keypair']['private_key'],
-                server=server,
-                servers_client=self.servers_client)
-
             disks = linux_client.get_disks()
             self.assertNotIn(device_name_to_match, disks)
 
@@ -140,51 +112,39 @@
     def test_list_get_volume_attachments(self):
         # List volume attachment of the server
         server = self._create_server()
-        volume = self.create_volume()
-        attachment = self._attach_volume(server['id'], volume['id'],
-                                         device=self.device)
+        volume_1st = self.create_volume()
+        attachment_1st = self.attach_volume(server, volume_1st,
+                                            device=('/dev/%s' % self.device))
         body = self.servers_client.list_volume_attachments(
             server['id'])['volumeAttachments']
         self.assertEqual(1, len(body))
-        self.assertIn(attachment, body)
+        self.assertIn(attachment_1st, body)
 
         # Get volume attachment of the server
         body = self.servers_client.show_volume_attachment(
             server['id'],
-            attachment['id'])['volumeAttachment']
+            attachment_1st['id'])['volumeAttachment']
         self.assertEqual(server['id'], body['serverId'])
-        self.assertEqual(volume['id'], body['volumeId'])
-        self.assertEqual(attachment['id'], body['id'])
+        self.assertEqual(volume_1st['id'], body['volumeId'])
+        self.assertEqual(attachment_1st['id'], body['id'])
 
-    @decorators.idempotent_id('757d488b-a951-4bc7-b3cd-f417028da08a')
-    def test_list_get_two_volume_attachments(self):
-        # NOTE: This test is using the volume device auto-assignment
-        # without specifying the device ("/dev/sdb", etc). The feature
-        # is supported since Nova Liberty release or later. So this should
-        # be skipped on older clouds.
-        server = self._create_server()
-        volume_1st = self.create_volume()
+        # attach one more volume to server
         volume_2nd = self.create_volume()
-        attachment_1st = self._attach_volume(server['id'], volume_1st['id'])
-        attachment_2nd = self._attach_volume(server['id'], volume_2nd['id'])
-
+        attachment_2nd = self.attach_volume(server, volume_2nd)
         body = self.servers_client.list_volume_attachments(
             server['id'])['volumeAttachments']
         self.assertEqual(2, len(body))
 
-        body = self.servers_client.show_volume_attachment(
-            server['id'],
-            attachment_1st['id'])['volumeAttachment']
-        self.assertEqual(server['id'], body['serverId'])
-        self.assertEqual(attachment_1st['volumeId'], body['volumeId'])
-        self.assertEqual(attachment_1st['id'], body['id'])
-
-        body = self.servers_client.show_volume_attachment(
-            server['id'],
-            attachment_2nd['id'])['volumeAttachment']
-        self.assertEqual(server['id'], body['serverId'])
-        self.assertEqual(attachment_2nd['volumeId'], body['volumeId'])
-        self.assertEqual(attachment_2nd['id'], body['id'])
+        for attachment in [attachment_1st, attachment_2nd]:
+            body = self.servers_client.show_volume_attachment(
+                server['id'], attachment['id'])['volumeAttachment']
+            self.assertEqual(server['id'], body['serverId'])
+            self.assertEqual(attachment['volumeId'], body['volumeId'])
+            self.assertEqual(attachment['id'], body['id'])
+            self.servers_client.detach_volume(server['id'],
+                                              attachment['volumeId'])
+            waiters.wait_for_volume_resource_status(
+                self.volumes_client, attachment['volumeId'], 'available')
 
 
 class AttachVolumeShelveTestJSON(AttachVolumeTestJSON):
@@ -197,6 +157,12 @@
     min_microversion = '2.20'
     max_microversion = 'latest'
 
+    @classmethod
+    def skip_checks(cls):
+        super(AttachVolumeShelveTestJSON, cls).skip_checks()
+        if not CONF.compute_feature_enabled.shelve:
+            raise cls.skipException('Shelve is not available.')
+
     def _count_volumes(self, server):
         # Count number of volumes on an instance
         volumes = 0
@@ -244,8 +210,6 @@
             self.assertEqual(number_of_volumes, counted_volumes)
 
     @decorators.idempotent_id('13a940b6-3474-4c3c-b03f-29b89112bfee')
-    @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
-                          'Shelve is not available.')
     def test_attach_volume_shelved_or_offload_server(self):
         # Create server, count number of volumes on it, shelve
         # server and attach pre-created volume to shelved server
@@ -253,8 +217,9 @@
         volume = self.create_volume()
         num_vol = self._count_volumes(server)
         self._shelve_server(server)
-        attachment = self._attach_volume(server['id'], volume['id'],
-                                         device=self.device)
+        attachment = self.attach_volume(server, volume,
+                                        device=('/dev/%s' % self.device),
+                                        check_reserved=True)
 
         # Unshelve the instance and check that attached volume exists
         self._unshelve_server_and_check_volumes(server, num_vol + 1)
@@ -270,8 +235,6 @@
         self.assertIsNotNone(volume_attachment['device'])
 
     @decorators.idempotent_id('b54e86dd-a070-49c4-9c07-59ae6dae15aa')
-    @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
-                          'Shelve is not available.')
     def test_detach_volume_shelved_or_offload_server(self):
         # Count number of volumes on instance, shelve
         # server and attach pre-created volume to shelved server
@@ -279,9 +242,13 @@
         volume = self.create_volume()
         num_vol = self._count_volumes(server)
         self._shelve_server(server)
-        self._attach_volume(server['id'], volume['id'], device=self.device)
-        # Detach the volume
-        self._detach_volume(server['id'], volume['id'])
+
+        # Attach and then detach the volume
+        self.attach_volume(server, volume, device=('/dev/%s' % self.device),
+                           check_reserved=True)
+        self.servers_client.detach_volume(server['id'], volume['id'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
 
         # Unshelve the instance and check that we have the expected number of
         # volume(s)
diff --git a/tempest/api/compute/volumes/test_attach_volume_negative.py b/tempest/api/compute/volumes/test_attach_volume_negative.py
index c017690..eabb907 100644
--- a/tempest/api/compute/volumes/test_attach_volume_negative.py
+++ b/tempest/api/compute/volumes/test_attach_volume_negative.py
@@ -16,7 +16,6 @@
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -30,8 +29,8 @@
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
 
-    @test.attr(type=['negative'])
-    @test.related_bug('1630783', status_code=500)
+    @decorators.attr(type=['negative'])
+    @decorators.related_bug('1630783', status_code=500)
     @decorators.idempotent_id('a313b5cd-fbd0-49cc-94de-870e99f763c7')
     def test_delete_attached_volume(self):
         server = self.create_test_server(wait_until='ACTIVE')
diff --git a/tempest/api/compute/volumes/test_volume_snapshots.py b/tempest/api/compute/volumes/test_volume_snapshots.py
index 3d5d23b..b8ca81d 100644
--- a/tempest/api/compute/volumes/test_volume_snapshots.py
+++ b/tempest/api/compute/volumes/test_volume_snapshots.py
@@ -13,12 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
-
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 
@@ -27,12 +25,20 @@
 
 class VolumesSnapshotsTestJSON(base.BaseV2ComputeTest):
 
+    # These tests will fail with a 404 starting from microversion 2.36. For
+    # more information, see:
+    # https://developer.openstack.org/api-ref/compute/#volume-extension-os-volumes-os-snapshots-deprecated
+    max_microversion = '2.35'
+
     @classmethod
     def skip_checks(cls):
         super(VolumesSnapshotsTestJSON, cls).skip_checks()
         if not CONF.service_available.cinder:
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
+        if not CONF.volume_feature_enabled.snapshot:
+            skip_msg = ("Cinder volume snapshots are disabled")
+            raise cls.skipException(skip_msg)
 
     @classmethod
     def setup_clients(cls):
@@ -41,8 +47,6 @@
         cls.snapshots_client = cls.snapshots_extensions_client
 
     @decorators.idempotent_id('cd4ec87d-7825-450d-8040-6e2068f2da8f')
-    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
-                          'Cinder volume snapshots are disabled')
     def test_volume_snapshot_create_get_list_delete(self):
         volume = self.create_volume()
         self.addCleanup(self.delete_volume, volume['id'])
@@ -54,9 +58,9 @@
             display_name=s_name)['snapshot']
 
         def delete_snapshot(snapshot_id):
-            waiters.wait_for_snapshot_status(self.snapshots_client,
-                                             snapshot_id,
-                                             'available')
+            waiters.wait_for_volume_resource_status(self.snapshots_client,
+                                                    snapshot_id,
+                                                    'available')
             # Delete snapshot
             self.snapshots_client.delete_snapshot(snapshot_id)
             self.snapshots_client.wait_for_resource_deletion(snapshot_id)
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index 63c247e..d83d49e 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -16,9 +16,8 @@
 from testtools import matchers
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 
@@ -27,6 +26,11 @@
 
 class VolumesGetTestJSON(base.BaseV2ComputeTest):
 
+    # These tests will fail with a 404 starting from microversion 2.36. For
+    # more information, see:
+    # https://developer.openstack.org/api-ref/compute/#volume-extension-os-volumes-os-snapshots-deprecated
+    max_microversion = '2.35'
+
     @classmethod
     def skip_checks(cls):
         super(VolumesGetTestJSON, cls).skip_checks()
@@ -37,7 +41,7 @@
     @classmethod
     def setup_clients(cls):
         super(VolumesGetTestJSON, cls).setup_clients()
-        cls.client = cls.volumes_extensions_client
+        cls.volumes_client = cls.volumes_extensions_client
 
     @decorators.idempotent_id('f10f25eb-9775-4d9d-9cbe-1cf54dae9d5f')
     def test_volume_create_get_delete(self):
@@ -45,21 +49,15 @@
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
         # Create volume
-        volume = self.client.create_volume(size=CONF.volume.volume_size,
-                                           display_name=v_name,
-                                           metadata=metadata)['volume']
-        self.assertIn('id', volume)
-        self.addCleanup(self.delete_volume, volume['id'])
-        self.assertIn('displayName', volume)
+        volume = self.create_volume(size=CONF.volume.volume_size,
+                                    display_name=v_name,
+                                    metadata=metadata)
         self.assertEqual(volume['displayName'], v_name,
                          "The created volume name is not equal "
                          "to the requested name")
-        self.assertIsNotNone(volume['id'],
-                             "Field volume id is empty or not found.")
-        # Wait for Volume status to become ACTIVE
-        waiters.wait_for_volume_status(self.client, volume['id'], 'available')
         # GET Volume
-        fetched_volume = self.client.show_volume(volume['id'])['volume']
+        fetched_volume = self.volumes_client.show_volume(
+            volume['id'])['volume']
         # Verification of details of fetched Volume
         self.assertEqual(v_name,
                          fetched_volume['displayName'],
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index dd9d408..b2aebe7 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -27,6 +27,11 @@
     # If you are running a Devstack environment, ensure that the
     # VOLUME_BACKING_FILE_SIZE is at least 4G in your localrc
 
+    # These tests will fail with a 404 starting from microversion 2.36. For
+    # more information, see:
+    # https://developer.openstack.org/api-ref/compute/#volume-extension-os-volumes-os-snapshots-deprecated
+    max_microversion = '2.35'
+
     @classmethod
     def skip_checks(cls):
         super(VolumesTestJSON, cls).skip_checks()
@@ -44,13 +49,11 @@
         super(VolumesTestJSON, cls).resource_setup()
         # Create 3 Volumes
         cls.volume_list = []
-        cls.volume_id_list = []
         for _ in range(3):
             metadata = {'Type': 'work'}
             volume = cls.create_volume(metadata=metadata)
             volume = cls.client.show_volume(volume['id'])['volume']
             cls.volume_list.append(volume)
-            cls.volume_id_list.append(volume['id'])
 
     @decorators.idempotent_id('bc2dd1a0-15af-48e5-9990-f2e75a48325d')
     def test_volume_list(self):
diff --git a/tempest/api/compute/volumes/test_volumes_negative.py b/tempest/api/compute/volumes/test_volumes_negative.py
index 2ad8631..87f7d8a 100644
--- a/tempest/api/compute/volumes/test_volumes_negative.py
+++ b/tempest/api/compute/volumes/test_volumes_negative.py
@@ -14,17 +14,21 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
 
 class VolumesNegativeTest(base.BaseV2ComputeTest):
 
+    # These tests will fail with a 404 starting from microversion 2.36. For
+    # more information, see:
+    # https://developer.openstack.org/api-ref/compute/#volume-extension-os-volumes-os-snapshots-deprecated
+    max_microversion = '2.35'
+
     @classmethod
     def skip_checks(cls):
         super(VolumesNegativeTest, cls).skip_checks()
@@ -37,7 +41,7 @@
         super(VolumesNegativeTest, cls).setup_clients()
         cls.client = cls.volumes_extensions_client
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c03ea686-905b-41a2-8748-9635154b7c57')
     def test_volume_get_nonexistent_volume_id(self):
         # Negative: Should not be able to get details of nonexistent volume
@@ -46,7 +50,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.show_volume,
                           data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('54a34226-d910-4b00-9ef8-8683e6c55846')
     def test_volume_delete_nonexistent_volume_id(self):
         # Negative: Should not be able to delete nonexistent Volume
@@ -55,7 +59,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.delete_volume,
                           data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5125ae14-152b-40a7-b3c5-eae15e9022ef')
     def test_create_volume_with_invalid_size(self):
         # Negative: Should not be able to create volume with invalid size
@@ -65,7 +69,7 @@
         self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
                           size='#$%', display_name=v_name, metadata=metadata)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('131cb3a1-75cc-4d40-b4c3-1317f64719b0')
     def test_create_volume_without_passing_size(self):
         # Negative: Should not be able to create volume without passing size
@@ -75,7 +79,7 @@
         self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
                           size='', display_name=v_name, metadata=metadata)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8cce995e-0a83-479a-b94d-e1e40b8a09d1')
     def test_create_volume_with_size_zero(self):
         # Negative: Should not be able to create volume with size zero
@@ -84,13 +88,13 @@
         self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
                           size='0', display_name=v_name, metadata=metadata)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('62bab09a-4c03-4617-8cca-8572bc94af9b')
     def test_get_volume_without_passing_volume_id(self):
         # Negative: Should not be able to get volume when empty ID is passed
         self.assertRaises(lib_exc.NotFound, self.client.show_volume, '')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('62972737-124b-4513-b6cf-2f019f178494')
     def test_delete_invalid_volume_id(self):
         # Negative: Should not be able to delete volume when invalid ID is
@@ -99,7 +103,7 @@
                           self.client.delete_volume,
                           data_utils.rand_name('invalid'))
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0d1417c5-4ae8-4c2c-adc5-5f0b864253e5')
     def test_delete_volume_without_passing_volume_id(self):
         # Negative: Should not be able to delete volume when empty ID is passed
diff --git a/tempest/api/identity/admin/v2/test_endpoints.py b/tempest/api/identity/admin/v2/test_endpoints.py
index df55d2f..59fc4d8 100644
--- a/tempest/api/identity/admin/v2/test_endpoints.py
+++ b/tempest/api/identity/admin/v2/test_endpoints.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 
@@ -27,10 +27,10 @@
         s_name = data_utils.rand_name('service')
         s_type = data_utils.rand_name('type')
         s_description = data_utils.rand_name('description')
-        cls.service_data = cls.services_client.create_service(
+        service_data = cls.services_client.create_service(
             name=s_name, type=s_type,
             description=s_description)['OS-KSADM:service']
-        cls.service_id = cls.service_data['id']
+        cls.service_id = service_data['id']
         cls.service_ids.append(cls.service_id)
         # Create endpoints so as to use for LIST and GET test cases
         cls.setup_endpoints = list()
@@ -62,7 +62,7 @@
         # Asserting LIST endpoints
         missing_endpoints =\
             [e for e in self.setup_endpoints if e not in fetched_endpoints]
-        self.assertEqual(0, len(missing_endpoints),
+        self.assertEmpty(missing_endpoints,
                          "Failed to find endpoint %s in fetched list" %
                          ', '.join(str(e) for e in missing_endpoints))
 
diff --git a/tempest/api/identity/admin/v2/test_roles.py b/tempest/api/identity/admin/v2/test_roles.py
index 799b653..124bb5f 100644
--- a/tempest/api/identity/admin/v2/test_roles.py
+++ b/tempest/api/identity/admin/v2/test_roles.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
@@ -54,7 +54,7 @@
         """Return a list of all roles."""
         body = self.roles_client.list_roles()['roles']
         found = [role for role in body if role in self.roles]
-        self.assertTrue(any(found))
+        self.assertNotEmpty(found)
         self.assertEqual(len(found), len(self.roles))
 
     @decorators.idempotent_id('c62d909d-6c21-48c0-ae40-0a0760e6db5e')
@@ -68,13 +68,13 @@
 
         body = self.roles_client.list_roles()['roles']
         found = [role for role in body if role['name'] == role_name]
-        self.assertTrue(any(found))
+        self.assertNotEmpty(found)
 
         body = self.roles_client.delete_role(found[0]['id'])
 
         body = self.roles_client.list_roles()['roles']
         found = [role for role in body if role['name'] == role_name]
-        self.assertFalse(any(found))
+        self.assertEmpty(found)
 
     @decorators.idempotent_id('db6870bd-a6ed-43be-a9b1-2f10a5c9994f')
     def test_get_role_by_id(self):
diff --git a/tempest/api/identity/admin/v2/test_roles_negative.py b/tempest/api/identity/admin/v2/test_roles_negative.py
index b017b44..f3b7494 100644
--- a/tempest/api/identity/admin/v2/test_roles_negative.py
+++ b/tempest/api/identity/admin/v2/test_roles_negative.py
@@ -14,10 +14,9 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class RolesNegativeTestJSON(base.BaseIdentityV2AdminTest):
@@ -28,14 +27,14 @@
         role = self.setup_test_role()
         return (user, tenant, role)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d5d5f1df-f8ca-4de0-b2ef-259c1cc67025')
     def test_list_roles_by_unauthorized_user(self):
         # Non-administrator user should not be able to list roles
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_roles_client.list_roles)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('11a3c7da-df6c-40c2-abc2-badd682edf9f')
     def test_list_roles_request_without_token(self):
         # Request to list roles without a valid token should fail
@@ -44,14 +43,14 @@
         self.assertRaises(lib_exc.Unauthorized, self.roles_client.list_roles)
         self.client.auth_provider.clear_auth()
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c0b89e56-accc-4c73-85f8-9c0f866104c1')
     def test_role_create_blank_name(self):
         # Should not be able to create a role with a blank name
         self.assertRaises(lib_exc.BadRequest, self.roles_client.create_role,
                           name='')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('585c8998-a8a4-4641-a5dd-abef7a8ced00')
     def test_create_role_by_unauthorized_user(self):
         # Non-administrator user should not be able to create role
@@ -60,7 +59,7 @@
                           self.non_admin_roles_client.create_role,
                           name=role_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a7edd17a-e34a-4aab-8bb7-fa6f498645b8')
     def test_create_role_request_without_token(self):
         # Request to create role without a valid token should fail
@@ -71,7 +70,7 @@
                           self.roles_client.create_role, name=role_name)
         self.client.auth_provider.clear_auth()
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c0cde2c8-81c1-4bb0-8fe2-cf615a3547a8')
     def test_role_create_duplicate(self):
         # Role names should be unique
@@ -82,7 +81,7 @@
         self.assertRaises(lib_exc.Conflict, self.roles_client.create_role,
                           name=role_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('15347635-b5b1-4a87-a280-deb2bd6d865e')
     def test_delete_role_by_unauthorized_user(self):
         # Non-administrator user should not be able to delete role
@@ -93,7 +92,7 @@
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_roles_client.delete_role, role_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('44b60b20-70de-4dac-beaf-a3fc2650a16b')
     def test_delete_role_request_without_token(self):
         # Request to delete role without a valid token should fail
@@ -108,7 +107,7 @@
                           role_id)
         self.client.auth_provider.clear_auth()
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('38373691-8551-453a-b074-4260ad8298ef')
     def test_delete_role_non_existent(self):
         # Attempt to delete a non existent role should fail
@@ -116,7 +115,7 @@
         self.assertRaises(lib_exc.NotFound, self.roles_client.delete_role,
                           non_existent_role)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('391df5cf-3ec3-46c9-bbe5-5cb58dd4dc41')
     def test_assign_user_role_by_unauthorized_user(self):
         # Non-administrator user should not be authorized to
@@ -127,7 +126,7 @@
             self.non_admin_roles_client.create_user_role_on_project,
             tenant['id'], user['id'], role['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f0d2683c-5603-4aee-95d7-21420e87cfd8')
     def test_assign_user_role_request_without_token(self):
         # Request to assign a role to a user without a valid token
@@ -140,27 +139,27 @@
             user['id'], role['id'])
         self.client.auth_provider.clear_auth()
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('99b297f6-2b5d-47c7-97a9-8b6bb4f91042')
     def test_assign_user_role_for_non_existent_role(self):
         # Attempt to assign a non existent role to user should fail
-        (user, tenant, role) = self._get_role_params()
+        (user, tenant, _) = self._get_role_params()
         non_existent_role = data_utils.rand_uuid_hex()
         self.assertRaises(lib_exc.NotFound,
                           self.roles_client.create_user_role_on_project,
                           tenant['id'], user['id'], non_existent_role)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b2285aaa-9e76-4704-93a9-7a8acd0a6c8f')
     def test_assign_user_role_for_non_existent_tenant(self):
         # Attempt to assign a role on a non existent tenant should fail
-        (user, tenant, role) = self._get_role_params()
+        (user, _, role) = self._get_role_params()
         non_existent_tenant = data_utils.rand_uuid_hex()
         self.assertRaises(lib_exc.NotFound,
                           self.roles_client.create_user_role_on_project,
                           non_existent_tenant, user['id'], role['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5c3132cd-c4c8-4402-b5ea-71eb44e97793')
     def test_assign_duplicate_user_role(self):
         # Duplicate user role should not get assigned
@@ -172,7 +171,7 @@
                           self.roles_client.create_user_role_on_project,
                           tenant['id'], user['id'], role['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d0537987-0977-448f-a435-904c15de7298')
     def test_remove_user_role_by_unauthorized_user(self):
         # Non-administrator user should not be authorized to
@@ -186,7 +185,7 @@
             self.non_admin_roles_client.delete_role_from_user_on_project,
             tenant['id'], user['id'], role['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.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
@@ -201,7 +200,7 @@
                           tenant['id'], user['id'], role['id'])
         self.client.auth_provider.clear_auth()
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ab32d759-cd16-41f1-a86e-44405fa9f6d2')
     def test_remove_user_role_non_existent_role(self):
         # Attempt to delete a non existent role from a user should fail
@@ -214,7 +213,7 @@
                           self.roles_client.delete_role_from_user_on_project,
                           tenant['id'], user['id'], non_existent_role)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('67a679ec-03dd-4551-bbfc-d1c93284f023')
     def test_remove_user_role_non_existent_tenant(self):
         # Attempt to remove a role from a non existent tenant should fail
@@ -227,7 +226,7 @@
                           self.roles_client.delete_role_from_user_on_project,
                           non_existent_tenant, user['id'], role['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7391ab4c-06f3-477a-a64a-c8e55ce89837')
     def test_list_user_roles_by_unauthorized_user(self):
         # Non-administrator user should not be authorized to list
@@ -241,11 +240,11 @@
             self.non_admin_roles_client.list_user_roles_on_project,
             tenant['id'], user['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('682adfb2-fd5f-4b0a-a9ca-322e9bebb907')
     def test_list_user_roles_request_without_token(self):
         # Request to list user's roles without a valid token should fail
-        (user, tenant, role) = self._get_role_params()
+        (user, tenant, _) = self._get_role_params()
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
         try:
diff --git a/tempest/api/identity/admin/v2/test_services.py b/tempest/api/identity/admin/v2/test_services.py
index 3b0ddbb..e2ed5ef 100644
--- a/tempest/api/identity/admin/v2/test_services.py
+++ b/tempest/api/identity/admin/v2/test_services.py
@@ -14,10 +14,9 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ServicesTestJSON(base.BaseIdentityV2AdminTest):
@@ -42,7 +41,6 @@
         self.assertIsNotNone(service_data['id'])
         self.addCleanup(self._del_service, service_data['id'])
         # Verifying response body of create service
-        self.assertIn('id', service_data)
         self.assertIn('name', service_data)
         self.assertEqual(name, service_data['name'])
         self.assertIn('type', service_data)
@@ -78,7 +76,7 @@
         self.assertIn('type', service)
         self.assertEqual(s_type, service['type'])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('34ea6489-012d-4a86-9038-1287cadd5eca')
     def test_list_services(self):
         # Create, List, Verify and Delete Services
diff --git a/tempest/api/identity/admin/v2/test_tenant_negative.py b/tempest/api/identity/admin/v2/test_tenant_negative.py
index e8c32b9..49bb949 100644
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ b/tempest/api/identity/admin/v2/test_tenant_negative.py
@@ -14,22 +14,21 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class TenantsNegativeTestJSON(base.BaseIdentityV2AdminTest):
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ca9bb202-63dd-4240-8a07-8ef9c19c04bb')
     def test_list_tenants_by_unauthorized_user(self):
         # Non-administrator user should not be able to list tenants
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_tenants_client.list_tenants)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('df33926c-1c96-4d8d-a762-79cc6b0c3cf4')
     def test_list_tenant_request_without_token(self):
         # Request to list tenants without a valid token should fail
@@ -39,24 +38,20 @@
                           self.tenants_client.list_tenants)
         self.client.auth_provider.clear_auth()
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('162ba316-f18b-4987-8c0c-fd9140cd63ed')
     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(name=tenant_name)['tenant']
-        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant()
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_tenants_client.delete_tenant,
                           tenant['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e450db62-2e9d-418f-893a-54772d6386b1')
     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(name=tenant_name)['tenant']
-        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant()
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
         self.assertRaises(lib_exc.Unauthorized,
@@ -64,26 +59,23 @@
                           tenant['id'])
         self.client.auth_provider.clear_auth()
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9c9a2aed-6e3c-467a-8f5c-89da9d1b516b')
     def test_delete_non_existent_tenant(self):
         # Attempt to delete a non existent tenant should fail
         self.assertRaises(lib_exc.NotFound, self.tenants_client.delete_tenant,
                           data_utils.rand_uuid_hex())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('af16f44b-a849-46cb-9f13-a751c388f739')
     def test_tenant_create_duplicate(self):
         # Tenant names should be unique
         tenant_name = data_utils.rand_name(name='tenant')
-        body = self.tenants_client.create_tenant(name=tenant_name)['tenant']
-        tenant1_id = body.get('id')
-
-        self.addCleanup(self.tenants_client.delete_tenant, tenant1_id)
+        self.setup_test_tenant(name=tenant_name)
         self.assertRaises(lib_exc.Conflict, self.tenants_client.create_tenant,
                           name=tenant_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d26b278a-6389-4702-8d6e-5980d80137e0')
     def test_create_tenant_by_unauthorized_user(self):
         # Non-administrator user should not be authorized to create a tenant
@@ -92,7 +84,7 @@
                           self.non_admin_tenants_client.create_tenant,
                           name=tenant_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a3ee9d7e-6920-4dd5-9321-d4b2b7f0a638')
     def test_create_tenant_request_without_token(self):
         # Create tenant request without a token should not be authorized
@@ -104,7 +96,7 @@
                           name=tenant_name)
         self.client.auth_provider.clear_auth()
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5a2e4ca9-b0c0-486c-9c48-64a94fba2395')
     def test_create_tenant_with_empty_name(self):
         # Tenant name should not be empty
@@ -112,7 +104,7 @@
                           self.tenants_client.create_tenant,
                           name='')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('2ff18d1e-dfe3-4359-9dc3-abf582c196b9')
     def test_create_tenants_name_length_over_64(self):
         # Tenant name length should not be greater than 64 characters
@@ -121,31 +113,27 @@
                           self.tenants_client.create_tenant,
                           name=tenant_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('bd20dc2a-9557-4db7-b755-f48d952ad706')
     def test_update_non_existent_tenant(self):
         # Attempt to update a non existent tenant should fail
         self.assertRaises(lib_exc.NotFound, self.tenants_client.update_tenant,
                           data_utils.rand_uuid_hex())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('41704dc5-c5f7-4f79-abfa-76e6fedc570b')
     def test_tenant_update_by_unauthorized_user(self):
         # Non-administrator user should not be able to update a tenant
-        tenant_name = data_utils.rand_name(name='tenant')
-        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
-        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant()
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_tenants_client.update_tenant,
                           tenant['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7a421573-72c7-4c22-a98e-ce539219c657')
     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(name=tenant_name)['tenant']
-        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant()
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
         self.assertRaises(lib_exc.Unauthorized,
diff --git a/tempest/api/identity/admin/v2/test_tenants.py b/tempest/api/identity/admin/v2/test_tenants.py
index eb51b5a..0f955bf 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -14,8 +14,7 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 
@@ -26,12 +25,7 @@
         # Create several tenants and delete them
         tenants = []
         for _ in range(3):
-            tenant_name = data_utils.rand_name(name='tenant-new')
-            tenant = self.tenants_client.create_tenant(
-                name=tenant_name)['tenant']
-            # Add the tenant to the cleanup list
-            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                            self.tenants_client.delete_tenant, tenant['id'])
+            tenant = self.setup_test_tenant()
             tenants.append(tenant)
         tenant_ids = [tn['id'] for tn in tenants]
         body = self.tenants_client.list_tenants()['tenants']
@@ -43,19 +37,13 @@
 
         body = self.tenants_client.list_tenants()['tenants']
         found = [tenant for tenant in body if tenant['id'] in tenant_ids]
-        self.assertFalse(any(found), 'Tenants failed to delete')
+        self.assertEmpty(found, 'Tenants failed to delete')
 
     @decorators.idempotent_id('d25e9f24-1310-4d29-b61b-d91299c21d6d')
     def test_tenant_create_with_description(self):
         # 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(name=tenant_name,
-                                                 description=tenant_desc)
-        tenant = body['tenant']
-        # Add the tenant to the cleanup list
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant(description=tenant_desc)
         tenant_id = tenant['id']
         desc1 = tenant['description']
         self.assertEqual(desc1, tenant_desc, 'Description should have '
@@ -69,13 +57,7 @@
     @decorators.idempotent_id('670bdddc-1cd7-41c7-b8e2-751cfb67df50')
     def test_tenant_create_enabled(self):
         # Create a tenant that is enabled
-        tenant_name = data_utils.rand_name(name='tenant')
-        body = self.tenants_client.create_tenant(name=tenant_name,
-                                                 enabled=True)
-        tenant = body['tenant']
-        # Add the tenant to the cleanup list
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant(enabled=True)
         tenant_id = tenant['id']
         en1 = tenant['enabled']
         self.assertTrue(en1, 'Enable should be True in response')
@@ -87,13 +69,7 @@
     @decorators.idempotent_id('3be22093-b30f-499d-b772-38340e5e16fb')
     def test_tenant_create_not_enabled(self):
         # Create a tenant that is not enabled
-        tenant_name = data_utils.rand_name(name='tenant')
-        body = self.tenants_client.create_tenant(name=tenant_name,
-                                                 enabled=False)
-        tenant = body['tenant']
-        # Add the tenant to the cleanup list
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant(enabled=False)
         tenant_id = tenant['id']
         en1 = tenant['enabled']
         self.assertEqual('false', str(en1).lower(),
@@ -108,14 +84,9 @@
     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(name=t_name1)['tenant']
-        tenant = body
-        # Add the tenant to the cleanup list
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant, tenant['id'])
-
-        t_id = body['id']
-        resp1_name = body['name']
+        tenant = self.setup_test_tenant(name=t_name1)
+        t_id = tenant['id']
+        resp1_name = tenant['name']
 
         t_name2 = data_utils.rand_name(name='tenant2')
         body = self.tenants_client.update_tenant(t_id, name=t_name2)['tenant']
@@ -134,15 +105,8 @@
     @decorators.idempotent_id('859fcfe1-3a03-41ef-86f9-b19a47d1cd87')
     def test_tenant_update_desc(self):
         # Update description attribute of a tenant
-        t_name = data_utils.rand_name(name='tenant')
         t_desc = data_utils.rand_name(name='desc')
-        body = self.tenants_client.create_tenant(name=t_name,
-                                                 description=t_desc)
-        tenant = body['tenant']
-        # Add the tenant to the cleanup list
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant, tenant['id'])
-
+        tenant = self.setup_test_tenant(description=t_desc)
         t_id = tenant['id']
         resp1_desc = tenant['description']
 
@@ -164,14 +128,8 @@
     @decorators.idempotent_id('8fc8981f-f12d-4c66-9972-2bdcf2bc2e1a')
     def test_tenant_update_enable(self):
         # Update the enabled attribute of a tenant
-        t_name = data_utils.rand_name(name='tenant')
         t_en = False
-        body = self.tenants_client.create_tenant(name=t_name, enabled=t_en)
-        tenant = body['tenant']
-        # Add the tenant to the cleanup list
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant, tenant['id'])
-
+        tenant = self.setup_test_tenant(enabled=t_en)
         t_id = tenant['id']
         resp1_en = tenant['enabled']
 
diff --git a/tempest/api/identity/admin/v2/test_tokens.py b/tempest/api/identity/admin/v2/test_tokens.py
index 7b5e01e..6b30d23 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -14,29 +14,28 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+CONF = config.CONF
 
 
 class TokensTestJSON(base.BaseIdentityV2AdminTest):
 
     @decorators.idempotent_id('453ad4d5-e486-4b2f-be72-cffc8149e586')
-    def test_create_get_delete_token(self):
+    def test_create_check_get_delete_token(self):
         # get a token by username and password
         user_name = data_utils.rand_name(name='user')
         user_password = data_utils.rand_password()
         # first:create a tenant
-        tenant_name = data_utils.rand_name(name='tenant')
-        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
-        # Delete the tenant at the end of the test
-        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant()
         # second:create a user
-        user = self.users_client.create_user(name=user_name,
-                                             password=user_password,
-                                             tenantId=tenant['id'],
-                                             email='')['user']
-        # Delete the user at the end of the test
-        self.addCleanup(self.users_client.delete_user, user['id'])
+        user = self.create_test_user(name=user_name,
+                                     password=user_password,
+                                     tenantId=tenant['id'],
+                                     email='')
         # then get a token for the user
         body = self.token_client.auth(user_name,
                                       user_password,
@@ -45,6 +44,7 @@
                          tenant['name'])
         # Perform GET Token
         token_id = body['token']['id']
+        self.client.check_token_existence(token_id)
         token_details = self.client.show_token(token_id)['access']
         self.assertEqual(token_id, token_details['token']['id'])
         self.assertEqual(user['id'], token_details['user']['id'])
@@ -53,6 +53,9 @@
                          token_details['token']['tenant']['name'])
         # then delete the token
         self.client.delete_token(token_id)
+        self.assertRaises(lib_exc.NotFound,
+                          self.client.check_token_existence,
+                          token_id)
 
     @decorators.idempotent_id('25ba82ee-8a32-4ceb-8f50-8b8c71e8765e')
     def test_rescope_token(self):
@@ -65,32 +68,20 @@
         user_name = data_utils.rand_name(name='user')
         user_password = data_utils.rand_password()
         tenant_id = None  # No default tenant so will get unscoped token.
-        email = ''
-        user = self.users_client.create_user(name=user_name,
-                                             password=user_password,
-                                             tenantId=tenant_id,
-                                             email=email)['user']
-        # Delete the user at the end of the test
-        self.addCleanup(self.users_client.delete_user, user['id'])
+        user = self.create_test_user(name=user_name,
+                                     password=user_password,
+                                     tenantId=tenant_id,
+                                     email='')
 
         # Create a couple tenants.
         tenant1_name = data_utils.rand_name(name='tenant')
-        tenant1 = self.tenants_client.create_tenant(
-            name=tenant1_name)['tenant']
-        # Delete the tenant at the end of the test
-        self.addCleanup(self.tenants_client.delete_tenant, tenant1['id'])
+        tenant1 = self.setup_test_tenant(name=tenant1_name)
 
         tenant2_name = data_utils.rand_name(name='tenant')
-        tenant2 = self.tenants_client.create_tenant(
-            name=tenant2_name)['tenant']
-        # Delete the tenant at the end of the test
-        self.addCleanup(self.tenants_client.delete_tenant, tenant2['id'])
+        tenant2 = self.setup_test_tenant(name=tenant2_name)
 
         # Create a role
-        role_name = data_utils.rand_name(name='role')
-        role = self.roles_client.create_role(name=role_name)['role']
-        # Delete the role at the end of the test
-        self.addCleanup(self.roles_client.delete_role, role['id'])
+        role = self.setup_test_role()
 
         # Grant the user the role on the tenants.
         self.roles_client.create_user_role_on_project(tenant1['id'],
@@ -118,3 +109,25 @@
         # Use the unscoped token to get a token scoped to tenant2
         body = self.token_client.auth_token(token_id,
                                             tenant=tenant2_name)
+
+    @decorators.idempotent_id('ca3ea6f7-ed08-4a61-adbd-96906456ad31')
+    def test_list_endpoints_for_token(self):
+        # get a token for the user
+        creds = self.os_primary.credentials
+        username = creds.username
+        password = creds.password
+        tenant_name = creds.tenant_name
+        token = self.token_client.auth(username,
+                                       password,
+                                       tenant_name)['token']
+        endpoints = self.client.list_endpoints_for_token(
+            token['id'])['endpoints']
+        self.assertIsInstance(endpoints, list)
+        # Store list of service names
+        service_names = [e['name'] for e in endpoints]
+        # Get the list of available services.
+        available_services = [s[0] for s in list(
+            CONF.service_available.items()) if s[1] is True]
+        # Verify that all available services are present.
+        for service in available_services:
+            self.assertIn(service, service_names)
diff --git a/tempest/api/identity/admin/v2/test_tokens_negative.py b/tempest/api/identity/admin/v2/test_tokens_negative.py
new file mode 100644
index 0000000..eb3e365
--- /dev/null
+++ b/tempest/api/identity/admin/v2/test_tokens_negative.py
@@ -0,0 +1,38 @@
+# Copyright 2017 AT&T Corporation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.identity import base
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+
+class TokensAdminTestNegative(base.BaseIdentityV2AdminTest):
+
+    credentials = ['primary', 'admin', 'alt']
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('a0a0a600-4292-4364-99c5-922c834fdf05')
+    def test_check_token_existence_negative(self):
+        creds = self.os_primary.credentials
+        creds_alt = self.os_alt.credentials
+        username = creds.username
+        password = creds.password
+        tenant_name = creds.tenant_name
+        alt_tenant_name = creds_alt.tenant_name
+        body = self.token_client.auth(username, password, tenant_name)
+        self.assertRaises(lib_exc.Unauthorized,
+                          self.client.check_token_existence,
+                          body['token']['id'],
+                          belongsTo=alt_tenant_name)
diff --git a/tempest/api/identity/admin/v2/test_users.py b/tempest/api/identity/admin/v2/test_users.py
index 5889813..0d98af5 100644
--- a/tempest/api/identity/admin/v2/test_users.py
+++ b/tempest/api/identity/admin/v2/test_users.py
@@ -18,10 +18,8 @@
 from testtools import matchers
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class UsersTestJSON(base.BaseIdentityV2AdminTest):
@@ -30,20 +28,14 @@
     def resource_setup(cls):
         super(UsersTestJSON, cls).resource_setup()
         cls.alt_user = data_utils.rand_name('test_user')
-        cls.alt_password = data_utils.rand_password()
         cls.alt_email = cls.alt_user + '@testmail.tm'
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('2d55a71e-da1d-4b43-9c03-d269fd93d905')
     def test_create_user(self):
         # Create a user
         tenant = self.setup_test_tenant()
-        user = self.users_client.create_user(name=self.alt_user,
-                                             password=self.alt_password,
-                                             tenantId=tenant['id'],
-                                             email=self.alt_email)['user']
-        # Delete the User at the end of the test
-        self.addCleanup(self.users_client.delete_user, user['id'])
+        user = self.create_test_user(name=self.alt_user, tenantId=tenant['id'])
         self.assertEqual(self.alt_user, user['name'])
 
     @decorators.idempotent_id('89d9fdb8-15c2-4304-a429-48715d0af33d')
@@ -51,13 +43,10 @@
         # Create a user with enabled : False
         tenant = self.setup_test_tenant()
         name = data_utils.rand_name('test_user')
-        user = self.users_client.create_user(name=name,
-                                             password=self.alt_password,
-                                             tenantId=tenant['id'],
-                                             email=self.alt_email,
-                                             enabled=False)['user']
-        # Delete the User at the end of the test
-        self.addCleanup(self.users_client.delete_user, user['id'])
+        user = self.create_test_user(name=name,
+                                     tenantId=tenant['id'],
+                                     email=self.alt_email,
+                                     enabled=False)
         self.assertEqual(name, user['name'])
         self.assertEqual(False, user['enabled'])
         self.assertEqual(self.alt_email, user['email'])
@@ -65,14 +54,9 @@
     @decorators.idempotent_id('39d05857-e8a5-4ed4-ba83-0b52d3ab97ee')
     def test_update_user(self):
         # Test case to check if updating of user attributes is successful.
-        test_user = data_utils.rand_name('test_user')
         tenant = self.setup_test_tenant()
-        user = self.users_client.create_user(name=test_user,
-                                             password=self.alt_password,
-                                             tenantId=tenant['id'],
-                                             email=self.alt_email)['user']
-        # Delete the User at the end of this method
-        self.addCleanup(self.users_client.delete_user, user['id'])
+        user = self.create_test_user(tenantId=tenant['id'])
+
         # Updating user details with new values
         u_name2 = data_utils.rand_name('user2')
         u_email2 = u_name2 + '@testmail.tm'
@@ -92,15 +76,8 @@
     @decorators.idempotent_id('29ed26f4-a74e-4425-9a85-fdb49fa269d2')
     def test_delete_user(self):
         # Delete a user
-        test_user = data_utils.rand_name('test_user')
         tenant = self.setup_test_tenant()
-        user = self.users_client.create_user(name=test_user,
-                                             password=self.alt_password,
-                                             tenantId=tenant['id'],
-                                             email=self.alt_email)['user']
-        # Delete the User at the end of the test
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.users_client.delete_user, user['id'])
+        user = self.create_test_user(tenantId=tenant['id'])
         self.users_client.delete_user(user['id'])
 
     @decorators.idempotent_id('aca696c3-d645-4f45-b728-63646045beb1')
@@ -152,24 +129,10 @@
         tenant = self.setup_test_tenant()
         user_ids = list()
         fetched_user_ids = list()
-        password1 = data_utils.rand_password()
-        alt_tenant_user1 = data_utils.rand_name('tenant_user1')
-        user1 = self.users_client.create_user(name=alt_tenant_user1,
-                                              password=password1,
-                                              tenantId=tenant['id'],
-                                              email='user1@123')['user']
+        user1 = self.create_test_user(tenantId=tenant['id'])
         user_ids.append(user1['id'])
-        # Delete the User at the end of the test
-        self.addCleanup(self.users_client.delete_user, user1['id'])
-        password2 = data_utils.rand_password()
-        alt_tenant_user2 = data_utils.rand_name('tenant_user2')
-        user2 = self.users_client.create_user(name=alt_tenant_user2,
-                                              password=password2,
-                                              tenantId=tenant['id'],
-                                              email='user2@123')['user']
+        user2 = self.create_test_user(tenantId=tenant['id'])
         user_ids.append(user2['id'])
-        # Delete the User at the end of the test
-        self.addCleanup(self.users_client.delete_user, user2['id'])
         # List of users for the respective tenant ID
         body = (self.tenants_client.list_tenant_users(tenant['id'])
                 ['users'])
@@ -178,7 +141,7 @@
         # verifying the user Id in the list
         missing_users =\
             [user for user in user_ids if user not in fetched_user_ids]
-        self.assertEqual(0, len(missing_users),
+        self.assertEmpty(missing_users,
                          "Failed to find user %s in fetched list" %
                          ', '.join(m_user for m_user in missing_users))
 
@@ -195,16 +158,8 @@
         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(
-            name=alt_user2,
-            password=alt_password2,
-            tenantId=tenant['id'],
-            email='user2@123')['user']
+        second_user = self.create_test_user(tenantId=tenant['id'])
         user_ids.append(second_user['id'])
-        # Delete the User at the end of the test
-        self.addCleanup(self.users_client.delete_user, second_user['id'])
         role = self.roles_client.create_user_role_on_project(
             tenant['id'], second_user['id'], role['id'])['role']
         # List of users with roles for the respective tenant ID
@@ -214,7 +169,7 @@
         # verifying the user Id in the list
         missing_users = [missing_user for missing_user in user_ids
                          if missing_user not in fetched_user_ids]
-        self.assertEqual(0, len(missing_users),
+        self.assertEmpty(missing_users,
                          "Failed to find user %s in fetched list" %
                          ', '.join(m_user for m_user in missing_users))
 
diff --git a/tempest/api/identity/admin/v2/test_users_negative.py b/tempest/api/identity/admin/v2/test_users_negative.py
index 49fda4e..4f47e41 100644
--- a/tempest/api/identity/admin/v2/test_users_negative.py
+++ b/tempest/api/identity/admin/v2/test_users_negative.py
@@ -14,10 +14,9 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class UsersNegativeTestJSON(base.BaseIdentityV2AdminTest):
@@ -29,7 +28,7 @@
         cls.alt_password = data_utils.rand_password()
         cls.alt_email = cls.alt_user + '@testmail.tm'
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('60a1f5fa-5744-4cdf-82bf-60b7de2d29a4')
     def test_create_user_by_unauthorized_user(self):
         # Non-administrator should not be authorized to create a user
@@ -40,7 +39,7 @@
                           tenantId=tenant['id'],
                           email=self.alt_email)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d80d0c2f-4514-4d1e-806d-0930dfc5a187')
     def test_create_user_with_empty_name(self):
         # User with an empty name should not be created
@@ -50,7 +49,7 @@
                           tenantId=tenant['id'],
                           email=self.alt_email)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7704b4f3-3b75-4b82-87cc-931d41c8f780')
     def test_create_user_with_name_length_over_255(self):
         # Length of user name filed should be restricted to 255 characters
@@ -60,7 +59,7 @@
                           tenantId=tenant['id'],
                           email=self.alt_email)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('57ae8558-120c-4723-9308-3751474e7ecf')
     def test_create_user_with_duplicate_name(self):
         # Duplicate user should not be created
@@ -73,7 +72,7 @@
                           tenantId=tenant['id'],
                           email=user['email'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.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
@@ -83,7 +82,7 @@
                           tenantId='49ffgg99999',
                           email=self.alt_email)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('55bbb103-d1ae-437b-989b-bcdf8175c1f4')
     def test_create_user_request_without_a_token(self):
         # Request to create a user without a valid token should fail
@@ -101,7 +100,7 @@
                           tenantId=tenant['id'],
                           email=self.alt_email)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('23a2f3da-4a1a-41da-abdd-632328a861ad')
     def test_create_user_with_enabled_non_bool(self):
         # Attempt to create a user with valid enabled para should fail
@@ -112,7 +111,7 @@
                           tenantId=tenant['id'],
                           email=self.alt_email, enabled=3)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('3d07e294-27a0-4144-b780-a2a1bf6fee19')
     def test_update_user_for_non_existent_user(self):
         # Attempt to update a user non-existent user should fail
@@ -121,7 +120,7 @@
         self.assertRaises(lib_exc.NotFound, self.users_client.update_user,
                           non_existent_id, name=user_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('3cc2a64b-83aa-4b02-88f0-d6ab737c4466')
     def test_update_user_request_without_a_token(self):
         # Request to update a user without a valid token should fail
@@ -137,7 +136,7 @@
         self.assertRaises(lib_exc.Unauthorized, self.users_client.update_user,
                           self.alt_user)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('424868d5-18a7-43e1-8903-a64f95ee3aac')
     def test_update_user_by_unauthorized_user(self):
         # Non-administrator should not be authorized to update user
@@ -145,7 +144,7 @@
                           self.non_admin_users_client.update_user,
                           self.alt_user)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d45195d5-33ed-41b9-a452-7d0d6a00f6e9')
     def test_delete_users_by_unauthorized_user(self):
         # Non-administrator user should not be authorized to delete a user
@@ -154,14 +153,14 @@
                           self.non_admin_users_client.delete_user,
                           user['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7cc82f7e-9998-4f89-abae-23df36495867')
     def test_delete_non_existent_user(self):
         # Attempt to delete a non-existent user should fail
         self.assertRaises(lib_exc.NotFound, self.users_client.delete_user,
                           'junk12345123')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('57fe1df8-0aa7-46c0-ae9f-c2e785c7504a')
     def test_delete_user_request_without_a_token(self):
         # Request to delete a user without a valid token should fail
@@ -177,7 +176,7 @@
         self.assertRaises(lib_exc.Unauthorized, self.users_client.delete_user,
                           self.alt_user)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('593a4981-f6d4-460a-99a1-57a78bf20829')
     def test_authentication_for_disabled_user(self):
         # Disabled user's token should not get authenticated
@@ -190,7 +189,7 @@
                           password,
                           tenant['name'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('440a7a8d-9328-4b7b-83e0-d717010495e4')
     def test_authentication_when_tenant_is_disabled(self):
         # User's token for a disabled tenant should not be authenticated
@@ -203,7 +202,7 @@
                           password,
                           tenant['name'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('921f1ad6-7907-40b8-853f-637e7ee52178')
     def test_authentication_with_invalid_tenant(self):
         # User's token for an invalid tenant should not be authenticated
@@ -214,7 +213,7 @@
                           password,
                           'junktenant1234')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('bde9aecd-3b1c-4079-858f-beb5deaa5b5e')
     def test_authentication_with_invalid_username(self):
         # Non-existent user's token should not get authenticated
@@ -224,7 +223,7 @@
         self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
                           'junkuser123', password, tenant['name'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d5308b33-3574-43c3-8d87-1c090c5e1eca')
     def test_authentication_with_invalid_password(self):
         # User's token with invalid password should not be authenticated
@@ -233,14 +232,14 @@
         self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
                           user['name'], 'junkpass1234', tenant['name'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('284192ce-fb7c-4909-a63b-9a502e0ddd11')
     def test_get_users_by_unauthorized_user(self):
         # Non-administrator user should not be authorized to get user list
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_users_client.list_users)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a73591ec-1903-4ffe-be42-282b39fefc9d')
     def test_get_users_request_without_token(self):
         # Request to get list of users without a valid token should fail
@@ -252,7 +251,7 @@
 
         self.assertRaises(lib_exc.Unauthorized, self.users_client.list_users)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f5d39046-fc5f-425c-b29e-bac2632da28e')
     def test_list_users_with_invalid_tenant(self):
         # Should not be able to return a list of all
diff --git a/tempest/api/identity/admin/v3/test_credentials.py b/tempest/api/identity/admin/v3/test_credentials.py
index e5a218d..15b2008 100644
--- a/tempest/api/identity/admin/v3/test_credentials.py
+++ b/tempest/api/identity/admin/v3/test_credentials.py
@@ -15,9 +15,8 @@
 from oslo_serialization import jsonutils as json
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class CredentialsTestJSON(base.BaseIdentityV3AdminTest):
@@ -52,7 +51,7 @@
     def _delete_credential(self, cred_id):
         self.creds_client.delete_credential(cred_id)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('7cd59bf9-bda4-4c72-9467-d21cab278355')
     def test_credentials_create_get_update_delete(self):
         blob = '{"access": "%s", "secret": "%s"}' % (
@@ -108,6 +107,6 @@
             fetched_cred_ids.append(i['id'])
         missing_creds = [c for c in created_cred_ids
                          if c not in fetched_cred_ids]
-        self.assertEqual(0, len(missing_creds),
+        self.assertEmpty(missing_creds,
                          "Failed to find cred %s in fetched list" %
                          ', '.join(m_cred for m_cred in missing_creds))
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 110695d..302a0e5 100644
--- a/tempest/api/identity/admin/v3/test_default_project_id.py
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -11,9 +11,9 @@
 #    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.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -42,13 +42,10 @@
         self.addCleanup(self._delete_domain, dom_id)
 
         # create a project in the domain
-        proj_name = data_utils.rand_name('proj')
-        proj_body = self.projects_client.create_project(
-            proj_name, domain_id=dom_id)['project']
+        proj_body = self.setup_test_project(domain_id=dom_id)
         proj_id = proj_body['id']
-        self.addCleanup(self.projects_client.delete_project, proj_id)
         self.assertEqual(proj_body['domain_id'], dom_id,
-                         "project " + proj_name +
+                         "project " + proj_body['name'] +
                          "doesn't have domain id " + dom_id)
 
         # create a user in the domain, with the previous project as his
@@ -82,7 +79,7 @@
         admin_client = clients.Manager(credentials=creds)
 
         # verify the user's token and see that it is scoped to the project
-        token, auth_data = admin_client.auth_provider.get_auth()
+        token, _ = admin_client.auth_provider.get_auth()
         result = admin_client.identity_v3_client.show_token(token)['token']
         self.assertEqual(result['project']['domain']['id'], dom_id)
         self.assertEqual(result['project']['id'], proj_id)
diff --git a/tempest/api/identity/admin/v3/test_domain_configuration.py b/tempest/api/identity/admin/v3/test_domain_configuration.py
new file mode 100644
index 0000000..f731697
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_domain_configuration.py
@@ -0,0 +1,184 @@
+# Copyright 2017 AT&T Corporation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+
+class DomainConfigurationTestJSON(base.BaseIdentityV3AdminTest):
+
+    custom_config = {
+        "identity": {
+            "driver": "ldap"
+        },
+        "ldap": {
+            "url": "ldap://myldap.com:389/",
+            "user_tree_dn": "ou=Users,dc=my_new_root,dc=org"
+        }
+    }
+
+    @classmethod
+    def setup_clients(cls):
+        super(DomainConfigurationTestJSON, cls).setup_clients()
+        cls.client = cls.domain_config_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(DomainConfigurationTestJSON, cls).resource_setup()
+        cls.group = cls.groups_client.create_group(
+            name=data_utils.rand_name('group'),
+            description=data_utils.rand_name('group-desc'))['group']
+
+    @classmethod
+    def resource_cleanup(cls):
+        cls.groups_client.delete_group(cls.group['id'])
+        super(DomainConfigurationTestJSON, cls).resource_cleanup()
+
+    def _create_domain_and_config(self, config):
+        domain = self.setup_test_domain()
+        config = self.client.create_domain_config(domain['id'], **config)[
+            'config']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_domain_config, domain['id'])
+        return domain, config
+
+    @decorators.idempotent_id('11a02bf0-6f94-4380-b3b0-c8dc18fc0d22')
+    def test_show_default_group_config_and_options(self):
+        # The API supports only the identity and ldap groups. For the ldap
+        # group, a valid value is url or user_tree_dn. For the identity group,
+        # a valid value is driver.
+
+        # Check that the default config has the identity and ldap groups.
+        config = self.client.show_default_config_settings()['config']
+        self.assertIsInstance(config, dict)
+        self.assertIn('identity', config)
+        self.assertIn('ldap', config)
+
+        # Check that the identity group is correct.
+        identity_config = self.client.show_default_group_config('identity')[
+            'config']
+
+        self.assertIsInstance(identity_config, dict)
+        self.assertIn('identity', identity_config)
+        self.assertIn('driver', identity_config['identity'])
+        self.assertIn('list_limit', identity_config['identity'])
+
+        # Show each option for the default domain and identity group.
+        for config_opt_name in ['driver', 'list_limit']:
+            retrieved_config_opt = self.client.show_default_group_option(
+                'identity', config_opt_name)['config']
+            self.assertIn(config_opt_name, retrieved_config_opt)
+
+        # Check that the ldap group is correct.
+        ldap_config = self.client.show_default_group_config('ldap')['config']
+
+        self.assertIsInstance(ldap_config, dict)
+        self.assertIn('ldap', ldap_config)
+
+        # Several valid options exist for ldap group.
+        valid_options = ldap_config['ldap'].keys()
+
+        # Show each option for the default domain and ldap group.
+        for config_opt_name in valid_options:
+            retrieved_config_opt = self.client.show_default_group_option(
+                'ldap', config_opt_name)['config']
+            self.assertIn(config_opt_name, retrieved_config_opt)
+
+    @decorators.idempotent_id('9e3ff13c-f597-4f01-9377-d6c06c2a1477')
+    def test_create_domain_config_and_show_config_groups_and_options(self):
+        domain, created_config = self._create_domain_and_config(
+            self.custom_config)
+
+        # Check that the entire configuration is correct.
+        self.assertEqual(self.custom_config, created_config)
+
+        # Check that each configuration group is correct.
+        for group_name in self.custom_config.keys():
+            group_cfg = self.client.show_domain_group_config(
+                domain['id'], group_name)['config']
+            self.assertIn(group_name, group_cfg)
+            self.assertEqual(self.custom_config[group_name],
+                             group_cfg[group_name])
+
+            # Check that each configuration option is correct.
+            for opt_name in self.custom_config[group_name].keys():
+                group_opt = self.client.show_domain_group_option_config(
+                    domain['id'], group_name, opt_name)['config']
+                self.assertIn(opt_name, group_opt)
+                self.assertEqual(self.custom_config[group_name][opt_name],
+                                 group_opt[opt_name])
+
+    @decorators.idempotent_id('7161023e-5dd0-4612-9da0-1bac6ac30b63')
+    def test_create_update_and_delete_domain_config(self):
+        domain, created_config = self._create_domain_and_config(
+            self.custom_config)
+
+        new_config = created_config
+        new_config['ldap']['url'] = data_utils.rand_url()
+
+        # Check that the altered configuration is reflected in updated_config.
+        updated_config = self.client.update_domain_config(
+            domain['id'], **new_config)['config']
+        self.assertEqual(new_config, updated_config)
+
+        # Check that showing the domain config shows the altered configuration.
+        retrieved_config = self.client.show_domain_config(domain['id'])[
+            'config']
+        self.assertEqual(new_config, retrieved_config)
+
+        # Check that deleting a configuration works.
+        self.client.delete_domain_config(domain['id'])
+        self.assertRaises(lib_exc.NotFound, self.client.show_domain_config,
+                          domain['id'])
+
+    @decorators.idempotent_id('c7510fa2-6661-4170-9c6b-4783a80651e9')
+    def test_create_update_and_delete_domain_config_groups_and_opts(self):
+        domain, _ = self._create_domain_and_config(self.custom_config)
+
+        # Check that updating configuration groups work.
+        new_driver = data_utils.rand_name('driver')
+        new_limit = data_utils.rand_int_id(0, 100)
+        new_group_config = {'identity': {'driver': new_driver,
+                                         'list_limit': new_limit}}
+
+        updated_config = self.client.update_domain_group_config(
+            domain['id'], 'identity', **new_group_config)['config']
+
+        self.assertEqual(new_driver, updated_config['identity']['driver'])
+        self.assertEqual(new_limit, updated_config['identity']['list_limit'])
+
+        # Check that updating individual configuration group options work.
+        new_driver = data_utils.rand_name('driver')
+
+        updated_config = self.client.update_domain_group_option_config(
+            domain['id'], 'identity', 'driver', driver=new_driver)['config']
+
+        self.assertEqual(new_driver, updated_config['identity']['driver'])
+
+        # Check that deleting individual configuration group options work.
+        self.client.delete_domain_group_option_config(
+            domain['id'], 'identity', 'driver')
+        self.assertRaises(lib_exc.NotFound,
+                          self.client.show_domain_group_option_config,
+                          domain['id'], 'identity', 'driver')
+
+        # Check that deleting configuration groups work.
+        self.client.delete_domain_group_config(domain['id'], 'identity')
+        self.assertRaises(lib_exc.NotFound,
+                          self.client.show_domain_group_config,
+                          domain['id'], 'identity')
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index 4cf9f66..bf04ede 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -14,11 +14,10 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -32,10 +31,7 @@
         # One of those domains will be disabled
         cls.setup_domains = list()
         for i in range(3):
-            domain = cls.domains_client.create_domain(
-                name=data_utils.rand_name('domain'),
-                description=data_utils.rand_name('domain-desc'),
-                enabled=i < 2)['domain']
+            domain = cls.create_domain(enabled=i < 2)
             cls.setup_domains.append(domain)
 
     @classmethod
@@ -61,7 +57,7 @@
             fetched_ids.append(d['id'])
         missing_doms = [d for d in self.setup_domains
                         if d['id'] not in fetched_ids]
-        self.assertEqual(0, len(missing_doms))
+        self.assertEmpty(missing_doms)
 
     @decorators.idempotent_id('c6aee07b-4981-440c-bb0b-eb598f58ffe9')
     def test_list_domains_filter_by_name(self):
@@ -87,7 +83,7 @@
         for domain in fetched_domains:
             self.assertEqual(True, domain['enabled'])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('f2f5b44a-82e8-4dad-8084-0661ea3b18cf')
     def test_create_update_delete_domain(self):
         # Create domain
@@ -97,7 +93,6 @@
             name=d_name, description=d_desc)['domain']
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self._delete_domain, domain['id'])
-        self.assertIn('id', domain)
         self.assertIn('description', domain)
         self.assertIn('name', domain)
         self.assertIn('enabled', domain)
@@ -151,13 +146,8 @@
         d_name = data_utils.rand_name('domain')
         domain = self.domains_client.create_domain(name=d_name)['domain']
         self.addCleanup(self._delete_domain, domain['id'])
-        self.assertIn('id', domain)
         expected_data = {'name': d_name, 'enabled': True}
-        # TODO(gmann): there is bug in keystone liberty version where
-        # description is not being returned if it is not being passed in
-        # request. Bug#1649245. Once bug is fixed then we can enable the below
-        # check.
-        # self.assertEqual('', domain['description'])
+        self.assertEqual('', domain['description'])
         self.assertDictContainsSubset(expected_data, domain)
 
 
@@ -168,7 +158,7 @@
         cls.domain_id = CONF.identity.default_domain_id
         super(DefaultDomainTestJSON, cls).resource_setup()
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('17a5de24-e6a0-4e4a-a9ee-d85b6e5612b5')
     def test_default_domain_exists(self):
         domain = self.domains_client.show_domain(self.domain_id)['domain']
diff --git a/tempest/api/identity/admin/v3/test_domains_negative.py b/tempest/api/identity/admin/v3/test_domains_negative.py
index 280a5a8..56f7d32 100644
--- a/tempest/api/identity/admin/v3/test_domains_negative.py
+++ b/tempest/api/identity/admin/v3/test_domains_negative.py
@@ -17,20 +17,14 @@
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class DomainsNegativeTestJSON(base.BaseIdentityV3AdminTest):
-    _interface = 'json'
 
-    @test.attr(type=['negative', 'gate'])
+    @decorators.attr(type=['negative', 'gate'])
     @decorators.idempotent_id('1f3fbff5-4e44-400d-9ca1-d953f05f609b')
     def test_delete_active_domain(self):
-        d_name = data_utils.rand_name('domain')
-        d_desc = data_utils.rand_name('domain-desc')
-        domain = self.domains_client.create_domain(
-            name=d_name,
-            description=d_desc)['domain']
+        domain = self.create_domain()
         domain_id = domain['id']
 
         self.addCleanup(self.delete_domain, domain_id)
@@ -39,14 +33,14 @@
         self.assertRaises(lib_exc.Forbidden, self.domains_client.delete_domain,
                           domain_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9018461d-7d24-408d-b3fe-ae37e8cd5c9e')
     def test_create_domain_with_empty_name(self):
         # Domain name should not be empty
         self.assertRaises(lib_exc.BadRequest,
                           self.domains_client.create_domain, name='')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('37b1bbf2-d664-4785-9a11-333438586eae')
     def test_create_domain_with_name_length_over_64(self):
         # Domain name length should not ne greater than 64 characters
@@ -55,14 +49,14 @@
                           self.domains_client.create_domain,
                           name=d_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('43781c07-764f-4cf2-a405-953c1916f605')
     def test_delete_non_existent_domain(self):
         # Attempt to delete a non existent domain should fail
         self.assertRaises(lib_exc.NotFound, self.domains_client.delete_domain,
                           data_utils.rand_uuid_hex())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e6f9e4a2-4f36-4be8-bdbc-4e199ae29427')
     def test_domain_create_duplicate(self):
         domain_name = data_utils.rand_name('domain-dup')
diff --git a/tempest/api/identity/admin/v3/test_endpoint_groups.py b/tempest/api/identity/admin/v3/test_endpoint_groups.py
new file mode 100644
index 0000000..49dbba1
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_endpoint_groups.py
@@ -0,0 +1,157 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+
+class EndPointGroupsTest(base.BaseIdentityV3AdminTest):
+
+    @classmethod
+    def setup_clients(cls):
+        super(EndPointGroupsTest, cls).setup_clients()
+        cls.client = cls.endpoint_groups_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(EndPointGroupsTest, cls).resource_setup()
+        cls.service_ids = list()
+        cls.endpoint_groups = list()
+
+        # Create endpoint group so as to use it for LIST test
+        service_id = cls._create_service()
+
+        name = data_utils.rand_name('service_group')
+        description = data_utils.rand_name('description')
+        filters = {'service_id': service_id}
+
+        endpoint_group = cls.client.create_endpoint_group(
+            name=name,
+            description=description,
+            filters=filters)['endpoint_group']
+
+        cls.endpoint_groups.append(endpoint_group)
+
+    @classmethod
+    def resource_cleanup(cls):
+        for e in cls.endpoint_groups:
+            cls.client.delete_endpoint_group(e['id'])
+        for s in cls.service_ids:
+            cls.services_client.delete_service(s)
+        super(EndPointGroupsTest, cls).resource_cleanup()
+
+    @classmethod
+    def _create_service(cls):
+        s_name = data_utils.rand_name('service')
+        s_type = data_utils.rand_name('type')
+        s_description = data_utils.rand_name('description')
+        service_data = (
+            cls.services_client.create_service(name=s_name,
+                                               type=s_type,
+                                               description=s_description))
+
+        service_id = service_data['service']['id']
+        cls.service_ids.append(service_id)
+        return service_id
+
+    @decorators.idempotent_id('7c69e7a1-f865-402d-a2ea-44493017315a')
+    def test_create_list_show_check_delete_endpoint_group(self):
+        service_id = self._create_service()
+        name = data_utils.rand_name('service_group')
+        description = data_utils.rand_name('description')
+        filters = {'service_id': service_id}
+
+        endpoint_group = self.client.create_endpoint_group(
+            name=name,
+            description=description,
+            filters=filters)['endpoint_group']
+
+        self.endpoint_groups.append(endpoint_group)
+
+        # Asserting created endpoint group response body
+        self.assertIn('id', endpoint_group)
+        self.assertEqual(name, endpoint_group['name'])
+        self.assertEqual(description, endpoint_group['description'])
+
+        # Checking if endpoint groups are present in the list of endpoints
+        # Note that there are two endpoint groups in the list, one created
+        # in the resource setup, one created in this test case.
+        fetched_endpoints = \
+            self.client.list_endpoint_groups()['endpoint_groups']
+
+        missing_endpoints = \
+            [e for e in self.endpoint_groups if e not in fetched_endpoints]
+
+        # Asserting LIST endpoints
+        self.assertEmpty(missing_endpoints,
+                         "Failed to find endpoint %s in fetched list" %
+                         ', '.join(str(e) for e in missing_endpoints))
+
+        # Show endpoint group
+        fetched_endpoint = self.client.show_endpoint_group(
+            endpoint_group['id'])['endpoint_group']
+
+        # Asserting if the attributes of endpoint group are the same
+        self.assertEqual(service_id,
+                         fetched_endpoint['filters']['service_id'])
+        for attr in ('id', 'name', 'description'):
+            self.assertEqual(endpoint_group[attr], fetched_endpoint[attr])
+
+        # Check endpoint group
+        self.client.check_endpoint_group(endpoint_group['id'])
+
+        # Deleting the endpoint group created in this method
+        self.client.delete_endpoint_group(endpoint_group['id'])
+        self.endpoint_groups.remove(endpoint_group)
+
+        # Checking whether endpoint group is deleted successfully
+        fetched_endpoints = \
+            self.client.list_endpoint_groups()['endpoint_groups']
+        fetched_endpoint_ids = [e['id'] for e in fetched_endpoints]
+        self.assertNotIn(endpoint_group['id'], fetched_endpoint_ids)
+
+    @decorators.idempotent_id('51c8fc38-fa84-4e76-b5b6-6fc37770fb26')
+    def test_update_endpoint_group(self):
+        # Creating an endpoint group so as to check update endpoint group
+        # with new values
+        service1_id = self._create_service()
+        name = data_utils.rand_name('service_group')
+        description = data_utils.rand_name('description')
+        filters = {'service_id': service1_id}
+
+        endpoint_group = self.client.create_endpoint_group(
+            name=name,
+            description=description,
+            filters=filters)['endpoint_group']
+        self.endpoint_groups.append(endpoint_group)
+
+        # Creating new attr values to update endpoint group
+        service2_id = self._create_service()
+        name2 = data_utils.rand_name('service_group2')
+        description2 = data_utils.rand_name('description2')
+        filters = {'service_id': service2_id}
+
+        # Updating endpoint group with new attr values
+        updated_endpoint_group = self.client.update_endpoint_group(
+            endpoint_group['id'],
+            name=name2,
+            description=description2,
+            filters=filters)['endpoint_group']
+
+        self.assertEqual(name2, updated_endpoint_group['name'])
+        self.assertEqual(description2, updated_endpoint_group['description'])
+        self.assertEqual(service2_id,
+                         updated_endpoint_group['filters']['service_id'])
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 686743b..5d48f68 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -14,9 +14,8 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class EndPointsTestJSON(base.BaseIdentityV3AdminTest):
@@ -30,59 +29,94 @@
     def resource_setup(cls):
         super(EndPointsTestJSON, cls).resource_setup()
         cls.service_ids = list()
-        s_name = data_utils.rand_name('service')
-        s_type = data_utils.rand_name('type')
-        s_description = data_utils.rand_name('description')
-        cls.service_data = (
-            cls.services_client.create_service(name=s_name, type=s_type,
-                                               description=s_description))
-        cls.service_data = cls.service_data['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
-        cls.setup_endpoints = list()
-        for _ in range(2):
+        interfaces = ['public', 'internal']
+        cls.setup_endpoint_ids = list()
+        for i in range(2):
+            cls._create_service()
             region = data_utils.rand_name('region')
             url = data_utils.rand_url()
-            interface = 'public'
-            endpoint = cls.client.create_endpoint(service_id=cls.service_id,
-                                                  interface=interface,
-                                                  url=url, region=region,
-                                                  enabled=True)['endpoint']
-            cls.setup_endpoints.append(endpoint)
+            endpoint = cls.client.create_endpoint(
+                service_id=cls.service_ids[i], interface=interfaces[i],
+                url=url, region=region, enabled=True)['endpoint']
+            cls.setup_endpoint_ids.append(endpoint['id'])
+
+    @classmethod
+    def _create_service(cls, s_name=None, s_type=None, s_description=None):
+        if s_name is None:
+            s_name = data_utils.rand_name('service')
+        if s_type is None:
+            s_type = data_utils.rand_name('type')
+        if s_description is None:
+            s_description = data_utils.rand_name('description')
+        service_data = (
+            cls.services_client.create_service(name=s_name, type=s_type,
+                                               description=s_description))
+        service = service_data['service']
+        cls.service_ids.append(service['id'])
+        return service
 
     @classmethod
     def resource_cleanup(cls):
-        for e in cls.setup_endpoints:
-            cls.client.delete_endpoint(e['id'])
+        for e in cls.setup_endpoint_ids:
+            cls.client.delete_endpoint(e)
         for s in cls.service_ids:
             cls.services_client.delete_service(s)
         super(EndPointsTestJSON, cls).resource_cleanup()
 
     @decorators.idempotent_id('c19ecf90-240e-4e23-9966-21cee3f6a618')
     def test_list_endpoints(self):
-        # Get a list of endpoints
+        # Get the list of all the endpoints.
         fetched_endpoints = self.client.list_endpoints()['endpoints']
-        # Asserting LIST endpoints
+        fetched_endpoint_ids = [e['id'] for e in fetched_endpoints]
+        # Check that all the created endpoints are present in
+        # "fetched_endpoints".
         missing_endpoints =\
-            [e for e in self.setup_endpoints if e not in fetched_endpoints]
+            [e for e in self.setup_endpoint_ids
+             if e not in fetched_endpoint_ids]
         self.assertEqual(0, len(missing_endpoints),
                          "Failed to find endpoint %s in fetched list" %
                          ', '.join(str(e) for e in missing_endpoints))
 
+        # Check that filtering endpoints by service_id works.
+        fetched_endpoints_for_service = self.client.list_endpoints(
+            service_id=self.service_ids[0])['endpoints']
+        fetched_endpoints_for_alt_service = self.client.list_endpoints(
+            service_id=self.service_ids[1])['endpoints']
+
+        # Assert that both filters returned the correct result.
+        self.assertEqual(1, len(fetched_endpoints_for_service))
+        self.assertEqual(1, len(fetched_endpoints_for_alt_service))
+        self.assertEqual(set(self.setup_endpoint_ids),
+                         set([fetched_endpoints_for_service[0]['id'],
+                              fetched_endpoints_for_alt_service[0]['id']]))
+
+        # Check that filtering endpoints by interface works.
+        fetched_public_endpoints = self.client.list_endpoints(
+            interface='public')['endpoints']
+        fetched_internal_endpoints = self.client.list_endpoints(
+            interface='internal')['endpoints']
+
+        # Check that the expected endpoint_id is present per filter. [0] is
+        # public and [1] is internal.
+        self.assertIn(self.setup_endpoint_ids[0],
+                      [e['id'] for e in fetched_public_endpoints])
+        self.assertIn(self.setup_endpoint_ids[1],
+                      [e['id'] for e in fetched_internal_endpoints])
+
     @decorators.idempotent_id('0e2446d2-c1fd-461b-a729-b9e73e3e3b37')
     def test_create_list_show_delete_endpoint(self):
         region = data_utils.rand_name('region')
         url = data_utils.rand_url()
         interface = 'public'
-        endpoint = self.client.create_endpoint(service_id=self.service_id,
+        endpoint = self.client.create_endpoint(service_id=self.service_ids[0],
                                                interface=interface,
                                                url=url, region=region,
                                                enabled=True)['endpoint']
 
-        self.setup_endpoints.append(endpoint)
+        self.setup_endpoint_ids.append(endpoint['id'])
         # Asserting Create Endpoint response body
-        self.assertIn('id', endpoint)
         self.assertEqual(region, endpoint['region'])
         self.assertEqual(url, endpoint['url'])
 
@@ -95,7 +129,7 @@
         fetched_endpoint = (
             self.client.show_endpoint(endpoint['id'])['endpoint'])
         # Asserting if the attributes of endpoint are the same
-        self.assertEqual(self.service_id, fetched_endpoint['service_id'])
+        self.assertEqual(self.service_ids[0], fetched_endpoint['service_id'])
         self.assertEqual(interface, fetched_endpoint['interface'])
         self.assertEqual(url, fetched_endpoint['url'])
         self.assertEqual(region, fetched_endpoint['region'])
@@ -103,14 +137,14 @@
 
         # Deleting the endpoint created in this method
         self.client.delete_endpoint(endpoint['id'])
-        self.setup_endpoints.remove(endpoint)
+        self.setup_endpoint_ids.remove(endpoint['id'])
 
         # Checking whether endpoint is deleted successfully
         fetched_endpoints = self.client.list_endpoints()['endpoints']
         fetched_endpoints_id = [e['id'] for e in fetched_endpoints]
         self.assertNotIn(endpoint['id'], fetched_endpoints_id)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('37e8f15e-ee7c-4657-a1e7-f6b61e375eff')
     def test_update_endpoint(self):
         # Creating an endpoint so as to check update endpoint
@@ -119,7 +153,7 @@
         url1 = data_utils.rand_url()
         interface1 = 'public'
         endpoint_for_update = (
-            self.client.create_endpoint(service_id=self.service_id,
+            self.client.create_endpoint(service_id=self.service_ids[0],
                                         interface=interface1,
                                         url=url1, region=region1,
                                         enabled=True)['endpoint'])
@@ -128,11 +162,8 @@
         s_name = data_utils.rand_name('service')
         s_type = data_utils.rand_name('type')
         s_description = data_utils.rand_name('description')
-        service2 = (
-            self.services_client.create_service(name=s_name, type=s_type,
-                                                description=s_description))
-        service2 = service2['service']
-        self.service_ids.append(service2['id'])
+        service2 = self._create_service(s_name=s_name, s_type=s_type,
+                                        s_description=s_description)
         # Updating endpoint with new values
         region2 = data_utils.rand_name('region')
         url2 = data_utils.rand_url()
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index 53c2b1f..70dd7b5 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -15,10 +15,9 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class EndpointsNegativeTestJSON(base.BaseIdentityV3AdminTest):
@@ -35,11 +34,11 @@
         s_name = data_utils.rand_name('service')
         s_type = data_utils.rand_name('type')
         s_description = data_utils.rand_name('description')
-        cls.service_data = (
+        service_data = (
             cls.services_client.create_service(name=s_name, type=s_type,
                                                description=s_description)
             ['service'])
-        cls.service_id = cls.service_data['id']
+        cls.service_id = service_data['id']
         cls.service_ids.append(cls.service_id)
 
     @classmethod
@@ -48,7 +47,7 @@
             cls.services_client.delete_service(s)
         super(EndpointsNegativeTestJSON, cls).resource_cleanup()
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ac6c137e-4d3d-448f-8c83-4f13d0942651')
     def test_create_with_enabled_False(self):
         # Enabled should be a boolean, not a string like 'False'
@@ -59,7 +58,7 @@
                           service_id=self.service_id, interface=interface,
                           url=url, region=region, enabled='False')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9c43181e-0627-484a-8c79-923e8a59598b')
     def test_create_with_enabled_True(self):
         # Enabled should be a boolean, not a string like 'True'
@@ -86,13 +85,13 @@
         self.assertRaises(lib_exc.BadRequest, self.client.update_endpoint,
                           endpoint_for_update['id'], enabled=enabled)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('65e41f32-5eb7-498f-a92a-a6ccacf7439a')
     def test_update_with_enabled_False(self):
         # Enabled should be a boolean, not a string like 'False'
         self._assert_update_raises_bad_request('False')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('faba3587-f066-4757-a48e-b4a3f01803bb')
     def test_update_with_enabled_True(self):
         # Enabled should be a boolean, not a string like 'True'
diff --git a/tempest/api/identity/admin/v3/test_groups.py b/tempest/api/identity/admin/v3/test_groups.py
index 5ce0709..17db3ea 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -14,9 +14,11 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
+
+CONF = config.CONF
 
 
 class GroupsV3TestJSON(base.BaseIdentityV3AdminTest):
@@ -72,7 +74,7 @@
         # Verify that 'description' is not being updated or deleted.
         self.assertEqual(old_description, updated_group['description'])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('1598521a-2f36-4606-8df9-30772bd51339')
     def test_group_users_add_list_delete(self):
         name = data_utils.rand_name('Group')
@@ -82,12 +84,8 @@
         # add user into group
         users = []
         for _ in range(3):
-            name = data_utils.rand_name('User')
-            password = data_utils.rand_password()
-            user = self.users_client.create_user(name=name,
-                                                 password=password)['user']
+            user = self.create_test_user()
             users.append(user)
-            self.addCleanup(self.users_client.delete_user, user['id'])
             self.groups_client.add_group_user(group['id'], user['id'])
 
         # list users in group
@@ -105,10 +103,7 @@
     @decorators.idempotent_id('64573281-d26a-4a52-b899-503cb0f4e4ec')
     def test_list_user_groups(self):
         # create a user
-        user = self.users_client.create_user(
-            name=data_utils.rand_name('User'),
-            password=data_utils.rand_password())['user']
-        self.addCleanup(self.users_client.delete_user, user['id'])
+        user = self.create_test_user()
         # create two groups, and add user into them
         groups = []
         for _ in range(2):
@@ -138,8 +133,15 @@
             self.addCleanup(self.groups_client.delete_group, group['id'])
             group_ids.append(group['id'])
         # List and Verify Groups
-        body = self.groups_client.list_groups()['groups']
+        # When domain specific drivers are enabled the operations
+        # of listing all users and listing all groups are not supported,
+        # they need a domain filter to be specified
+        if CONF.identity_feature_enabled.domain_specific_drivers:
+            body = self.groups_client.list_groups(
+                domain_id=self.domain['id'])['groups']
+        else:
+            body = self.groups_client.list_groups()['groups']
         for g in body:
             fetched_ids.append(g['id'])
         missing_groups = [g for g in group_ids if g not in fetched_ids]
-        self.assertEqual([], missing_groups)
+        self.assertEmpty(missing_groups)
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index 8523396..8b687cd 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -11,29 +11,27 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
-class BaseInheritsV3Test(base.BaseIdentityV3AdminTest):
+class InheritsV3TestJSON(base.BaseIdentityV3AdminTest):
 
     @classmethod
     def skip_checks(cls):
-        super(BaseInheritsV3Test, cls).skip_checks()
-        if not test.is_extension_enabled('OS-INHERIT', 'identity'):
+        super(InheritsV3TestJSON, cls).skip_checks()
+        if not utils.is_extension_enabled('OS-INHERIT', 'identity'):
             raise cls.skipException("Inherits aren't enabled")
 
     @classmethod
     def resource_setup(cls):
-        super(BaseInheritsV3Test, cls).resource_setup()
+        super(InheritsV3TestJSON, cls).resource_setup()
         u_name = data_utils.rand_name('user-')
         u_desc = '%s description' % u_name
         u_email = '%s@testmail.tm' % u_name
         u_password = data_utils.rand_name('pass-')
-        cls.domain = cls.domains_client.create_domain(
-            name=data_utils.rand_name('domain-'),
-            description=data_utils.rand_name('domain-desc-'))['domain']
+        cls.domain = cls.create_domain()
         cls.project = cls.projects_client.create_project(
             data_utils.rand_name('project-'),
             description=data_utils.rand_name('project-desc-'),
@@ -53,21 +51,16 @@
         cls.projects_client.delete_project(cls.project['id'])
         cls.domains_client.update_domain(cls.domain['id'], enabled=False)
         cls.domains_client.delete_domain(cls.domain['id'])
-        super(BaseInheritsV3Test, cls).resource_cleanup()
+        super(InheritsV3TestJSON, cls).resource_cleanup()
 
     def _list_assertions(self, body, fetched_role_ids, role_id):
         self.assertEqual(len(body), 1)
         self.assertIn(role_id, fetched_role_ids)
 
-
-class InheritsV3TestJSON(BaseInheritsV3Test):
-
     @decorators.idempotent_id('4e6f0366-97c8-423c-b2be-41eae6ac91c8')
     def test_inherit_assign_list_check_revoke_roles_on_domains_user(self):
         # Create role
-        src_role = self.roles_client.create_role(
-            name=data_utils.rand_name('Role'))['role']
-        self.addCleanup(self.roles_client.delete_role, src_role['id'])
+        src_role = self.setup_test_role()
         # Assign role on domains user
         self.inherited_roles_client.create_inherited_role_on_domains_user(
             self.domain['id'], self.user['id'], src_role['id'])
@@ -91,9 +84,7 @@
     @decorators.idempotent_id('c7a8dda2-be50-4fb4-9a9c-e830771078b1')
     def test_inherit_assign_list_check_revoke_roles_on_domains_group(self):
         # Create role
-        src_role = self.roles_client.create_role(
-            name=data_utils.rand_name('Role'))['role']
-        self.addCleanup(self.roles_client.delete_role, src_role['id'])
+        src_role = self.setup_test_role()
         # Assign role on domains group
         self.inherited_roles_client.create_inherited_role_on_domains_group(
             self.domain['id'], self.group['id'], src_role['id'])
@@ -117,9 +108,7 @@
     @decorators.idempotent_id('18b70e45-7687-4b72-8277-b8f1a47d7591')
     def test_inherit_assign_check_revoke_roles_on_projects_user(self):
         # Create role
-        src_role = self.roles_client.create_role(
-            name=data_utils.rand_name('Role'))['role']
-        self.addCleanup(self.roles_client.delete_role, src_role['id'])
+        src_role = self.setup_test_role()
         # Assign role on projects user
         self.inherited_roles_client.create_inherited_role_on_projects_user(
             self.project['id'], self.user['id'], src_role['id'])
@@ -134,9 +123,7 @@
     @decorators.idempotent_id('26021436-d5a4-4256-943c-ded01e0d4b45')
     def test_inherit_assign_check_revoke_roles_on_projects_group(self):
         # Create role
-        src_role = self.roles_client.create_role(
-            name=data_utils.rand_name('Role'))['role']
-        self.addCleanup(self.roles_client.delete_role, src_role['id'])
+        src_role = self.setup_test_role()
         # Assign role on projects group
         self.inherited_roles_client.create_inherited_role_on_projects_group(
             self.project['id'], self.group['id'], src_role['id'])
@@ -152,17 +139,11 @@
     @decorators.idempotent_id('3acf666e-5354-42ac-8e17-8b68893bcd36')
     def test_inherit_assign_list_revoke_user_roles_on_domain(self):
         # Create role
-        src_role = self.roles_client.create_role(
-            name=data_utils.rand_name('Role'))['role']
-        self.addCleanup(self.roles_client.delete_role, src_role['id'])
+        src_role = self.setup_test_role()
 
         # Create a project hierarchy
-        leaf_project_name = data_utils.rand_name('project')
-        leaf_project = self.projects_client.create_project(
-            leaf_project_name, domain_id=self.domain['id'],
-            parent_id=self.project['id'])['project']
-        self.addCleanup(
-            self.projects_client.delete_project, leaf_project['id'])
+        leaf_project = self.setup_test_project(domain_id=self.domain['id'],
+                                               parent_id=self.project['id'])
 
         # Assign role on domain
         self.inherited_roles_client.create_inherited_role_on_domains_user(
@@ -202,17 +183,11 @@
     @decorators.idempotent_id('9f02ccd9-9b57-46b4-8f77-dd5a736f3a06')
     def test_inherit_assign_list_revoke_user_roles_on_project_tree(self):
         # Create role
-        src_role = self.roles_client.create_role(
-            name=data_utils.rand_name('Role'))['role']
-        self.addCleanup(self.roles_client.delete_role, src_role['id'])
+        src_role = self.setup_test_role()
 
         # Create a project hierarchy
-        leaf_project_name = data_utils.rand_name('project')
-        leaf_project = self.projects_client.create_project(
-            leaf_project_name, domain_id=self.domain['id'],
-            parent_id=self.project['id'])['project']
-        self.addCleanup(
-            self.projects_client.delete_project, leaf_project['id'])
+        leaf_project = self.setup_test_project(domain_id=self.domain['id'],
+                                               parent_id=self.project['id'])
 
         # Assign role on parent project
         self.inherited_roles_client.create_inherited_role_on_projects_user(
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 2d046bb..7e70c14 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 
diff --git a/tempest/api/identity/admin/v3/test_list_users.py b/tempest/api/identity/admin/v3/test_list_users.py
index 0a0d65a..506c729 100644
--- a/tempest/api/identity/admin/v3/test_list_users.py
+++ b/tempest/api/identity/admin/v3/test_list_users.py
@@ -14,9 +14,12 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
+CONF = config.CONF
+
 
 class UsersV3TestJSON(base.BaseIdentityV3AdminTest):
 
@@ -82,6 +85,11 @@
     def test_list_users_with_name(self):
         # List users with name
         params = {'name': self.domain_enabled_user['name']}
+        # When domain specific drivers are enabled the operations
+        # of listing all users and listing all groups are not supported,
+        # they need a domain filter to be specified
+        if CONF.identity_feature_enabled.domain_specific_drivers:
+            params['domain_id'] = self.domain_enabled_user['domain_id']
         self._list_users_with_params(params, 'name',
                                      self.domain_enabled_user,
                                      self.non_domain_enabled_user)
@@ -89,11 +97,22 @@
     @decorators.idempotent_id('b30d4651-a2ea-4666-8551-0c0e49692635')
     def test_list_users(self):
         # List users
-        body = self.users_client.list_users()['users']
+        # When domain specific drivers are enabled the operations
+        # of listing all users and listing all groups are not supported,
+        # they need a domain filter to be specified
+        if CONF.identity_feature_enabled.domain_specific_drivers:
+            body_enabled_user = self.users_client.list_users(
+                domain_id=self.domain_enabled_user['domain_id'])['users']
+            body_non_enabled_user = self.users_client.list_users(
+                domain_id=self.non_domain_enabled_user['domain_id'])['users']
+            body = (body_enabled_user + body_non_enabled_user)
+        else:
+            body = self.users_client.list_users()['users']
+
         fetched_ids = [u['id'] for u in body]
         missing_users = [u['id'] for u in self.users
                          if u['id'] not in fetched_ids]
-        self.assertEqual(0, len(missing_users),
+        self.assertEmpty(missing_users,
                          "Failed to find user %s in fetched list" %
                          ', '.join(m_user for m_user in missing_users))
 
diff --git a/tempest/api/identity/admin/v3/test_oauth_consumers.py b/tempest/api/identity/admin/v3/test_oauth_consumers.py
new file mode 100644
index 0000000..970ead3
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_oauth_consumers.py
@@ -0,0 +1,91 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as exceptions
+
+
+class OAUTHConsumersV3Test(base.BaseIdentityV3AdminTest):
+
+    def _create_consumer(self):
+        """Creates a consumer with a random description."""
+        description = data_utils.rand_name('test_create_consumer')
+        consumer = self.oauth_consumers_client.create_consumer(
+            description)['consumer']
+        # cleans up created consumers after tests
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.oauth_consumers_client.delete_consumer,
+                        consumer['id'])
+        return consumer
+
+    @decorators.idempotent_id('c8307ea6-a86c-47fd-ae7b-5b3b2caca76d')
+    def test_create_and_show_consumer(self):
+        """Tests to make sure that a consumer with parameters is made"""
+        consumer = self._create_consumer()
+        # fetch created consumer from client
+        fetched_consumer = self.oauth_consumers_client.show_consumer(
+            consumer['id'])['consumer']
+        # assert that the fetched consumer matches the created one and
+        # has all parameters
+        for key in ['description', 'id', 'links']:
+            self.assertEqual(consumer[key], fetched_consumer[key])
+
+    @decorators.idempotent_id('fdfa1b7f-2a31-4354-b2c7-f6ae20554f93')
+    def test_delete_consumer(self):
+        """Tests the delete function."""
+        consumer = self._create_consumer()
+        # fetch consumer from client to confirm it exists
+        fetched_consumer = self.oauth_consumers_client.show_consumer(
+            consumer['id'])['consumer']
+        self.assertEqual(consumer['id'], fetched_consumer['id'])
+        # delete existing consumer
+        self.oauth_consumers_client.delete_consumer(consumer['id'])
+        # check that consumer no longer exists
+        self.assertRaises(exceptions.NotFound,
+                          self.oauth_consumers_client.show_consumer,
+                          consumer['id'])
+
+    @decorators.idempotent_id('080a9b1a-c009-47c0-9979-5305bf72e3dc')
+    def test_update_consumer(self):
+        """Tests the update functionality"""
+        # create a new consumer to update
+        consumer = self._create_consumer()
+        # create new description
+        new_description = data_utils.rand_name('test_update_consumer')
+        # update consumer
+        self.oauth_consumers_client.update_consumer(consumer['id'],
+                                                    new_description)
+        # check that the same consumer now has the new description
+        updated_consumer = self.oauth_consumers_client.show_consumer(
+            consumer['id'])['consumer']
+        self.assertEqual(new_description, updated_consumer['description'])
+
+    @decorators.idempotent_id('09ca50de-78f2-4ffb-ac71-f2254036b2b8')
+    def test_list_consumers(self):
+        """Test for listing consumers"""
+        # create two consumers to populate list
+        new_consumer_one = self._create_consumer()
+        new_consumer_two = self._create_consumer()
+        # fetch the list of consumers
+        consumer_list = self.oauth_consumers_client \
+                            .list_consumers()['consumers']
+        # add fetched consumer ids to a list
+        id_list = [consumer['id'] for consumer in consumer_list]
+        # check if created consumers are in the list
+        self.assertIn(new_consumer_one['id'], id_list)
+        self.assertIn(new_consumer_two['id'], id_list)
diff --git a/tempest/api/identity/admin/v3/test_policies.py b/tempest/api/identity/admin/v3/test_policies.py
index eb1c69e..2908fc4 100644
--- a/tempest/api/identity/admin/v3/test_policies.py
+++ b/tempest/api/identity/admin/v3/test_policies.py
@@ -14,9 +14,8 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class PoliciesTestJSON(base.BaseIdentityV3AdminTest):
@@ -42,9 +41,9 @@
         for p in body:
             fetched_ids.append(p['id'])
         missing_pols = [p for p in policy_ids if p not in fetched_ids]
-        self.assertEqual(0, len(missing_pols))
+        self.assertEmpty(missing_pols)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('e544703a-2f03-4cf2-9b0f-350782fdb0d3')
     def test_create_update_delete_policy(self):
         # Test to update policy
@@ -53,7 +52,6 @@
         policy = self.policies_client.create_policy(blob=blob,
                                                     type=policy_type)['policy']
         self.addCleanup(self._delete_policy, policy['id'])
-        self.assertIn('id', policy)
         self.assertIn('type', policy)
         self.assertIn('blob', policy)
         self.assertIsNotNone(policy['id'])
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index b37d33d..1b1d3f7 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -13,11 +13,9 @@
 #    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.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -28,11 +26,8 @@
     @decorators.idempotent_id('0ecf465c-0dc4-4532-ab53-91ffeb74d12d')
     def test_project_create_with_description(self):
         # Create project with a description
-        project_name = data_utils.rand_name('project')
         project_desc = data_utils.rand_name('desc')
-        project = self.projects_client.create_project(
-            project_name, description=project_desc)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(description=project_desc)
         project_id = project['id']
         desc1 = project['description']
         self.assertEqual(desc1, project_desc, 'Description should have '
@@ -47,9 +42,8 @@
         # Create project with a domain
         domain = self.setup_test_domain()
         project_name = data_utils.rand_name('project')
-        project = self.projects_client.create_project(
-            project_name, domain_id=domain['id'])['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(
+            name=project_name, domain_id=domain['id'])
         project_id = project['id']
         self.assertEqual(project_name, project['name'])
         self.assertEqual(domain['id'], project['domain_id'])
@@ -57,8 +51,6 @@
         self.assertEqual(project_name, body['name'])
         self.assertEqual(domain['id'], body['domain_id'])
 
-    @testtools.skipUnless(CONF.identity_feature_enabled.reseller,
-                          'Reseller not available.')
     @decorators.idempotent_id('1854f9c0-70bc-4d11-a08a-1c789d339e3d')
     def test_project_create_with_parent(self):
         # Create root project without providing a parent_id
@@ -66,10 +58,8 @@
         domain_id = domain['id']
 
         root_project_name = data_utils.rand_name('root_project')
-        root_project = self.projects_client.create_project(
-            root_project_name, domain_id=domain_id)['project']
-        self.addCleanup(
-            self.projects_client.delete_project, root_project['id'])
+        root_project = self.setup_test_project(
+            name=root_project_name, domain_id=domain_id)
 
         root_project_id = root_project['id']
         parent_id = root_project['parent_id']
@@ -80,23 +70,16 @@
 
         # 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'])
+        project = self.setup_test_project(
+            name=project_name, domain_id=domain_id, parent_id=root_project_id)
         parent_id = project['parent_id']
         self.assertEqual(project_name, project['name'])
         self.assertEqual(root_project_id, parent_id)
 
     @decorators.idempotent_id('a7eb9416-6f9b-4dbb-b71b-7f73aaef59d5')
-    @testtools.skipUnless(CONF.identity_feature_enabled.reseller,
-                          'Reseller not available.')
     def test_create_is_domain_project(self):
-        project_name = data_utils.rand_name('is_domain_project')
-        project = self.projects_client.create_project(
-            project_name, domain_id=None, is_domain=True)['project']
+        project = self.setup_test_project(domain_id=None, is_domain=True)
         # To delete a domain, we need to disable it first
-        self.addCleanup(self.projects_client.delete_project, project['id'])
         self.addCleanup(self.projects_client.update_project, project['id'],
                         enabled=False)
 
@@ -115,10 +98,7 @@
     @decorators.idempotent_id('1f66dc76-50cc-4741-a200-af984509e480')
     def test_project_create_enabled(self):
         # Create a project that is enabled
-        project_name = data_utils.rand_name('project')
-        project = self.projects_client.create_project(
-            project_name, enabled=True)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(enabled=True)
         project_id = project['id']
         en1 = project['enabled']
         self.assertTrue(en1, 'Enable should be True in response')
@@ -129,10 +109,7 @@
     @decorators.idempotent_id('78f96a9c-e0e0-4ee6-a3ba-fbf6dfd03207')
     def test_project_create_not_enabled(self):
         # Create a project that is not enabled
-        project_name = data_utils.rand_name('project')
-        project = self.projects_client.create_project(
-            project_name, enabled=False)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(enabled=False)
         en1 = project['enabled']
         self.assertEqual('false', str(en1).lower(),
                          'Enable should be False in response')
@@ -145,8 +122,7 @@
     def test_project_update_name(self):
         # Update name attribute of a project
         p_name1 = data_utils.rand_name('project')
-        project = self.projects_client.create_project(p_name1)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(name=p_name1)
 
         resp1_name = project['name']
 
@@ -166,11 +142,8 @@
     @decorators.idempotent_id('f138b715-255e-4a7d-871d-351e1ef2e153')
     def test_project_update_desc(self):
         # Update description attribute of a project
-        p_name = data_utils.rand_name('project')
         p_desc = data_utils.rand_name('desc')
-        project = self.projects_client.create_project(
-            p_name, description=p_desc)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(description=p_desc)
         resp1_desc = project['description']
 
         p_desc2 = data_utils.rand_name('desc2')
@@ -189,11 +162,8 @@
     @decorators.idempotent_id('b6b25683-c97f-474d-a595-55d410b68100')
     def test_project_update_enable(self):
         # Update the enabled attribute of a project
-        p_name = data_utils.rand_name('project')
         p_en = False
-        project = self.projects_client.create_project(p_name,
-                                                      enabled=p_en)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(enabled=p_en)
 
         resp1_en = project['enabled']
 
@@ -214,9 +184,7 @@
     def test_associate_user_to_project(self):
         # Associate a user to a project
         # Create a Project
-        p_name = data_utils.rand_name('project')
-        project = self.projects_client.create_project(p_name)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project()
 
         # Create a User
         u_name = data_utils.rand_name('user')
diff --git a/tempest/api/identity/admin/v3/test_projects_negative.py b/tempest/api/identity/admin/v3/test_projects_negative.py
index 87f8684..33a9c8c 100644
--- a/tempest/api/identity/admin/v3/test_projects_negative.py
+++ b/tempest/api/identity/admin/v3/test_projects_negative.py
@@ -14,33 +14,31 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ProjectsNegativeTestJSON(base.BaseIdentityV3AdminTest):
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('24c49279-45dd-4155-887a-cb738c2385aa')
     def test_list_projects_by_unauthorized_user(self):
         # Non-admin user should not be able to list projects
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_projects_client.list_projects)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('874c3e84-d174-4348-a16b-8c01f599561b')
     def test_project_create_duplicate(self):
         # Project names should be unique
         project_name = data_utils.rand_name('project-dup')
-        project = self.projects_client.create_project(project_name)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        self.setup_test_project(name=project_name)
 
         self.assertRaises(lib_exc.Conflict,
                           self.projects_client.create_project, project_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8fba9de2-3e1f-4e77-812a-60cb68f8df13')
     def test_create_project_by_unauthorized_user(self):
         # Non-admin user should not be authorized to create a project
@@ -49,14 +47,14 @@
             lib_exc.Forbidden, self.non_admin_projects_client.create_project,
             project_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7828db17-95e5-475b-9432-9a51b4aa79a9')
     def test_create_project_with_empty_name(self):
         # Project name should not be empty
         self.assertRaises(lib_exc.BadRequest,
                           self.projects_client.create_project, name='')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('502b6ceb-b0c8-4422-bf53-f08fdb21e2f0')
     def test_create_projects_name_length_over_64(self):
         # Project name length should not be greater than 64 characters
@@ -64,18 +62,16 @@
         self.assertRaises(lib_exc.BadRequest,
                           self.projects_client.create_project, project_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8d68c012-89e0-4394-8d6b-ccd7196def97')
     def test_project_delete_by_unauthorized_user(self):
         # Non-admin user should not be able to delete a project
-        project_name = data_utils.rand_name('project')
-        project = self.projects_client.create_project(project_name)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project()
         self.assertRaises(
             lib_exc.Forbidden, self.non_admin_projects_client.delete_project,
             project['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7965b581-60c1-43b7-8169-95d4ab7fc6fb')
     def test_delete_non_existent_project(self):
         # Attempt to delete a non existent project should fail
diff --git a/tempest/api/identity/admin/v3/test_regions.py b/tempest/api/identity/admin/v3/test_regions.py
index f57a07c..d00e408 100644
--- a/tempest/api/identity/admin/v3/test_regions.py
+++ b/tempest/api/identity/admin/v3/test_regions.py
@@ -14,10 +14,9 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class RegionsTestJSON(base.BaseIdentityV3AdminTest):
@@ -79,7 +78,7 @@
         regions_list = [r['id'] for r in body]
         self.assertNotIn(region['id'], regions_list)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('2c12c5b5-efcf-4aa5-90c5-bff1ab0cdbe2')
     def test_create_region_with_specific_id(self):
         # Create a region with a specific id
@@ -99,7 +98,7 @@
         missing_regions =\
             [e for e in self.setup_regions if e not in fetched_regions]
         # Asserting List Regions response
-        self.assertEqual(0, len(missing_regions),
+        self.assertEmpty(missing_regions,
                          "Failed to find region %s in fetched list" %
                          ', '.join(str(e) for e in missing_regions))
 
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index e89be4b..ec904e6 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -14,9 +14,13 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
-from tempest import test
+from tempest.lib import exceptions as lib_exc
+
+CONF = config.CONF
 
 
 class RolesV3TestJSON(base.BaseIdentityV3AdminTest):
@@ -29,14 +33,11 @@
             role_name = data_utils.rand_name(name='role')
             role = cls.roles_client.create_role(name=role_name)['role']
             cls.roles.append(role)
-        cls.fetched_role_ids = list()
         u_name = data_utils.rand_name('user')
         u_desc = '%s description' % u_name
         u_email = '%s@testmail.tm' % u_name
         cls.u_password = data_utils.rand_password()
-        cls.domain = cls.domains_client.create_domain(
-            name=data_utils.rand_name('domain'),
-            description=data_utils.rand_name('domain-desc'))['domain']
+        cls.domain = cls.create_domain()
         cls.project = cls.projects_client.create_project(
             data_utils.rand_name('project'),
             description=data_utils.rand_name('project-desc'),
@@ -65,11 +66,7 @@
             cls.roles_client.delete_role(role['id'])
         super(RolesV3TestJSON, cls).resource_cleanup()
 
-    def _list_assertions(self, body, fetched_role_ids, role_id):
-        self.assertEqual(len(body), 1)
-        self.assertIn(role_id, fetched_role_ids)
-
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('18afc6c0-46cf-4911-824e-9989cc056c3a')
     def test_role_create_update_show_list(self):
         r_name = data_utils.rand_name('Role')
@@ -102,11 +99,8 @@
         roles = self.roles_client.list_user_roles_on_project(
             self.project['id'], self.user_body['id'])['roles']
 
-        for i in roles:
-            self.fetched_role_ids.append(i['id'])
-
-        self._list_assertions(roles, self.fetched_role_ids,
-                              self.role['id'])
+        self.assertEqual(1, len(roles))
+        self.assertEqual(self.role['id'], roles[0]['id'])
 
         self.roles_client.check_user_role_existence_on_project(
             self.project['id'], self.user_body['id'], self.role['id'])
@@ -122,11 +116,8 @@
         roles = self.roles_client.list_user_roles_on_domain(
             self.domain['id'], self.user_body['id'])['roles']
 
-        for i in roles:
-            self.fetched_role_ids.append(i['id'])
-
-        self._list_assertions(roles, self.fetched_role_ids,
-                              self.role['id'])
+        self.assertEqual(1, len(roles))
+        self.assertEqual(self.role['id'], roles[0]['id'])
 
         self.roles_client.check_user_role_existence_on_domain(
             self.domain['id'], self.user_body['id'], self.role['id'])
@@ -143,11 +134,9 @@
         roles = self.roles_client.list_group_roles_on_project(
             self.project['id'], self.group_body['id'])['roles']
 
-        for i in roles:
-            self.fetched_role_ids.append(i['id'])
+        self.assertEqual(1, len(roles))
+        self.assertEqual(self.role['id'], roles[0]['id'])
 
-        self._list_assertions(roles, self.fetched_role_ids,
-                              self.role['id'])
         # Add user to group, and insure user has role on project
         self.groups_client.add_group_user(self.group_body['id'],
                                           self.user_body['id'])
@@ -177,11 +166,8 @@
         roles = self.roles_client.list_group_roles_on_domain(
             self.domain['id'], self.group_body['id'])['roles']
 
-        for i in roles:
-            self.fetched_role_ids.append(i['id'])
-
-        self._list_assertions(roles, self.fetched_role_ids,
-                              self.role['id'])
+        self.assertEqual(1, len(roles))
+        self.assertEqual(self.role['id'], roles[0]['id'])
 
         self.roles_client.check_role_from_group_on_domain_existence(
             self.domain['id'], self.group_body['id'], self.role['id'])
@@ -195,3 +181,239 @@
         body = self.roles_client.list_roles()['roles']
         found = [role for role in body if role in self.roles]
         self.assertEqual(len(found), len(self.roles))
+
+    def _create_implied_role(self, prior_role_id, implies_role_id,
+                             ignore_not_found=False):
+        self.roles_client.create_role_inference_rule(
+            prior_role_id, implies_role_id)
+        if ignore_not_found:
+            self.addCleanup(
+                test_utils.call_and_ignore_notfound_exc,
+                self.roles_client.delete_role_inference_rule,
+                prior_role_id,
+                implies_role_id)
+        else:
+            self.addCleanup(
+                self.roles_client.delete_role_inference_rule,
+                prior_role_id,
+                implies_role_id)
+
+    @decorators.idempotent_id('c90c316c-d706-4728-bcba-eb1912081b69')
+    def test_implied_roles_create_check_show_delete(self):
+        prior_role_id = self.roles[0]['id']
+        implies_role_id = self.roles[1]['id']
+
+        # Create an inference rule from prior_role to implies_role
+        self._create_implied_role(prior_role_id, implies_role_id,
+                                  ignore_not_found=True)
+
+        # Check if the inference rule exists
+        self.roles_client.check_role_inference_rule(
+            prior_role_id, implies_role_id)
+
+        # Show the inference rule and check its elements
+        resp_body = self.roles_client.show_role_inference_rule(
+            prior_role_id, implies_role_id)
+        self.assertIn('role_inference', resp_body)
+        role_inference = resp_body['role_inference']
+        for key1 in ['prior_role', 'implies']:
+            self.assertIn(key1, role_inference)
+            for key2 in ['id', 'links', 'name']:
+                self.assertIn(key2, role_inference[key1])
+
+        # Delete the inference rule
+        self.roles_client.delete_role_inference_rule(
+            prior_role_id, implies_role_id)
+        # Check if the inference rule no longer exists
+        self.assertRaises(
+            lib_exc.NotFound,
+            self.roles_client.show_role_inference_rule,
+            prior_role_id,
+            implies_role_id)
+
+    @decorators.idempotent_id('dc6f5959-b74d-4e30-a9e5-a8255494ff00')
+    def test_roles_hierarchy(self):
+        # Create inference rule from "roles[0]" to "role[1]"
+        self._create_implied_role(
+            self.roles[0]['id'], self.roles[1]['id'])
+
+        # Create inference rule from "roles[0]" to "role[2]"
+        self._create_implied_role(
+            self.roles[0]['id'], self.roles[2]['id'])
+
+        # Create inference rule from "roles[2]" to "role"
+        self._create_implied_role(
+            self.roles[2]['id'], self.role['id'])
+
+        # Listing inferences rules from "roles[2]" should only return "role"
+        rules = self.roles_client.list_role_inferences_rules(
+            self.roles[2]['id'])['role_inference']
+        self.assertEqual(1, len(rules['implies']))
+        self.assertEqual(self.role['id'], rules['implies'][0]['id'])
+
+        # Listing inferences rules from "roles[0]" should return "roles[1]" and
+        # "roles[2]" (only direct rules are listed)
+        rules = self.roles_client.list_role_inferences_rules(
+            self.roles[0]['id'])['role_inference']
+        implies_ids = [role['id'] for role in rules['implies']]
+        self.assertEqual(2, len(implies_ids))
+        self.assertIn(self.roles[1]['id'], implies_ids)
+        self.assertIn(self.roles[2]['id'], implies_ids)
+
+    @decorators.idempotent_id('c8828027-df48-4021-95df-b65b92c7429e')
+    def test_assignments_for_implied_roles_create_delete(self):
+        # Create a grant using "roles[0]"
+        self.roles_client.create_user_role_on_project(
+            self.project['id'], self.user_body['id'], self.roles[0]['id'])
+        self.addCleanup(
+            self.roles_client.delete_role_from_user_on_project,
+            self.project['id'], self.user_body['id'], self.roles[0]['id'])
+
+        # Create an inference rule from "roles[0]" to "roles[1]"
+        self._create_implied_role(self.roles[0]['id'], self.roles[1]['id'],
+                                  ignore_not_found=True)
+
+        # In the effective list of role assignments, both prior role and
+        # implied role should be present. This means that a user can
+        # authenticate using both roles (both roles will be present
+        # in the token).
+        params = {'scope.project.id': self.project['id'],
+                  'user.id': self.user_body['id']}
+        role_assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
+        self.assertEqual(2, len(role_assignments))
+
+        roles_ids = [assignment['role']['id']
+                     for assignment in role_assignments]
+        self.assertIn(self.roles[0]['id'], roles_ids)
+        self.assertIn(self.roles[1]['id'], roles_ids)
+
+        # After deleting the implied role, only the assignment with "roles[0]"
+        # should be present.
+        self.roles_client.delete_role_inference_rule(
+            self.roles[0]['id'], self.roles[1]['id'])
+
+        role_assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
+        self.assertEqual(1, len(role_assignments))
+
+        roles_ids = [assignment['role']['id']
+                     for assignment in role_assignments]
+        self.assertIn(self.roles[0]['id'], roles_ids)
+
+    @decorators.idempotent_id('d92a41d2-5501-497a-84bb-6e294330e8f8')
+    def test_domain_roles_create_delete(self):
+        domain_role = self.roles_client.create_role(
+            name=data_utils.rand_name('domain_role'),
+            domain_id=self.domain['id'])['role']
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.roles_client.delete_role,
+            domain_role['id'])
+
+        domain_roles = self.roles_client.list_roles(
+            domain_id=self.domain['id'])['roles']
+        self.assertEqual(1, len(domain_roles))
+        self.assertIn(domain_role, domain_roles)
+
+        self.roles_client.delete_role(domain_role['id'])
+        domain_roles = self.roles_client.list_roles(
+            domain_id=self.domain['id'])['roles']
+        self.assertEmpty(domain_roles)
+
+    @decorators.idempotent_id('eb1e1c24-1bc4-4d47-9748-e127a1852c82')
+    def test_implied_domain_roles(self):
+        # Create two roles in the same domain
+        domain_role1 = self.setup_test_role(domain_id=self.domain['id'])
+        domain_role2 = self.setup_test_role(domain_id=self.domain['id'])
+
+        # Check if we can create an inference rule from roles in the same
+        # domain
+        self._create_implied_role(domain_role1['id'], domain_role2['id'])
+
+        # Create another role in a different domain
+        domain2 = self.setup_test_domain()
+        domain_role3 = self.setup_test_role(domain_id=domain2['id'])
+
+        # Check if we can create cross domain implied roles
+        self._create_implied_role(domain_role1['id'], domain_role3['id'])
+
+        # Finally, we also should be able to create an implied from a
+        # domain role to a global one
+        self._create_implied_role(domain_role1['id'], self.role['id'])
+
+        if CONF.identity_feature_enabled.forbid_global_implied_dsr:
+            # The contrary is not true: we can't create an inference rule
+            # from a global role to a domain role
+            self.assertRaises(
+                lib_exc.Forbidden,
+                self.roles_client.create_role_inference_rule,
+                self.role['id'],
+                domain_role1['id'])
+
+    @decorators.idempotent_id('3859df7e-5b78-4e4d-b10e-214c8953842a')
+    def test_assignments_for_domain_roles(self):
+        domain_role = self.setup_test_role(domain_id=self.domain['id'])
+
+        # Create a grant using "domain_role"
+        self.roles_client.create_user_role_on_project(
+            self.project['id'], self.user_body['id'], domain_role['id'])
+        self.addCleanup(
+            self.roles_client.delete_role_from_user_on_project,
+            self.project['id'], self.user_body['id'], domain_role['id'])
+
+        # NOTE(rodrigods): Regular roles would appear in the effective
+        # list of role assignments (meaning the role would be returned in
+        # a token) as a result from the grant above. This is not the case
+        # for domain roles, they should not appear in the effective role
+        # assignments list.
+        params = {'scope.project.id': self.project['id'],
+                  'user.id': self.user_body['id']}
+        role_assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
+        self.assertEmpty(role_assignments)
+
+    @decorators.idempotent_id('3748c316-c18f-4b08-997b-c60567bc6235')
+    def test_list_all_implied_roles(self):
+        # Create inference rule from "roles[0]" to "roles[1]"
+        self._create_implied_role(
+            self.roles[0]['id'], self.roles[1]['id'])
+
+        # Create inference rule from "roles[0]" to "roles[2]"
+        self._create_implied_role(
+            self.roles[0]['id'], self.roles[2]['id'])
+
+        # Create inference rule from "roles[2]" to "role"
+        self._create_implied_role(
+            self.roles[2]['id'], self.role['id'])
+
+        rules = self.roles_client.list_all_role_inference_rules()[
+            'role_inferences']
+        # Sort the rules by the number of inferences, since there should be 1
+        # inference between "roles[2]" and "role" and 2 inferences for
+        # "roles[0]": between "roles[1]" and "roles[2]".
+        sorted_rules = sorted(rules, key=lambda r: len(r['implies']))
+
+        # Check that 2 sets of rules are returned.
+        self.assertEqual(2, len(sorted_rules))
+        # Check that only 1 inference rule exists between "roles[2]" and "role"
+        self.assertEqual(1, len(sorted_rules[0]['implies']))
+        # Check that 2 inference rules exist for "roles[0]": one between
+        # "roles[1]" and one between "roles[2]".
+        self.assertEqual(2, len(sorted_rules[1]['implies']))
+
+        # Check that "roles[2]" is the "prior_role" and that "role" is the
+        # "implies" role.
+        self.assertEqual(self.roles[2]['id'],
+                         sorted_rules[0]['prior_role']['id'])
+        self.assertEqual(self.role['id'],
+                         sorted_rules[0]['implies'][0]['id'])
+
+        # Check that "roles[0]" is the "prior_role" and that "roles[1]" and
+        # "roles[2]" are the "implies" roles.
+        self.assertEqual(self.roles[0]['id'],
+                         sorted_rules[1]['prior_role']['id'])
+
+        implies_ids = [r['id'] for r in sorted_rules[1]['implies']]
+        self.assertIn(self.roles[1]['id'], implies_ids)
+        self.assertIn(self.roles[2]['id'], implies_ids)
diff --git a/tempest/api/identity/admin/v3/test_services.py b/tempest/api/identity/admin/v3/test_services.py
index 35e1814..5afeb98 100644
--- a/tempest/api/identity/admin/v3/test_services.py
+++ b/tempest/api/identity/admin/v3/test_services.py
@@ -14,10 +14,9 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ServicesTestJSON(base.BaseIdentityV3AdminTest):
@@ -29,7 +28,7 @@
         self.assertRaises(lib_exc.NotFound, self.services_client.show_service,
                           service_id)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('5193aad5-bcb7-411d-85b0-b3b61b96ef06')
     def test_create_update_get_service(self):
         # Creating a Service
@@ -70,7 +69,6 @@
         service = self.services_client.create_service(
             type=serv_type, name=name)['service']
         self.addCleanup(self.services_client.delete_service, service['id'])
-        self.assertIn('id', service)
         expected_data = {'name': name, 'type': serv_type}
         self.assertDictContainsSubset(expected_data, service)
 
@@ -78,17 +76,26 @@
     def test_list_services(self):
         # Create, List, Verify and Delete Services
         service_ids = list()
+        service_types = list()
         for _ in range(3):
-            name = data_utils.rand_name('service')
-            serv_type = data_utils.rand_name('type')
+            name = data_utils.rand_name(self.__class__.__name__ + '-Service')
+            serv_type = data_utils.rand_name(self.__class__.__name__ + '-Type')
             create_service = self.services_client.create_service(
                 type=serv_type, name=name)['service']
             self.addCleanup(self.services_client.delete_service,
                             create_service['id'])
             service_ids.append(create_service['id'])
+            service_types.append(serv_type)
 
         # List and Verify Services
         services = self.services_client.list_services()['services']
         fetched_ids = [service['id'] for service in services]
         found = [s for s in fetched_ids if s in service_ids]
         self.assertEqual(len(found), len(service_ids))
+
+        # Check that filtering by service type works.
+        for serv_type in service_types:
+            fetched_services = self.services_client.list_services(
+                type=serv_type)['services']
+            self.assertEqual(1, len(fetched_services))
+            self.assertEqual(serv_type, fetched_services[0]['type'])
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index a9508d1..5c3cd26 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -16,10 +16,13 @@
 import six
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
+CONF = config.CONF
+
 
 class TokensV3TestJSON(base.BaseIdentityV3AdminTest):
 
@@ -29,16 +32,14 @@
         # Create a User
         u_name = data_utils.rand_name('user')
         u_desc = '%s-description' % u_name
-        u_email = '%s@testmail.tm' % u_name
         u_password = data_utils.rand_password()
-        user = self.users_client.create_user(
-            name=u_name, description=u_desc, password=u_password,
-            email=u_email)['user']
-        self.addCleanup(self.users_client.delete_user, user['id'])
+        user = self.create_test_user(
+            name=u_name, description=u_desc, password=u_password)
         # Perform Authentication
         resp = self.token.auth(user_id=user['id'],
                                password=u_password).response
         subject_token = resp['x-subject-token']
+        self.client.check_token_existence(subject_token)
         # Perform GET Token
         token_details = self.client.show_token(subject_token)['token']
         self.assertEqual(resp['x-subject-token'], subject_token)
@@ -46,7 +47,7 @@
         self.assertEqual(token_details['user']['name'], u_name)
         # Perform Delete Token
         self.client.delete_token(subject_token)
-        self.assertRaises(lib_exc.NotFound, self.client.show_token,
+        self.assertRaises(lib_exc.NotFound, self.client.check_token_existence,
                           subject_token)
 
     @decorators.idempotent_id('565fa210-1da1-4563-999b-f7b5b67cf112')
@@ -60,27 +61,19 @@
         """
 
         # Create a user.
-        user_name = data_utils.rand_name(name='user')
         user_password = data_utils.rand_password()
-        user = self.users_client.create_user(name=user_name,
-                                             password=user_password)['user']
-        self.addCleanup(self.users_client.delete_user, user['id'])
+        user = self.create_test_user(password=user_password)
 
         # Create a couple projects
         project1_name = data_utils.rand_name(name='project')
-        project1 = self.projects_client.create_project(
-            project1_name)['project']
-        self.addCleanup(self.projects_client.delete_project, project1['id'])
+        project1 = self.setup_test_project(name=project1_name)
 
         project2_name = data_utils.rand_name(name='project')
-        project2 = self.projects_client.create_project(
-            project2_name)['project']
+        project2 = self.setup_test_project(name=project2_name)
         self.addCleanup(self.projects_client.delete_project, project2['id'])
 
         # Create a role
-        role_name = data_utils.rand_name(name='role')
-        role = self.roles_client.create_role(name=role_name)['role']
-        self.addCleanup(self.roles_client.delete_role, role['id'])
+        role = self.setup_test_role()
 
         # Grant the user the role on both projects.
         self.roles_client.create_user_role_on_project(project1['id'],
@@ -104,18 +97,18 @@
         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'])
-        self.assertEqual('default',
+        self.assertEqual(CONF.identity.default_domain_id,
                          token_auth['token']['user']['domain']['id'])
-        self.assertEqual('Default',
-                         token_auth['token']['user']['domain']['name'])
+        self.assertIsNotNone(token_auth['token']['user']['domain']['name'])
         self.assertNotIn('catalog', token_auth['token'])
         self.assertNotIn('project', token_auth['token'])
         self.assertNotIn('roles', token_auth['token'])
 
         # Use the unscoped token to get a scoped token.
-        token_auth = self.token.auth(token=token_id,
-                                     project_name=project1_name,
-                                     project_domain_name='Default')
+        token_auth = self.token.auth(
+            token=token_id,
+            project_name=project1_name,
+            project_domain_id=CONF.identity.default_domain_id)
         token1_id = token_auth.response['x-subject-token']
 
         self.assertEqual(orig_expires_at, token_auth['token']['expires_at'],
@@ -130,10 +123,9 @@
                          token_auth['token']['project']['id'])
         self.assertEqual(project1['name'],
                          token_auth['token']['project']['name'])
-        self.assertEqual('default',
+        self.assertEqual(CONF.identity.default_domain_id,
                          token_auth['token']['project']['domain']['id'])
-        self.assertEqual('Default',
-                         token_auth['token']['project']['domain']['name'])
+        self.assertIsNotNone(token_auth['token']['project']['domain']['name'])
         self.assertEqual(1, len(token_auth['token']['roles']))
         self.assertEqual(role['id'], token_auth['token']['roles'][0]['id'])
         self.assertEqual(role['name'], token_auth['token']['roles'][0]['name'])
@@ -142,11 +134,43 @@
         self.client.delete_token(token1_id)
 
         # Now get another scoped token using the unscoped token.
-        token_auth = self.token.auth(token=token_id,
-                                     project_name=project2_name,
-                                     project_domain_name='Default')
+        token_auth = self.token.auth(
+            token=token_id,
+            project_name=project2_name,
+            project_domain_id=CONF.identity.default_domain_id)
 
         self.assertEqual(project2['id'],
                          token_auth['token']['project']['id'])
         self.assertEqual(project2['name'],
                          token_auth['token']['project']['name'])
+
+    @decorators.idempotent_id('08ed85ce-2ba8-4864-b442-bcc61f16ae89')
+    def test_get_available_project_scopes(self):
+        manager_project_id = self.os_primary.credentials.project_id
+        admin_user_id = self.os_admin.credentials.user_id
+        admin_role_id = self.get_role_by_name(CONF.identity.admin_role)['id']
+
+        # Grant the user the role on both projects.
+        self.roles_client.create_user_role_on_project(
+            manager_project_id, admin_user_id, admin_role_id)
+        self.addCleanup(
+            self.roles_client.delete_role_from_user_on_project,
+            manager_project_id, admin_user_id, admin_role_id)
+
+        assigned_project_ids = [self.os_admin.credentials.project_id,
+                                manager_project_id]
+
+        # Get available project scopes
+        available_projects =\
+            self.client.list_auth_projects()['projects']
+
+        # create list to save fetched project's id
+        fetched_project_ids = [i['id'] for i in available_projects]
+
+        # verifying the project ids in list
+        missing_project_ids = \
+            [p for p in assigned_project_ids
+             if p not in fetched_project_ids]
+        self.assertEmpty(missing_project_ids,
+                         "Failed to find project_id %s in fetched list" %
+                         ', '.join(missing_project_ids))
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 1a466a0..2530072 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -18,52 +18,57 @@
 from tempest.api.identity import base
 from tempest import clients
 from tempest.common import credentials_factory as common_creds
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
 
-class BaseTrustsV3Test(base.BaseIdentityV3AdminTest):
+class TrustsV3TestJSON(base.BaseIdentityV3AdminTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(TrustsV3TestJSON, cls).skip_checks()
+        if not CONF.identity_feature_enabled.trust:
+            raise cls.skipException("Trusts aren't enabled")
 
     def setUp(self):
-        super(BaseTrustsV3Test, self).setUp()
+        super(TrustsV3TestJSON, self).setUp()
         # Use alt_username as the trustee
-        if not CONF.identity_feature_enabled.trust:
-            raise self.skipException("Trusts aren't enabled")
-
         self.trust_id = None
+        self.create_trustor_and_roles()
+        self.addCleanup(self.cleanup_user_and_roles)
 
     def tearDown(self):
         if self.trust_id:
             # Do the delete in tearDown not addCleanup - we want the test to
             # fail in the event there is a bug which causes undeletable trusts
             self.delete_trust()
-        super(BaseTrustsV3Test, self).tearDown()
+        super(TrustsV3TestJSON, self).tearDown()
 
     def create_trustor_and_roles(self):
         # create a project that trusts will be granted on
-        self.trustor_project_name = data_utils.rand_name(name='project')
+        trustor_project_name = data_utils.rand_name(name='project')
         project = self.projects_client.create_project(
-            self.trustor_project_name, domain_id='default')['project']
+            trustor_project_name,
+            domain_id=CONF.identity.default_domain_id)['project']
         self.trustor_project_id = project['id']
         self.assertIsNotNone(self.trustor_project_id)
 
         # Create a trustor User
-        self.trustor_username = data_utils.rand_name('user')
-        u_desc = self.trustor_username + 'description'
-        u_email = self.trustor_username + '@testmail.xx'
-        self.trustor_password = data_utils.rand_password()
+        trustor_username = data_utils.rand_name('user')
+        u_desc = trustor_username + 'description'
+        u_email = trustor_username + '@testmail.xx'
+        trustor_password = data_utils.rand_password()
         user = self.users_client.create_user(
-            name=self.trustor_username,
+            name=trustor_username,
             description=u_desc,
-            password=self.trustor_password,
+            password=trustor_password,
             email=u_email,
             project_id=self.trustor_project_id,
-            domain_id='default')['user']
+            domain_id=CONF.identity.default_domain_id)['user']
         self.trustor_user_id = user['id']
 
         # And two roles, one we'll delegate and one we won't
@@ -95,12 +100,12 @@
         # Initialize a new client with the trustor credentials
         creds = common_creds.get_credentials(
             identity_version='v3',
-            username=self.trustor_username,
-            password=self.trustor_password,
-            user_domain_id='default',
-            tenant_name=self.trustor_project_name,
-            project_domain_id='default',
-            domain_id='default')
+            username=trustor_username,
+            password=trustor_password,
+            user_domain_id=CONF.identity.default_domain_id,
+            tenant_name=trustor_project_name,
+            project_domain_id=CONF.identity.default_domain_id,
+            domain_id=CONF.identity.default_domain_id)
         os = clients.Manager(credentials=creds)
         self.trustor_client = os.trusts_client
 
@@ -190,14 +195,6 @@
                           self.trust_id)
         self.trust_id = None
 
-
-class TrustsV3TestJSON(BaseTrustsV3Test):
-
-    def setUp(self):
-        super(TrustsV3TestJSON, self).setUp()
-        self.create_trustor_and_roles()
-        self.addCleanup(self.cleanup_user_and_roles)
-
     @decorators.idempotent_id('5a0a91a4-baef-4a14-baba-59bf4d7fcace')
     def test_trust_impersonate(self):
         # Test case to check we can create, get and delete a trust
@@ -233,10 +230,12 @@
         # For example, when creating a trust, we will set the expiry time of
         # the trust to 2015-02-17T17:34:01.907051Z. However, if we make a GET
         # request on the trust, the response will contain the time rounded up
-        # to 2015-02-17T17:34:02.000000Z. That is why we shouldn't set flag
-        # "subsecond" to True when we invoke timeutils.isotime(...) to avoid
-        # problems with rounding.
-        expires_str = timeutils.isotime(at=expires_at)
+        # to 2015-02-17T17:34:02.000000Z. That is why we set microsecond to
+        # 0 when we invoke isoformat to avoid problems with rounding.
+        expires_at = expires_at.replace(microsecond=0)
+        # NOTE(ekhugen) Python datetime does not support military timezones
+        # since we used UTC we'll add the Z so our compare works.
+        expires_str = expires_at.isoformat() + 'Z'
 
         trust = self.create_trust(expires=expires_str)
         self.validate_trust(trust, expires=expires_str)
@@ -265,7 +264,7 @@
         self.assertEqual(1, len(trusts_get))
         self.validate_trust(trusts_get[0], summary=True)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('4773ebd5-ecbf-4255-b8d8-b63e6f72b65d')
     def test_get_trusts_all(self):
 
@@ -277,9 +276,9 @@
         # 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)
+        original_scope = self.os_admin.auth_provider.scope
+        set_scope(self.os_admin.auth_provider, 'project')
+        self.addCleanup(set_scope, self.os_admin.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/admin/v3/test_users.py b/tempest/api/identity/admin/v3/test_users.py
index e0ec883..3813568 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -18,8 +18,8 @@
 import testtools
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 
@@ -41,35 +41,26 @@
             email=u_email, enabled=False)['user']
         # Delete the User at the end of this method
         self.addCleanup(self.users_client.delete_user, user['id'])
+
         # Creating second project for updation
-        project = self.projects_client.create_project(
-            data_utils.rand_name('project'),
-            description=data_utils.rand_name('project-desc'))['project']
-        # Delete the Project at the end of this method
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project()
+
         # Updating user details with new values
-        u_name2 = data_utils.rand_name('user2')
-        u_email2 = u_name2 + '@testmail.tm'
-        u_description2 = u_name2 + ' description'
-        update_user = self.users_client.update_user(
-            user['id'], name=u_name2, description=u_description2,
-            project_id=project['id'],
-            email=u_email2, enabled=False)['user']
-        self.assertEqual(u_name2, update_user['name'])
-        self.assertEqual(u_description2, update_user['description'])
-        self.assertEqual(project['id'],
-                         update_user['project_id'])
-        self.assertEqual(u_email2, update_user['email'])
-        self.assertEqual(False, update_user['enabled'])
-        # GET by id after updation
+        update_kwargs = {'name': data_utils.rand_name('user2'),
+                         'description': data_utils.rand_name('desc2'),
+                         'project_id': project['id'],
+                         'email': 'user2@testmail.tm',
+                         'enabled': False}
+        updated_user = self.users_client.update_user(
+            user['id'], **update_kwargs)['user']
+        for field in update_kwargs:
+            self.assertEqual(update_kwargs[field], updated_user[field])
+
+        # GET by id after updating
         new_user_get = self.users_client.show_user(user['id'])['user']
         # Assert response body of GET after updation
-        self.assertEqual(u_name2, new_user_get['name'])
-        self.assertEqual(u_description2, new_user_get['description'])
-        self.assertEqual(project['id'],
-                         new_user_get['project_id'])
-        self.assertEqual(u_email2, new_user_get['email'])
-        self.assertEqual(False, new_user_get['enabled'])
+        for field in update_kwargs:
+            self.assertEqual(update_kwargs[field], new_user_get[field])
 
     @decorators.idempotent_id('2d223a0e-e457-4a70-9fb1-febe027a0ff9')
     def test_update_user_password(self):
@@ -102,11 +93,7 @@
         # List the projects that a user has access upon
         assigned_project_ids = list()
         fetched_project_ids = list()
-        u_project = self.projects_client.create_project(
-            data_utils.rand_name('project'),
-            description=data_utils.rand_name('project-desc'))['project']
-        # Delete the Project at the end of this method
-        self.addCleanup(self.projects_client.delete_project, u_project['id'])
+        u_project = self.setup_test_project()
         # Create a user.
         u_name = data_utils.rand_name('user')
         u_desc = u_name + 'description'
@@ -118,23 +105,15 @@
         # Delete the User at the end of this method
         self.addCleanup(self.users_client.delete_user, user_body['id'])
         # Creating Role
-        role_body = self.roles_client.create_role(
-            name=data_utils.rand_name('role'))['role']
-        # Delete the Role at the end of this method
-        self.addCleanup(self.roles_client.delete_role, role_body['id'])
+        role_body = self.setup_test_role()
 
         user = self.users_client.show_user(user_body['id'])['user']
         role = self.roles_client.show_role(role_body['id'])['role']
         for _ in range(2):
             # Creating project so as to assign role
-            project_body = self.projects_client.create_project(
-                data_utils.rand_name('project'),
-                description=data_utils.rand_name('project-desc'))['project']
+            project_body = self.setup_test_project()
             project = self.projects_client.show_project(
                 project_body['id'])['project']
-            # Delete the Project at the end of this method
-            self.addCleanup(
-                self.projects_client.delete_project, project_body['id'])
             # Assigning roles to user on project
             self.roles_client.create_user_role_on_project(project['id'],
                                                           user['id'],
@@ -147,7 +126,7 @@
         missing_projects =\
             [p for p in assigned_project_ids
              if p not in fetched_project_ids]
-        self.assertEqual(0, len(missing_projects),
+        self.assertEmpty(missing_projects,
                          "Failed to find project %s in fetched list" %
                          ', '.join(m_project for m_project
                                    in missing_projects))
@@ -163,8 +142,8 @@
                           'Security compliance not available.')
     @decorators.idempotent_id('568cd46c-ee6c-4ab4-a33a-d3791931979e')
     def test_password_history_not_enforced_in_admin_reset(self):
-        old_password = self.os.credentials.password
-        user_id = self.os.credentials.user_id
+        old_password = self.os_primary.credentials.password
+        user_id = self.os_primary.credentials.user_id
 
         new_password = data_utils.rand_password()
         self.users_client.update_user(user_id, password=new_password)
diff --git a/tempest/api/identity/admin/v3/test_users_negative.py b/tempest/api/identity/admin/v3/test_users_negative.py
index 9626108..11dcdb0 100644
--- a/tempest/api/identity/admin/v3/test_users_negative.py
+++ b/tempest/api/identity/admin/v3/test_users_negative.py
@@ -14,15 +14,17 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
+
+CONF = config.CONF
 
 
 class UsersNegativeTest(base.BaseIdentityV3AdminTest):
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e75f006c-89cc-477b-874d-588e4eab4b17')
     def test_create_user_for_non_existent_domain(self):
         # Attempt to create a user in a non-existent domain should fail
@@ -34,7 +36,7 @@
                           email=u_email,
                           domain_id=data_utils.rand_uuid_hex())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b3c9fccc-4134-46f5-b600-1da6fb0a3b1f')
     def test_authentication_for_disabled_user(self):
         # Attempt to authenticate for disabled user should fail
@@ -44,4 +46,4 @@
         self.assertRaises(lib_exc.Unauthorized, self.token.auth,
                           username=user['name'],
                           password=password,
-                          user_domain_id='default')
+                          user_domain_id=CONF.identity.default_domain_id)
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 9515788..30d2a36 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 import tempest.test
 
 CONF = config.CONF
@@ -46,7 +47,7 @@
         else:
             users = cls.users_client.list_users()['users']
         user = [u for u in users if u['name'] == name]
-        if len(user) > 0:
+        if user:
             return user[0]
 
     @classmethod
@@ -56,31 +57,42 @@
         except AttributeError:
             tenants = cls.projects_client.list_projects()['projects']
         tenant = [t for t in tenants if t['name'] == name]
-        if len(tenant) > 0:
+        if tenant:
             return tenant[0]
 
     @classmethod
     def get_role_by_name(cls, name):
         roles = cls.roles_client.list_roles()['roles']
         role = [r for r in roles if r['name'] == name]
-        if len(role) > 0:
+        if role:
             return role[0]
 
-    def _create_test_user(self, **kwargs):
-        if kwargs['password'] is None:
-            user_password = data_utils.rand_password()
-            kwargs['password'] = user_password
+    def create_test_user(self, **kwargs):
+        if kwargs.get('password', None) is None:
+            kwargs['password'] = data_utils.rand_password()
+        if 'name' not in kwargs:
+            kwargs['name'] = data_utils.rand_name('test_user')
+        if 'email' not in kwargs:
+            kwargs['email'] = kwargs['name'] + '@testmail.tm'
+
         user = self.users_client.create_user(**kwargs)['user']
         # Delete the user at the end of the test
-        self.addCleanup(self.users_client.delete_user, user['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.users_client.delete_user, user['id'])
         return user
 
-    def setup_test_role(self):
+    def setup_test_role(self, name=None, domain_id=None):
         """Set up a test role."""
-        role = self.roles_client.create_role(
-            name=data_utils.rand_name('test_role'))['role']
+        params = {'name': name or data_utils.rand_name('test_role')}
+        if domain_id:
+            params['domain_id'] = domain_id
+
+        role = self.roles_client.create_role(**params)['role']
         # Delete the role at the end of the test
-        self.addCleanup(self.roles_client.delete_role, role['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.roles_client.delete_role, role['id'])
         return role
 
 
@@ -95,10 +107,10 @@
     @classmethod
     def setup_clients(cls):
         super(BaseIdentityV2Test, cls).setup_clients()
-        cls.non_admin_client = cls.os.identity_public_client
-        cls.non_admin_token_client = cls.os.token_client
-        cls.non_admin_tenants_client = cls.os.tenants_public_client
-        cls.non_admin_users_client = cls.os.users_public_client
+        cls.non_admin_client = cls.os_primary.identity_public_client
+        cls.non_admin_token_client = cls.os_primary.token_client
+        cls.non_admin_tenants_client = cls.os_primary.tenants_public_client
+        cls.non_admin_users_client = cls.os_primary.users_public_client
 
 
 class BaseIdentityV2AdminTest(BaseIdentityV2Test):
@@ -115,19 +127,25 @@
     force_tenant_isolation = True
 
     @classmethod
+    def skip_checks(cls):
+        super(BaseIdentityV2AdminTest, cls).skip_checks()
+        if not CONF.identity_feature_enabled.api_v2_admin:
+            raise cls.skipException('Identity v2 admin not available')
+
+    @classmethod
     def setup_clients(cls):
         super(BaseIdentityV2AdminTest, cls).setup_clients()
-        cls.client = cls.os_adm.identity_client
-        cls.non_admin_client = cls.os.identity_client
-        cls.token_client = cls.os_adm.token_client
-        cls.tenants_client = cls.os_adm.tenants_client
-        cls.non_admin_tenants_client = cls.os.tenants_client
-        cls.roles_client = cls.os_adm.roles_client
-        cls.non_admin_roles_client = cls.os.roles_client
-        cls.users_client = cls.os_adm.users_client
-        cls.non_admin_users_client = cls.os.users_client
-        cls.services_client = cls.os_adm.identity_services_client
-        cls.endpoints_client = cls.os_adm.endpoints_client
+        cls.client = cls.os_admin.identity_client
+        cls.non_admin_client = cls.os_primary.identity_client
+        cls.token_client = cls.os_admin.token_client
+        cls.tenants_client = cls.os_admin.tenants_client
+        cls.non_admin_tenants_client = cls.os_primary.tenants_client
+        cls.roles_client = cls.os_admin.roles_client
+        cls.non_admin_roles_client = cls.os_primary.roles_client
+        cls.users_client = cls.os_admin.users_client
+        cls.non_admin_users_client = cls.os_primary.users_client
+        cls.services_client = cls.os_admin.identity_services_client
+        cls.endpoints_client = cls.os_admin.endpoints_client
 
     @classmethod
     def resource_setup(cls):
@@ -137,19 +155,20 @@
     def setup_test_user(self, password=None):
         """Set up a test user."""
         tenant = self.setup_test_tenant()
-        username = data_utils.rand_name('test_user')
-        email = username + '@testmail.tm'
-        user = self._create_test_user(name=username, email=email,
-                                      tenantId=tenant['id'], password=password)
+        user = self.create_test_user(tenantId=tenant['id'], password=password)
         return user
 
-    def setup_test_tenant(self):
+    def setup_test_tenant(self, **kwargs):
         """Set up a test tenant."""
-        tenant = self.projects_client.create_tenant(
-            name=data_utils.rand_name('test_tenant'),
-            description=data_utils.rand_name('desc'))['tenant']
+        if 'name' not in kwargs:
+            kwargs['name'] = data_utils.rand_name('test_tenant')
+        if 'description' not in kwargs:
+            kwargs['description'] = data_utils.rand_name('desc')
+        tenant = self.projects_client.create_tenant(**kwargs)['tenant']
         # Delete the tenant at the end of the test
-        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.tenants_client.delete_tenant, tenant['id'])
         return tenant
 
 
@@ -164,10 +183,13 @@
     @classmethod
     def setup_clients(cls):
         super(BaseIdentityV3Test, cls).setup_clients()
-        cls.non_admin_client = cls.os.identity_v3_client
-        cls.non_admin_users_client = cls.os.users_v3_client
-        cls.non_admin_token = cls.os.token_v3_client
-        cls.non_admin_projects_client = cls.os.projects_client
+        cls.non_admin_client = cls.os_primary.identity_v3_client
+        cls.non_admin_users_client = cls.os_primary.users_v3_client
+        cls.non_admin_token = cls.os_primary.token_v3_client
+        cls.non_admin_projects_client = cls.os_primary.projects_client
+        cls.non_admin_catalog_client = cls.os_primary.catalog_client
+        cls.non_admin_versions_client =\
+            cls.os_primary.identity_versions_v3_client
 
 
 class BaseIdentityV3AdminTest(BaseIdentityV3Test):
@@ -186,27 +208,33 @@
     @classmethod
     def setup_clients(cls):
         super(BaseIdentityV3AdminTest, cls).setup_clients()
-        cls.client = cls.os_adm.identity_v3_client
-        cls.domains_client = cls.os_adm.domains_client
-        cls.users_client = cls.os_adm.users_v3_client
-        cls.trusts_client = cls.os_adm.trusts_client
-        cls.roles_client = cls.os_adm.roles_v3_client
-        cls.inherited_roles_client = cls.os_adm.inherited_roles_client
-        cls.token = cls.os_adm.token_v3_client
-        cls.endpoints_client = cls.os_adm.endpoints_v3_client
-        cls.regions_client = cls.os_adm.regions_client
-        cls.services_client = cls.os_adm.identity_services_v3_client
-        cls.policies_client = cls.os_adm.policies_client
-        cls.creds_client = cls.os_adm.credentials_client
-        cls.groups_client = cls.os_adm.groups_client
-        cls.projects_client = cls.os_adm.projects_client
+        cls.client = cls.os_admin.identity_v3_client
+        cls.domains_client = cls.os_admin.domains_client
+        cls.users_client = cls.os_admin.users_v3_client
+        cls.trusts_client = cls.os_admin.trusts_client
+        cls.roles_client = cls.os_admin.roles_v3_client
+        cls.inherited_roles_client = cls.os_admin.inherited_roles_client
+        cls.token = cls.os_admin.token_v3_client
+        cls.endpoints_client = cls.os_admin.endpoints_v3_client
+        cls.regions_client = cls.os_admin.regions_client
+        cls.services_client = cls.os_admin.identity_services_v3_client
+        cls.policies_client = cls.os_admin.policies_client
+        cls.creds_client = cls.os_admin.credentials_client
+        cls.groups_client = cls.os_admin.groups_client
+        cls.projects_client = cls.os_admin.projects_client
         cls.role_assignments = cls.os_admin.role_assignments_client
+        cls.oauth_consumers_client = cls.os_admin.oauth_consumers_client
+        cls.oauth_token_client = cls.os_admin.oauth_token_client
+        cls.domain_config_client = cls.os_admin.domain_config_client
+        cls.endpoint_filter_client = cls.os_admin.endpoint_filter_client
+        cls.endpoint_groups_client = cls.os_admin.endpoint_groups_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'
+            cls.os_admin.auth_provider.scope = 'domain'
 
     @classmethod
     def disable_user(cls, user_name, domain_id=None):
@@ -214,11 +242,13 @@
         cls.users_client.update_user(user['id'], name=user_name, enabled=False)
 
     @classmethod
-    def create_domain(cls):
+    def create_domain(cls, **kwargs):
         """Create a domain."""
-        domain = cls.domains_client.create_domain(
-            name=data_utils.rand_name('test_domain'),
-            description=data_utils.rand_name('desc'))['domain']
+        if 'name' not in kwargs:
+            kwargs['name'] = data_utils.rand_name('test_domain')
+        if 'description' not in kwargs:
+            kwargs['description'] = data_utils.rand_name('desc')
+        domain = cls.domains_client.create_domain(**kwargs)['domain']
         return domain
 
     def delete_domain(self, domain_id):
@@ -230,25 +260,28 @@
     def setup_test_user(self, password=None):
         """Set up a test user."""
         project = self.setup_test_project()
-        username = data_utils.rand_name('test_user')
-        email = username + '@testmail.tm'
-        user = self._create_test_user(name=username, email=email,
-                                      project_id=project['id'],
-                                      password=password)
+        user = self.create_test_user(project_id=project['id'],
+                                     password=password)
         return user
 
-    def setup_test_project(self):
+    def setup_test_project(self, **kwargs):
         """Set up a test project."""
-        project = self.projects_client.create_project(
-            name=data_utils.rand_name('test_project'),
-            description=data_utils.rand_name('desc'))['project']
+        if 'name' not in kwargs:
+            kwargs['name'] = data_utils.rand_name('test_project')
+        if 'description' not in kwargs:
+            kwargs['description'] = data_utils.rand_name('test_description')
+        project = self.projects_client.create_project(**kwargs)['project']
         # Delete the project at the end of the test
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.projects_client.delete_project, project['id'])
         return project
 
     def setup_test_domain(self):
         """Set up a test domain."""
         domain = self.create_domain()
         # Delete the domain at the end of the test
-        self.addCleanup(self.delete_domain, domain['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.delete_domain, domain['id'])
         return domain
diff --git a/tempest/api/identity/v2/test_api_discovery.py b/tempest/api/identity/v2/test_api_discovery.py
index 02caef5..5b9d38c 100644
--- a/tempest/api/identity/v2/test_api_discovery.py
+++ b/tempest/api/identity/v2/test_api_discovery.py
@@ -15,13 +15,12 @@
 
 from tempest.api.identity import base
 from tempest.lib import decorators
-from tempest import test
 
 
 class TestApiDiscovery(base.BaseIdentityV2Test):
     """Tests for API discovery features."""
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('ea889a68-a15f-4166-bfb1-c12456eae853')
     def test_api_version_resources(self):
         descr = self.non_admin_client.show_api_description()['version']
@@ -32,7 +31,7 @@
         for res in expected_resources:
             self.assertIn(res, keys)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('007a0be0-78fe-4fdb-bbee-e9216cc17bb2')
     def test_api_media_types(self):
         descr = self.non_admin_client.show_api_description()['version']
@@ -47,7 +46,7 @@
         for s_type in supported_types:
             self.assertIn(s_type, media_types)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('77fd6be0-8801-48e6-b9bf-38cdd2f253ec')
     def test_api_version_statuses(self):
         descr = self.non_admin_client.show_api_description()['version']
diff --git a/tempest/api/identity/v2/test_ec2_credentials.py b/tempest/api/identity/v2/test_ec2_credentials.py
index 7a0f3d7..237e728 100644
--- a/tempest/api/identity/v2/test_ec2_credentials.py
+++ b/tempest/api/identity/v2/test_ec2_credentials.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 from tempest.api.identity import base
+from tempest.common import utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class EC2CredentialsTest(base.BaseIdentityV2Test):
@@ -24,14 +24,14 @@
     @classmethod
     def skip_checks(cls):
         super(EC2CredentialsTest, cls).skip_checks()
-        if not test.is_extension_enabled('OS-EC2', 'identity'):
+        if not utils.is_extension_enabled('OS-EC2', 'identity'):
             msg = "OS-EC2 identity extension not enabled."
             raise cls.skipException(msg)
 
     @classmethod
     def resource_setup(cls):
         super(EC2CredentialsTest, cls).resource_setup()
-        cls.creds = cls.os.credentials
+        cls.creds = cls.os_primary.credentials
 
     @decorators.idempotent_id('b580fab9-7ae9-46e8-8138-417260cb6f9f')
     def test_create_ec2_credential(self):
diff --git a/tempest/api/identity/test_extension.py b/tempest/api/identity/v2/test_extension.py
similarity index 95%
rename from tempest/api/identity/test_extension.py
rename to tempest/api/identity/v2/test_extension.py
index 7095181..c538c14 100644
--- a/tempest/api/identity/test_extension.py
+++ b/tempest/api/identity/v2/test_extension.py
@@ -17,7 +17,7 @@
 from tempest.lib import decorators
 
 
-class ExtensionTestJSON(base.BaseIdentityV2AdminTest):
+class ExtensionTestJSON(base.BaseIdentityV2Test):
 
     @decorators.idempotent_id('85f3f661-f54c-4d48-b563-72ae952b9383')
     def test_list_extensions(self):
diff --git a/tempest/api/identity/v2/test_tenants.py b/tempest/api/identity/v2/test_tenants.py
index 2689998..b2a6d13 100644
--- a/tempest/api/identity/v2/test_tenants.py
+++ b/tempest/api/identity/v2/test_tenants.py
@@ -24,7 +24,7 @@
 
     @decorators.idempotent_id('ecae2459-243d-4ba1-ad02-65f15dc82b78')
     def test_list_tenants_returns_only_authorized_tenants(self):
-        alt_tenant_name = self.alt_manager.credentials.tenant_name
+        alt_tenant_name = self.os_alt.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
@@ -32,18 +32,19 @@
         # from received tenants list
         for tenant in resp['tenants']:
             body = self.non_admin_token_client.auth(
-                self.os.credentials.username,
-                self.os.credentials.password,
+                self.os_primary.credentials.username,
+                self.os_primary.credentials.password,
                 tenant['name'])
             self.assertNotEmpty(body['token']['id'])
             self.assertEqual(body['token']['tenant']['id'], tenant['id'])
             self.assertEqual(body['token']['tenant']['name'], tenant['name'])
-            self.assertEqual(body['user']['id'], self.os.credentials.user_id)
+            self.assertEqual(
+                body['user']['id'], self.os_primary.credentials.user_id)
 
         # check that user cannot log in to alt user's tenant
         self.assertRaises(
             lib_exc.Unauthorized,
             self.non_admin_token_client.auth,
-            self.os.credentials.username,
-            self.os.credentials.password,
+            self.os_primary.credentials.username,
+            self.os_primary.credentials.password,
             alt_tenant_name)
diff --git a/tempest/api/identity/v2/test_tokens.py b/tempest/api/identity/v2/test_tokens.py
index 79a1765..64b81c2 100644
--- a/tempest/api/identity/v2/test_tokens.py
+++ b/tempest/api/identity/v2/test_tokens.py
@@ -27,7 +27,7 @@
         token_client = self.non_admin_token_client
 
         # get a token for the user
-        creds = self.os.credentials
+        creds = self.os_primary.credentials
         username = creds.username
         password = creds.password
         tenant_name = creds.tenant_name
diff --git a/tempest/api/identity/v2/test_users.py b/tempest/api/identity/v2/test_users.py
index 2b42981..9c77fff 100644
--- a/tempest/api/identity/v2/test_users.py
+++ b/tempest/api/identity/v2/test_users.py
@@ -30,7 +30,7 @@
     @classmethod
     def resource_setup(cls):
         super(IdentityUsersTest, cls).resource_setup()
-        cls.creds = cls.os.credentials
+        cls.creds = cls.os_primary.credentials
         cls.username = cls.creds.username
         cls.password = cls.creds.password
         cls.tenant_name = cls.creds.tenant_name
diff --git a/tempest/api/identity/v3/test_api_discovery.py b/tempest/api/identity/v3/test_api_discovery.py
index 74e9ec1..c04c21b 100644
--- a/tempest/api/identity/v3/test_api_discovery.py
+++ b/tempest/api/identity/v3/test_api_discovery.py
@@ -14,14 +14,28 @@
 #    under the License.
 
 from tempest.api.identity import base
-from tempest import test
+from tempest.lib import decorators
 
 
 class TestApiDiscovery(base.BaseIdentityV3Test):
     """Tests for API discovery features."""
 
-    @test.attr(type='smoke')
-    @test.idempotent_id('b9232f5e-d9e5-4d97-b96c-28d3db4de1bd')
+    @decorators.idempotent_id('721f480f-35b6-46c7-846e-047e6acea0dc')
+    @decorators.attr(type='smoke')
+    def test_list_api_versions(self):
+        # NOTE: Actually this API doesn't depend on v3 API at all, because
+        # the API operation is "GET /" without v3's endpoint. The reason of
+        # this test path is just v3 API is CURRENT on Keystone side.
+        versions = self.non_admin_versions_client.list_versions()
+        expected_resources = ('id', 'links', 'media-types', 'status',
+                              'updated')
+
+        for version in versions['versions']["values"]:
+            for res in expected_resources:
+                self.assertIn(res, version)
+
+    @decorators.attr(type='smoke')
+    @decorators.idempotent_id('b9232f5e-d9e5-4d97-b96c-28d3db4de1bd')
     def test_api_version_resources(self):
         descr = self.non_admin_client.show_api_description()['version']
         expected_resources = ('id', 'links', 'media-types', 'status',
@@ -31,8 +45,8 @@
         for res in expected_resources:
             self.assertIn(res, keys)
 
-    @test.attr(type='smoke')
-    @test.idempotent_id('657c1970-4722-4189-8831-7325f3bc4265')
+    @decorators.attr(type='smoke')
+    @decorators.idempotent_id('657c1970-4722-4189-8831-7325f3bc4265')
     def test_api_media_types(self):
         descr = self.non_admin_client.show_api_description()['version']
         # Get MIME type bases and descriptions
@@ -46,8 +60,8 @@
         for s_type in supported_types:
             self.assertIn(s_type, media_types)
 
-    @test.attr(type='smoke')
-    @test.idempotent_id('8879a470-abfb-47bb-bb8d-5a7fd279ad1e')
+    @decorators.attr(type='smoke')
+    @decorators.idempotent_id('8879a470-abfb-47bb-bb8d-5a7fd279ad1e')
     def test_api_version_statuses(self):
         descr = self.non_admin_client.show_api_description()['version']
         status = descr['status'].lower()
diff --git a/tempest/api/identity/v3/test_catalog.py b/tempest/api/identity/v3/test_catalog.py
new file mode 100755
index 0000000..deec2dc
--- /dev/null
+++ b/tempest/api/identity/v3/test_catalog.py
@@ -0,0 +1,41 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES 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 config
+from tempest.lib import decorators
+
+
+CONF = config.CONF
+
+
+class IdentityCatalogTest(base.BaseIdentityV3Test):
+
+    @decorators.idempotent_id('56b57ced-22b8-4127-9b8a-565dfb0207e2')
+    def test_catalog_standardization(self):
+        # http://git.openstack.org/cgit/openstack/service-types-authority
+        # /tree/service-types.yaml
+        standard_service_values = [{'name': 'keystone', 'type': 'identity'},
+                                   {'name': 'nova', 'type': 'compute'},
+                                   {'name': 'glance', 'type': 'image'},
+                                   {'name': 'swift', 'type': 'object-store'}]
+        # next, we need to GET the catalog using the catalog client
+        catalog = self.non_admin_catalog_client.show_catalog()['catalog']
+        # get list of the service types present in the catalog
+        catalog_services = []
+        for service in catalog:
+            catalog_services.append(service['type'])
+        for service in standard_service_values:
+            # if service enabled, check if it has a standard typevalue
+            if service['name'] == 'keystone' or\
+                    getattr(CONF.service_available, service['name']):
+                self.assertIn(service['type'], catalog_services)
diff --git a/tempest/api/identity/v3/test_projects.py b/tempest/api/identity/v3/test_projects.py
index 26cb90b..0ae35ea 100644
--- a/tempest/api/identity/v3/test_projects.py
+++ b/tempest/api/identity/v3/test_projects.py
@@ -14,20 +14,20 @@
 #    under the License.
 
 from tempest.api.identity import base
+from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class IdentityV3ProjectsTest(base.BaseIdentityV3Test):
 
     credentials = ['primary', 'alt']
 
-    @test.idempotent_id('86128d46-e170-4644-866a-cc487f699e1d')
+    @decorators.idempotent_id('86128d46-e170-4644-866a-cc487f699e1d')
     def test_list_projects_returns_only_authorized_projects(self):
         alt_project_name =\
-            self.alt_manager.credentials.project_name
+            self.os_alt.credentials.project_name
         resp = self.non_admin_users_client.list_user_projects(
-            self.os.credentials.user_id)
+            self.os_primary.credentials.user_id)
 
         # check that user can see only that projects that he presents in so
         # user can successfully authenticate using his credentials and
@@ -36,23 +36,24 @@
             # 'user_domain_id' needs to be specified otherwise tempest.lib
             # assumes it to be 'default'
             token_id, body = self.non_admin_token.get_token(
-                username=self.os.credentials.username,
-                user_domain_id=self.os.credentials.user_domain_id,
-                password=self.os.credentials.password,
+                username=self.os_primary.credentials.username,
+                user_domain_id=self.os_primary.credentials.user_domain_id,
+                password=self.os_primary.credentials.password,
                 project_name=project['name'],
                 project_domain_id=project['domain_id'],
                 auth_data=True)
             self.assertNotEmpty(token_id)
             self.assertEqual(body['project']['id'], project['id'])
             self.assertEqual(body['project']['name'], project['name'])
-            self.assertEqual(body['user']['id'], self.os.credentials.user_id)
+            self.assertEqual(
+                body['user']['id'], self.os_primary.credentials.user_id)
 
         # check that user cannot log in to alt user's project
         self.assertRaises(
             lib_exc.Unauthorized,
             self.non_admin_token.get_token,
-            username=self.os.credentials.username,
-            user_domain_id=self.os.credentials.user_domain_id,
-            password=self.os.credentials.password,
+            username=self.os_primary.credentials.username,
+            user_domain_id=self.os_primary.credentials.user_domain_id,
+            password=self.os_primary.credentials.password,
             project_name=alt_project_name,
             project_domain_id=project['domain_id'])
diff --git a/tempest/api/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index b410da6..4c72d82 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -15,16 +15,43 @@
 
 from oslo_utils import timeutils
 import six
+
 from tempest.api.identity import base
-from tempest import test
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 
 class TokensV3Test(base.BaseIdentityV3Test):
 
-    @test.idempotent_id('6f8e4436-fc96-4282-8122-e41df57197a9')
+    @decorators.idempotent_id('a9512ac3-3909-48a4-b395-11f438e16260')
+    def test_validate_token(self):
+        creds = self.os_primary.credentials
+        user_id = creds.user_id
+        username = creds.username
+        password = creds.password
+        user_domain_id = creds.user_domain_id
+        # GET and validate token
+        subject_token, token_body = self.non_admin_token.get_token(
+            user_id=user_id,
+            username=username,
+            user_domain_id=user_domain_id,
+            password=password,
+            auth_data=True)
+        authenticated_token = self.non_admin_client.show_token(
+            subject_token)['token']
+        # sanity checking to make sure they are indeed the same token
+        self.assertEqual(authenticated_token, token_body)
+        # test to see if token has been properly authenticated
+        self.assertEqual(authenticated_token['user']['id'], user_id)
+        self.assertEqual(authenticated_token['user']['name'], username)
+        self.non_admin_client.delete_token(subject_token)
+        self.assertRaises(
+            lib_exc.NotFound, self.non_admin_client.show_token, subject_token)
+
+    @decorators.idempotent_id('6f8e4436-fc96-4282-8122-e41df57197a9')
     def test_create_token(self):
 
-        creds = self.os.credentials
+        creds = self.os_primary.credentials
         user_id = creds.user_id
         username = creds.username
         password = creds.password
@@ -54,15 +81,13 @@
             self.assertEqual(subject_id, user_id)
         else:
             # Expect a user ID, but don't know what it will be.
-            self.assertGreaterEqual(len(subject_id), 0,
-                                    'Expected user ID in token.')
+            self.assertIsNotNone(subject_id, 'Expected user ID in token.')
 
         subject_name = resp['user']['name']
         if username:
             self.assertEqual(subject_name, username)
         else:
             # Expect a user name, but don't know what it will be.
-            self.assertGreaterEqual(len(subject_name), 0,
-                                    'Expected user name in token.')
+            self.assertIsNotNone(subject_name, 'Expected user name in token.')
 
         self.assertEqual(resp['methods'][0], 'password')
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index 9592cb9..1f099df 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -20,8 +20,8 @@
 from tempest.api.identity import base
 from tempest import config
 from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
 from tempest.lib import exceptions
-from tempest import test
 
 
 CONF = config.CONF
@@ -32,10 +32,8 @@
     @classmethod
     def resource_setup(cls):
         super(IdentityV3UsersTest, cls).resource_setup()
-        cls.creds = cls.os.credentials
+        cls.creds = cls.os_primary.credentials
         cls.user_id = cls.creds.user_id
-        cls.username = cls.creds.username
-        cls.password = cls.creds.password
 
     def _update_password(self, original_password, password):
         self.non_admin_users_client.update_user_password(
@@ -78,7 +76,7 @@
         time.sleep(1)
         self.non_admin_users_client.auth_provider.set_auth()
 
-    @test.idempotent_id('ad71bd23-12ad-426b-bb8b-195d2b635f27')
+    @decorators.idempotent_id('ad71bd23-12ad-426b-bb8b-195d2b635f27')
     def test_user_update_own_password(self):
         old_pass = self.creds.password
         old_token = self.non_admin_client.token
@@ -103,7 +101,7 @@
 
     @testtools.skipUnless(CONF.identity_feature_enabled.security_compliance,
                           'Security compliance not available.')
-    @test.idempotent_id('941784ee-5342-4571-959b-b80dd2cea516')
+    @decorators.idempotent_id('941784ee-5342-4571-959b-b80dd2cea516')
     def test_password_history_check_self_service_api(self):
         old_pass = self.creds.password
         new_pass1 = data_utils.rand_password()
@@ -133,7 +131,7 @@
 
     @testtools.skipUnless(CONF.identity_feature_enabled.security_compliance,
                           'Security compliance not available.')
-    @test.idempotent_id('a7ad8bbf-2cff-4520-8c1d-96332e151658')
+    @decorators.idempotent_id('a7ad8bbf-2cff-4520-8c1d-96332e151658')
     def test_user_account_lockout(self):
         password = self.creds.password
 
diff --git a/tempest/api/image/admin/v2/__init__.py b/tempest/api/image/admin/v2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/image/admin/v2/__init__.py
+++ /dev/null
diff --git a/tempest/api/image/admin/v2/test_images.py b/tempest/api/image/admin/v2/test_images.py
deleted file mode 100644
index fc5ed79..0000000
--- a/tempest/api/image/admin/v2/test_images.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright 2015 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 six
-import testtools
-
-from tempest.api.image import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-CONF = config.CONF
-
-
-class BasicAdminOperationsImagesTest(base.BaseV2ImageAdminTest):
-    """Here we test admin operations of images"""
-
-    @testtools.skipUnless(CONF.image_feature_enabled.deactivate_image,
-                          'deactivate-image is not available.')
-    @decorators.idempotent_id('951ebe01-969f-4ea9-9898-8a3f1f442ab0')
-    def test_admin_deactivate_reactivate_image(self):
-        # Create image by non-admin tenant
-        image_name = data_utils.rand_name('image')
-        image = self.create_image(name=image_name,
-                                  container_format='bare',
-                                  disk_format='raw',
-                                  visibility='private')
-        # upload an image file
-        content = data_utils.random_bytes()
-        image_file = six.BytesIO(content)
-        self.client.store_image_file(image['id'], image_file)
-        # deactivate image
-        self.admin_client.deactivate_image(image['id'])
-        body = self.client.show_image(image['id'])
-        self.assertEqual("deactivated", body['status'])
-        # non-admin user unable to download deactivated image
-        self.assertRaises(lib_exc.Forbidden, self.client.show_image_file,
-                          image['id'])
-        # reactivate image
-        self.admin_client.reactivate_image(image['id'])
-        body = self.client.show_image(image['id'])
-        self.assertEqual("active", body['status'])
-        # non-admin user able to download image after reactivation by admin
-        body = self.client.show_image_file(image['id'])
-        self.assertEqual(content, body.data)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index cd4f820..70ba2fe 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -15,8 +15,8 @@
 import six
 
 from tempest.common import image as common_image
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 import tempest.test
 
@@ -94,7 +94,7 @@
     @classmethod
     def setup_clients(cls):
         super(BaseV1ImageTest, cls).setup_clients()
-        cls.client = cls.os.image_client
+        cls.client = cls.os_primary.image_client
 
     @classmethod
     def _get_create_params(cls, **kwargs):
@@ -108,7 +108,7 @@
     @classmethod
     def setup_clients(cls):
         super(BaseV1ImageMembersTest, cls).setup_clients()
-        cls.image_member_client = cls.os.image_member_client
+        cls.image_member_client = cls.os_primary.image_member_client
         cls.alt_image_member_client = cls.os_alt.image_member_client
         cls.alt_img_cli = cls.os_alt.image_client
 
@@ -138,13 +138,15 @@
     @classmethod
     def setup_clients(cls):
         super(BaseV2ImageTest, cls).setup_clients()
-        cls.client = cls.os.image_client_v2
-        cls.namespaces_client = cls.os.namespaces_client
-        cls.resource_types_client = cls.os.resource_types_client
-        cls.namespace_properties_client = cls.os.namespace_properties_client
-        cls.namespace_objects_client = cls.os.namespace_objects_client
-        cls.namespace_tags_client = cls.os.namespace_tags_client
-        cls.schemas_client = cls.os.schemas_client
+        cls.client = cls.os_primary.image_client_v2
+        cls.namespaces_client = cls.os_primary.namespaces_client
+        cls.resource_types_client = cls.os_primary.resource_types_client
+        cls.namespace_properties_client =\
+            cls.os_primary.namespace_properties_client
+        cls.namespace_objects_client = cls.os_primary.namespace_objects_client
+        cls.namespace_tags_client = cls.os_primary.namespace_tags_client
+        cls.schemas_client = cls.os_primary.schemas_client
+        cls.versions_client = cls.os_primary.image_versions_client
 
     def create_namespace(cls, namespace_name=None, visibility='public',
                          description='Tempest', protected=False,
@@ -166,7 +168,7 @@
     @classmethod
     def setup_clients(cls):
         super(BaseV2MemberImageTest, cls).setup_clients()
-        cls.image_member_client = cls.os.image_member_client_v2
+        cls.image_member_client = cls.os_primary.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
 
@@ -195,8 +197,8 @@
     @classmethod
     def setup_clients(cls):
         super(BaseV1ImageAdminTest, cls).setup_clients()
-        cls.client = cls.os.image_client
-        cls.admin_client = cls.os_adm.image_client
+        cls.client = cls.os_primary.image_client
+        cls.admin_client = cls.os_admin.image_client
 
 
 class BaseV2ImageAdminTest(BaseImageTest):
@@ -205,5 +207,5 @@
     @classmethod
     def setup_clients(cls):
         super(BaseV2ImageAdminTest, cls).setup_clients()
-        cls.client = cls.os.image_client_v2
-        cls.admin_client = cls.os_adm.image_client_v2
+        cls.client = cls.os_primary.image_client_v2
+        cls.admin_client = cls.os_admin.image_client_v2
diff --git a/tempest/api/image/v1/test_image_members.py b/tempest/api/image/v1/test_image_members.py
index 4902316..bf2e510 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -54,6 +54,6 @@
                                                      self.alt_tenant_id)
         body = self.image_member_client.list_image_members(image_id)
         members = body['members']
-        self.assertEqual(0, len(members), str(members))
+        self.assertEmpty(members)
         self.assertRaises(
             lib_exc.NotFound, self.alt_img_cli.show_image, image_id)
diff --git a/tempest/api/image/v1/test_image_members_negative.py b/tempest/api/image/v1/test_image_members_negative.py
index d1aa801..2748bd5 100644
--- a/tempest/api/image/v1/test_image_members_negative.py
+++ b/tempest/api/image/v1/test_image_members_negative.py
@@ -13,15 +13,14 @@
 #    under the License.
 
 from tempest.api.image import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ImageMembersNegativeTest(base.BaseV1ImageMembersTest):
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('147a9536-18e3-45da-91ea-b037a028f364')
     def test_add_member_with_non_existing_image(self):
         # Add member with non existing image.
@@ -30,7 +29,7 @@
                           self.image_member_client.create_image_member,
                           non_exist_image, self.alt_tenant_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e1559f05-b667-4f1b-a7af-518b52dc0c0f')
     def test_delete_member_with_non_existing_image(self):
         # Delete member with non existing image.
@@ -39,7 +38,7 @@
                           self.image_member_client.delete_image_member,
                           non_exist_image, self.alt_tenant_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f5720333-dd69-4194-bb76-d2f048addd56')
     def test_delete_member_with_non_existing_tenant(self):
         # Delete member with non existing tenant.
@@ -49,7 +48,7 @@
                           self.image_member_client.delete_image_member,
                           image_id, non_exist_tenant)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f25f89e4-0b6c-453b-a853-1f80b9d7ef26')
     def test_get_image_without_membership(self):
         # Image is hidden from another tenants.
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index a79c18c..76723f4 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -17,9 +17,9 @@
 
 from tempest.api.image import base
 from tempest.common import image as common_image
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions
 
@@ -43,7 +43,8 @@
         else:
             msg = ("The container format and the disk format don't match. "
                    "Container format: %(container)s, Disk format: %(disk)s." %
-                   {'container': container_format, 'disk': disk_format})
+                   {'container': container_format, 'disk':
+                       CONF.image.disk_formats})
             raise exceptions.InvalidConfiguration(msg)
     else:
         disk_format = CONF.image.disk_formats[0]
@@ -145,24 +146,24 @@
         a_formats = ['ami', 'ari', 'aki']
 
         (cls.container_format,
-         cls.container_format_alt) = CONF.image.container_formats[:2]
+         container_format_alt) = CONF.image.container_formats[:2]
         cls.disk_format, cls.disk_format_alt = CONF.image.disk_formats[:2]
         if cls.container_format in a_formats:
             cls.disk_format = cls.container_format
-        if cls.container_format_alt in a_formats:
-            cls.disk_format_alt = cls.container_format_alt
+        if container_format_alt in a_formats:
+            cls.disk_format_alt = container_format_alt
 
         img1 = cls._create_remote_image('one', cls.container_format,
                                         cls.disk_format)
-        img2 = cls._create_remote_image('two', cls.container_format_alt,
+        img2 = cls._create_remote_image('two', container_format_alt,
                                         cls.disk_format_alt)
         img3 = cls._create_remote_image('dup', cls.container_format,
                                         cls.disk_format)
         img4 = cls._create_remote_image('dup', cls.container_format,
                                         cls.disk_format)
-        img5 = cls._create_standard_image('1', cls.container_format_alt,
+        img5 = cls._create_standard_image('1', container_format_alt,
                                           cls.disk_format_alt, 42)
-        img6 = cls._create_standard_image('2', cls.container_format_alt,
+        img6 = cls._create_standard_image('2', container_format_alt,
                                           cls.disk_format_alt, 142)
         img7 = cls._create_standard_image('33', cls.container_format,
                                           cls.disk_format, 142)
diff --git a/tempest/api/image/v1/test_images_negative.py b/tempest/api/image/v1/test_images_negative.py
index abec82a..690b8da 100644
--- a/tempest/api/image/v1/test_images_negative.py
+++ b/tempest/api/image/v1/test_images_negative.py
@@ -15,16 +15,15 @@
 
 
 from tempest.api.image import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class CreateDeleteImagesNegativeTest(base.BaseV1ImageTest):
     """Here are negative tests for the deletion and creation of images."""
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('036ede36-6160-4463-8c01-c781eee6369d')
     def test_register_with_invalid_container_format(self):
         # Negative tests for invalid data supplied to POST /images
@@ -33,7 +32,7 @@
                                    'x-image-meta-container_format': 'wrong',
                                    'x-image-meta-disk_format': 'vhd'})
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('993face5-921d-4e84-aabf-c1bba4234a67')
     def test_register_with_invalid_disk_format(self):
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
@@ -41,7 +40,7 @@
                                    'x-image-meta-container_format': 'bare',
                                    'x-image-meta-disk_format': 'wrong'})
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ec652588-7e3c-4b67-a2f2-0fa96f57c8fc')
     def test_delete_non_existent_image(self):
         # Return an error while trying to delete a non-existent image
@@ -50,13 +49,13 @@
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
                           non_existent_image_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('04f72aa3-fcec-45a3-81a3-308ef7cc82bc')
     def test_delete_image_blank_id(self):
         # Return an error while trying to delete an image with blank Id
         self.assertRaises(lib_exc.NotFound, self.client.delete_image, '')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('950e5054-a3c7-4dee-ada5-e576f1087abd')
     def test_delete_image_non_hex_string_id(self):
         # Return an error while trying to delete an image with non hex id
@@ -64,13 +63,13 @@
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
                           invalid_image_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4ed757cd-450c-44b1-9fd1-c819748c650d')
     def test_delete_image_negative_image_id(self):
         # Return an error while trying to delete an image with negative id
         self.assertRaises(lib_exc.NotFound, self.client.delete_image, -1)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a4a448ab-3db2-4d2d-b9b2-6a1271241dfe')
     def test_delete_image_id_over_character_limit(self):
         # Return an error while trying to delete image with id over limit
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 56b3517..c846f88 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -18,12 +18,14 @@
 
 import six
 
+import testtools
+
 from oslo_log import log as logging
 from tempest.api.image import base
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
+from tempest.lib import exceptions as lib_exc
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
@@ -32,7 +34,7 @@
 class BasicOperationsImagesTest(base.BaseV2ImageTest):
     """Here we test the basic operations of images"""
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('139b765e-7f3d-4b3d-8b37-3ca3876ee318')
     def test_register_upload_get_image_file(self):
         """Here we test these functionalities
@@ -74,7 +76,7 @@
         body = self.client.show_image_file(image['id'])
         self.assertEqual(file_content, body.data)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('f848bb94-1c6e-45a4-8726-39e3a5b23535')
     def test_delete_image(self):
         # Deletes an image by image_id
@@ -96,7 +98,7 @@
         images_id = [item['id'] for item in images]
         self.assertNotIn(image['id'], images_id)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('f66891a7-a35c-41a8-b590-a065c2a1caa6')
     def test_update_image(self):
         # Updates an image by image_id
@@ -117,7 +119,7 @@
 
         # Update Image
         new_image_name = data_utils.rand_name('new-image')
-        body = self.client.update_image(image['id'], [
+        self.client.update_image(image['id'], [
             dict(replace='/name', value=new_image_name)])
 
         # Verifying updating
@@ -126,6 +128,40 @@
         self.assertEqual(image['id'], body['id'])
         self.assertEqual(new_image_name, body['name'])
 
+    @testtools.skipUnless(CONF.image_feature_enabled.deactivate_image,
+                          'deactivate-image is not available.')
+    @decorators.idempotent_id('951ebe01-969f-4ea9-9898-8a3f1f442ab0')
+    def test_deactivate_reactivate_image(self):
+        # Create image
+        image_name = data_utils.rand_name('image')
+        image = self.create_image(name=image_name,
+                                  container_format='bare',
+                                  disk_format='raw',
+                                  visibility='private')
+
+        # Upload an image file
+        content = data_utils.random_bytes()
+        image_file = six.BytesIO(content)
+        self.client.store_image_file(image['id'], image_file)
+
+        # Deactivate image
+        self.client.deactivate_image(image['id'])
+        body = self.client.show_image(image['id'])
+        self.assertEqual("deactivated", body['status'])
+
+        # User unable to download deactivated image
+        self.assertRaises(lib_exc.Forbidden, self.client.show_image_file,
+                          image['id'])
+
+        # Reactivate image
+        self.client.reactivate_image(image['id'])
+        body = self.client.show_image(image['id'])
+        self.assertEqual("active", body['status'])
+
+        # User able to download image after reactivation
+        body = self.client.show_image_file(image['id'])
+        self.assertEqual(content, body.data)
+
 
 class ListUserImagesTest(base.BaseV2ImageTest):
     """Here we test the listing of image information"""
@@ -164,7 +200,6 @@
         cls.client.store_image_file(image['id'], data=image_file)
         # Keep the data of one test image so it can be used to filter lists
         cls.test_data = image
-        cls.test_data['size'] = size
 
         return image['id']
 
@@ -329,7 +364,7 @@
     @classmethod
     def setup_clients(cls):
         super(ListSharedImagesTest, cls).setup_clients()
-        cls.image_member_client = cls.os.image_member_client_v2
+        cls.image_member_client = cls.os_primary.image_member_client_v2
         cls.alt_img_client = cls.os_alt.image_client_v2
 
     @decorators.idempotent_id('3fa50be4-8e38-4c02-a8db-7811bb780122')
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index 7a495e7..0208780 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -96,20 +96,3 @@
     def test_get_image_members_schema(self):
         body = self.schemas_client.show_schema("members")
         self.assertEqual("members", body['name'])
-
-    @decorators.idempotent_id('cb961424-3f68-4d21-8e36-30ad66fb6bfb')
-    def test_get_private_image(self):
-        image_id = self._create_image()
-        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_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.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 ae9630a..caa90f9 100644
--- a/tempest/api/image/v2/test_images_member_negative.py
+++ b/tempest/api/image/v2/test_images_member_negative.py
@@ -13,12 +13,11 @@
 from tempest.api.image import base
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ImagesMemberNegativeTest(base.BaseV2MemberImageTest):
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b79efb37-820d-4cf0-b54c-308b00cf842c')
     def test_image_share_invalid_status(self):
         image_id = self._create_image()
@@ -30,7 +29,7 @@
                           image_id, self.alt_tenant_id,
                           status='notavalidstatus')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('27002f74-109e-4a37-acd0-f91cd4597967')
     def test_image_share_owner_cannot_accept(self):
         image_id = self._create_image()
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_objects.py b/tempest/api/image/v2/test_images_metadefs_namespace_objects.py
index 38e56d4..80f8112 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespace_objects.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_objects.py
@@ -11,7 +11,7 @@
 #    under the License.
 
 from tempest.api.image import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_properties.py b/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
index ead70f2..ed91726 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
@@ -11,7 +11,7 @@
 #    under the License.
 
 from tempest.api.image import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_tags.py b/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
index 608d9fc..69bebfe 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
@@ -11,7 +11,7 @@
 #    under the License.
 
 from tempest.api.image import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
@@ -58,7 +58,7 @@
             namespace['namespace'])
         body = self.namespace_tags_client.list_namespace_tags(
             namespace['namespace'])
-        self.assertEqual([], body['tags'])
+        self.assertEmpty(body['tags'])
 
     @decorators.idempotent_id('a2a3765e-1a2c-3f6d-a3a7-3cc3466ab875')
     def test_create_update_delete_tag(self):
diff --git a/tempest/api/image/v2/test_images_metadefs_namespaces.py b/tempest/api/image/v2/test_images_metadefs_namespaces.py
index 9fda937..f71b16c 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespaces.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespaces.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 from tempest.api.image import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index 04c752a..b4baf05 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -18,7 +18,6 @@
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ImagesNegativeTest(base.BaseV2ImageTest):
@@ -34,7 +33,7 @@
         ** delete the deleted image
      """
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('668743d5-08ad-4480-b2b8-15da34f81d9f')
     def test_get_non_existent_image(self):
         # get the non-existent image
@@ -42,14 +41,14 @@
         self.assertRaises(lib_exc.NotFound, self.client.show_image,
                           non_existent_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ef45000d-0a72-4781-866d-4cb7bf2562ad')
     def test_get_image_null_id(self):
         # get image with image_id = NULL
         image_id = ""
         self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e57fc127-7ba0-4693-92d7-1d8a05ebcba9')
     def test_get_delete_deleted_image(self):
         # get and delete the deleted image
@@ -68,7 +67,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
                           image['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6fe40f1c-57bd-4918-89cc-8500f850f3de')
     def test_delete_non_existing_image(self):
         # delete non-existent image
@@ -76,7 +75,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
                           non_existent_image_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('32248db1-ab88-4821-9604-c7c369f1f88c')
     def test_delete_image_null_id(self):
         # delete image with image_id=NULL
@@ -84,7 +83,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
                           image_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('292bd310-369b-41c7-a7a3-10276ef76753')
     def test_register_with_invalid_container_format(self):
         # Negative tests for invalid data supplied to POST /images
@@ -92,9 +91,22 @@
                           name='test', container_format='wrong',
                           disk_format='vhd')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('70c6040c-5a97-4111-9e13-e73665264ce1')
     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')
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('ab980a34-8410-40eb-872b-f264752f46e5')
+    def test_delete_protected_image(self):
+        # Create a protected image
+        image = self.create_image(protected=True)
+        self.addCleanup(self.client.update_image, image['id'],
+                        [dict(replace="/protected", value=False)])
+
+        # Try deleting the protected image
+        self.assertRaises(lib_exc.Forbidden,
+                          self.client.delete_image,
+                          image['id'])
diff --git a/tempest/api/image/v2/test_images_tags.py b/tempest/api/image/v2/test_images_tags.py
index fd9591f..601826e 100644
--- a/tempest/api/image/v2/test_images_tags.py
+++ b/tempest/api/image/v2/test_images_tags.py
@@ -13,7 +13,7 @@
 # under the License.
 
 from tempest.api.image import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 
diff --git a/tempest/api/image/v2/test_images_tags_negative.py b/tempest/api/image/v2/test_images_tags_negative.py
index 2a08f3a..440fa36 100644
--- a/tempest/api/image/v2/test_images_tags_negative.py
+++ b/tempest/api/image/v2/test_images_tags_negative.py
@@ -13,15 +13,14 @@
 # under the License.
 
 from tempest.api.image import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ImagesTagsNegativeTest(base.BaseV2ImageTest):
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8cd30f82-6f9a-4c6e-8034-c1b51fba43d9')
     def test_update_tags_for_non_existing_image(self):
         # Update tag with non existing image.
@@ -30,7 +29,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.add_image_tag,
                           non_exist_image, tag)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('39c023a2-325a-433a-9eea-649bf1414b19')
     def test_delete_non_existing_tag(self):
         # Delete non existing tag.
diff --git a/tempest/api/image/v2/test_versions.py b/tempest/api/image/v2/test_versions.py
new file mode 100644
index 0000000..84f1068
--- /dev/null
+++ b/tempest/api/image/v2/test_versions.py
@@ -0,0 +1,29 @@
+# Copyright 2017 NEC Corporation.  All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.image import base
+from tempest.lib import decorators
+
+
+class VersionsTest(base.BaseV2ImageTest):
+
+    @decorators.idempotent_id('659ea30a-a17c-4317-832c-0f68ed23c31d')
+    @decorators.attr(type='smoke')
+    def test_list_versions(self):
+        versions = self.versions_client.list_versions()['versions']
+        expected_resources = ('id', 'links', 'status')
+
+        for version in versions:
+            for res in expected_resources:
+                self.assertIn(res, version)
diff --git a/tempest/api/network/admin/test_agent_management.py b/tempest/api/network/admin/test_agent_management.py
index 6389489..5068fc4 100644
--- a/tempest/api/network/admin/test_agent_management.py
+++ b/tempest/api/network/admin/test_agent_management.py
@@ -14,8 +14,8 @@
 
 from tempest.api.network import base
 from tempest.common import tempest_fixtures as fixtures
+from tempest.common import utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class AgentManagementTestJSON(base.BaseAdminNetworkTest):
@@ -23,7 +23,7 @@
     @classmethod
     def skip_checks(cls):
         super(AgentManagementTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('agent', 'network'):
+        if not utils.is_extension_enabled('agent', 'network'):
             msg = "agent extension not enabled."
             raise cls.skipException(msg)
 
@@ -49,7 +49,7 @@
     @decorators.idempotent_id('e335be47-b9a1-46fd-be30-0874c0b751e6')
     def test_list_agents_non_admin(self):
         body = self.agents_client.list_agents()
-        self.assertEqual(len(body["agents"]), 0)
+        self.assertEmpty(body["agents"])
 
     @decorators.idempotent_id('869bc8e8-0fda-4a30-9b71-f8a7cf58ca9f')
     def test_show_agent(self):
diff --git a/tempest/api/network/admin/test_dhcp_agent_scheduler.py b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
index 868771f..8315c5d 100644
--- a/tempest/api/network/admin/test_dhcp_agent_scheduler.py
+++ b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
@@ -13,8 +13,8 @@
 #    under the License.
 
 from tempest.api.network import base
+from tempest.common import utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class DHCPAgentSchedulersTestJSON(base.BaseAdminNetworkTest):
@@ -22,7 +22,7 @@
     @classmethod
     def skip_checks(cls):
         super(DHCPAgentSchedulersTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('dhcp_agent_scheduler', 'network'):
+        if not utils.is_extension_enabled('dhcp_agent_scheduler', 'network'):
             msg = "dhcp_agent_scheduler extension not enabled."
             raise cls.skipException(msg)
 
@@ -32,7 +32,7 @@
         # Create a network and make sure it will be hosted by a
         # dhcp agent: this is done by creating a regular port
         cls.network = cls.create_network()
-        cls.subnet = cls.create_subnet(cls.network)
+        cls.create_subnet(cls.network)
         cls.port = cls.create_port(cls.network)
 
     @decorators.idempotent_id('5032b1fe-eb42-4a64-8f3b-6e189d8b5c7d')
diff --git a/tempest/api/network/admin/test_external_network_extension.py b/tempest/api/network/admin/test_external_network_extension.py
index dc440d3..4d41e33 100644
--- a/tempest/api/network/admin/test_external_network_extension.py
+++ b/tempest/api/network/admin/test_external_network_extension.py
@@ -10,11 +10,16 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.network import base
-from tempest.common.utils import data_utils
+from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
+CONF = config.CONF
+
 
 class ExternalNetworksTestJSON(base.BaseAdminNetworkTest):
 
@@ -91,6 +96,8 @@
         self.assertFalse(show_net['router:external'])
 
     @decorators.idempotent_id('82068503-2cf2-4ed4-b3be-ecb89432e4bb')
+    @testtools.skipUnless(CONF.network_feature_enabled.floating_ips,
+                          'Floating ips are not availabled')
     def test_delete_external_networks_with_floating_ip(self):
         # Verifies external network can be deleted while still holding
         # (unassociated) floating IPs
diff --git a/tempest/api/network/admin/test_external_networks_negative.py b/tempest/api/network/admin/test_external_networks_negative.py
index 743089a..0709d2a 100644
--- a/tempest/api/network/admin/test_external_networks_negative.py
+++ b/tempest/api/network/admin/test_external_networks_negative.py
@@ -12,21 +12,23 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+import testtools
 
 from tempest.api.network import base
 from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
 
 class ExternalNetworksAdminNegativeTestJSON(base.BaseAdminNetworkTest):
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d402ae6c-0be0-4d8e-833b-a738895d98d0')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
     def test_create_port_with_precreated_floatingip_as_fixed_ip(self):
         # NOTE: External networks can be used to create both floating-ip as
         # well as instance-ip. So, creating an instance-ip with a value of a
diff --git a/tempest/api/network/admin/test_floating_ips_admin_actions.py b/tempest/api/network/admin/test_floating_ips_admin_actions.py
index c36323a..5aa337c 100644
--- a/tempest/api/network/admin/test_floating_ips_admin_actions.py
+++ b/tempest/api/network/admin/test_floating_ips_admin_actions.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 from tempest.api.network import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -28,14 +28,19 @@
     @classmethod
     def skip_checks(cls):
         super(FloatingIPAdminTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('router', 'network'):
+        if not utils.is_extension_enabled('router', 'network'):
             msg = "router extension not enabled."
             raise cls.skipException(msg)
+        if not CONF.network.public_network_id:
+            msg = "The public_network_id option must be specified."
+            raise cls.skipException(msg)
+        if not CONF.network_feature_enabled.floating_ips:
+            raise cls.skipException("Floating ips are not available")
 
     @classmethod
     def setup_clients(cls):
         super(FloatingIPAdminTestJSON, cls).setup_clients()
-        cls.alt_floating_ips_client = cls.alt_manager.floating_ips_client
+        cls.alt_floating_ips_client = cls.os_alt.floating_ips_client
 
     @classmethod
     def resource_setup(cls):
@@ -43,9 +48,9 @@
         cls.ext_net_id = CONF.network.public_network_id
         cls.floating_ip = cls.create_floatingip(cls.ext_net_id)
         cls.network = cls.create_network()
-        cls.subnet = cls.create_subnet(cls.network)
-        cls.router = cls.create_router(external_network_id=cls.ext_net_id)
-        cls.create_router_interface(cls.router['id'], cls.subnet['id'])
+        subnet = cls.create_subnet(cls.network)
+        router = cls.create_router(external_network_id=cls.ext_net_id)
+        cls.create_router_interface(router['id'], subnet['id'])
         cls.port = cls.create_port(cls.network)
 
     @decorators.idempotent_id('64f2100b-5471-4ded-b46c-ddeeeb4f231b')
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index 5a54ae0..1a7b0ec 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -13,10 +13,10 @@
 #    under the License.
 
 from tempest.api.network import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions
-from tempest import test
 
 CONF = config.CONF
 AGENT_TYPE = 'L3 agent'
@@ -27,12 +27,7 @@
 
 
 class L3AgentSchedulerTestJSON(base.BaseAdminNetworkTest):
-    _agent_mode = 'legacy'
-    is_dvr_router = False
-
-    """
-    Tests the following operations in the Neutron API using the REST client for
-    Neutron:
+    """Tests the following operations in the Neutron API:
 
         List routers that the given L3 agent is hosting.
         List L3 agents hosting the given router.
@@ -46,21 +41,17 @@
     @classmethod
     def skip_checks(cls):
         super(L3AgentSchedulerTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('l3_agent_scheduler', 'network'):
+        if not utils.is_extension_enabled('l3_agent_scheduler', 'network'):
             msg = "L3 Agent Scheduler Extension not enabled."
             raise cls.skipException(msg)
 
     @classmethod
     def resource_setup(cls):
         super(L3AgentSchedulerTestJSON, cls).resource_setup()
-        body = cls.admin_agents_client.list_agents()
-        agents = body['agents']
+        agents = cls.admin_agents_client.list_agents(
+            agent_type=AGENT_TYPE)['agents']
         for agent in agents:
-            # TODO(armax): falling back on default _agent_mode can be
-            # dropped as soon as Icehouse is dropped.
-            agent_mode = (
-                agent['configurations'].get('agent_mode', cls._agent_mode))
-            if agent['agent_type'] == AGENT_TYPE and agent_mode in AGENT_MODES:
+            if agent['configurations']['agent_mode'] in AGENT_MODES:
                 cls.agent = agent
                 break
         else:
@@ -68,42 +59,6 @@
             raise exceptions.InvalidConfiguration(msg)
         cls.router = cls.create_router()
 
-        if CONF.network.dvr_extra_resources:
-            # NOTE(armax): If DVR is an available extension, and the created
-            # router is indeed a distributed one, more resources need to be
-            # provisioned in order to bind the router to the L3 agent in the
-            # Liberty release or older, and are not required since the Mitaka
-            # release.
-            if test.is_extension_enabled('dvr', 'network'):
-                cls.is_dvr_router = cls.admin_routers_client.show_router(
-                    cls.router['id'])['router'].get('distributed', False)
-                if cls.is_dvr_router:
-                    cls.network = cls.create_network()
-                    cls.subnet = cls.create_subnet(cls.network)
-                    cls.port = cls.create_port(cls.network)
-                    cls.routers_client.add_router_interface(
-                        cls.router['id'], port_id=cls.port['id'])
-                    # NOTE: Sometimes we have seen this test fail with dvr in,
-                    # multinode tests, since the dhcp port is not created
-                    # before the test gets executed and so the router is not
-                    # scheduled on the given agent. By adding the external
-                    # gateway info to the router, the router should be properly
-                    # scheduled in the dvr_snat node. This is a temporary work
-                    # around to prevent a race condition.
-                    external_gateway_info = {
-                        'network_id': CONF.network.public_network_id,
-                        'enable_snat': True}
-                    cls.admin_routers_client.update_router(
-                        cls.router['id'],
-                        external_gateway_info=external_gateway_info)
-
-    @classmethod
-    def resource_cleanup(cls):
-        if cls.is_dvr_router:
-            cls.routers_client.remove_router_interface(cls.router['id'],
-                                                       port_id=cls.port['id'])
-        super(L3AgentSchedulerTestJSON, cls).resource_cleanup()
-
     @decorators.idempotent_id('b7ce6e89-e837-4ded-9b78-9ed3c9c6a45a')
     def test_list_routers_on_l3_agent(self):
         self.admin_agents_client.list_routers_on_l3_agent(self.agent['id'])
diff --git a/tempest/api/network/test_metering_extensions.py b/tempest/api/network/admin/test_metering_extensions.py
similarity index 83%
rename from tempest/api/network/test_metering_extensions.py
rename to tempest/api/network/admin/test_metering_extensions.py
index 42fa9e4..fd86782 100644
--- a/tempest/api/network/test_metering_extensions.py
+++ b/tempest/api/network/admin/test_metering_extensions.py
@@ -13,9 +13,9 @@
 # under the License.
 
 from tempest.api.network import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class MeteringTestJSON(base.BaseAdminNetworkTest):
@@ -28,7 +28,7 @@
     @classmethod
     def skip_checks(cls):
         super(MeteringTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('metering', 'network'):
+        if not utils.is_extension_enabled('metering', 'network'):
             msg = "metering extension not enabled."
             raise cls.skipException(msg)
 
@@ -45,6 +45,28 @@
             remote_ip_prefix, direction,
             metering_label_id=cls.metering_label['id'])
 
+    @classmethod
+    def create_metering_label(cls, name, description):
+        """Wrapper utility that returns a test metering label."""
+        body = cls.admin_metering_labels_client.create_metering_label(
+            description=description,
+            name=name)
+        metering_label = body['metering_label']
+        cls.metering_labels.append(metering_label)
+        return metering_label
+
+    @classmethod
+    def create_metering_label_rule(cls, remote_ip_prefix, direction,
+                                   metering_label_id):
+        """Wrapper utility that returns a test metering label rule."""
+        client = cls.admin_metering_label_rules_client
+        body = client.create_metering_label_rule(
+            remote_ip_prefix=remote_ip_prefix, direction=direction,
+            metering_label_id=metering_label_id)
+        metering_label_rule = body['metering_label_rule']
+        cls.metering_label_rules.append(metering_label_rule)
+        return metering_label_rule
+
     def _delete_metering_label(self, metering_label_id):
         # Deletes a label and verifies if it is deleted or not
         self.admin_metering_labels_client.delete_metering_label(
@@ -52,7 +74,7 @@
         # Asserting that the label is not found in list after deletion
         labels = self.admin_metering_labels_client.list_metering_labels(
             id=metering_label_id)
-        self.assertEqual(len(labels['metering_labels']), 0)
+        self.assertEmpty(labels['metering_labels'])
 
     def _delete_metering_label_rule(self, metering_label_rule_id):
         client = self.admin_metering_label_rules_client
@@ -67,7 +89,7 @@
         # Verify label filtering
         body = self.admin_metering_labels_client.list_metering_labels(id=33)
         metering_labels = body['metering_labels']
-        self.assertEqual(0, len(metering_labels))
+        self.assertEmpty(metering_labels)
 
     @decorators.idempotent_id('ec8e15ff-95d0-433b-b8a6-b466bddb1e50')
     def test_create_delete_metering_label_with_filters(self):
@@ -104,7 +126,7 @@
         # Verify rule filtering
         body = client.list_metering_label_rules(id=33)
         metering_label_rules = body['metering_label_rules']
-        self.assertEqual(0, len(metering_label_rules))
+        self.assertEmpty(metering_label_rules)
 
     @decorators.idempotent_id('f4d547cd-3aee-408f-bf36-454f8825e045')
     def test_create_delete_metering_label_rule_with_filters(self):
diff --git a/tempest/api/network/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index 435e672..6849653 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 from tempest.api.network import base
+from tempest.common import utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class QuotasNegativeTest(base.BaseAdminNetworkTest):
@@ -35,10 +35,11 @@
     @classmethod
     def skip_checks(cls):
         super(QuotasNegativeTest, cls).skip_checks()
-        if not test.is_extension_enabled('quotas', 'network'):
+        if not utils.is_extension_enabled('quotas', 'network'):
             msg = "quotas extension not enabled."
             raise cls.skipException(msg)
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('644f4e1b-1bf9-4af0-9fd8-eb56ac0f51cf')
     def test_network_quota_exceeding(self):
         # Set the network quota to two
diff --git a/tempest/api/network/admin/test_ports.py b/tempest/api/network/admin/test_ports.py
new file mode 100644
index 0000000..807994b
--- /dev/null
+++ b/tempest/api/network/admin/test_ports.py
@@ -0,0 +1,99 @@
+# Copyright 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import socket
+
+from tempest.api.network import base
+from tempest import config
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class PortsAdminExtendedAttrsTestJSON(base.BaseAdminNetworkTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(PortsAdminExtendedAttrsTestJSON, cls).resource_setup()
+        cls.network = cls.create_network()
+        cls.host_id = socket.gethostname()
+
+    @decorators.idempotent_id('8e8569c1-9ac7-44db-8bc1-f5fb2814f29b')
+    def test_create_port_binding_ext_attr(self):
+        post_body = {"network_id": self.network['id'],
+                     "binding:host_id": self.host_id}
+        body = self.admin_ports_client.create_port(**post_body)
+        port = body['port']
+        self.addCleanup(self.admin_ports_client.delete_port, port['id'])
+        host_id = port['binding:host_id']
+        self.assertIsNotNone(host_id)
+        self.assertEqual(self.host_id, host_id)
+
+    @decorators.idempotent_id('6f6c412c-711f-444d-8502-0ac30fbf5dd5')
+    def test_update_port_binding_ext_attr(self):
+        post_body = {"network_id": self.network['id']}
+        body = self.admin_ports_client.create_port(**post_body)
+        port = body['port']
+        self.addCleanup(self.admin_ports_client.delete_port, port['id'])
+        update_body = {"binding:host_id": self.host_id}
+        body = self.admin_ports_client.update_port(port['id'], **update_body)
+        updated_port = body['port']
+        host_id = updated_port['binding:host_id']
+        self.assertIsNotNone(host_id)
+        self.assertEqual(self.host_id, host_id)
+
+    @decorators.idempotent_id('1c82a44a-6c6e-48ff-89e1-abe7eaf8f9f8')
+    def test_list_ports_binding_ext_attr(self):
+        # Create a new port
+        post_body = {"network_id": self.network['id']}
+        body = self.admin_ports_client.create_port(**post_body)
+        port = body['port']
+        self.addCleanup(self.admin_ports_client.delete_port, port['id'])
+
+        # Update the port's binding attributes so that is now 'bound'
+        # to a host
+        update_body = {"binding:host_id": self.host_id}
+        self.admin_ports_client.update_port(port['id'], **update_body)
+
+        # List all ports, ensure new port is part of list and its binding
+        # attributes are set and accurate
+        body = self.admin_ports_client.list_ports()
+        ports_list = body['ports']
+        pids_list = [p['id'] for p in ports_list]
+        self.assertIn(port['id'], pids_list)
+        listed_port = [p for p in ports_list if p['id'] == port['id']]
+        self.assertEqual(1, len(listed_port),
+                         'Multiple ports listed with id %s in ports listing: '
+                         '%s' % (port['id'], ports_list))
+        self.assertEqual(self.host_id, listed_port[0]['binding:host_id'])
+
+    @decorators.idempotent_id('b54ac0ff-35fc-4c79-9ca3-c7dbd4ea4f13')
+    def test_show_port_binding_ext_attr(self):
+        body = self.admin_ports_client.create_port(
+            network_id=self.network['id'])
+        port = body['port']
+        self.addCleanup(self.admin_ports_client.delete_port, port['id'])
+        body = self.admin_ports_client.show_port(port['id'])
+        show_port = body['port']
+        self.assertEqual(port['binding:host_id'],
+                         show_port['binding:host_id'])
+        self.assertEqual(port['binding:vif_type'],
+                         show_port['binding:vif_type'])
+        self.assertEqual(port['binding:vif_details'],
+                         show_port['binding:vif_details'])
+
+
+class PortsAdminExtendedAttrsIpV6TestJSON(PortsAdminExtendedAttrsTestJSON):
+    _ip_version = 6
diff --git a/tempest/api/network/admin/test_quotas.py b/tempest/api/network/admin/test_quotas.py
index 7b6ebef..f69d7c5 100644
--- a/tempest/api/network/admin/test_quotas.py
+++ b/tempest/api/network/admin/test_quotas.py
@@ -14,10 +14,10 @@
 #    under the License.
 
 from tempest.api.network import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class QuotasTest(base.BaseAdminNetworkTest):
@@ -38,7 +38,7 @@
     @classmethod
     def skip_checks(cls):
         super(QuotasTest, cls).skip_checks()
-        if not test.is_extension_enabled('quotas', 'network'):
+        if not utils.is_extension_enabled('quotas', 'network'):
             msg = "quotas extension not enabled."
             raise cls.skipException(msg)
 
diff --git a/tempest/api/network/admin/test_routers.py b/tempest/api/network/admin/test_routers.py
new file mode 100644
index 0000000..c97efe9
--- /dev/null
+++ b/tempest/api/network/admin/test_routers.py
@@ -0,0 +1,224 @@
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import testtools
+
+from tempest.api.network import base
+from tempest.common import utils
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class RoutersAdminTest(base.BaseAdminNetworkTest):
+    # NOTE(salv-orlando): This class inherits from BaseAdminNetworkTest
+    # as some router operations, such as enabling or disabling SNAT
+    # require admin credentials by default
+
+    def _cleanup_router(self, router):
+        self.delete_router(router)
+        self.routers.remove(router)
+
+    def _create_router(self, name=None, admin_state_up=False,
+                       external_network_id=None, enable_snat=None):
+        # associate a cleanup with created routers to avoid quota limits
+        router = self.create_router(name, admin_state_up,
+                                    external_network_id, enable_snat)
+        self.addCleanup(self._cleanup_router, router)
+        return router
+
+    def _add_router_interface_with_subnet_id(self, router_id, subnet_id):
+        interface = self.routers_client.add_router_interface(
+            router_id, subnet_id=subnet_id)
+        self.addCleanup(self._remove_router_interface_with_subnet_id,
+                        router_id, subnet_id)
+        self.assertEqual(subnet_id, interface['subnet_id'])
+        return interface
+
+    def _remove_router_interface_with_subnet_id(self, router_id, subnet_id):
+        body = self.routers_client.remove_router_interface(router_id,
+                                                           subnet_id=subnet_id)
+        self.assertEqual(subnet_id, body['subnet_id'])
+
+    @classmethod
+    def skip_checks(cls):
+        super(RoutersAdminTest, cls).skip_checks()
+        if not utils.is_extension_enabled('router', 'network'):
+            msg = "router extension not enabled."
+            raise cls.skipException(msg)
+
+    @decorators.idempotent_id('e54dd3a3-4352-4921-b09d-44369ae17397')
+    def test_create_router_setting_project_id(self):
+        # Test creating router from admin user setting project_id.
+        project = data_utils.rand_name('test_tenant_')
+        description = data_utils.rand_name('desc_')
+        project = self.identity_utils.create_project(name=project,
+                                                     description=description)
+        project_id = project['id']
+        self.addCleanup(self.identity_utils.delete_project, project_id)
+
+        name = data_utils.rand_name('router-')
+        create_body = self.admin_routers_client.create_router(
+            name=name, tenant_id=project_id)
+        self.addCleanup(self.admin_routers_client.delete_router,
+                        create_body['router']['id'])
+        self.assertEqual(project_id, create_body['router']['tenant_id'])
+
+    @decorators.idempotent_id('847257cc-6afd-4154-b8fb-af49f5670ce8')
+    @utils.requires_ext(extension='ext-gw-mode', service='network')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    def test_create_router_with_default_snat_value(self):
+        # Create a router with default snat rule
+        router = self._create_router(
+            external_network_id=CONF.network.public_network_id)
+        self._verify_router_gateway(
+            router['id'], {'network_id': CONF.network.public_network_id,
+                           'enable_snat': True})
+
+    @decorators.idempotent_id('ea74068d-09e9-4fd7-8995-9b6a1ace920f')
+    @utils.requires_ext(extension='ext-gw-mode', service='network')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    def test_create_router_with_snat_explicit(self):
+        name = data_utils.rand_name('snat-router')
+        # Create a router enabling snat attributes
+        enable_snat_states = [False, True]
+        for enable_snat in enable_snat_states:
+            external_gateway_info = {
+                'network_id': CONF.network.public_network_id,
+                'enable_snat': enable_snat}
+            create_body = self.admin_routers_client.create_router(
+                name=name, external_gateway_info=external_gateway_info)
+            self.addCleanup(self.admin_routers_client.delete_router,
+                            create_body['router']['id'])
+            # Verify snat attributes after router creation
+            self._verify_router_gateway(create_body['router']['id'],
+                                        exp_ext_gw_info=external_gateway_info)
+
+    def _verify_router_gateway(self, router_id, exp_ext_gw_info=None):
+        show_body = self.admin_routers_client.show_router(router_id)
+        actual_ext_gw_info = show_body['router']['external_gateway_info']
+        if exp_ext_gw_info is None:
+            self.assertIsNone(actual_ext_gw_info)
+            return
+        # Verify only keys passed in exp_ext_gw_info
+        for k, v in exp_ext_gw_info.items():
+            self.assertEqual(v, actual_ext_gw_info[k])
+
+    def _verify_gateway_port(self, router_id):
+        list_body = self.admin_ports_client.list_ports(
+            network_id=CONF.network.public_network_id,
+            device_id=router_id)
+        self.assertEqual(len(list_body['ports']), 1)
+        gw_port = list_body['ports'][0]
+        fixed_ips = gw_port['fixed_ips']
+        self.assertNotEmpty(fixed_ips)
+        # Assert that all of the IPs from the router gateway port
+        # are allocated from a valid public subnet.
+        public_net_body = self.admin_networks_client.show_network(
+            CONF.network.public_network_id)
+        public_subnet_ids = public_net_body['network']['subnets']
+        for fixed_ip in fixed_ips:
+            subnet_id = fixed_ip['subnet_id']
+            self.assertIn(subnet_id, public_subnet_ids)
+
+    @decorators.idempotent_id('6cc285d8-46bf-4f36-9b1a-783e3008ba79')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    def test_update_router_set_gateway(self):
+        router = self._create_router()
+        self.routers_client.update_router(
+            router['id'],
+            external_gateway_info={
+                'network_id': CONF.network.public_network_id})
+        # Verify operation - router
+        self._verify_router_gateway(
+            router['id'],
+            {'network_id': CONF.network.public_network_id})
+        self._verify_gateway_port(router['id'])
+
+    @decorators.idempotent_id('b386c111-3b21-466d-880c-5e72b01e1a33')
+    @utils.requires_ext(extension='ext-gw-mode', service='network')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    def test_update_router_set_gateway_with_snat_explicit(self):
+        router = self._create_router()
+        self.admin_routers_client.update_router(
+            router['id'],
+            external_gateway_info={
+                'network_id': CONF.network.public_network_id,
+                'enable_snat': True})
+        self._verify_router_gateway(
+            router['id'],
+            {'network_id': CONF.network.public_network_id,
+             'enable_snat': True})
+        self._verify_gateway_port(router['id'])
+
+    @decorators.idempotent_id('96536bc7-8262-4fb2-9967-5c46940fa279')
+    @utils.requires_ext(extension='ext-gw-mode', service='network')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    def test_update_router_set_gateway_without_snat(self):
+        router = self._create_router()
+        self.admin_routers_client.update_router(
+            router['id'],
+            external_gateway_info={
+                'network_id': CONF.network.public_network_id,
+                'enable_snat': False})
+        self._verify_router_gateway(
+            router['id'],
+            {'network_id': CONF.network.public_network_id,
+             'enable_snat': False})
+        self._verify_gateway_port(router['id'])
+
+    @decorators.idempotent_id('ad81b7ee-4f81-407b-a19c-17e623f763e8')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    def test_update_router_unset_gateway(self):
+        router = self._create_router(
+            external_network_id=CONF.network.public_network_id)
+        self.routers_client.update_router(router['id'],
+                                          external_gateway_info={})
+        self._verify_router_gateway(router['id'])
+        # No gateway port expected
+        list_body = self.admin_ports_client.list_ports(
+            network_id=CONF.network.public_network_id,
+            device_id=router['id'])
+        self.assertFalse(list_body['ports'])
+
+    @decorators.idempotent_id('f2faf994-97f4-410b-a831-9bc977b64374')
+    @utils.requires_ext(extension='ext-gw-mode', service='network')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    def test_update_router_reset_gateway_without_snat(self):
+        router = self._create_router(
+            external_network_id=CONF.network.public_network_id)
+        self.admin_routers_client.update_router(
+            router['id'],
+            external_gateway_info={
+                'network_id': CONF.network.public_network_id,
+                'enable_snat': False})
+        self._verify_router_gateway(
+            router['id'],
+            {'network_id': CONF.network.public_network_id,
+             'enable_snat': False})
+        self._verify_gateway_port(router['id'])
+
+
+class RoutersIpV6AdminTest(RoutersAdminTest):
+    _ip_version = 6
diff --git a/tempest/api/network/admin/test_routers_dvr.py b/tempest/api/network/admin/test_routers_dvr.py
index 78ca494..93478e6 100644
--- a/tempest/api/network/admin/test_routers_dvr.py
+++ b/tempest/api/network/admin/test_routers_dvr.py
@@ -15,18 +15,19 @@
 
 import testtools
 
-from tempest.api.network import base_routers as base
-from tempest.common.utils import data_utils
+from tempest.api.network import base
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
-class RoutersTestDVR(base.BaseRouterTest):
+class RoutersTestDVR(base.BaseAdminNetworkTest):
 
     @classmethod
-    def resource_setup(cls):
+    def skip_checks(cls):
+        super(RoutersTestDVR, cls).skip_checks()
         for ext in ['router', 'dvr']:
-            if not test.is_extension_enabled(ext, 'network'):
+            if not utils.is_extension_enabled(ext, 'network'):
                 msg = "%s extension not enabled." % ext
                 raise cls.skipException(msg)
         # The check above will pass if api_extensions=all, which does
@@ -35,6 +36,9 @@
         # admin credentials to create router with distributed=True attribute
         # and checking for BadRequest exception and that the resulting router
         # has a distributed attribute.
+
+    @classmethod
+    def resource_setup(cls):
         super(RoutersTestDVR, cls).resource_setup()
         name = data_utils.rand_name('pretest-check')
         router = cls.admin_routers_client.create_router(name=name)
@@ -83,7 +87,7 @@
         self.assertFalse(router['router']['distributed'])
 
     @decorators.idempotent_id('acd43596-c1fb-439d-ada8-31ad48ae3c2e')
-    @testtools.skipUnless(test.is_extension_enabled('l3-ha', 'network'),
+    @testtools.skipUnless(utils.is_extension_enabled('l3-ha', 'network'),
                           'HA routers are not available.')
     def test_centralized_router_update_to_dvr(self):
         """Test centralized router update
diff --git a/tempest/api/network/admin/test_routers_negative.py b/tempest/api/network/admin/test_routers_negative.py
new file mode 100644
index 0000000..9356bcc
--- /dev/null
+++ b/tempest/api/network/admin/test_routers_negative.py
@@ -0,0 +1,63 @@
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import testtools
+
+from tempest.api.network import base
+from tempest.common import utils
+from tempest import config
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+CONF = config.CONF
+
+
+class RoutersAdminNegativeTest(base.BaseAdminNetworkTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(RoutersAdminNegativeTest, cls).skip_checks()
+        if not utils.is_extension_enabled('router', 'network'):
+            msg = "router extension not enabled."
+            raise cls.skipException(msg)
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('7101cc02-058a-11e7-93e1-fa163e4fa634')
+    @utils.requires_ext(extension='ext-gw-mode', service='network')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    def test_router_set_gateway_used_ip_returns_409(self):
+        # At first create a address from public_network_id
+        port = self.admin_ports_client.create_port(
+            network_id=CONF.network.public_network_id)['port']
+        self.addCleanup(self.admin_ports_client.delete_port,
+                        port_id=port['id'])
+        # Add used ip and subnet_id in external_fixed_ips
+        fixed_ip = {
+            'subnet_id': port['fixed_ips'][0]['subnet_id'],
+            'ip_address': port['fixed_ips'][0]['ip_address']
+        }
+        external_gateway_info = {
+            'network_id': CONF.network.public_network_id,
+            'external_fixed_ips': [fixed_ip]
+        }
+        # Create a router and set gateway to used ip
+        self.assertRaises(lib_exc.Conflict,
+                          self.admin_routers_client.create_router,
+                          external_gateway_info=external_gateway_info)
+
+
+class RoutersAdminNegativeIpV6Test(RoutersAdminNegativeTest):
+    _ip_version = 6
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 132e23e..6bec0d7 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -15,9 +15,9 @@
 
 import netaddr
 
-from tempest.common.utils import data_utils
 from tempest import config
 from tempest import exceptions
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
 import tempest.test
@@ -68,20 +68,22 @@
     @classmethod
     def setup_clients(cls):
         super(BaseNetworkTest, cls).setup_clients()
-        cls.agents_client = cls.os.network_agents_client
-        cls.network_extensions_client = cls.os.network_extensions_client
-        cls.networks_client = cls.os.networks_client
-        cls.routers_client = cls.os.routers_client
-        cls.subnetpools_client = cls.os.subnetpools_client
-        cls.subnets_client = cls.os.subnets_client
-        cls.ports_client = cls.os.ports_client
-        cls.quotas_client = cls.os.network_quotas_client
-        cls.floating_ips_client = cls.os.floating_ips_client
-        cls.security_groups_client = cls.os.security_groups_client
+        cls.agents_client = cls.os_primary.network_agents_client
+        cls.network_extensions_client =\
+            cls.os_primary.network_extensions_client
+        cls.networks_client = cls.os_primary.networks_client
+        cls.routers_client = cls.os_primary.routers_client
+        cls.subnetpools_client = cls.os_primary.subnetpools_client
+        cls.subnets_client = cls.os_primary.subnets_client
+        cls.ports_client = cls.os_primary.ports_client
+        cls.quotas_client = cls.os_primary.network_quotas_client
+        cls.floating_ips_client = cls.os_primary.floating_ips_client
+        cls.security_groups_client = cls.os_primary.security_groups_client
         cls.security_group_rules_client = (
-            cls.os.security_group_rules_client)
-        cls.network_versions_client = cls.os.network_versions_client
-        cls.service_providers_client = cls.os.service_providers_client
+            cls.os_primary.security_group_rules_client)
+        cls.network_versions_client = cls.os_primary.network_versions_client
+        cls.service_providers_client = cls.os_primary.service_providers_client
+        cls.tags_client = cls.os_primary.tags_client
 
     @classmethod
     def resource_setup(cls):
@@ -106,7 +108,7 @@
 
             # Clean up metering label rules
             # Not all classes in the hierarchy have the client class variable
-            if len(cls.metering_label_rules) > 0:
+            if cls.metering_label_rules:
                 label_rules_client = cls.admin_metering_label_rules_client
                 for metering_label_rule in cls.metering_label_rules:
                     test_utils.call_and_ignore_notfound_exc(
@@ -258,35 +260,13 @@
     @classmethod
     def setup_clients(cls):
         super(BaseAdminNetworkTest, cls).setup_clients()
-        cls.admin_agents_client = cls.os_adm.network_agents_client
-        cls.admin_networks_client = cls.os_adm.networks_client
-        cls.admin_routers_client = cls.os_adm.routers_client
-        cls.admin_subnets_client = cls.os_adm.subnets_client
-        cls.admin_ports_client = cls.os_adm.ports_client
-        cls.admin_quotas_client = cls.os_adm.network_quotas_client
-        cls.admin_floating_ips_client = cls.os_adm.floating_ips_client
-        cls.admin_metering_labels_client = cls.os_adm.metering_labels_client
+        cls.admin_agents_client = cls.os_admin.network_agents_client
+        cls.admin_networks_client = cls.os_admin.networks_client
+        cls.admin_routers_client = cls.os_admin.routers_client
+        cls.admin_subnets_client = cls.os_admin.subnets_client
+        cls.admin_ports_client = cls.os_admin.ports_client
+        cls.admin_quotas_client = cls.os_admin.network_quotas_client
+        cls.admin_floating_ips_client = cls.os_admin.floating_ips_client
+        cls.admin_metering_labels_client = cls.os_admin.metering_labels_client
         cls.admin_metering_label_rules_client = (
-            cls.os_adm.metering_label_rules_client)
-
-    @classmethod
-    def create_metering_label(cls, name, description):
-        """Wrapper utility that returns a test metering label."""
-        body = cls.admin_metering_labels_client.create_metering_label(
-            description=description,
-            name=name)
-        metering_label = body['metering_label']
-        cls.metering_labels.append(metering_label)
-        return metering_label
-
-    @classmethod
-    def create_metering_label_rule(cls, remote_ip_prefix, direction,
-                                   metering_label_id):
-        """Wrapper utility that returns a test metering label rule."""
-        client = cls.admin_metering_label_rules_client
-        body = client.create_metering_label_rule(
-            remote_ip_prefix=remote_ip_prefix, direction=direction,
-            metering_label_id=metering_label_id)
-        metering_label_rule = body['metering_label_rule']
-        cls.metering_label_rules.append(metering_label_rule)
-        return metering_label_rule
+            cls.os_admin.metering_label_rules_client)
diff --git a/tempest/api/network/base_routers.py b/tempest/api/network/base_routers.py
deleted file mode 100644
index 5fb5232..0000000
--- a/tempest/api/network/base_routers.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.network import base
-
-
-class BaseRouterTest(base.BaseAdminNetworkTest):
-    # NOTE(salv-orlando): This class inherits from BaseAdminNetworkTest
-    # as some router operations, such as enabling or disabling SNAT
-    # require admin credentials by default
-
-    def _cleanup_router(self, router):
-        self.delete_router(router)
-        self.routers.remove(router)
-
-    def _create_router(self, name=None, admin_state_up=False,
-                       external_network_id=None, enable_snat=None):
-        # associate a cleanup with created routers to avoid quota limits
-        router = self.create_router(name, admin_state_up,
-                                    external_network_id, enable_snat)
-        self.addCleanup(self._cleanup_router, router)
-        return router
-
-    def _delete_router(self, router_id, routers_client=None):
-        client = routers_client or self.routers_client
-        client.delete_router(router_id)
-        # Asserting that the router is not found in the list
-        # after deletion
-        list_body = self.routers_client.list_routers()
-        routers_list = list()
-        for router in list_body['routers']:
-            routers_list.append(router['id'])
-        self.assertNotIn(router_id, routers_list)
-
-    def _add_router_interface_with_subnet_id(self, router_id, subnet_id):
-        interface = self.routers_client.add_router_interface(
-            router_id, subnet_id=subnet_id)
-        self.addCleanup(self._remove_router_interface_with_subnet_id,
-                        router_id, subnet_id)
-        self.assertEqual(subnet_id, interface['subnet_id'])
-        return interface
-
-    def _remove_router_interface_with_subnet_id(self, router_id, subnet_id):
-        body = self.routers_client.remove_router_interface(router_id,
-                                                           subnet_id=subnet_id)
-        self.assertEqual(subnet_id, body['subnet_id'])
-
-    def _remove_router_interface_with_port_id(self, router_id, port_id):
-        body = self.routers_client.remove_router_interface(router_id,
-                                                           port_id=port_id)
-        self.assertEqual(port_id, body['port_id'])
diff --git a/tempest/api/network/base_security_groups.py b/tempest/api/network/base_security_groups.py
index 3ea3aea..b8d677a 100644
--- a/tempest/api/network/base_security_groups.py
+++ b/tempest/api/network/base_security_groups.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 from tempest.api.network import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 
 
 class BaseSecGroupTest(base.BaseNetworkTest):
diff --git a/tempest/api/network/test_allowed_address_pair.py b/tempest/api/network/test_allowed_address_pair.py
index a90e4bf..a471bd6 100644
--- a/tempest/api/network/test_allowed_address_pair.py
+++ b/tempest/api/network/test_allowed_address_pair.py
@@ -17,9 +17,9 @@
 import six
 
 from tempest.api.network import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -41,10 +41,12 @@
         api_extensions
     """
 
+    _project_network_cidr = CONF.network.project_network_cidr
+
     @classmethod
     def skip_checks(cls):
         super(AllowedAddressPairTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('allowed-address-pairs', 'network'):
+        if not utils.is_extension_enabled('allowed-address-pairs', 'network'):
             msg = "Allowed Address Pairs extension not enabled."
             raise cls.skipException(msg)
 
@@ -103,7 +105,7 @@
     @decorators.idempotent_id('4d6d178f-34f6-4bff-a01c-0a2f8fe909e4')
     def test_update_port_with_cidr_address_pair(self):
         # Update allowed address pair with cidr
-        cidr = str(netaddr.IPNetwork(CONF.network.project_network_cidr))
+        cidr = str(netaddr.IPNetwork(self._project_network_cidr))
         self._update_port_with_address(cidr)
 
     @decorators.idempotent_id('b3f20091-6cd5-472b-8487-3516137df933')
@@ -133,3 +135,4 @@
 
 class AllowedAddressPairIpV6TestJSON(AllowedAddressPairTestJSON):
     _ip_version = 6
+    _project_network_cidr = CONF.network.project_network_v6_cidr
diff --git a/tempest/api/network/test_dhcp_ipv6.py b/tempest/api/network/test_dhcp_ipv6.py
index fa4010d..9d6d700 100644
--- a/tempest/api/network/test_dhcp_ipv6.py
+++ b/tempest/api/network/test_dhcp_ipv6.py
@@ -13,13 +13,15 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import netaddr
 import random
 
+import netaddr
+from oslo_utils import netutils
+
 from tempest.api.network import base
-from tempest.common.utils import data_utils
 from tempest.common.utils import net_info
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
@@ -91,8 +93,8 @@
         port_mac = data_utils.rand_mac_address()
         port = self.create_port(self.network, mac_address=port_mac)
         real_ip = next(iter(port['fixed_ips']), None)['ip_address']
-        eui_ip = data_utils.get_ipv6_addr_by_EUI64(subnet['cidr'],
-                                                   port_mac).format()
+        eui_ip = str(netutils.get_ipv6_addr_by_EUI64(
+            subnet['cidr'], port_mac))
         return real_ip, eui_ip
 
     @decorators.idempotent_id('e5517e62-6f16-430d-a672-f80875493d4c')
@@ -187,10 +189,8 @@
                         self.network, **kwargs_dhcp)
                     subnet_slaac = self.create_subnet(self.network, **kwargs)
                 port_mac = data_utils.rand_mac_address()
-                eui_ip = data_utils.get_ipv6_addr_by_EUI64(
-                    subnet_slaac['cidr'],
-                    port_mac
-                ).format()
+                eui_ip = str(netutils.get_ipv6_addr_by_EUI64(
+                    subnet_slaac['cidr'], port_mac))
                 port = self.create_port(self.network, mac_address=port_mac)
                 real_ips = dict([(k['subnet_id'], k['ip_address'])
                                  for k in port['fixed_ips']])
@@ -235,10 +235,8 @@
                         self.network, ip_version=4)
                     subnet_slaac = self.create_subnet(self.network, **kwargs)
                 port_mac = data_utils.rand_mac_address()
-                eui_ip = data_utils.get_ipv6_addr_by_EUI64(
-                    subnet_slaac['cidr'],
-                    port_mac
-                ).format()
+                eui_ip = str(netutils.get_ipv6_addr_by_EUI64(
+                    subnet_slaac['cidr'], port_mac))
                 port = self.create_port(self.network, mac_address=port_mac)
                 real_ips = dict([(k['subnet_id'], k['ip_address'])
                                  for k in port['fixed_ips']])
diff --git a/tempest/api/network/test_extensions.py b/tempest/api/network/test_extensions.py
index 2662e9c..4804ada 100644
--- a/tempest/api/network/test_extensions.py
+++ b/tempest/api/network/test_extensions.py
@@ -15,8 +15,8 @@
 
 
 from tempest.api.network import base
+from tempest.common import utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class ExtensionsTestJSON(base.BaseNetworkTest):
@@ -29,7 +29,7 @@
     etc/tempest.conf.
     """
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('ef28c7e6-e646-4979-9d67-deb207bc5564')
     def test_list_show_extensions(self):
         # List available extensions for the project
@@ -40,7 +40,7 @@
                           'allowed-address-pairs', 'extra_dhcp_opt',
                           'metering', 'dvr']
         expected_alias = [ext for ext in expected_alias if
-                          test.is_extension_enabled(ext, 'network')]
+                          utils.is_extension_enabled(ext, 'network')]
         actual_alias = list()
         extensions = self.network_extensions_client.list_extensions()
         list_extensions = extensions['extensions']
@@ -66,5 +66,5 @@
         # of extensions returned, but only for those that have been
         # enabled via configuration
         for e in expected_alias:
-            if test.is_extension_enabled(e, 'network'):
+            if utils.is_extension_enabled(e, 'network'):
                 self.assertIn(e, actual_alias)
diff --git a/tempest/api/network/test_extra_dhcp_options.py b/tempest/api/network/test_extra_dhcp_options.py
index 52507f9..0d42033 100644
--- a/tempest/api/network/test_extra_dhcp_options.py
+++ b/tempest/api/network/test_extra_dhcp_options.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 from tempest.api.network import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class ExtraDHCPOptionsTestJSON(base.BaseNetworkTest):
@@ -35,7 +35,7 @@
     @classmethod
     def skip_checks(cls):
         super(ExtraDHCPOptionsTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('extra_dhcp_opt', 'network'):
+        if not utils.is_extension_enabled('extra_dhcp_opt', 'network'):
             msg = "Extra DHCP Options extension not enabled."
             raise cls.skipException(msg)
 
@@ -43,16 +43,16 @@
     def resource_setup(cls):
         super(ExtraDHCPOptionsTestJSON, cls).resource_setup()
         cls.network = cls.create_network()
-        cls.subnet = cls.create_subnet(cls.network)
+        cls.create_subnet(cls.network)
         cls.port = cls.create_port(cls.network)
-        cls.ip_tftp = ('123.123.123.123' if cls._ip_version == 4
-                       else '2015::dead')
-        cls.ip_server = ('123.123.123.45' if cls._ip_version == 4
-                         else '2015::badd')
+        ip_tftp = ('123.123.123.123' if cls._ip_version == 4
+                   else '2015::dead')
+        ip_server = ('123.123.123.45' if cls._ip_version == 4
+                     else '2015::badd')
         cls.extra_dhcp_opts = [
             {'opt_value': 'pxelinux.0', 'opt_name': 'bootfile-name'},
-            {'opt_value': cls.ip_tftp, 'opt_name': 'tftp-server'},
-            {'opt_value': cls.ip_server, 'opt_name': 'server-ip-address'}
+            {'opt_value': ip_tftp, 'opt_name': 'tftp-server'},
+            {'opt_value': ip_server, 'opt_name': 'server-ip-address'}
         ]
 
     @decorators.idempotent_id('d2c17063-3767-4a24-be4f-a23dbfa133c9')
@@ -75,7 +75,7 @@
     def test_update_show_port_with_extra_dhcp_options(self):
         # Update port with extra dhcp options
         name = data_utils.rand_name('new-port-name')
-        body = self.ports_client.update_port(
+        self.ports_client.update_port(
             self.port['id'],
             name=name,
             extra_dhcp_opts=self.extra_dhcp_opts)
diff --git a/tempest/api/network/test_floating_ips.py b/tempest/api/network/test_floating_ips.py
index 23614d6..ef4a23a 100644
--- a/tempest/api/network/test_floating_ips.py
+++ b/tempest/api/network/test_floating_ips.py
@@ -14,10 +14,10 @@
 #    under the License.
 
 from tempest.api.network import base
+from tempest.common import utils
 from tempest.common.utils import net_utils
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -43,9 +43,14 @@
     @classmethod
     def skip_checks(cls):
         super(FloatingIPTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('router', 'network'):
+        if not utils.is_extension_enabled('router', 'network'):
             msg = "router extension not enabled."
             raise cls.skipException(msg)
+        if not CONF.network.public_network_id:
+            msg = "The public_network_id option must be specified."
+            raise cls.skipException(msg)
+        if not CONF.network_feature_enabled.floating_ips:
+            raise cls.skipException("Floating ips are not available")
 
     @classmethod
     def resource_setup(cls):
@@ -61,7 +66,7 @@
         for i in range(2):
             cls.create_port(cls.network)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('62595970-ab1c-4b7f-8fcc-fddfe55e8718')
     def test_create_list_show_update_delete_floating_ip(self):
         # Creates a floating IP
@@ -168,7 +173,7 @@
                          port_other_router['id'])
         self.assertIsNotNone(updated_floating_ip['fixed_ip_address'])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('36de4bd0-f09c-43e3-a8e1-1decc1ffd3a5')
     def test_create_floating_ip_specifying_a_fixed_ip_address(self):
         body = self.floating_ips_client.create_floatingip(
diff --git a/tempest/api/network/test_floating_ips_negative.py b/tempest/api/network/test_floating_ips_negative.py
index 9ccda05..e904a81 100644
--- a/tempest/api/network/test_floating_ips_negative.py
+++ b/tempest/api/network/test_floating_ips_negative.py
@@ -15,10 +15,10 @@
 #    under the License.
 
 from tempest.api.network import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -34,9 +34,14 @@
     @classmethod
     def skip_checks(cls):
         super(FloatingIPNegativeTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('router', 'network'):
+        if not utils.is_extension_enabled('router', 'network'):
             msg = "router extension not enabled."
             raise cls.skipException(msg)
+        if not CONF.network.public_network_id:
+            msg = "The public_network_id option must be specified."
+            raise cls.skipException(msg)
+        if not CONF.network_feature_enabled.floating_ips:
+            raise cls.skipException("Floating ips are not available")
 
     @classmethod
     def resource_setup(cls):
@@ -44,12 +49,12 @@
         cls.ext_net_id = CONF.network.public_network_id
         # Create a network with a subnet connected to a router.
         cls.network = cls.create_network()
-        cls.subnet = cls.create_subnet(cls.network)
-        cls.router = cls.create_router()
-        cls.create_router_interface(cls.router['id'], cls.subnet['id'])
+        subnet = cls.create_subnet(cls.network)
+        router = cls.create_router()
+        cls.create_router_interface(router['id'], subnet['id'])
         cls.port = cls.create_port(cls.network)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('22996ea8-4a81-4b27-b6e1-fa5df92fa5e8')
     def test_create_floatingip_with_port_ext_net_unreachable(self):
         self.assertRaises(
@@ -58,7 +63,7 @@
             fixed_ip_address=self.port['fixed_ips'][0]
                                       ['ip_address'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('50b9aeb4-9f0b-48ee-aa31-fa955a48ff54')
     def test_create_floatingip_in_private_network(self):
         self.assertRaises(lib_exc.BadRequest,
@@ -68,7 +73,7 @@
                           fixed_ip_address=self.port['fixed_ips'][0]
                                                     ['ip_address'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6b3b8797-6d43-4191-985c-c48b773eb429')
     def test_associate_floatingip_port_ext_net_unreachable(self):
         # Create floating ip
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 1426798..88340c1 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -18,12 +18,12 @@
 
 from tempest.api.network import base
 from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -34,7 +34,6 @@
     def resource_setup(cls):
         super(BaseNetworkTestResources, cls).resource_setup()
         cls.network = cls.create_network()
-        cls.name = cls.network['name']
         cls.subnet = cls._create_subnet_with_last_subnet_block(cls.network,
                                                                cls._ip_version)
         cls._subnet_data = {6: {'gateway':
@@ -175,7 +174,7 @@
         project_network_v6_mask_bits is the equivalent for ipv6 subnets
     """
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('0e269138-0da6-4efc-a46d-578161e7b221')
     def test_create_update_delete_network_subnet(self):
         # Create a network
@@ -197,7 +196,7 @@
         updated_subnet = body['subnet']
         self.assertEqual(updated_subnet['name'], new_name)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('2bf13842-c93f-4a69-83ed-717d2ec3b44e')
     def test_show_network(self):
         # Verify the details of a network
@@ -210,7 +209,7 @@
     def test_show_network_fields(self):
         # Verify specific fields of a network
         fields = ['id', 'name']
-        if test.is_extension_enabled('net-mtu', 'network'):
+        if utils.is_extension_enabled('net-mtu', 'network'):
             fields.append('mtu')
         body = self.networks_client.show_network(self.network['id'],
                                                  fields=fields)
@@ -221,7 +220,7 @@
         self.assertNotIn('tenant_id', network)
         self.assertNotIn('project_id', network)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('f7ffdeda-e200-4a7a-bcbe-05716e86bf43')
     def test_list_networks(self):
         # Verify the network exists in the list of all networks
@@ -234,7 +233,7 @@
     def test_list_networks_fields(self):
         # Verify specific fields of the networks
         fields = ['id', 'name']
-        if test.is_extension_enabled('net-mtu', 'network'):
+        if utils.is_extension_enabled('net-mtu', 'network'):
             fields.append('mtu')
         body = self.networks_client.list_networks(fields=fields)
         networks = body['networks']
@@ -242,7 +241,7 @@
         for network in networks:
             self.assertEqual(sorted(network.keys()), sorted(fields))
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('bd635d81-6030-4dd1-b3b9-31ba0cfdf6cc')
     def test_show_subnet(self):
         # Verify the details of a subnet
@@ -264,7 +263,7 @@
         for field_name in fields:
             self.assertEqual(subnet[field_name], self.subnet[field_name])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('db68ba48-f4ea-49e9-81d1-e367f6d0b20a')
     def test_list_subnets(self):
         # Verify the subnet exists in the list of all subnets
@@ -369,32 +368,46 @@
             enable_dhcp=True,
             **self.subnet_dict(['gateway', 'host_routes', 'dns_nameservers']))
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('af774677-42a9-4e4b-bb58-16fe6a5bc1ec')
-    @test.requires_ext(extension='external-net', service='network')
+    @utils.requires_ext(extension='external-net', service='network')
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
     def test_external_network_visibility(self):
-        """Verifies user can see external networks but not subnets."""
+        public_network_id = CONF.network.public_network_id
+
+        # find external network matching public_network_id
         body = self.networks_client.list_networks(**{'router:external': True})
-        networks = [network['id'] for network in body['networks']]
-        self.assertNotEmpty(networks, "No external networks found")
+        external_network = next((network for network in body['networks']
+                                 if network['id'] == public_network_id), None)
+        self.assertIsNotNone(external_network, "Public network %s not found "
+                                               "in external network list"
+                             % public_network_id)
 
         nonexternal = [net for net in body['networks'] if
                        not net['router:external']]
         self.assertEmpty(nonexternal, "Found non-external networks"
                                       " in filtered list (%s)." % nonexternal)
-        self.assertIn(CONF.network.public_network_id, networks)
+
         # only check the public network ID because the other networks may
         # belong to other tests and their state may have changed during this
         # test
-        body = self.subnets_client.list_subnets(
-            network_id=CONF.network.public_network_id)
-        self.assertEmpty(body['subnets'], "Public subnets visible")
+        body = self.subnets_client.list_subnets(network_id=public_network_id)
+
+        # check subnet visibility of external_network
+        if external_network['shared']:
+            self.assertNotEmpty(body['subnets'], "Subnets should be visible "
+                                                 "for shared public network %s"
+                                % public_network_id)
+        else:
+            self.assertEmpty(body['subnets'], "Subnets should not be visible "
+                                              "for non-shared public "
+                                              "network %s"
+                             % public_network_id)
 
     @decorators.idempotent_id('c72c1c0c-2193-4aca-ccc4-b1442640bbbb')
-    @test.requires_ext(extension="standard-attr-description",
-                       service="network")
+    @utils.requires_ext(extension="standard-attr-description",
+                        service="network")
     def test_create_update_network_description(self):
         body = self.create_network(description='d1')
         self.assertEqual('d1', body['description'])
@@ -453,7 +466,7 @@
         for n in created_ports:
             self.assertNotIn(n['id'], ports_list)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('d4f9024d-1e28-4fc1-a6b1-25dbc6fa11e2')
     def test_bulk_create_delete_network(self):
         # Creates 2 networks in one request
@@ -469,7 +482,7 @@
             self.assertIsNotNone(n['id'])
             self.assertIn(n['id'], networks_list)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('8936533b-c0aa-4f29-8e53-6cc873aec489')
     def test_bulk_create_delete_subnet(self):
         networks = [self.create_network(), self.create_network()]
@@ -504,7 +517,7 @@
             self.assertIsNotNone(n['id'])
             self.assertIn(n['id'], subnets_list)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('48037ff2-e889-4c3b-b86a-8e3f34d2d060')
     def test_bulk_create_delete_port(self):
         networks = [self.create_network(), self.create_network()]
diff --git a/tempest/api/network/test_networks_negative.py b/tempest/api/network/test_networks_negative.py
index 3cc6fb7..bc4f41f 100644
--- a/tempest/api/network/test_networks_negative.py
+++ b/tempest/api/network/test_networks_negative.py
@@ -15,36 +15,35 @@
 #    under the License.
 
 from tempest.api.network import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class NetworksNegativeTestJSON(base.BaseNetworkTest):
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9293e937-824d-42d2-8d5b-e985ea67002a')
     def test_show_non_existent_network(self):
         non_exist_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.networks_client.show_network,
                           non_exist_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d746b40c-5e09-4043-99f7-cba1be8b70df')
     def test_show_non_existent_subnet(self):
         non_exist_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.subnets_client.show_subnet,
                           non_exist_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a954861d-cbfd-44e8-b0a9-7fab111f235d')
     def test_show_non_existent_port(self):
         non_exist_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.ports_client.show_port,
                           non_exist_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('98bfe4e3-574e-4012-8b17-b2647063de87')
     def test_update_non_existent_network(self):
         non_exist_id = data_utils.rand_uuid()
@@ -52,7 +51,7 @@
             lib_exc.NotFound, self.networks_client.update_network,
             non_exist_id, name="new_name")
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('03795047-4a94-4120-a0a1-bd376e36fd4e')
     def test_delete_non_existent_network(self):
         non_exist_id = data_utils.rand_uuid()
@@ -60,21 +59,21 @@
                           self.networks_client.delete_network,
                           non_exist_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1cc47884-ac52-4415-a31c-e7ce5474a868')
     def test_update_non_existent_subnet(self):
         non_exist_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.subnets_client.update_subnet,
                           non_exist_id, name='new_name')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a176c859-99fb-42ec-a208-8a85b552a239')
     def test_delete_non_existent_subnet(self):
         non_exist_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound,
                           self.subnets_client.delete_subnet, non_exist_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('13d3b106-47e6-4b9b-8d53-dae947f092fe')
     def test_create_port_on_non_existent_network(self):
         non_exist_net_id = data_utils.rand_uuid()
@@ -82,14 +81,14 @@
                           self.ports_client.create_port,
                           network_id=non_exist_net_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('cf8eef21-4351-4f53-adcd-cc5cb1e76b92')
     def test_update_non_existent_port(self):
         non_exist_port_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.ports_client.update_port,
                           non_exist_port_id, name='new_name')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('49ec2bbd-ac2e-46fd-8054-798e679ff894')
     def test_delete_non_existent_port(self):
         non_exist_port_id = data_utils.rand_uuid()
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 3908aae..5c36747 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -13,19 +13,16 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import socket
-
 import netaddr
 import testtools
 
-from tempest.api.network import base
 from tempest.api.network import base_security_groups as sec_base
 from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions
-from tempest import test
 
 CONF = config.CONF
 
@@ -52,7 +49,7 @@
         ports_list = body['ports']
         self.assertFalse(port_id in [n['id'] for n in ports_list])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('c72c1c0c-2193-4aca-aaa4-b1442640f51c')
     def test_create_update_delete_port(self):
         # Verify port creation
@@ -100,7 +97,7 @@
 
         return cidr
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('0435f278-40ae-48cb-a404-b8a087bc09b1')
     def test_create_port_in_allowed_allocation_pools(self):
         network = self.create_network()
@@ -125,7 +122,7 @@
         ip_range = netaddr.IPRange(start_ip_address, end_ip_address)
         self.assertIn(ip_address, ip_range)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('c9a685bd-e83f-499c-939f-9f7863ca259f')
     def test_show_port(self):
         # Verify the details of port
@@ -153,7 +150,7 @@
         for field_name in fields:
             self.assertEqual(port[field_name], self.port[field_name])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('cf95b358-3e92-4a29-a148-52445e1ac50e')
     def test_list_ports(self):
         # Verify the port exists in the list of all ports
@@ -310,7 +307,7 @@
 
     @decorators.idempotent_id('58091b66-4ff4-4cc1-a549-05d60c7acd1a')
     @testtools.skipUnless(
-        test.is_extension_enabled('security-group', 'network'),
+        utils.is_extension_enabled('security-group', 'network'),
         'security-group extension not enabled.')
     def test_update_port_with_security_group_and_extra_attributes(self):
         self._update_port_with_security_groups(
@@ -318,7 +315,7 @@
 
     @decorators.idempotent_id('edf6766d-3d40-4621-bc6e-2521a44c257d')
     @testtools.skipUnless(
-        test.is_extension_enabled('security-group', 'network'),
+        utils.is_extension_enabled('security-group', 'network'),
         'security-group extension not enabled.')
     def test_update_port_with_two_security_groups_and_extra_attributes(self):
         self._update_port_with_security_groups(
@@ -342,10 +339,10 @@
         self.assertEqual(free_mac_address,
                          show_port['mac_address'])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('4179dcb9-1382-4ced-84fe-1b91c54f5735')
     @testtools.skipUnless(
-        test.is_extension_enabled('security-group', 'network'),
+        utils.is_extension_enabled('security-group', 'network'),
         'security-group extension not enabled.')
     def test_create_port_with_no_securitygroups(self):
         network = self.create_network()
@@ -358,82 +355,5 @@
         self.assertEmpty(port['security_groups'])
 
 
-class PortsAdminExtendedAttrsTestJSON(base.BaseAdminNetworkTest):
-
-    @classmethod
-    def resource_setup(cls):
-        super(PortsAdminExtendedAttrsTestJSON, cls).resource_setup()
-        cls.network = cls.create_network()
-        cls.host_id = socket.gethostname()
-
-    @decorators.idempotent_id('8e8569c1-9ac7-44db-8bc1-f5fb2814f29b')
-    def test_create_port_binding_ext_attr(self):
-        post_body = {"network_id": self.network['id'],
-                     "binding:host_id": self.host_id}
-        body = self.admin_ports_client.create_port(**post_body)
-        port = body['port']
-        self.addCleanup(self.admin_ports_client.delete_port, port['id'])
-        host_id = port['binding:host_id']
-        self.assertIsNotNone(host_id)
-        self.assertEqual(self.host_id, host_id)
-
-    @decorators.idempotent_id('6f6c412c-711f-444d-8502-0ac30fbf5dd5')
-    def test_update_port_binding_ext_attr(self):
-        post_body = {"network_id": self.network['id']}
-        body = self.admin_ports_client.create_port(**post_body)
-        port = body['port']
-        self.addCleanup(self.admin_ports_client.delete_port, port['id'])
-        update_body = {"binding:host_id": self.host_id}
-        body = self.admin_ports_client.update_port(port['id'], **update_body)
-        updated_port = body['port']
-        host_id = updated_port['binding:host_id']
-        self.assertIsNotNone(host_id)
-        self.assertEqual(self.host_id, host_id)
-
-    @decorators.idempotent_id('1c82a44a-6c6e-48ff-89e1-abe7eaf8f9f8')
-    def test_list_ports_binding_ext_attr(self):
-        # Create a new port
-        post_body = {"network_id": self.network['id']}
-        body = self.admin_ports_client.create_port(**post_body)
-        port = body['port']
-        self.addCleanup(self.admin_ports_client.delete_port, port['id'])
-
-        # Update the port's binding attributes so that is now 'bound'
-        # to a host
-        update_body = {"binding:host_id": self.host_id}
-        self.admin_ports_client.update_port(port['id'], **update_body)
-
-        # List all ports, ensure new port is part of list and its binding
-        # attributes are set and accurate
-        body = self.admin_ports_client.list_ports()
-        ports_list = body['ports']
-        pids_list = [p['id'] for p in ports_list]
-        self.assertIn(port['id'], pids_list)
-        listed_port = [p for p in ports_list if p['id'] == port['id']]
-        self.assertEqual(1, len(listed_port),
-                         'Multiple ports listed with id %s in ports listing: '
-                         '%s' % (port['id'], ports_list))
-        self.assertEqual(self.host_id, listed_port[0]['binding:host_id'])
-
-    @decorators.idempotent_id('b54ac0ff-35fc-4c79-9ca3-c7dbd4ea4f13')
-    def test_show_port_binding_ext_attr(self):
-        body = self.admin_ports_client.create_port(
-            network_id=self.network['id'])
-        port = body['port']
-        self.addCleanup(self.admin_ports_client.delete_port, port['id'])
-        body = self.admin_ports_client.show_port(port['id'])
-        show_port = body['port']
-        self.assertEqual(port['binding:host_id'],
-                         show_port['binding:host_id'])
-        self.assertEqual(port['binding:vif_type'],
-                         show_port['binding:vif_type'])
-        self.assertEqual(port['binding:vif_details'],
-                         show_port['binding:vif_details'])
-
-
 class PortsIpV6TestJSON(PortsTestJSON):
     _ip_version = 6
-
-
-class PortsAdminExtendedAttrsIpV6TestJSON(PortsAdminExtendedAttrsTestJSON):
-    _ip_version = 6
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 524ab9e..3883cc2 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -14,22 +14,48 @@
 #    under the License.
 
 import netaddr
+import testtools
 
-from tempest.api.network import base_routers as base
-from tempest.common.utils import data_utils
+from tempest.api.network import base
+from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
 
-class RoutersTest(base.BaseRouterTest):
+class RoutersTest(base.BaseNetworkTest):
+
+    def _cleanup_router(self, router):
+        self.delete_router(router)
+        self.routers.remove(router)
+
+    def _create_router(self, name=None, admin_state_up=False,
+                       external_network_id=None, enable_snat=None):
+        # associate a cleanup with created routers to avoid quota limits
+        router = self.create_router(name, admin_state_up,
+                                    external_network_id, enable_snat)
+        self.addCleanup(self._cleanup_router, router)
+        return router
+
+    def _add_router_interface_with_subnet_id(self, router_id, subnet_id):
+        interface = self.routers_client.add_router_interface(
+            router_id, subnet_id=subnet_id)
+        self.addCleanup(self._remove_router_interface_with_subnet_id,
+                        router_id, subnet_id)
+        self.assertEqual(subnet_id, interface['subnet_id'])
+        return interface
+
+    def _remove_router_interface_with_subnet_id(self, router_id, subnet_id):
+        body = self.routers_client.remove_router_interface(router_id,
+                                                           subnet_id=subnet_id)
+        self.assertEqual(subnet_id, body['subnet_id'])
 
     @classmethod
     def skip_checks(cls):
         super(RoutersTest, cls).skip_checks()
-        if not test.is_extension_enabled('router', 'network'):
+        if not utils.is_extension_enabled('router', 'network'):
             msg = "router extension not enabled."
             raise cls.skipException(msg)
 
@@ -40,8 +66,10 @@
                            if cls._ip_version == 4 else
                            CONF.network.project_network_v6_cidr)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('f64403e2-8483-4b34-8ccd-b09a87bcc68c')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
     def test_create_show_list_update_delete_router(self):
         # Create a router
         router = self._create_router(
@@ -70,52 +98,7 @@
             router['id'])['router']
         self.assertEqual(router_show['name'], updated_name)
 
-    @decorators.idempotent_id('e54dd3a3-4352-4921-b09d-44369ae17397')
-    def test_create_router_setting_project_id(self):
-        # Test creating router from admin user setting project_id.
-        project = data_utils.rand_name('test_tenant_')
-        description = data_utils.rand_name('desc_')
-        project = self.identity_utils.create_project(name=project,
-                                                     description=description)
-        project_id = project['id']
-        self.addCleanup(self.identity_utils.delete_project, project_id)
-
-        name = data_utils.rand_name('router-')
-        create_body = self.admin_routers_client.create_router(
-            name=name, tenant_id=project_id)
-        self.addCleanup(self.admin_routers_client.delete_router,
-                        create_body['router']['id'])
-        self.assertEqual(project_id, create_body['router']['tenant_id'])
-
-    @decorators.idempotent_id('847257cc-6afd-4154-b8fb-af49f5670ce8')
-    @test.requires_ext(extension='ext-gw-mode', service='network')
-    def test_create_router_with_default_snat_value(self):
-        # Create a router with default snat rule
-        router = self._create_router(
-            external_network_id=CONF.network.public_network_id)
-        self._verify_router_gateway(
-            router['id'], {'network_id': CONF.network.public_network_id,
-                           'enable_snat': True})
-
-    @decorators.idempotent_id('ea74068d-09e9-4fd7-8995-9b6a1ace920f')
-    @test.requires_ext(extension='ext-gw-mode', service='network')
-    def test_create_router_with_snat_explicit(self):
-        name = data_utils.rand_name('snat-router')
-        # Create a router enabling snat attributes
-        enable_snat_states = [False, True]
-        for enable_snat in enable_snat_states:
-            external_gateway_info = {
-                'network_id': CONF.network.public_network_id,
-                'enable_snat': enable_snat}
-            create_body = self.admin_routers_client.create_router(
-                name=name, external_gateway_info=external_gateway_info)
-            self.addCleanup(self.admin_routers_client.delete_router,
-                            create_body['router']['id'])
-            # Verify snat attributes after router creation
-            self._verify_router_gateway(create_body['router']['id'],
-                                        exp_ext_gw_info=external_gateway_info)
-
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('b42e6e39-2e37-49cc-a6f4-8467e940900a')
     def test_add_remove_router_interface_with_subnet_id(self):
         network = self.create_network()
@@ -134,7 +117,7 @@
         self.assertEqual(show_port_body['port']['device_id'],
                          router['id'])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('2b7d2f37-6748-4d78-92e5-1d590234f0d5')
     def test_add_remove_router_interface_with_port_id(self):
         network = self.create_network()
@@ -146,8 +129,8 @@
         interface = self.routers_client.add_router_interface(
             router['id'],
             port_id=port_body['port']['id'])
-        self.addCleanup(self._remove_router_interface_with_port_id,
-                        router['id'], port_body['port']['id'])
+        self.addCleanup(self.routers_client.remove_router_interface,
+                        router['id'], port_id=port_body['port']['id'])
         self.assertIn('subnet_id', interface.keys())
         self.assertIn('port_id', interface.keys())
         # Verify router id is equal to device id in port details
@@ -173,7 +156,7 @@
         self.assertEqual(len(list_body['ports']), 1)
         gw_port = list_body['ports'][0]
         fixed_ips = gw_port['fixed_ips']
-        self.assertGreaterEqual(len(fixed_ips), 1)
+        self.assertNotEmpty(fixed_ips)
         # Assert that all of the IPs from the router gateway port
         # are allocated from a valid public subnet.
         public_net_body = self.admin_networks_client.show_network(
@@ -183,80 +166,39 @@
             subnet_id = fixed_ip['subnet_id']
             self.assertIn(subnet_id, public_subnet_ids)
 
-    @decorators.idempotent_id('6cc285d8-46bf-4f36-9b1a-783e3008ba79')
-    def test_update_router_set_gateway(self):
-        router = self._create_router()
-        self.routers_client.update_router(
-            router['id'],
-            external_gateway_info={
-                'network_id': CONF.network.public_network_id})
-        # Verify operation - router
-        self._verify_router_gateway(
-            router['id'],
-            {'network_id': CONF.network.public_network_id})
-        self._verify_gateway_port(router['id'])
+    @decorators.idempotent_id('cbe42f84-04c2-11e7-8adb-fa163e4fa634')
+    @utils.requires_ext(extension='ext-gw-mode', service='network')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    @decorators.skip_because(bug='1676207')
+    def test_create_router_set_gateway_with_fixed_ip(self):
+        # Don't know public_network_address, so at first create address
+        # from public_network and delete
+        port = self.admin_ports_client.create_port(
+            network_id=CONF.network.public_network_id)['port']
+        self.admin_ports_client.delete_port(port_id=port['id'])
 
-    @decorators.idempotent_id('b386c111-3b21-466d-880c-5e72b01e1a33')
-    @test.requires_ext(extension='ext-gw-mode', service='network')
-    def test_update_router_set_gateway_with_snat_explicit(self):
-        router = self._create_router()
-        self.admin_routers_client.update_router(
-            router['id'],
-            external_gateway_info={
-                'network_id': CONF.network.public_network_id,
-                'enable_snat': True})
-        self._verify_router_gateway(
-            router['id'],
-            {'network_id': CONF.network.public_network_id,
-             'enable_snat': True})
-        self._verify_gateway_port(router['id'])
+        fixed_ip = {
+            'subnet_id': port['fixed_ips'][0]['subnet_id'],
+            'ip_address': port['fixed_ips'][0]['ip_address']
+        }
+        external_gateway_info = {
+            'network_id': CONF.network.public_network_id,
+            'external_fixed_ips': [fixed_ip]
+        }
 
-    @decorators.idempotent_id('96536bc7-8262-4fb2-9967-5c46940fa279')
-    @test.requires_ext(extension='ext-gw-mode', service='network')
-    def test_update_router_set_gateway_without_snat(self):
-        router = self._create_router()
-        self.admin_routers_client.update_router(
-            router['id'],
-            external_gateway_info={
-                'network_id': CONF.network.public_network_id,
-                'enable_snat': False})
-        self._verify_router_gateway(
-            router['id'],
-            {'network_id': CONF.network.public_network_id,
-             'enable_snat': False})
-        self._verify_gateway_port(router['id'])
-
-    @decorators.idempotent_id('ad81b7ee-4f81-407b-a19c-17e623f763e8')
-    def test_update_router_unset_gateway(self):
-        router = self._create_router(
-            external_network_id=CONF.network.public_network_id)
-        self.routers_client.update_router(router['id'],
-                                          external_gateway_info={})
-        self._verify_router_gateway(router['id'])
-        # No gateway port expected
-        list_body = self.admin_ports_client.list_ports(
-            network_id=CONF.network.public_network_id,
-            device_id=router['id'])
-        self.assertFalse(list_body['ports'])
-
-    @decorators.idempotent_id('f2faf994-97f4-410b-a831-9bc977b64374')
-    @test.requires_ext(extension='ext-gw-mode', service='network')
-    def test_update_router_reset_gateway_without_snat(self):
-        router = self._create_router(
-            external_network_id=CONF.network.public_network_id)
-        self.admin_routers_client.update_router(
-            router['id'],
-            external_gateway_info={
-                'network_id': CONF.network.public_network_id,
-                'enable_snat': False})
-        self._verify_router_gateway(
-            router['id'],
-            {'network_id': CONF.network.public_network_id,
-             'enable_snat': False})
-        self._verify_gateway_port(router['id'])
+        # Create a router and set gateway to fixed_ip
+        router = self.admin_routers_client.create_router(
+            external_gateway_info=external_gateway_info)['router']
+        self.addCleanup(self.admin_routers_client.delete_router,
+                        router_id=router['id'])
+        # Examine router's gateway is equal to fixed_ip
+        self.assertEqual(router['external_gateway_info'][
+                         'external_fixed_ips'][0]['ip_address'],
+                         fixed_ip['ip_address'])
 
     @decorators.idempotent_id('c86ac3a8-50bd-4b00-a6b8-62af84a0765c')
-    @test.requires_ext(extension='extraroute', service='network')
+    @utils.requires_ext(extension='extraroute', service='network')
     def test_update_delete_extra_route(self):
         # Create different cidr for each subnet to avoid cidr duplicate
         # The cidr starts from project_cidr
@@ -328,7 +270,7 @@
         show_body = self.routers_client.show_router(router['id'])
         self.assertTrue(show_body['router']['admin_state_up'])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('802c73c9-c937-4cef-824b-2191e24a6aab')
     def test_add_multiple_router_interfaces(self):
         network01 = self.create_network(
diff --git a/tempest/api/network/test_routers_negative.py b/tempest/api/network/test_routers_negative.py
index 2bda431..60b9de7 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -15,22 +15,22 @@
 
 import netaddr
 
-from tempest.api.network import base_routers as base
-from tempest.common.utils import data_utils
+from tempest.api.network import base
+from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
 
-class RoutersNegativeTest(base.BaseRouterTest):
+class RoutersNegativeTest(base.BaseNetworkTest):
 
     @classmethod
     def skip_checks(cls):
         super(RoutersNegativeTest, cls).skip_checks()
-        if not test.is_extension_enabled('router', 'network'):
+        if not utils.is_extension_enabled('router', 'network'):
             msg = "router extension not enabled."
             raise cls.skipException(msg)
 
@@ -44,7 +44,7 @@
                            if cls._ip_version == 4 else
                            CONF.network.project_network_v6_cidr)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('37a94fc0-a834-45b9-bd23-9a81d2fd1e22')
     def test_router_add_gateway_invalid_network_returns_404(self):
         self.assertRaises(lib_exc.NotFound,
@@ -53,7 +53,7 @@
                           external_gateway_info={
                               'network_id': self.router['id']})
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('11836a18-0b15-4327-a50b-f0d9dc66bddd')
     def test_router_add_gateway_net_not_external_returns_400(self):
         alt_network = self.create_network()
@@ -65,7 +65,7 @@
                           external_gateway_info={
                               'network_id': alt_network['id']})
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('957751a3-3c68-4fa2-93b6-eb52ea10db6e')
     def test_add_router_interfaces_on_overlapping_subnets_returns_400(self):
         network01 = self.create_network(
@@ -74,14 +74,17 @@
             network_name=data_utils.rand_name('router-network02-'))
         subnet01 = self.create_subnet(network01)
         subnet02 = self.create_subnet(network02)
-        self._add_router_interface_with_subnet_id(self.router['id'],
-                                                  subnet01['id'])
+        interface = self.routers_client.add_router_interface(
+            self.router['id'], subnet_id=subnet01['id'])
+        self.addCleanup(self.routers_client.remove_router_interface,
+                        self.router['id'], subnet_id=subnet01['id'])
+        self.assertEqual(subnet01['id'], interface['subnet_id'])
         self.assertRaises(lib_exc.BadRequest,
-                          self._add_router_interface_with_subnet_id,
+                          self.routers_client.add_router_interface,
                           self.router['id'],
-                          subnet02['id'])
+                          subnet_id=subnet02['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('04df80f9-224d-47f5-837a-bf23e33d1c20')
     def test_router_remove_interface_in_use_returns_409(self):
         self.routers_client.add_router_interface(self.router['id'],
@@ -90,21 +93,21 @@
                           self.routers_client.delete_router,
                           self.router['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c2a70d72-8826-43a7-8208-0209e6360c47')
     def test_show_non_existent_router_returns_404(self):
         router = data_utils.rand_name('non_exist_router')
         self.assertRaises(lib_exc.NotFound, self.routers_client.show_router,
                           router)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b23d1569-8b0c-4169-8d4b-6abd34fad5c7')
     def test_update_non_existent_router_returns_404(self):
         router = data_utils.rand_name('non_exist_router')
         self.assertRaises(lib_exc.NotFound, self.routers_client.update_router,
                           router, name="new_name")
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c7edc5ad-d09d-41e6-a344-5c0c31e2e3e4')
     def test_delete_non_existent_router_returns_404(self):
         router = data_utils.rand_name('non_exist_router')
@@ -116,23 +119,16 @@
     _ip_version = 6
 
 
-class DvrRoutersNegativeTest(base.BaseRouterTest):
+class DvrRoutersNegativeTest(base.BaseNetworkTest):
 
     @classmethod
     def skip_checks(cls):
         super(DvrRoutersNegativeTest, cls).skip_checks()
-        if not test.is_extension_enabled('dvr', 'network'):
+        if not utils.is_extension_enabled('dvr', 'network'):
             msg = "DVR extension not enabled."
             raise cls.skipException(msg)
 
-    @classmethod
-    def resource_setup(cls):
-        super(DvrRoutersNegativeTest, cls).resource_setup()
-        cls.router = cls.create_router()
-        cls.network = cls.create_network()
-        cls.subnet = cls.create_subnet(cls.network)
-
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4990b055-8fc7-48ab-bba7-aa28beaad0b9')
     def test_router_create_tenant_distributed_returns_forbidden(self):
         self.assertRaises(lib_exc.Forbidden, self.create_router,
diff --git a/tempest/api/network/test_security_groups.py b/tempest/api/network/test_security_groups.py
index 607baf0..97ccee9 100644
--- a/tempest/api/network/test_security_groups.py
+++ b/tempest/api/network/test_security_groups.py
@@ -14,10 +14,10 @@
 #    under the License.
 
 from tempest.api.network import base_security_groups as base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -28,7 +28,7 @@
     @classmethod
     def skip_checks(cls):
         super(SecGroupTest, cls).skip_checks()
-        if not test.is_extension_enabled('security-group', 'network'):
+        if not utils.is_extension_enabled('security-group', 'network'):
             msg = "security-group extension not enabled."
             raise cls.skipException(msg)
 
@@ -67,7 +67,7 @@
                              "rule does not match with %s." %
                              (key, value))
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('e30abd17-fef9-4739-8617-dc26da88e686')
     def test_list_security_groups(self):
         # Verify the security group belonging to project exist in list
@@ -80,10 +80,10 @@
         msg = "Security-group list doesn't contain default security-group"
         self.assertIsNotNone(found, msg)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('bfd128e5-3c92-44b6-9d66-7fe29d22c802')
     def test_create_list_update_show_delete_security_group(self):
-        group_create_body, name = self._create_security_group()
+        group_create_body, _ = self._create_security_group()
 
         # List security groups and verify if created group is there in response
         list_body = self.security_groups_client.list_security_groups()
@@ -109,7 +109,7 @@
         self.assertEqual(show_body['security_group']['description'],
                          new_description)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('cfb99e0e-7410-4a3d-8a0c-959a63ee77e9')
     def test_create_show_delete_security_group_rule(self):
         group_create_body, _ = self._create_security_group()
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index f46b873..435673b 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -14,11 +14,11 @@
 #    under the License.
 
 from tempest.api.network import base_security_groups as base
+from tempest.common import utils
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -29,11 +29,11 @@
     @classmethod
     def skip_checks(cls):
         super(NegativeSecGroupTest, cls).skip_checks()
-        if not test.is_extension_enabled('security-group', 'network'):
+        if not utils.is_extension_enabled('security-group', 'network'):
             msg = "security-group extension not enabled."
             raise cls.skipException(msg)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('424fd5c3-9ddc-486a-b45f-39bf0c820fc6')
     def test_show_non_existent_security_group(self):
         non_exist_id = data_utils.rand_uuid()
@@ -41,7 +41,7 @@
             lib_exc.NotFound, self.security_groups_client.show_security_group,
             non_exist_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4c094c09-000b-4e41-8100-9617600c02a6')
     def test_show_non_existent_security_group_rule(self):
         non_exist_id = data_utils.rand_uuid()
@@ -50,7 +50,7 @@
             self.security_group_rules_client.show_security_group_rule,
             non_exist_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1f1bb89d-5664-4956-9fcd-83ee0fa603df')
     def test_delete_non_existent_security_group(self):
         non_exist_id = data_utils.rand_uuid()
@@ -59,7 +59,7 @@
                           non_exist_id
                           )
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('981bdc22-ce48-41ed-900a-73148b583958')
     def test_create_security_group_rule_with_bad_protocol(self):
         group_create_body, _ = self._create_security_group()
@@ -72,7 +72,7 @@
             security_group_id=group_create_body['security_group']['id'],
             protocol=pname, direction='ingress', ethertype=self.ethertype)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5f8daf69-3c5f-4aaa-88c9-db1d66f68679')
     def test_create_security_group_rule_with_bad_remote_ip_prefix(self):
         group_create_body, _ = self._create_security_group()
@@ -87,7 +87,7 @@
                 protocol='tcp', direction='ingress', ethertype=self.ethertype,
                 remote_ip_prefix=remote_ip_prefix)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4bf786fd-2f02-443c-9716-5b98e159a49a')
     def test_create_security_group_rule_with_non_existent_remote_groupid(self):
         group_create_body, _ = self._create_security_group()
@@ -103,7 +103,7 @@
                 protocol='tcp', direction='ingress', ethertype=self.ethertype,
                 remote_group_id=remote_group_id)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b5c4b247-6b02-435b-b088-d10d45650881')
     def test_create_security_group_rule_with_remote_ip_and_group(self):
         sg1_body, _ = self._create_security_group()
@@ -119,7 +119,7 @@
             ethertype=self.ethertype, remote_ip_prefix=prefix,
             remote_group_id=sg2_body['security_group']['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5666968c-fff3-40d6-9efc-df1c8bd01abb')
     def test_create_security_group_rule_with_bad_ethertype(self):
         group_create_body, _ = self._create_security_group()
@@ -132,7 +132,7 @@
             security_group_id=group_create_body['security_group']['id'],
             protocol='udp', direction='ingress', ethertype=ethertype)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0d9c7791-f2ad-4e2f-ac73-abf2373b0d2d')
     def test_create_security_group_rule_with_invalid_ports(self):
         group_create_body, _ = self._create_security_group()
@@ -166,7 +166,7 @@
                 direction='ingress', ethertype=self.ethertype)
             self.assertIn(msg, str(ex))
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('2323061e-9fbf-4eb0-b547-7e8fafc90849')
     def test_create_additional_default_security_group_fails(self):
         # Create security group named 'default', it should be failed.
@@ -175,7 +175,17 @@
                           self.security_groups_client.create_security_group,
                           name=name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('966e2b96-023a-11e7-a9e4-fa163e4fa634')
+    def test_create_security_group_update_name_default(self):
+        # Update security group name to 'default', it should be failed.
+        group_create_body, _ = self._create_security_group()
+        self.assertRaises(lib_exc.Conflict,
+                          self.security_groups_client.update_security_group,
+                          group_create_body['security_group']['id'],
+                          name="default")
+
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8fde898f-ce88-493b-adc9-4e4692879fc5')
     def test_create_duplicate_security_group_rule_fails(self):
         # Create duplicate security group rule, it should fail.
@@ -201,7 +211,7 @@
             protocol='tcp', direction='ingress', ethertype=self.ethertype,
             port_range_min=min_port, port_range_max=max_port)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('be308db6-a7cf-4d5c-9baf-71bafd73f35e')
     def test_create_security_group_rule_with_non_existent_security_group(self):
         # Create security group rules with not existing security group.
@@ -217,7 +227,7 @@
     _ip_version = 6
     _project_network_cidr = CONF.network.project_network_v6_cidr
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7607439c-af73-499e-bf64-f687fd12a842')
     def test_create_security_group_rule_wrong_ip_prefix_version(self):
         group_create_body, _ = self._create_security_group()
diff --git a/tempest/api/network/test_service_providers.py b/tempest/api/network/test_service_providers.py
index b90c81b..9ebcd89 100644
--- a/tempest/api/network/test_service_providers.py
+++ b/tempest/api/network/test_service_providers.py
@@ -13,15 +13,15 @@
 import testtools
 
 from tempest.api.network import base
+from tempest.common import utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class ServiceProvidersTest(base.BaseNetworkTest):
 
     @decorators.idempotent_id('2cbbeea9-f010-40f6-8df5-4eaa0c918ea6')
     @testtools.skipUnless(
-        test.is_extension_enabled('service-type', 'network'),
+        utils.is_extension_enabled('service-type', 'network'),
         'service-type extension not enabled.')
     def test_service_providers_list(self):
         body = self.service_providers_client.list_service_providers()
diff --git a/tempest/api/network/test_subnetpools_extensions.py b/tempest/api/network/test_subnetpools_extensions.py
index d9599c4..bfc2609 100644
--- a/tempest/api/network/test_subnetpools_extensions.py
+++ b/tempest/api/network/test_subnetpools_extensions.py
@@ -13,12 +13,12 @@
 # under the License.
 
 from tempest.api.network import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -42,11 +42,11 @@
     @classmethod
     def skip_checks(cls):
         super(SubnetPoolsTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('subnet_allocation', 'network'):
+        if not utils.is_extension_enabled('subnet_allocation', 'network'):
             msg = "subnet_allocation extension not enabled."
             raise cls.skipException(msg)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('62595970-ab1c-4b7f-8fcc-fddfe55e9811')
     def test_create_list_show_update_delete_subnetpools(self):
         subnetpool_name = data_utils.rand_name('subnetpools')
diff --git a/tempest/api/network/test_tags.py b/tempest/api/network/test_tags.py
new file mode 100644
index 0000000..409d556
--- /dev/null
+++ b/tempest/api/network/test_tags.py
@@ -0,0 +1,202 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.network import base
+from tempest.common import utils
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+CONF = config.CONF
+
+
+class TagsTest(base.BaseNetworkTest):
+    """Tests the following operations in the tags API:
+
+        Update all tags.
+        Delete all tags.
+        Check tag existence.
+        Create a tag.
+        List tags.
+        Remove a tag.
+
+    v2.0 of the Neutron API is assumed. The tag extension allows users to set
+    tags on their networks. The extension supports networks only.
+    """
+
+    @classmethod
+    def skip_checks(cls):
+        super(TagsTest, cls).skip_checks()
+        if not utils.is_extension_enabled('tag', 'network'):
+            msg = "tag extension not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(TagsTest, cls).resource_setup()
+        cls.network = cls.create_network()
+
+    @decorators.idempotent_id('ee76bfaf-ac94-4d74-9ecc-4bbd4c583cb1')
+    def test_create_list_show_update_delete_tags(self):
+        # Validate that creating a tag on a network resource works.
+        tag_name = data_utils.rand_name(self.__class__.__name__ + '-Tag')
+        self.tags_client.create_tag('networks', self.network['id'], tag_name)
+        self.addCleanup(self.tags_client.delete_all_tags, 'networks',
+                        self.network['id'])
+        self.tags_client.check_tag_existence('networks', self.network['id'],
+                                             tag_name)
+
+        # Validate that listing tags on a network resource works.
+        retrieved_tags = self.tags_client.list_tags(
+            'networks', self.network['id'])['tags']
+        self.assertEqual([tag_name], retrieved_tags)
+
+        # Generate 3 new tag names.
+        replace_tags = [data_utils.rand_name(
+            self.__class__.__name__ + '-Tag') for _ in range(3)]
+
+        # Replace the current tag with the 3 new tags and validate that the
+        # network resource has the 3 new tags.
+        updated_tags = self.tags_client.update_all_tags(
+            'networks', self.network['id'], replace_tags)['tags']
+        self.assertEqual(3, len(updated_tags))
+        self.assertEqual(set(replace_tags), set(updated_tags))
+
+        # Delete the first tag and check that it has been removed.
+        self.tags_client.delete_tag(
+            'networks', self.network['id'], replace_tags[0])
+        self.assertRaises(lib_exc.NotFound,
+                          self.tags_client.check_tag_existence, 'networks',
+                          self.network['id'], replace_tags[0])
+        for i in range(1, 3):
+            self.tags_client.check_tag_existence(
+                'networks', self.network['id'], replace_tags[i])
+
+        # Delete all the remaining tags and check that they have been removed.
+        self.tags_client.delete_all_tags('networks', self.network['id'])
+        for i in range(1, 3):
+            self.assertRaises(lib_exc.NotFound,
+                              self.tags_client.check_tag_existence, 'networks',
+                              self.network['id'], replace_tags[i])
+
+
+class TagsExtTest(base.BaseNetworkTest):
+    """Tests the following operations in the tags API:
+
+        Update all tags.
+        Delete all tags.
+        Check tag existence.
+        Create a tag.
+        List tags.
+        Remove a tag.
+
+    v2.0 of the Neutron API is assumed. The tag-ext extension allows users to
+    set tags on the following resources: subnets, ports, routers and
+    subnetpools.
+    """
+
+    # NOTE(felipemonteiro): The supported resource names are plural. Use
+    # the singular case for the corresponding class resource object.
+    SUPPORTED_RESOURCES = ['subnets', 'ports', 'routers', 'subnetpools']
+
+    @classmethod
+    def skip_checks(cls):
+        super(TagsExtTest, cls).skip_checks()
+        if not utils.is_extension_enabled('tag-ext', 'network'):
+            msg = "tag-ext extension not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(TagsExtTest, cls).resource_setup()
+        cls.network = cls.create_network()
+        cls.subnet = cls.create_subnet(cls.network)
+        cls.port = cls.create_port(cls.network)
+        cls.router = cls.create_router()
+
+        subnetpool_name = data_utils.rand_name(cls.__name__ + '-Subnetpool')
+        prefix = CONF.network.default_network
+        cls.subnetpool = cls.subnetpools_client.create_subnetpool(
+            name=subnetpool_name, prefixes=prefix)['subnetpool']
+
+    @classmethod
+    def resource_cleanup(cls):
+        cls.subnetpools_client.delete_subnetpool(cls.subnetpool['id'])
+        super(TagsExtTest, cls).resource_cleanup()
+
+    def _create_tags_for_each_resource(self):
+        # Create a tag for each resource in `SUPPORTED_RESOURCES` and return
+        # the list of tag names.
+        tag_names = []
+
+        for resource in self.SUPPORTED_RESOURCES:
+            tag_name = data_utils.rand_name(self.__class__.__name__ + '-Tag')
+            tag_names.append(tag_name)
+            resource_object = getattr(self, resource[:-1])
+
+            self.tags_client.create_tag(resource, resource_object['id'],
+                                        tag_name)
+            self.addCleanup(self.tags_client.delete_all_tags, resource,
+                            resource_object['id'])
+
+        return tag_names
+
+    @decorators.idempotent_id('c6231efa-9a89-4adf-b050-2a3156b8a1d9')
+    def test_create_check_list_and_delete_tags(self):
+        tag_names = self._create_tags_for_each_resource()
+
+        for i, resource in enumerate(self.SUPPORTED_RESOURCES):
+            # Ensure that a tag was created for each resource.
+            resource_object = getattr(self, resource[:-1])
+            retrieved_tags = self.tags_client.list_tags(
+                resource, resource_object['id'])['tags']
+            self.assertEqual(1, len(retrieved_tags))
+            self.assertEqual(tag_names[i], retrieved_tags[0])
+
+            # Check that the expected tag exists for each resource.
+            self.tags_client.check_tag_existence(
+                resource, resource_object['id'], tag_names[i])
+
+            # Delete the tag and ensure it was deleted.
+            self.tags_client.delete_tag(
+                resource, resource_object['id'], tag_names[i])
+            retrieved_tags = self.tags_client.list_tags(
+                resource, resource_object['id'])['tags']
+            self.assertEmpty(retrieved_tags)
+
+    @decorators.idempotent_id('663a90f5-f334-4b44-afe0-c5fc1d408791')
+    def test_update_and_delete_all_tags(self):
+        self._create_tags_for_each_resource()
+
+        for resource in self.SUPPORTED_RESOURCES:
+            # Generate 3 new tag names.
+            replace_tags = [data_utils.rand_name(
+                self.__class__.__name__ + '-Tag') for _ in range(3)]
+
+            # Replace the current tag with the 3 new tags and validate that the
+            # current resource has the 3 new tags.
+            resource_object = getattr(self, resource[:-1])
+            updated_tags = self.tags_client.update_all_tags(
+                resource, resource_object['id'], replace_tags)['tags']
+            self.assertEqual(3, len(updated_tags))
+            self.assertEqual(set(replace_tags), set(updated_tags))
+
+            # Delete all the tags and check that they have been removed.
+            self.tags_client.delete_all_tags(resource, resource_object['id'])
+            for i in range(3):
+                self.assertRaises(
+                    lib_exc.NotFound, self.tags_client.check_tag_existence,
+                    resource, resource_object['id'], replace_tags[i])
diff --git a/tempest/api/network/test_versions.py b/tempest/api/network/test_versions.py
index 4f6d5ac..2f01e50 100644
--- a/tempest/api/network/test_versions.py
+++ b/tempest/api/network/test_versions.py
@@ -14,11 +14,10 @@
 
 from tempest.api.network import base
 from tempest.lib import decorators
-from tempest import test
 
 
 class NetworksApiDiscovery(base.BaseNetworkTest):
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('cac8a836-c2e0-4304-b556-cd299c7281d1')
     def test_api_version_resources(self):
         """Test that GET / returns expected resources.
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index e0216fd..4c49b2a 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -43,8 +43,7 @@
     for cont in containers:
         try:
             params = {'limit': 9999, 'format': 'json'}
-            resp, objlist = container_client.list_container_contents(
-                cont, params)
+            _, objlist = container_client.list_container_contents(cont, params)
             # delete every object in the container
             for obj in objlist:
                 test_utils.call_and_ignore_notfound_exc(
@@ -72,17 +71,15 @@
     def setup_credentials(cls):
         cls.set_network_resources()
         super(BaseObjectTest, cls).setup_credentials()
-        # credentials may be overwritten by children classes
-        if hasattr(cls, 'os_roles_operator'):
-            cls.os = cls.os_roles_operator
 
     @classmethod
     def setup_clients(cls):
         super(BaseObjectTest, cls).setup_clients()
-        cls.object_client = cls.os.object_client
-        cls.container_client = cls.os.container_client
-        cls.account_client = cls.os.account_client
-        cls.capabilities_client = cls.os.capabilities_client
+        cls.object_client = cls.os_roles_operator.object_client
+        cls.bulk_client = cls.os_roles_operator.bulk_client
+        cls.container_client = cls.os_roles_operator.container_client
+        cls.account_client = cls.os_roles_operator.account_client
+        cls.capabilities_client = cls.os_roles_operator.capabilities_client
 
     @classmethod
     def resource_setup(cls):
@@ -98,7 +95,7 @@
         cls.policies = None
 
         if CONF.object_storage_feature_enabled.discoverability:
-            _, body = cls.capabilities_client.list_capabilities()
+            body = cls.capabilities_client.list_capabilities()
 
             if 'swift' in body and 'policies' in body['swift']:
                 cls.policies = body['swift']['policies']
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index d882731..9abd59e 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -17,8 +17,8 @@
 
 from tempest.api.object_storage import base
 from tempest.common import custom_matchers
+from tempest.common import utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class BulkTest(base.BaseObjectTest):
@@ -56,12 +56,11 @@
 
     def _upload_archive(self, filepath):
         # upload an archived file
-        params = {'extract-archive': 'tar'}
         with open(filepath) as fh:
             mydata = fh.read()
-            resp, body = self.account_client.create_account(data=mydata,
-                                                            params=params)
-        return resp, body
+            resp = self.bulk_client.upload_archive(
+                upload_path='', data=mydata, archive_file_format='tar')
+        return resp
 
     def _check_contents_deleted(self, container_name):
         param = {'format': 'txt'}
@@ -70,25 +69,24 @@
         self.assertNotIn(container_name, body)
 
     @decorators.idempotent_id('a407de51-1983-47cc-9f14-47c2b059413c')
-    @test.requires_ext(extension='bulk_upload', service='object')
+    @utils.requires_ext(extension='bulk_upload', service='object')
     def test_extract_archive(self):
         # Test bulk operation of file upload with an archived file
         filepath, container_name, object_name = self._create_archive()
-        resp, _ = self._upload_archive(filepath)
-
+        resp = self._upload_archive(filepath)
         self.containers.append(container_name)
 
         # When uploading an archived file with the bulk operation, the response
         # does not contain 'content-length' header. This is the special case,
         # therefore the existence of response headers is checked without
         # custom matcher.
-        self.assertIn('transfer-encoding', resp)
-        self.assertIn('content-type', resp)
-        self.assertIn('x-trans-id', resp)
-        self.assertIn('date', resp)
+        self.assertIn('transfer-encoding', resp.response)
+        self.assertIn('content-type', resp.response)
+        self.assertIn('x-trans-id', resp.response)
+        self.assertIn('date', resp.response)
 
         # Check only the format of common headers with custom matcher
-        self.assertThat(resp, custom_matchers.AreAllWellFormatted())
+        self.assertThat(resp.response, custom_matchers.AreAllWellFormatted())
 
         param = {'format': 'json'}
         resp, body = self.account_client.list_account_containers(param)
@@ -106,56 +104,52 @@
         self.assertIn(object_name, [c['name'] for c in contents_list])
 
     @decorators.idempotent_id('c075e682-0d2a-43b2-808d-4116200d736d')
-    @test.requires_ext(extension='bulk_delete', service='object')
+    @utils.requires_ext(extension='bulk_delete', service='object')
     def test_bulk_delete(self):
         # Test bulk operation of deleting multiple files
         filepath, container_name, object_name = self._create_archive()
         self._upload_archive(filepath)
 
         data = '%s/%s\n%s' % (container_name, object_name, container_name)
-        params = {'bulk-delete': ''}
-        resp, body = self.account_client.delete_account(data=data,
-                                                        params=params)
+        resp = self.bulk_client.delete_bulk_data(data=data)
 
         # When deleting multiple files using the bulk operation, the response
         # does not contain 'content-length' header. This is the special case,
         # therefore the existence of response headers is checked without
         # custom matcher.
-        self.assertIn('transfer-encoding', resp)
-        self.assertIn('content-type', resp)
-        self.assertIn('x-trans-id', resp)
-        self.assertIn('date', resp)
+        self.assertIn('transfer-encoding', resp.response)
+        self.assertIn('content-type', resp.response)
+        self.assertIn('x-trans-id', resp.response)
+        self.assertIn('date', resp.response)
 
         # Check only the format of common headers with custom matcher
-        self.assertThat(resp, custom_matchers.AreAllWellFormatted())
+        self.assertThat(resp.response, custom_matchers.AreAllWellFormatted())
 
         # Check if uploaded contents are completely deleted
         self._check_contents_deleted(container_name)
 
     @decorators.idempotent_id('dbea2bcb-efbb-4674-ac8a-a5a0e33d1d79')
-    @test.requires_ext(extension='bulk_delete', service='object')
+    @utils.requires_ext(extension='bulk_delete', service='object')
     def test_bulk_delete_by_POST(self):
         # Test bulk operation of deleting multiple files
         filepath, container_name, object_name = self._create_archive()
         self._upload_archive(filepath)
 
         data = '%s/%s\n%s' % (container_name, object_name, container_name)
-        params = {'bulk-delete': ''}
 
-        resp, body = self.account_client.create_account_metadata(
-            {}, data=data, params=params)
+        resp = self.bulk_client.delete_bulk_data_with_post(data=data)
 
         # When deleting multiple files using the bulk operation, the response
         # does not contain 'content-length' header. This is the special case,
         # therefore the existence of response headers is checked without
         # custom matcher.
-        self.assertIn('transfer-encoding', resp)
-        self.assertIn('content-type', resp)
-        self.assertIn('x-trans-id', resp)
-        self.assertIn('date', resp)
+        self.assertIn('transfer-encoding', resp.response)
+        self.assertIn('content-type', resp.response)
+        self.assertIn('x-trans-id', resp.response)
+        self.assertIn('date', resp.response)
 
         # Check only the format of common headers with custom matcher
-        self.assertThat(resp, custom_matchers.AreAllWellFormatted())
+        self.assertThat(resp.response, custom_matchers.AreAllWellFormatted())
 
         # Check if uploaded contents are completely deleted
         self._check_contents_deleted(container_name)
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index cbf0d4b..48f42ec 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -13,10 +13,10 @@
 # under the License.
 
 from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -29,7 +29,6 @@
     @classmethod
     def setup_credentials(cls):
         super(AccountQuotasTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_reselleradmin = cls.os_roles_reseller
 
     @classmethod
@@ -54,8 +53,8 @@
         # Set a quota of 20 bytes on the user's account before each test
         headers = {"X-Account-Meta-Quota-Bytes": "20"}
 
-        self.os.account_client.request("POST", url="", headers=headers,
-                                       body="")
+        self.os_roles_operator.account_client.request(
+            "POST", url="", headers=headers, body="")
 
     def tearDown(self):
         # Set the reselleradmin auth in headers for next account_client
@@ -67,8 +66,8 @@
         # remove the quota from the container
         headers = {"X-Remove-Account-Meta-Quota-Bytes": "x"}
 
-        self.os.account_client.request("POST", url="", headers=headers,
-                                       body="")
+        self.os_roles_operator.account_client.request(
+            "POST", url="", headers=headers, body="")
         super(AccountQuotasTest, self).tearDown()
 
     @classmethod
@@ -76,9 +75,9 @@
         cls.delete_containers()
         super(AccountQuotasTest, cls).resource_cleanup()
 
-    @test.attr(type="smoke")
+    @decorators.attr(type="smoke")
     @decorators.idempotent_id('a22ef352-a342-4587-8f47-3bbdb5b039c4')
-    @test.requires_ext(extension='account_quotas', service='object')
+    @utils.requires_ext(extension='account_quotas', service='object')
     def test_upload_valid_object(self):
         object_name = data_utils.rand_name(name="TestObject")
         data = data_utils.arbitrary_string()
@@ -87,9 +86,9 @@
 
         self.assertHeaders(resp, 'Object', 'PUT')
 
-    @test.attr(type=["smoke"])
+    @decorators.attr(type=["smoke"])
     @decorators.idempotent_id('63f51f9f-5f1d-4fc6-b5be-d454d70949d6')
-    @test.requires_ext(extension='account_quotas', service='object')
+    @utils.requires_ext(extension='account_quotas', service='object')
     def test_admin_modify_quota(self):
         """Test ResellerAdmin can modify/remove the quota on a user's account
 
@@ -108,9 +107,8 @@
             )
             headers = {"X-Account-Meta-Quota-Bytes": quota}
 
-            resp, _ = self.os.account_client.request("POST", url="",
-                                                     headers=headers,
-                                                     body="")
+            resp, _ = self.os_roles_operator.account_client.request(
+                "POST", url="", headers=headers, body="")
 
             self.assertEqual(resp["status"], "204")
             self.assertHeaders(resp, 'Account', 'POST')
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index 2e85a43..798926b 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -13,10 +13,10 @@
 # under the License.
 
 from tempest.api.object_storage import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -29,13 +29,12 @@
     @classmethod
     def setup_credentials(cls):
         super(AccountQuotasNegativeTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_reselleradmin = cls.os_roles_reseller
 
     @classmethod
     def resource_setup(cls):
         super(AccountQuotasNegativeTest, cls).resource_setup()
-        cls.container_name = cls.create_container()
+        cls.create_container()
 
         # Retrieve a ResellerAdmin auth data and use it to set a quota
         # on the client's account
@@ -53,8 +52,8 @@
         # Set a quota of 20 bytes on the user's account before each test
         headers = {"X-Account-Meta-Quota-Bytes": "20"}
 
-        self.os.account_client.request("POST", url="", headers=headers,
-                                       body="")
+        self.os_roles_operator.account_client.request(
+            "POST", url="", headers=headers, body="")
 
     def tearDown(self):
         # Set the reselleradmin auth in headers for next account_client
@@ -66,8 +65,8 @@
         # remove the quota from the container
         headers = {"X-Remove-Account-Meta-Quota-Bytes": "x"}
 
-        self.os.account_client.request("POST", url="", headers=headers,
-                                       body="")
+        self.os_roles_operator.account_client.request(
+            "POST", url="", headers=headers, body="")
         super(AccountQuotasNegativeTest, self).tearDown()
 
     @classmethod
@@ -75,18 +74,20 @@
         cls.delete_containers()
         super(AccountQuotasNegativeTest, cls).resource_cleanup()
 
-    @test.attr(type=["negative"])
+    @decorators.attr(type=["negative"])
     @decorators.idempotent_id('d1dc5076-555e-4e6d-9697-28f1fe976324')
-    @test.requires_ext(extension='account_quotas', service='object')
+    @utils.requires_ext(extension='account_quotas', service='object')
     def test_user_modify_quota(self):
         """Test that a user cannot modify or remove a quota on its account."""
 
         # Not able to remove quota
-        self.assertRaises(lib_exc.Forbidden,
-                          self.account_client.create_account_metadata,
-                          {"Quota-Bytes": ""})
+        self.assertRaises(
+            lib_exc.Forbidden,
+            self.account_client.create_update_or_delete_account_metadata,
+            create_update_metadata={"Quota-Bytes": ""})
 
         # Not able to modify quota
-        self.assertRaises(lib_exc.Forbidden,
-                          self.account_client.create_account_metadata,
-                          {"Quota-Bytes": "100"})
+        self.assertRaises(
+            lib_exc.Forbidden,
+            self.account_client.create_update_or_delete_account_metadata,
+            create_update_metadata={"Quota-Bytes": "100"})
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index d779d42..0f86540 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -20,10 +20,9 @@
 
 from tempest.api.object_storage import base
 from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -37,14 +36,13 @@
     @classmethod
     def setup_credentials(cls):
         super(AccountTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_operator = cls.os_roles_operator_alt
 
     @classmethod
     def resource_setup(cls):
         super(AccountTest, cls).resource_setup()
         for i in range(ord('a'), ord('f') + 1):
-            name = data_utils.rand_name(name='%s-' % chr(i))
+            name = data_utils.rand_name(name='%s-' % six.int2byte(i))
             cls.container_client.create_container(name)
             cls.containers.append(name)
         cls.containers_count = len(cls.containers)
@@ -54,7 +52,7 @@
         cls.delete_containers()
         super(AccountTest, cls).resource_cleanup()
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('3499406a-ae53-4f8c-b43a-133d4dc6fe3f')
     def test_list_containers(self):
         # list of all containers should not be empty
@@ -102,7 +100,7 @@
         # Check only the format of common headers with custom matcher
         self.assertThat(resp, custom_matchers.AreAllWellFormatted())
 
-        self.assertEqual(len(container_list), 0)
+        self.assertEmpty(container_list)
 
     @decorators.idempotent_id('1c7efa35-e8a2-4b0b-b5ff-862c7fd83704')
     def test_list_containers_with_format_json(self):
@@ -136,7 +134,7 @@
         not CONF.object_storage_feature_enabled.discoverability,
         'Discoverability function is disabled')
     def test_list_extensions(self):
-        resp, extensions = self.capabilities_client.list_capabilities()
+        resp = self.capabilities_client.list_capabilities()
 
         self.assertThat(resp, custom_matchers.AreAllWellFormatted())
 
@@ -163,7 +161,7 @@
             self.account_client.list_account_containers(params=params)
         self.assertHeaders(resp, 'Account', 'GET')
 
-        self.assertEqual(len(container_list), 0)
+        self.assertEmpty(container_list)
 
         params = {'marker': self.containers[self.containers_count // 2]}
         resp, container_list = \
@@ -183,7 +181,7 @@
         resp, container_list = \
             self.account_client.list_account_containers(params=params)
         self.assertHeaders(resp, 'Account', 'GET')
-        self.assertEqual(len(container_list), 0)
+        self.assertEmpty(container_list)
 
         params = {'end_marker': self.containers[self.containers_count // 2]}
         resp, container_list = \
@@ -253,7 +251,19 @@
             self.assertEqual(True, container.decode(
                 'utf-8').startswith(prefix))
 
-    @test.attr(type='smoke')
+    @decorators.idempotent_id('b1811cff-d1ed-4c15-a52e-efd8de41cf34')
+    def test_list_containers_reverse_order(self):
+        # list containers in reverse order
+        _, orig_container_list = self.account_client.list_account_containers()
+
+        params = {'reverse': True}
+        resp, container_list = self.account_client.list_account_containers(
+            params=params)
+        self.assertHeaders(resp, 'Account', 'GET')
+        self.assertEqual(sorted(orig_container_list, reverse=True),
+                         container_list)
+
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('4894c312-6056-4587-8d6f-86ffbf861f80')
     def test_list_account_metadata(self):
         # list all account metadata
@@ -261,13 +271,15 @@
         # set metadata to account
         metadata = {'test-account-meta1': 'Meta1',
                     'test-account-meta2': 'Meta2'}
-        resp, _ = self.account_client.create_account_metadata(metadata)
+        resp, _ = self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata)
 
         resp, _ = self.account_client.list_account_metadata()
         self.assertHeaders(resp, 'Account', 'HEAD')
         self.assertIn('x-account-meta-test-account-meta1', resp)
         self.assertIn('x-account-meta-test-account-meta2', resp)
-        self.account_client.delete_account_metadata(metadata)
+        self.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=metadata)
 
     @decorators.idempotent_id('b904c2e3-24c2-4dba-ad7d-04e90a761be5')
     def test_list_no_account_metadata(self):
@@ -280,46 +292,53 @@
     def test_update_account_metadata_with_create_metadata(self):
         # add metadata to account
         metadata = {'test-account-meta1': 'Meta1'}
-        resp, _ = self.account_client.create_account_metadata(metadata)
+        resp, _ = self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata)
         self.assertHeaders(resp, 'Account', 'POST')
 
-        resp, body = self.account_client.list_account_metadata()
+        resp, _ = self.account_client.list_account_metadata()
         self.assertIn('x-account-meta-test-account-meta1', resp)
         self.assertEqual(resp['x-account-meta-test-account-meta1'],
                          metadata['test-account-meta1'])
 
-        self.account_client.delete_account_metadata(metadata)
+        self.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=metadata)
 
     @decorators.idempotent_id('9f60348d-c46f-4465-ae06-d51dbd470953')
-    def test_update_account_metadata_with_delete_matadata(self):
+    def test_update_account_metadata_with_delete_metadata(self):
         # delete metadata from account
         metadata = {'test-account-meta1': 'Meta1'}
-        self.account_client.create_account_metadata(metadata)
-        resp, _ = self.account_client.delete_account_metadata(metadata)
+        self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata)
+        resp, _ = self.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=metadata)
         self.assertHeaders(resp, 'Account', 'POST')
 
         resp, _ = self.account_client.list_account_metadata()
         self.assertNotIn('x-account-meta-test-account-meta1', resp)
 
     @decorators.idempotent_id('64fd53f3-adbd-4639-af54-436e4982dbfb')
-    def test_update_account_metadata_with_create_matadata_key(self):
+    def test_update_account_metadata_with_create_metadata_key(self):
         # if the value of metadata is not set, the metadata is not
         # registered at a server
         metadata = {'test-account-meta1': ''}
-        resp, _ = self.account_client.create_account_metadata(metadata)
+        resp, _ = self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata)
         self.assertHeaders(resp, 'Account', 'POST')
 
         resp, _ = self.account_client.list_account_metadata()
         self.assertNotIn('x-account-meta-test-account-meta1', resp)
 
     @decorators.idempotent_id('d4d884d3-4696-4b85-bc98-4f57c4dd2bf1')
-    def test_update_account_metadata_with_delete_matadata_key(self):
+    def test_update_account_metadata_with_delete_metadata_key(self):
         # Although the value of metadata is not set, the feature of
         # deleting metadata is valid
         metadata_1 = {'test-account-meta1': 'Meta1'}
-        self.account_client.create_account_metadata(metadata_1)
+        self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata_1)
         metadata_2 = {'test-account-meta1': ''}
-        resp, _ = self.account_client.delete_account_metadata(metadata_2)
+        resp, _ = self.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=metadata_2)
         self.assertHeaders(resp, 'Account', 'POST')
 
         resp, _ = self.account_client.list_account_metadata()
@@ -329,11 +348,13 @@
     def test_update_account_metadata_with_create_and_delete_metadata(self):
         # Send a request adding and deleting metadata requests simultaneously
         metadata_1 = {'test-account-meta1': 'Meta1'}
-        self.account_client.create_account_metadata(metadata_1)
+        self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata_1)
         metadata_2 = {'test-account-meta2': 'Meta2'}
-        resp, body = self.account_client.create_and_delete_account_metadata(
-            metadata_2,
-            metadata_1)
+        resp, _ = (
+            self.account_client.create_update_or_delete_account_metadata(
+                create_update_metadata=metadata_2,
+                delete_metadata=metadata_1))
         self.assertHeaders(resp, 'Account', 'POST')
 
         resp, _ = self.account_client.list_account_metadata()
@@ -342,4 +363,5 @@
         self.assertEqual(resp['x-account-meta-test-account-meta2'],
                          metadata_2['test-account-meta2'])
 
-        self.account_client.delete_account_metadata(metadata_2)
+        self.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=metadata_2)
diff --git a/tempest/api/object_storage/test_account_services_negative.py b/tempest/api/object_storage/test_account_services_negative.py
index d46534b..3e664d7 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -16,7 +16,6 @@
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -29,10 +28,9 @@
     @classmethod
     def setup_credentials(cls):
         super(AccountNegativeTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_operator = cls.os_roles_operator_alt
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('070e6aca-6152-4867-868d-1118d68fb38c')
     def test_list_containers_with_non_authorized_user(self):
         # list containers using non-authorized user
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index aa4e92c..4b66ebf 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -14,8 +14,8 @@
 #    under the License.
 
 from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -41,10 +41,10 @@
         tenant_name = self.os_roles_operator_alt.credentials.tenant_name
         username = self.os_roles_operator_alt.credentials.username
         cont_headers = {'X-Container-Read': tenant_name + ':' + username}
-        resp_meta, body = self.os_roles_operator.container_client.\
-            update_container_metadata(
+        resp_meta, _ = (
+            self.os_roles_operator.container_client.update_container_metadata(
                 self.container_name, metadata=cont_headers,
-                metadata_prefix='')
+                metadata_prefix=''))
         self.assertHeaders(resp_meta, 'Container', 'POST')
         # create object
         object_name = data_utils.rand_name(name='Object')
@@ -68,10 +68,10 @@
         tenant_name = self.os_roles_operator_alt.credentials.tenant_name
         username = self.os_roles_operator_alt.credentials.username
         cont_headers = {'X-Container-Write': tenant_name + ':' + username}
-        resp_meta, body = self.os_roles_operator.container_client.\
-            update_container_metadata(self.container_name,
-                                      metadata=cont_headers,
-                                      metadata_prefix='')
+        resp_meta, _ = (
+            self.os_roles_operator.container_client.update_container_metadata(
+                self.container_name, metadata=cont_headers,
+                metadata_prefix=''))
         self.assertHeaders(resp_meta, 'Container', 'POST')
         # set alternative authentication data; cannot simply use the
         # other object client.
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index d5d5ea7..e064753 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -13,11 +13,10 @@
 #    under the License.
 
 from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
@@ -30,7 +29,6 @@
     @classmethod
     def setup_credentials(cls):
         super(ObjectACLsNegativeTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_operator = cls.os_roles_operator_alt
 
     @classmethod
@@ -47,7 +45,7 @@
         self.delete_containers([self.container_name])
         super(ObjectACLsNegativeTest, self).tearDown()
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('af587587-0c24-4e15-9822-8352ce711013')
     def test_write_object_without_using_creds(self):
         # trying to create object with empty headers
@@ -61,13 +59,13 @@
                           self.object_client.create_object,
                           self.container_name, object_name, 'data', headers={})
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('af85af0b-a025-4e72-a90e-121babf55720')
     def test_delete_object_without_using_creds(self):
         # create object
         object_name = data_utils.rand_name(name='Object')
-        resp, _ = self.object_client.create_object(self.container_name,
-                                                   object_name, 'data')
+        self.object_client.create_object(self.container_name, object_name,
+                                         'data')
         # trying to delete object with empty headers
         # X-Auth-Token is not provided
         self.object_client.auth_provider.set_alt_auth_data(
@@ -78,7 +76,7 @@
                           self.object_client.delete_object,
                           self.container_name, object_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('63d84e37-55a6-42e2-9e5f-276e60e26a00')
     def test_write_object_with_non_authorized_user(self):
         # attempt to upload another file using non-authorized user
@@ -93,7 +91,7 @@
                           self.object_client.create_object,
                           self.container_name, object_name, 'data', headers={})
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('abf63359-be52-4feb-87dd-447689fc77fd')
     def test_read_object_with_non_authorized_user(self):
         # attempt to read object using non-authorized user
@@ -111,7 +109,7 @@
                           self.object_client.get_object,
                           self.container_name, object_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7343ac3d-cfed-4198-9bb0-00149741a492')
     def test_delete_object_with_non_authorized_user(self):
         # attempt to delete object using non-authorized user
@@ -129,13 +127,13 @@
                           self.object_client.delete_object,
                           self.container_name, object_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9ed01334-01e9-41ea-87ea-e6f465582823')
     def test_read_object_without_rights(self):
         # attempt to read object using non-authorized user
         # update X-Container-Read metadata ACL
         cont_headers = {'X-Container-Read': 'badtenant:baduser'}
-        resp_meta, body = self.container_client.update_container_metadata(
+        resp_meta, _ = self.container_client.update_container_metadata(
             self.container_name, metadata=cont_headers,
             metadata_prefix='')
         self.assertHeaders(resp_meta, 'Container', 'POST')
@@ -153,13 +151,13 @@
                           self.object_client.get_object,
                           self.container_name, object_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a3a585a7-d8cf-4b65-a1a0-edc2b1204f85')
     def test_write_object_without_rights(self):
         # attempt to write object using non-authorized user
         # update X-Container-Write metadata ACL
         cont_headers = {'X-Container-Write': 'badtenant:baduser'}
-        resp_meta, body = self.container_client.update_container_metadata(
+        resp_meta, _ = self.container_client.update_container_metadata(
             self.container_name, metadata=cont_headers,
             metadata_prefix='')
         self.assertHeaders(resp_meta, 'Container', 'POST')
@@ -174,7 +172,7 @@
                           self.container_name,
                           object_name, 'data', headers={})
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8ba512ad-aa6e-444e-b882-2906a0ea2052')
     def test_write_object_without_write_rights(self):
         # attempt to write object using non-authorized user
@@ -184,7 +182,7 @@
         cont_headers = {'X-Container-Read':
                         tenant_name + ':' + username,
                         'X-Container-Write': ''}
-        resp_meta, body = self.container_client.update_container_metadata(
+        resp_meta, _ = self.container_client.update_container_metadata(
             self.container_name, metadata=cont_headers,
             metadata_prefix='')
         self.assertHeaders(resp_meta, 'Container', 'POST')
@@ -199,7 +197,7 @@
                           self.container_name,
                           object_name, 'data', headers={})
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b4e366f8-f185-47ab-b789-df4416f9ecdb')
     def test_delete_object_without_write_rights(self):
         # attempt to delete object using non-authorized user
@@ -209,7 +207,7 @@
         cont_headers = {'X-Container-Read':
                         tenant_name + ':' + username,
                         'X-Container-Write': ''}
-        resp_meta, body = self.container_client.update_container_metadata(
+        resp_meta, _ = self.container_client.update_container_metadata(
             self.container_name, metadata=cont_headers,
             metadata_prefix='')
         self.assertHeaders(resp_meta, 'Container', 'POST')
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index d3b456a..c87bed5 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -14,10 +14,10 @@
 #    under the License.
 
 from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 QUOTA_BYTES = 10
 QUOTA_COUNT = 3
@@ -49,8 +49,8 @@
         super(ContainerQuotasTest, self).tearDown()
 
     @decorators.idempotent_id('9a0fb034-86af-4df0-86fa-f8bd7db21ae0')
-    @test.requires_ext(extension='container_quotas', service='object')
-    @test.attr(type="smoke")
+    @utils.requires_ext(extension='container_quotas', service='object')
+    @decorators.attr(type="smoke")
     def test_upload_valid_object(self):
         """Attempts to uploads an object smaller than the bytes quota."""
         object_name = data_utils.rand_name(name="TestObject")
@@ -66,8 +66,8 @@
         self.assertEqual(nbefore + len(data), nafter)
 
     @decorators.idempotent_id('22eeeb2b-3668-4160-baef-44790f65a5a0')
-    @test.requires_ext(extension='container_quotas', service='object')
-    @test.attr(type="smoke")
+    @utils.requires_ext(extension='container_quotas', service='object')
+    @decorators.attr(type="smoke")
     def test_upload_large_object(self):
         """Attempts to upload an object larger than the bytes quota."""
         object_name = data_utils.rand_name(name="TestObject")
@@ -83,8 +83,8 @@
         self.assertEqual(nbefore, nafter)
 
     @decorators.idempotent_id('3a387039-697a-44fc-a9c0-935de31f426b')
-    @test.requires_ext(extension='container_quotas', service='object')
-    @test.attr(type="smoke")
+    @utils.requires_ext(extension='container_quotas', service='object')
+    @decorators.attr(type="smoke")
     def test_upload_too_many_objects(self):
         """Attempts to upload many objects that exceeds the count limit."""
         for _ in range(QUOTA_COUNT):
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index 4b65584..76fe8d4 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -16,7 +16,6 @@
 from tempest.api.object_storage import base
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class ContainerTest(base.BaseObjectTest):
@@ -24,11 +23,11 @@
         self.delete_containers()
         super(ContainerTest, self).tearDown()
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('92139d73-7819-4db1-85f8-3f2f22a8d91f')
     def test_create_container(self):
         container_name = data_utils.rand_name(name='TestContainer')
-        resp, body = self.container_client.create_container(container_name)
+        resp, _ = self.container_client.create_container(container_name)
         self.containers.append(container_name)
         self.assertHeaders(resp, 'Container', 'PUT')
 
@@ -124,7 +123,7 @@
         resp, _ = self.container_client.delete_container(container_name)
         self.assertHeaders(resp, 'Container', 'DELETE')
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('312ff6bd-5290-497f-bda1-7c5fec6697ab')
     def test_list_container_contents(self):
         # get container contents list
@@ -166,7 +165,7 @@
         container_name = self.create_container()
         object_name, _ = self.create_object(container_name)
 
-        params = {'end_marker': 'ZzzzObject1234567890'}
+        params = {'end_marker': object_name + 'zzzz'}
         resp, object_list = self.container_client.list_container_contents(
             container_name,
             params=params)
@@ -246,7 +245,8 @@
     def test_list_container_contents_with_path(self):
         # get container contents list using path param
         container_name = self.create_container()
-        object_name = data_utils.rand_name(name='Swift/TestObject')
+        object_name = data_utils.rand_name(name='TestObject')
+        object_name = 'Swift/' + object_name
         self.create_object(container_name, object_name)
 
         params = {'path': 'Swift'}
@@ -270,7 +270,7 @@
         self.assertHeaders(resp, 'Container', 'GET')
         self.assertEqual([object_name], object_list)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('96e68f0e-19ec-4aa2-86f3-adc6a45e14dd')
     def test_list_container_metadata(self):
         # List container metadata
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index be066ba..387b7b6 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -20,7 +20,6 @@
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions
-from tempest import test
 
 CONF = config.CONF
 
@@ -33,10 +32,10 @@
 
         if CONF.object_storage_feature_enabled.discoverability:
             # use /info to get default constraints
-            _, body = cls.capabilities_client.list_capabilities()
+            body = cls.capabilities_client.list_capabilities()
             cls.constraints = body['swift']
 
-    @test.attr(type=["negative"])
+    @decorators.attr(type=["negative"])
     @decorators.idempotent_id('30686921-4bed-4764-a038-40d741ed4e78')
     @testtools.skipUnless(
         CONF.object_storage_feature_enabled.discoverability,
@@ -52,7 +51,7 @@
         self.assertIn('Container name length of ' + str(max_length + 1) +
                       ' longer than ' + str(max_length), str(ex))
 
-    @test.attr(type=["negative"])
+    @decorators.attr(type=["negative"])
     @decorators.idempotent_id('41e645bf-2e68-4f84-bf7b-c71aa5cd76ce')
     @testtools.skipUnless(
         CONF.object_storage_feature_enabled.discoverability,
@@ -69,7 +68,7 @@
                                container_name, metadata=metadata)
         self.assertIn('Metadata name too long', str(ex))
 
-    @test.attr(type=["negative"])
+    @decorators.attr(type=["negative"])
     @decorators.idempotent_id('81e36922-326b-4b7c-8155-3bbceecd7a82')
     @testtools.skipUnless(
         CONF.object_storage_feature_enabled.discoverability,
@@ -86,7 +85,7 @@
                                container_name, metadata=metadata)
         self.assertIn('Metadata value longer than ' + str(max_length), str(ex))
 
-    @test.attr(type=["negative"])
+    @decorators.attr(type=["negative"])
     @decorators.idempotent_id('ac666539-d566-4f02-8ceb-58e968dfb732')
     @testtools.skipUnless(
         CONF.object_storage_feature_enabled.discoverability,
@@ -106,7 +105,7 @@
         self.assertIn('Too many metadata items; max ' + str(max_count),
                       str(ex))
 
-    @test.attr(type=["negative"])
+    @decorators.attr(type=["negative"])
     @decorators.idempotent_id('1a95ab2e-b712-4a98-8a4d-8ce21b7557d6')
     def test_get_metadata_headers_with_invalid_container_name(self):
         # Attempts to retrieve metadata headers with an invalid
@@ -115,7 +114,7 @@
                           self.container_client.list_container_metadata,
                           'invalid_container_name')
 
-    @test.attr(type=["negative"])
+    @decorators.attr(type=["negative"])
     @decorators.idempotent_id('125a24fa-90a7-4cfc-b604-44e49d788390')
     def test_update_metadata_with_nonexistent_container_name(self):
         # Attempts to update metadata using a nonexistent container name.
@@ -125,7 +124,7 @@
                           self.container_client.update_container_metadata,
                           'nonexistent_container_name', metadata)
 
-    @test.attr(type=["negative"])
+    @decorators.attr(type=["negative"])
     @decorators.idempotent_id('65387dbf-a0e2-4aac-9ddc-16eb3f1f69ba')
     def test_delete_with_nonexistent_container_name(self):
         # Attempts to delete metadata using a nonexistent container name.
@@ -135,7 +134,7 @@
                           self.container_client.delete_container_metadata,
                           'nonexistent_container_name', metadata)
 
-    @test.attr(type=["negative"])
+    @decorators.attr(type=["negative"])
     @decorators.idempotent_id('14331d21-1e81-420a-beea-19cb5e5207f5')
     def test_list_all_container_objects_with_nonexistent_container(self):
         # Attempts to get a listing of all objects on a container
@@ -145,7 +144,7 @@
                           self.container_client.list_container_contents,
                           'nonexistent_container_name', params)
 
-    @test.attr(type=["negative"])
+    @decorators.attr(type=["negative"])
     @decorators.idempotent_id('86b2ab08-92d5-493d-acd2-85f0c848819e')
     def test_list_all_container_objects_on_deleted_container(self):
         # Attempts to get a listing of all objects on a container
@@ -159,7 +158,7 @@
                           self.container_client.list_container_contents,
                           container_name, params)
 
-    @test.attr(type=["negative"])
+    @decorators.attr(type=["negative"])
     @decorators.idempotent_id('42da116e-1e8c-4c96-9e06-2f13884ed2b1')
     def test_delete_non_empty_container(self):
         # create a container and an object within it
diff --git a/tempest/api/object_storage/test_container_staticweb.py b/tempest/api/object_storage/test_container_staticweb.py
index a280248..92fa690 100644
--- a/tempest/api/object_storage/test_container_staticweb.py
+++ b/tempest/api/object_storage/test_container_staticweb.py
@@ -14,10 +14,10 @@
 
 from tempest.api.object_storage import base
 from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class StaticWebTest(base.BaseObjectTest):
@@ -27,7 +27,7 @@
         super(StaticWebTest, cls).resource_setup()
 
         # This header should be posted on the container before every test
-        cls.headers_public_read_acl = {'Read': '.r:*,.rlistings'}
+        headers_public_read_acl = {'Read': '.r:*,.rlistings'}
 
         # Create test container and create one object in it
         cls.container_name = cls.create_container()
@@ -36,7 +36,7 @@
 
         cls.container_client.update_container_metadata(
             cls.container_name,
-            metadata=cls.headers_public_read_acl,
+            metadata=headers_public_read_acl,
             metadata_prefix="X-Container-")
 
     @classmethod
@@ -45,7 +45,7 @@
         super(StaticWebTest, cls).resource_cleanup()
 
     @decorators.idempotent_id('c1f055ab-621d-4a6a-831f-846fcb578b8b')
-    @test.requires_ext(extension='staticweb', service='object')
+    @utils.requires_ext(extension='staticweb', service='object')
     def test_web_index(self):
         headers = {'web-index': self.object_name}
 
@@ -76,7 +76,7 @@
         self.assertNotIn('x-container-meta-web-index', body)
 
     @decorators.idempotent_id('941814cf-db9e-4b21-8112-2b6d0af10ee5')
-    @test.requires_ext(extension='staticweb', service='object')
+    @utils.requires_ext(extension='staticweb', service='object')
     def test_web_listing(self):
         headers = {'web-listings': 'true'}
 
@@ -108,7 +108,7 @@
         self.assertNotIn('x-container-meta-web-listings', body)
 
     @decorators.idempotent_id('bc37ec94-43c8-4990-842e-0e5e02fc8926')
-    @test.requires_ext(extension='staticweb', service='object')
+    @utils.requires_ext(extension='staticweb', service='object')
     def test_web_listing_css(self):
         headers = {'web-listings': 'true',
                    'web-listings-css': 'listings.css'}
@@ -124,15 +124,14 @@
 
         # test GET on http://account_url/container_name
         # we should retrieve a listing of objects
-        resp, body = self.account_client.request("GET",
-                                                 self.container_name,
-                                                 headers={})
+        _, body = self.account_client.request("GET", self.container_name,
+                                              headers={})
         self.assertIn(self.object_name, body.decode())
         css = '<link rel="stylesheet" type="text/css" href="listings.css" />'
         self.assertIn(css, body.decode())
 
     @decorators.idempotent_id('f18b4bef-212e-45e7-b3ca-59af3a465f82')
-    @test.requires_ext(extension='staticweb', service='object')
+    @utils.requires_ext(extension='staticweb', service='object')
     def test_web_error(self):
         headers = {'web-listings': 'true',
                    'web-error': self.object_name}
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 63fb93d..7665b48 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -18,12 +18,10 @@
 from six.moves.urllib import parse as urlparse
 import testtools
 
-
 from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -43,7 +41,6 @@
     @classmethod
     def setup_credentials(cls):
         super(ContainerSyncTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_alt = cls.os_roles_operator_alt
 
     @classmethod
@@ -92,8 +89,7 @@
             cont_client = [self.clients[c][0] for c in cont]
             obj_client = [self.clients[c][1] for c in cont]
             headers = make_headers(cont[1], cont_client[1])
-            resp, body = \
-                cont_client[0].put(str(cont[0]), body=None, headers=headers)
+            cont_client[0].put(str(cont[0]), body=None, headers=headers)
             # create object in container
             object_name = data_utils.rand_name(name='TestSyncObject')
             data = object_name[::-1].encode()  # Raw data, we need bytes
@@ -129,7 +125,7 @@
                 resp, object_content = obj_client.get_object(cont, obj_name)
                 self.assertEqual(object_content, obj_name[::-1].encode())
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.skip_because(bug='1317133')
     @decorators.idempotent_id('be008325-1bba-4925-b7dd-93b58f22ce9b')
     @testtools.skipIf(
diff --git a/tempest/api/object_storage/test_container_sync_middleware.py b/tempest/api/object_storage/test_container_sync_middleware.py
index df738b3..e77b079 100644
--- a/tempest/api/object_storage/test_container_sync_middleware.py
+++ b/tempest/api/object_storage/test_container_sync_middleware.py
@@ -13,9 +13,9 @@
 #    under the License.
 
 from tempest.api.object_storage import test_container_sync
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -37,9 +37,9 @@
         cls.key = 'sync_key'
         cls.cluster_name = CONF.object_storage.cluster_name
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('ea4645a1-d147-4976-82f7-e5a7a3065f80')
-    @test.requires_ext(extension='container_sync', service='object')
+    @utils.requires_ext(extension='container_sync', service='object')
     def test_container_synchronization(self):
         def make_headers(cont, cont_client):
             # tell first container to synchronize to a second
diff --git a/tempest/api/object_storage/test_crossdomain.py b/tempest/api/object_storage/test_crossdomain.py
index c47aa93..f61d9f8 100644
--- a/tempest/api/object_storage/test_crossdomain.py
+++ b/tempest/api/object_storage/test_crossdomain.py
@@ -14,8 +14,8 @@
 
 from tempest.api.object_storage import base
 from tempest.common import custom_matchers
+from tempest.common import utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class CrossdomainTest(base.BaseObjectTest):
@@ -38,7 +38,7 @@
         self.account_client.skip_path()
 
     @decorators.idempotent_id('d1b8b031-b622-4010-82f9-ff78a9e915c7')
-    @test.requires_ext(extension='crossdomain', service='object')
+    @utils.requires_ext(extension='crossdomain', service='object')
     def test_get_crossdomain_policy(self):
         resp, body = self.account_client.get("crossdomain.xml", {})
         body = body.decode()
diff --git a/tempest/api/object_storage/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index 7768d23..ed1be90 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -54,8 +54,8 @@
         # actually expire, so figure out how many secs in the future that is.
         sleepy_time = int(resp['x-delete-at']) - int(time.time())
         sleepy_time = sleepy_time if sleepy_time > 0 else 0
-        resp, body = self.object_client.get_object(self.container_name,
-                                                   self.object_name)
+        resp, _ = self.object_client.get_object(self.container_name,
+                                                self.object_name)
         self.assertHeaders(resp, 'Object', 'GET')
         self.assertIn('x-delete-at', resp)
 
diff --git a/tempest/api/object_storage/test_object_formpost.py b/tempest/api/object_storage/test_object_formpost.py
index 2829ac7..cd834bf 100644
--- a/tempest/api/object_storage/test_object_formpost.py
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -19,9 +19,9 @@
 from six.moves.urllib import parse as urlparse
 
 from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class ObjectFormPostTest(base.BaseObjectTest):
@@ -37,7 +37,8 @@
 
         cls.key = 'Meta'
         cls.metadata = {'Temp-URL-Key': cls.key}
-        cls.account_client.create_account_metadata(metadata=cls.metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=cls.metadata)
 
     def setUp(self):
         super(ObjectFormPostTest, self).setUp()
@@ -53,7 +54,8 @@
 
     @classmethod
     def resource_cleanup(cls):
-        cls.account_client.delete_account_metadata(metadata=cls.metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=cls.metadata)
         cls.delete_containers()
         super(ObjectFormPostTest, cls).resource_cleanup()
 
@@ -106,7 +108,7 @@
         return body, content_type
 
     @decorators.idempotent_id('80fac02b-6e54-4f7b-be0d-a965b5cbef76')
-    @test.requires_ext(extension='formpost', service='object')
+    @utils.requires_ext(extension='formpost', service='object')
     def test_post_object_using_form(self):
         body, content_type = self.get_multipart_form()
 
diff --git a/tempest/api/object_storage/test_object_formpost_negative.py b/tempest/api/object_storage/test_object_formpost_negative.py
index 2174940..df6a0fd 100644
--- a/tempest/api/object_storage/test_object_formpost_negative.py
+++ b/tempest/api/object_storage/test_object_formpost_negative.py
@@ -19,10 +19,10 @@
 from six.moves.urllib import parse as urlparse
 
 from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ObjectFormPostNegativeTest(base.BaseObjectTest):
@@ -38,7 +38,8 @@
 
         cls.key = 'Meta'
         cls.metadata = {'Temp-URL-Key': cls.key}
-        cls.account_client.create_account_metadata(metadata=cls.metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=cls.metadata)
 
     def setUp(self):
         super(ObjectFormPostNegativeTest, self).setUp()
@@ -54,7 +55,8 @@
 
     @classmethod
     def resource_cleanup(cls):
-        cls.account_client.delete_account_metadata(metadata=cls.metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=cls.metadata)
         cls.delete_containers()
         super(ObjectFormPostNegativeTest, cls).resource_cleanup()
 
@@ -107,8 +109,8 @@
         return body, content_type
 
     @decorators.idempotent_id('d3fb3c4d-e627-48ce-9379-a1631f21336d')
-    @test.requires_ext(extension='formpost', service='object')
-    @test.attr(type=['negative'])
+    @utils.requires_ext(extension='formpost', service='object')
+    @decorators.attr(type=['negative'])
     def test_post_object_using_form_expired(self):
         body, content_type = self.get_multipart_form(expires=1)
         time.sleep(2)
@@ -124,7 +126,8 @@
         self.assertIn('FormPost: Form Expired', str(exc))
 
     @decorators.idempotent_id('b277257f-113c-4499-b8d1-5fead79f7360')
-    @test.requires_ext(extension='formpost', service='object')
+    @utils.requires_ext(extension='formpost', service='object')
+    @decorators.attr(type=['negative'])
     def test_post_object_using_form_invalid_signature(self):
         self.key = "Wrong"
         body, content_type = self.get_multipart_form()
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 58fd822..d3cdb72 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -21,10 +21,9 @@
 
 from tempest.api.object_storage import base
 from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -49,7 +48,7 @@
         data_segments = [data + str(i) for i in range(segments)]
         # uploading segments
         for i in range(segments):
-            resp, _ = self.object_client.create_object_segments(
+            self.object_client.create_object_segments(
                 self.container_name, object_name, i, data_segments[i])
 
         return object_name, data_segments
@@ -75,7 +74,7 @@
             for meta_key in not_in_meta:
                 self.assertNotIn('x-object-meta-' + meta_key, resp)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('5b4ce26f-3545-46c9-a2ba-5754358a4c62')
     def test_create_object(self):
         # create object
@@ -185,7 +184,7 @@
         # create object with transfer_encoding
         object_name = data_utils.rand_name(name='TestObject')
         data = data_utils.random_bytes(1024)
-        status, _, resp_headers = self.object_client.put_object_with_chunk(
+        _, _, resp_headers = self.object_client.put_object_with_chunk(
             container=self.container_name,
             name=object_name,
             contents=data_utils.chunkify(data, 512)
@@ -319,7 +318,7 @@
                                                    object_name)
         self.assertHeaders(resp, 'Object', 'DELETE')
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('7a94c25d-66e6-434c-9c38-97d4e2c29945')
     def test_update_object_metadata(self):
         # update object metadata
@@ -395,7 +394,7 @@
         # update object metadata with x_object_manifest
 
         # uploading segments
-        object_name, data_segments = self._upload_segments()
+        object_name, _ = self._upload_segments()
         # creating a manifest file
         data_empty = ''
         self.object_client.create_object(self.container_name,
@@ -415,7 +414,7 @@
             self.container_name,
             object_name)
         self.assertIn('x-object-manifest', resp)
-        self.assertNotEqual(len(resp['x-object-manifest']), 0)
+        self.assertNotEmpty(resp['x-object-manifest'])
 
     @decorators.idempotent_id('0dbbe89c-6811-4d84-a2df-eca2bdd40c0e')
     def test_update_object_metadata_with_x_object_metakey(self):
@@ -460,7 +459,7 @@
             object_name)
         self.assertNotIn('x-object-meta-test-meta', resp)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('9a447cf6-de06-48de-8226-a8c6ed31caf2')
     def test_list_object_metadata(self):
         # get object metadata
@@ -495,7 +494,7 @@
         # get object metadata with x_object_manifest
 
         # uploading segments
-        object_name, data_segments = self._upload_segments()
+        object_name, _ = self._upload_segments()
         # creating a manifest file
         object_prefix = '%s/%s' % (self.container_name, object_name)
         metadata = {'X-Object-Manifest': object_prefix}
@@ -521,16 +520,16 @@
         self.assertTrue(resp['etag'].startswith('\"'))
         self.assertTrue(resp['etag'].endswith('\"'))
         self.assertTrue(resp['etag'].strip('\"').isalnum())
-        self.assertTrue(re.match("^\d+\.?\d*\Z", resp['x-timestamp']))
-        self.assertNotEqual(len(resp['content-type']), 0)
+        self.assertTrue(re.match(r"^\d+\.?\d*\Z", resp['x-timestamp']))
+        self.assertNotEmpty(resp['content-type'])
         self.assertTrue(re.match("^tx[0-9a-f]{21}-[0-9a-f]{10}.*",
                                  resp['x-trans-id']))
-        self.assertNotEqual(len(resp['date']), 0)
+        self.assertNotEmpty(resp['date'])
         self.assertEqual(resp['accept-ranges'], 'bytes')
         self.assertEqual(resp['x-object-manifest'],
                          '%s/%s' % (self.container_name, object_name))
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('02610ba7-86b7-4272-9ed8-aa8d417cb3cd')
     def test_get_object(self):
         # retrieve object's data (in response body)
@@ -613,11 +612,11 @@
         self.assertTrue(resp['etag'].startswith('\"'))
         self.assertTrue(resp['etag'].endswith('\"'))
         self.assertTrue(resp['etag'].strip('\"').isalnum())
-        self.assertTrue(re.match("^\d+\.?\d*\Z", resp['x-timestamp']))
-        self.assertNotEqual(len(resp['content-type']), 0)
+        self.assertTrue(re.match(r"^\d+\.?\d*\Z", resp['x-timestamp']))
+        self.assertNotEmpty(resp['content-type'])
         self.assertTrue(re.match("^tx[0-9a-f]{21}-[0-9a-f]{10}.*",
                                  resp['x-trans-id']))
-        self.assertNotEqual(len(resp['date']), 0)
+        self.assertNotEmpty(resp['date'])
         self.assertEqual(resp['accept-ranges'], 'bytes')
         self.assertEqual(resp['x-object-manifest'],
                          '%s/%s' % (self.container_name, object_name))
@@ -956,7 +955,7 @@
         local_data = "something different"
         md5 = hashlib.md5(local_data.encode()).hexdigest()
         headers = {'If-None-Match': md5}
-        resp, body = self.object_client.get(url, headers=headers)
+        resp, _ = self.object_client.get(url, headers=headers)
         self.assertHeaders(resp, 'Object', 'GET')
 
 
@@ -968,13 +967,12 @@
     @classmethod
     def setup_credentials(cls):
         super(PublicObjectTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_alt = cls.os_roles_operator_alt
 
     @classmethod
     def setup_clients(cls):
         super(PublicObjectTest, cls).setup_clients()
-        cls.identity_client_alt = cls.os_alt.identity_client
+        cls.object_client_alt = cls.os_alt.object_client
 
     def setUp(self):
         super(PublicObjectTest, self).setUp()
@@ -1048,7 +1046,7 @@
         self.assertEqual(resp['x-container-read'], '.r:*,.rlistings')
 
         # get auth token of alternative user
-        alt_auth_data = self.identity_client_alt.auth_provider.auth_data
+        alt_auth_data = self.object_client_alt.auth_provider.auth_data
         self.object_client.auth_provider.set_alt_auth_data(
             request_part='headers',
             auth_data=alt_auth_data
diff --git a/tempest/api/object_storage/test_object_slo.py b/tempest/api/object_storage/test_object_slo.py
index 8ed9bf8..65da63d 100644
--- a/tempest/api/object_storage/test_object_slo.py
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -18,10 +18,10 @@
 
 from tempest.api.object_storage import base
 from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
-from tempest import test
 
 # Each segment, except for the final one, must be at least 1 megabyte
 MIN_SEGMENT_SIZE = 1024 * 1024
@@ -107,7 +107,7 @@
         self.assertHeaders(resp, 'Object', method)
 
     @decorators.idempotent_id('2c3f24a6-36e8-4711-9aa2-800ee1fc7b5b')
-    @test.requires_ext(extension='slo', service='object')
+    @utils.requires_ext(extension='slo', service='object')
     def test_upload_manifest(self):
         # create static large object from multipart manifest
         manifest = self._create_manifest()
@@ -122,19 +122,19 @@
         self._assertHeadersSLO(resp, 'PUT')
 
     @decorators.idempotent_id('e69ad766-e1aa-44a2-bdd2-bf62c09c1456')
-    @test.requires_ext(extension='slo', service='object')
+    @utils.requires_ext(extension='slo', service='object')
     def test_list_large_object_metadata(self):
         # list static large object metadata using multipart manifest
         object_name = self._create_large_object()
 
-        resp, body = self.object_client.list_object_metadata(
+        resp, _ = self.object_client.list_object_metadata(
             self.container_name,
             object_name)
 
         self._assertHeadersSLO(resp, 'HEAD')
 
     @decorators.idempotent_id('49bc49bc-dd1b-4c0f-904e-d9f10b830ee8')
-    @test.requires_ext(extension='slo', service='object')
+    @utils.requires_ext(extension='slo', service='object')
     def test_retrieve_large_object(self):
         # list static large object using multipart manifest
         object_name = self._create_large_object()
@@ -149,13 +149,13 @@
         self.assertEqual(body, sum_data)
 
     @decorators.idempotent_id('87b6dfa1-abe9-404d-8bf0-6c3751e6aa77')
-    @test.requires_ext(extension='slo', service='object')
+    @utils.requires_ext(extension='slo', service='object')
     def test_delete_large_object(self):
         # delete static large object using multipart manifest
         object_name = self._create_large_object()
 
         params_del = {'multipart-manifest': 'delete'}
-        resp, body = self.object_client.delete_object(
+        resp, _ = self.object_client.delete_object(
             self.container_name,
             object_name,
             params=params_del)
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 5e3c9f7..b99f93a 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -19,9 +19,9 @@
 from six.moves.urllib import parse as urlparse
 
 from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class ObjectTempUrlTest(base.BaseObjectTest):
@@ -37,7 +37,8 @@
         cls.metadatas = []
         metadata = {'Temp-URL-Key': cls.key}
         cls.metadatas.append(metadata)
-        cls.account_client.create_account_metadata(metadata=metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata)
 
         # create an object
         cls.object_name, cls.content = cls.create_object(cls.container_name)
@@ -45,8 +46,8 @@
     @classmethod
     def resource_cleanup(cls):
         for metadata in cls.metadatas:
-            cls.account_client.delete_account_metadata(
-                metadata=metadata)
+            cls.account_client.create_update_or_delete_account_metadata(
+                delete_metadata=metadata)
 
         cls.delete_containers()
 
@@ -87,7 +88,7 @@
         return url
 
     @decorators.idempotent_id('f91c96d4-1230-4bba-8eb9-84476d18d991')
-    @test.requires_ext(extension='tempurl', service='object')
+    @utils.requires_ext(extension='tempurl', service='object')
     def test_get_object_using_temp_url(self):
         expires = self._get_expiry_date()
 
@@ -102,15 +103,16 @@
         self.assertEqual(body, self.content)
 
         # Testing a HEAD on this Temp URL
-        resp, body = self.object_client.head(url)
+        resp, _ = self.object_client.head(url)
         self.assertHeaders(resp, 'Object', 'HEAD')
 
     @decorators.idempotent_id('671f9583-86bd-4128-a034-be282a68c5d8')
-    @test.requires_ext(extension='tempurl', service='object')
+    @utils.requires_ext(extension='tempurl', service='object')
     def test_get_object_using_temp_url_key_2(self):
         key2 = 'Meta2-'
         metadata = {'Temp-URL-Key-2': key2}
-        self.account_client.create_account_metadata(metadata=metadata)
+        self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata)
         self.metadatas.append(metadata)
 
         # make sure the metadata has been set
@@ -126,11 +128,11 @@
         url = self._get_temp_url(self.container_name,
                                  self.object_name, "GET",
                                  expires, key2)
-        resp, body = self.object_client.get(url)
+        _, body = self.object_client.get(url)
         self.assertEqual(body, self.content)
 
     @decorators.idempotent_id('9b08dade-3571-4152-8a4f-a4f2a873a735')
-    @test.requires_ext(extension='tempurl', service='object')
+    @utils.requires_ext(extension='tempurl', service='object')
     def test_put_object_using_temp_url(self):
         new_data = data_utils.random_bytes(size=len(self.object_name))
 
@@ -140,11 +142,11 @@
                                  expires, self.key)
 
         # trying to put random data in the object using temp url
-        resp, body = self.object_client.put(url, new_data, None)
+        resp, _ = self.object_client.put(url, new_data, None)
         self.assertHeaders(resp, 'Object', 'PUT')
 
         # Testing a HEAD on this Temp URL
-        resp, body = self.object_client.head(url)
+        resp, _ = self.object_client.head(url)
         self.assertHeaders(resp, 'Object', 'HEAD')
 
         # Validate that the content of the object has been modified
@@ -156,7 +158,7 @@
         self.assertEqual(body, new_data)
 
     @decorators.idempotent_id('249a0111-5ad3-4534-86a7-1993d55f9185')
-    @test.requires_ext(extension='tempurl', service='object')
+    @utils.requires_ext(extension='tempurl', service='object')
     def test_head_object_using_temp_url(self):
         expires = self._get_expiry_date()
 
@@ -166,11 +168,11 @@
                                  expires, self.key)
 
         # Testing a HEAD on this Temp URL
-        resp, body = self.object_client.head(url)
+        resp, _ = self.object_client.head(url)
         self.assertHeaders(resp, 'Object', 'HEAD')
 
     @decorators.idempotent_id('9d9cfd90-708b-465d-802c-e4a8090b823d')
-    @test.requires_ext(extension='tempurl', service='object')
+    @utils.requires_ext(extension='tempurl', service='object')
     def test_get_object_using_temp_url_with_inline_query_parameter(self):
         expires = self._get_expiry_date()
 
diff --git a/tempest/api/object_storage/test_object_temp_url_negative.py b/tempest/api/object_storage/test_object_temp_url_negative.py
index d0e0935..17ae6c1 100644
--- a/tempest/api/object_storage/test_object_temp_url_negative.py
+++ b/tempest/api/object_storage/test_object_temp_url_negative.py
@@ -19,10 +19,10 @@
 from six.moves.urllib import parse as urlparse
 
 from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 
 class ObjectTempUrlNegativeTest(base.BaseObjectTest):
@@ -39,14 +39,15 @@
         # update account metadata
         cls.key = 'Meta'
         cls.metadata = {'Temp-URL-Key': cls.key}
-        cls.account_client.create_account_metadata(metadata=cls.metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=cls.metadata)
         cls.account_client_metadata, _ = \
             cls.account_client.list_account_metadata()
 
     @classmethod
     def resource_cleanup(cls):
-        resp, _ = cls.account_client.delete_account_metadata(
-            metadata=cls.metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=cls.metadata)
 
         cls.delete_containers()
 
@@ -64,10 +65,10 @@
 
         # create object
         self.object_name = data_utils.rand_name(name='ObjectTemp')
-        self.content = data_utils.arbitrary_string(size=len(self.object_name),
-                                                   base_text=self.object_name)
+        content = data_utils.arbitrary_string(size=len(self.object_name),
+                                              base_text=self.object_name)
         self.object_client.create_object(self.container_name,
-                                         self.object_name, self.content)
+                                         self.object_name, content)
 
     def _get_expiry_date(self, expiration_time=1000):
         return int(time.time() + expiration_time)
@@ -91,9 +92,9 @@
 
         return url
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5a583aca-c804-41ba-9d9a-e7be132bdf0b')
-    @test.requires_ext(extension='tempurl', service='object')
+    @utils.requires_ext(extension='tempurl', service='object')
     def test_get_object_after_expiration_time(self):
 
         expires = self._get_expiry_date(1)
diff --git a/tempest/api/object_storage/test_object_version.py b/tempest/api/object_storage/test_object_version.py
index 1b12dac..4799053 100644
--- a/tempest/api/object_storage/test_object_version.py
+++ b/tempest/api/object_storage/test_object_version.py
@@ -16,8 +16,8 @@
 import testtools
 
 from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -51,7 +51,7 @@
     def test_versioned_container(self):
         # create container
         vers_container_name = data_utils.rand_name(name='TestVersionContainer')
-        resp, body = self.container_client.create_container(
+        resp, _ = self.container_client.create_container(
             vers_container_name)
         self.containers.append(vers_container_name)
         self.assertHeaders(resp, 'Container', 'PUT')
@@ -59,7 +59,7 @@
 
         base_container_name = data_utils.rand_name(name='TestBaseContainer')
         headers = {'X-versions-Location': vers_container_name}
-        resp, body = self.container_client.create_container(
+        resp, _ = self.container_client.create_container(
             base_container_name,
             metadata=headers,
             metadata_prefix='')
@@ -76,20 +76,20 @@
         data_2 = data_utils.random_bytes()
         resp, _ = self.object_client.create_object(base_container_name,
                                                    object_name, data_2)
-        resp, body = self.object_client.get_object(base_container_name,
-                                                   object_name)
+        _, body = self.object_client.get_object(base_container_name,
+                                                object_name)
         self.assertEqual(body, data_2)
         # delete object version 2
         resp, _ = self.object_client.delete_object(base_container_name,
                                                    object_name)
         self.assertContainer(base_container_name, '1', '1024',
                              vers_container_name)
-        resp, body = self.object_client.get_object(base_container_name,
-                                                   object_name)
+        _, body = self.object_client.get_object(base_container_name,
+                                                object_name)
         self.assertEqual(body, data_1)
         # delete object version 1
-        resp, _ = self.object_client.delete_object(base_container_name,
-                                                   object_name)
+        self.object_client.delete_object(base_container_name,
+                                         object_name)
         # containers should be empty
         self.assertContainer(base_container_name, '0', '0',
                              vers_container_name)
diff --git a/tempest/api/orchestration/__init__.py b/tempest/api/orchestration/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/orchestration/__init__.py
+++ /dev/null
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
deleted file mode 100644
index 3701b55..0000000
--- a/tempest/api/orchestration/base.py
+++ /dev/null
@@ -1,174 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import os.path
-
-import yaml
-
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest.lib.common.utils import test_utils
-import tempest.test
-
-CONF = config.CONF
-
-
-class BaseOrchestrationTest(tempest.test.BaseTestCase):
-    """Base test case class for all Orchestration API tests."""
-
-    credentials = ['primary']
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaseOrchestrationTest, cls).skip_checks()
-        if not CONF.service_available.heat:
-            raise cls.skipException("Heat support is required")
-
-    @classmethod
-    def setup_credentials(cls):
-        super(BaseOrchestrationTest, cls).setup_credentials()
-        stack_owner_role = CONF.orchestration.stack_owner_role
-        cls.os = cls.get_client_manager(roles=[stack_owner_role])
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseOrchestrationTest, cls).setup_clients()
-        cls.orchestration_client = cls.os.orchestration_client
-        cls.client = cls.orchestration_client
-        cls.servers_client = cls.os.servers_client
-        cls.keypairs_client = cls.os.keypairs_client
-        cls.networks_client = cls.os.networks_client
-        cls.volumes_client = cls.os.volumes_client
-        cls.images_v2_client = cls.os.image_client_v2
-
-        if CONF.volume_feature_enabled.api_v2:
-            cls.volumes_client = cls.os.volumes_v2_client
-        else:
-            cls.volumes_client = cls.os.volumes_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(BaseOrchestrationTest, cls).resource_setup()
-        cls.build_timeout = CONF.orchestration.build_timeout
-        cls.build_interval = CONF.orchestration.build_interval
-        cls.stacks = []
-        cls.keypairs = []
-        cls.images = []
-
-    @classmethod
-    def create_stack(cls, stack_name, template_data, parameters=None,
-                     environment=None, files=None):
-        if parameters is None:
-            parameters = {}
-        body = cls.client.create_stack(
-            stack_name,
-            template=template_data,
-            parameters=parameters,
-            environment=environment,
-            files=files)
-        stack_id = body.response['location'].split('/')[-1]
-        stack_identifier = '%s/%s' % (stack_name, stack_id)
-        cls.stacks.append(stack_identifier)
-        return stack_identifier
-
-    @classmethod
-    def _clear_stacks(cls):
-        for stack_identifier in cls.stacks:
-            test_utils.call_and_ignore_notfound_exc(
-                cls.client.delete_stack, stack_identifier)
-
-        for stack_identifier in cls.stacks:
-            test_utils.call_and_ignore_notfound_exc(
-                cls.client.wait_for_stack_status, stack_identifier,
-                'DELETE_COMPLETE')
-
-    @classmethod
-    def _create_keypair(cls, name_start='keypair-heat-'):
-        kp_name = data_utils.rand_name(name_start)
-        body = cls.keypairs_client.create_keypair(name=kp_name)['keypair']
-        cls.keypairs.append(kp_name)
-        return body
-
-    @classmethod
-    def _clear_keypairs(cls):
-        for kp_name in cls.keypairs:
-            try:
-                cls.keypairs_client.delete_keypair(kp_name)
-            except Exception:
-                pass
-
-    @classmethod
-    def _create_image(cls, name_start='image-heat-', container_format='bare',
-                      disk_format='iso'):
-        image_name = data_utils.rand_name(name_start)
-        body = cls.images_v2_client.create_image(image_name,
-                                                 container_format,
-                                                 disk_format)
-        image_id = body['id']
-        cls.images.append(image_id)
-        return body
-
-    @classmethod
-    def _clear_images(cls):
-        for image_id in cls.images:
-            test_utils.call_and_ignore_notfound_exc(
-                cls.images_v2_client.delete_image, image_id)
-
-    @classmethod
-    def read_template(cls, name, ext='yaml'):
-        loc = ["stacks", "templates", "%s.%s" % (name, ext)]
-        fullpath = os.path.join(os.path.dirname(__file__), *loc)
-
-        with open(fullpath, "r") as f:
-            content = f.read()
-            return content
-
-    @classmethod
-    def load_template(cls, name, ext='yaml'):
-        loc = ["stacks", "templates", "%s.%s" % (name, ext)]
-        fullpath = os.path.join(os.path.dirname(__file__), *loc)
-
-        with open(fullpath, "r") as f:
-            return yaml.safe_load(f)
-
-    @classmethod
-    def resource_cleanup(cls):
-        cls._clear_stacks()
-        cls._clear_keypairs()
-        cls._clear_images()
-        super(BaseOrchestrationTest, cls).resource_cleanup()
-
-    @staticmethod
-    def stack_output(stack, output_key):
-        """Return a stack output value for a given key."""
-        return next((o['output_value'] for o in stack['outputs']
-                    if o['output_key'] == output_key), None)
-
-    def assert_fields_in_dict(self, obj, *fields):
-        for field in fields:
-            self.assertIn(field, obj)
-
-    def list_resources(self, stack_identifier):
-        """Get a dict mapping of resource names to types."""
-        resources = self.client.list_resources(stack_identifier)['resources']
-        self.assertIsInstance(resources, list)
-        for res in resources:
-            self.assert_fields_in_dict(res, 'logical_resource_id',
-                                       'resource_type', 'resource_status',
-                                       'updated_time')
-
-        return dict((r['resource_name'], r['resource_type'])
-                    for r in resources)
-
-    def get_stack_output(self, stack_identifier, output_key):
-        body = self.client.show_stack(stack_identifier)['stack']
-        return self.stack_output(body, output_key)
diff --git a/tempest/api/orchestration/stacks/__init__.py b/tempest/api/orchestration/stacks/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/orchestration/stacks/__init__.py
+++ /dev/null
diff --git a/tempest/api/orchestration/stacks/templates/cinder_basic.yaml b/tempest/api/orchestration/stacks/templates/cinder_basic.yaml
deleted file mode 100644
index 61c271c..0000000
--- a/tempest/api/orchestration/stacks/templates/cinder_basic.yaml
+++ /dev/null
@@ -1,33 +0,0 @@
-heat_template_version: 2013-05-23
-
-parameters:
-  volume_size:
-    type: number
-    default: 1
-
-resources:
-    volume:
-        type: OS::Cinder::Volume
-        properties:
-            size: { get_param: volume_size }
-            description: a descriptive description
-            name: volume_name
-
-outputs:
-  status:
-    description: status
-    value: { get_attr: ['volume', 'status'] }
-
-  size:
-    description: size
-    value: { get_attr: ['volume', 'size'] }
-
-  display_description:
-    description: display_description
-    value: { get_attr: ['volume', 'display_description'] }
-
-  display_name:
-    value: { get_attr: ['volume', 'display_name'] }
-
-  volume_id:
-    value: { get_resource: volume }
diff --git a/tempest/api/orchestration/stacks/templates/cinder_basic_delete_retain.yaml b/tempest/api/orchestration/stacks/templates/cinder_basic_delete_retain.yaml
deleted file mode 100644
index 0bc6d69..0000000
--- a/tempest/api/orchestration/stacks/templates/cinder_basic_delete_retain.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-heat_template_version: 2013-05-23
-
-parameters:
-  volume_size:
-    type: number
-    default: 1
-
-resources:
-    volume:
-        deletion_policy: 'Retain'
-        type: OS::Cinder::Volume
-        properties:
-            size: { get_param: volume_size }
-            description: a descriptive description
-            name: volume_name
-
-outputs:
-  status:
-    description: status
-    value: { get_attr: ['volume', 'status'] }
-
-  size:
-    description: size
-    value: { get_attr: ['volume', 'size'] }
-
-  display_description:
-    description: display_description
-    value: { get_attr: ['volume', 'display_description'] }
-
-  display_name:
-    value: { get_attr: ['volume', 'display_name'] }
-
-  volume_id:
-    value: { get_resource: volume }
diff --git a/tempest/api/orchestration/stacks/templates/neutron_basic.yaml b/tempest/api/orchestration/stacks/templates/neutron_basic.yaml
deleted file mode 100644
index ccb1b54..0000000
--- a/tempest/api/orchestration/stacks/templates/neutron_basic.yaml
+++ /dev/null
@@ -1,72 +0,0 @@
-heat_template_version: '2013-05-23'
-description: |
-  Template which creates single EC2 instance
-parameters:
-  KeyName:
-    type: string
-  InstanceType:
-    type: string
-  ImageId:
-    type: string
-  SubNetCidr:
-    type: string
-  ExternalNetworkId:
-    type: string
-  DNSServers:
-    type: comma_delimited_list
-  timeout:
-    type: number
-resources:
-  Network:
-    type: OS::Neutron::Net
-    properties:
-      name: NewNetwork
-  Subnet:
-    type: OS::Neutron::Subnet
-    properties:
-      network_id: {Ref: Network}
-      name: NewSubnet
-      ip_version: 4
-      cidr: { get_param: SubNetCidr }
-      dns_nameservers: { get_param: DNSServers }
-  Router:
-    type: OS::Neutron::Router
-    properties:
-      name: NewRouter
-      admin_state_up: true
-      external_gateway_info:
-        network: {get_param: ExternalNetworkId}
-  RouterInterface:
-    type: OS::Neutron::RouterInterface
-    properties:
-      router_id: {get_resource: Router}
-      subnet_id: {get_resource: Subnet}
-  Server:
-    type: OS::Nova::Server
-    metadata:
-      Name: SmokeServerNeutron
-    properties:
-      image: {get_param: ImageId}
-      flavor: {get_param: InstanceType}
-      key_name: {get_param: KeyName}
-      networks:
-      - network: {get_resource: Network}
-      user_data_format: RAW
-      user_data:
-        str_replace:
-          template: |
-            #!/bin/sh -v
-
-            SIGNAL_DATA='{"Status": "SUCCESS", "Reason": "SmokeServerNeutron created", "Data": "Application has completed configuration.", "UniqueId": "00000"}'
-            while ! curl --insecure --fail -X PUT -H 'Content-Type:' --data-binary "$SIGNAL_DATA" \
-            'wait_handle' ; do sleep 3; done
-          params:
-            wait_handle: {get_resource: WaitHandleNeutron}
-  WaitHandleNeutron:
-    type: AWS::CloudFormation::WaitConditionHandle
-  WaitCondition:
-    type: AWS::CloudFormation::WaitCondition
-    depends_on: Server
-    properties:
-      Handle: {get_resource: WaitHandleNeutron}
-      Timeout: {get_param: timeout}
diff --git a/tempest/api/orchestration/stacks/templates/non_empty_stack.yaml b/tempest/api/orchestration/stacks/templates/non_empty_stack.yaml
deleted file mode 100644
index 4f9df91..0000000
--- a/tempest/api/orchestration/stacks/templates/non_empty_stack.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-HeatTemplateFormatVersion: '2012-12-12'
-Description: |
-  Template which creates some simple resources
-Parameters:
-  trigger:
-    Type: String
-    Default: not_yet
-  image:
-    Type: String
-  flavor:
-    Type: String
-Resources:
-  fluffy:
-    Type: AWS::AutoScaling::LaunchConfiguration
-    Metadata:
-      kittens:
-      - Tom
-      - Stinky
-    Properties:
-      ImageId: {Ref: image}
-      InstanceType: {Ref: flavor}
-      UserData:
-        Fn::Replace:
-        - variable_a: {Ref: trigger}
-          variable_b: bee
-        - |
-          A == variable_a
-          B == variable_b
-Outputs:
-  fluffy:
-    Description: "fluffies irc nick"
-    Value:
-      Fn::Replace:
-      - nick: {Ref: fluffy}
-      - |
-        #nick
diff --git a/tempest/api/orchestration/stacks/templates/nova_keypair.json b/tempest/api/orchestration/stacks/templates/nova_keypair.json
deleted file mode 100644
index 63d3817..0000000
--- a/tempest/api/orchestration/stacks/templates/nova_keypair.json
+++ /dev/null
@@ -1,48 +0,0 @@
-{
-  "AWSTemplateFormatVersion" : "2010-09-09",
-  "Description" : "Template which create two key pairs.",
-  "Parameters" : {
-    "KeyPairName1": {
-      "Type": "String",
-      "Default": "testkey1"
-      },
-    "KeyPairName2": {
-      "Type": "String",
-      "Default": "testkey2"
-      }
-   },
-   "Resources" : {
-     "KeyPairSavePrivate": {
-       "Type": "OS::Nova::KeyPair",
-       "Properties": {
-         "name" : { "Ref" : "KeyPairName1" },
-         "save_private_key": true
-       }
-     },
-     "KeyPairDontSavePrivate": {
-       "Type": "OS::Nova::KeyPair",
-       "Properties": {
-         "name" : { "Ref" : "KeyPairName2" },
-         "save_private_key": false
-      }
-     }
-  },
- "Outputs": {
-   "KeyPair_PublicKey": {
-     "Description": "Public Key of generated keypair.",
-     "Value": { "Fn::GetAtt" : ["KeyPairSavePrivate", "public_key"] }
-    },
-   "KeyPair_PrivateKey": {
-     "Description": "Private Key of generated keypair.",
-     "Value": { "Fn::GetAtt" : ["KeyPairSavePrivate", "private_key"] }
-   },
-   "KeyPairDontSavePrivate_PublicKey": {
-     "Description": "Public Key of generated keypair.",
-     "Value": { "Fn::GetAtt" : ["KeyPairDontSavePrivate", "public_key"] }
-   },
-  "KeyPairDontSavePrivate_PrivateKey": {
-     "Description": "Private Key of generated keypair.",
-     "Value": { "Fn::GetAtt" : ["KeyPairDontSavePrivate", "private_key"] }
-   }
-  }
-}
diff --git a/tempest/api/orchestration/stacks/templates/nova_keypair.yaml b/tempest/api/orchestration/stacks/templates/nova_keypair.yaml
deleted file mode 100644
index 81ad99c..0000000
--- a/tempest/api/orchestration/stacks/templates/nova_keypair.yaml
+++ /dev/null
@@ -1,43 +0,0 @@
-heat_template_version: 2013-05-23
-
-description: >
-  Template which creates two key pairs.
-
-parameters:
-  KeyPairName1:
-    type: string
-    default: testkey
-
-  KeyPairName2:
-    type: string
-    default: testkey2
-
-resources:
-  KeyPairSavePrivate:
-    type: OS::Nova::KeyPair
-    properties:
-      name: { get_param: KeyPairName1 }
-      save_private_key: true
-
-  KeyPairDontSavePrivate:
-    type: OS::Nova::KeyPair
-    properties:
-      name: { get_param: KeyPairName2 }
-      save_private_key: false
-
-outputs:
-  KeyPair_PublicKey:
-    description: Public Key of generated keypair
-    value: { get_attr: [KeyPairSavePrivate, public_key] }
-
-  KeyPair_PrivateKey:
-    description: Private Key of generated keypair
-    value: { get_attr: [KeyPairSavePrivate, private_key] }
-
-  KeyPairDontSavePrivate_PublicKey:
-    description: Public Key of generated keypair
-    value: { get_attr: [KeyPairDontSavePrivate, public_key] }
-
-  KeyPairDontSavePrivate_PrivateKey:
-    description: Private Key of generated keypair
-    value: { get_attr: [KeyPairDontSavePrivate, private_key] }
diff --git a/tempest/api/orchestration/stacks/templates/random_string.yaml b/tempest/api/orchestration/stacks/templates/random_string.yaml
deleted file mode 100644
index dfd2342..0000000
--- a/tempest/api/orchestration/stacks/templates/random_string.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-heat_template_version: 2013-05-23
-
-parameters:
-  random_length:
-    type: number
-    default: 10
-
-resources:
-  random:
-    type: OS::Heat::RandomString
-    properties:
-        length: {get_param: random_length}
-
-outputs:
-  random_length:
-    value: {get_param: random_length}
-  random_value:
-    value: {get_attr: [random, value]}
diff --git a/tempest/api/orchestration/stacks/templates/swift_basic.yaml b/tempest/api/orchestration/stacks/templates/swift_basic.yaml
deleted file mode 100644
index 713f8bc..0000000
--- a/tempest/api/orchestration/stacks/templates/swift_basic.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-heat_template_version: 2013-05-23
-description: Template which creates a Swift container resource
-
-resources:
-  SwiftContainerWebsite:
-    deletion_policy: "Delete"
-    type: OS::Swift::Container
-    properties:
-      X-Container-Read: ".r:*"
-      X-Container-Meta:
-        web-index: "index.html"
-        web-error: "error.html"
-
-  SwiftContainer:
-    type: OS::Swift::Container
-
-outputs:
-  WebsiteURL:
-    description: "URL for website hosted on S3"
-    value: { get_attr: [SwiftContainer, WebsiteURL] }
-  DomainName:
-    description: "Domain of Swift host"
-    value: { get_attr: [SwiftContainer, DomainName] }
diff --git a/tempest/api/orchestration/stacks/test_environment.py b/tempest/api/orchestration/stacks/test_environment.py
deleted file mode 100644
index 0a9b3e5..0000000
--- a/tempest/api/orchestration/stacks/test_environment.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest.lib import decorators
-
-
-class StackEnvironmentTest(base.BaseOrchestrationTest):
-
-    @decorators.idempotent_id('37d4346b-1abd-4442-b7b1-2a4e5749a1e3')
-    def test_environment_parameter(self):
-        """Test passing a stack parameter via the environment."""
-        stack_name = data_utils.rand_name('heat')
-        template = self.read_template('random_string')
-        environment = {'parameters': {'random_length': 20}}
-
-        stack_identifier = self.create_stack(stack_name, template,
-                                             environment=environment)
-        self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
-
-        random_len = self.get_stack_output(stack_identifier, 'random_length')
-        self.assertEqual(20, random_len)
-
-        random_value = self.get_stack_output(stack_identifier, 'random_value')
-        self.assertEqual(20, len(random_value))
-
-    @decorators.idempotent_id('73bce717-ad22-4853-bbef-6ed89b632701')
-    def test_environment_provider_resource(self):
-        """Test passing resource_registry defining a provider resource."""
-        stack_name = data_utils.rand_name('heat')
-        template = '''
-heat_template_version: 2013-05-23
-resources:
-  random:
-    type: My:Random::String
-outputs:
-    random_value:
-        value: {get_attr: [random, random_value]}
-'''
-        environment = {'resource_registry':
-                       {'My:Random::String': 'my_random.yaml'}}
-        files = {'my_random.yaml': self.read_template('random_string')}
-
-        stack_identifier = self.create_stack(stack_name, template,
-                                             environment=environment,
-                                             files=files)
-        self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
-
-        # random_string.yaml specifies a length of 10
-        random_value = self.get_stack_output(stack_identifier, 'random_value')
-        random_string_template = self.load_template('random_string')
-        expected_length = random_string_template['parameters'][
-            'random_length']['default']
-        self.assertEqual(expected_length, len(random_value))
-
-    @decorators.idempotent_id('9d682e5a-f4bb-47d5-8472-9d3cacb855df')
-    def test_files_provider_resource(self):
-        """Test untyped defining of a provider resource via "files"."""
-        # It's also possible to specify the filename directly in the template.
-        # without adding the type alias to resource_registry
-        stack_name = data_utils.rand_name('heat')
-        template = '''
-heat_template_version: 2013-05-23
-resources:
-  random:
-    type: my_random.yaml
-outputs:
-    random_value:
-        value: {get_attr: [random, random_value]}
-'''
-        files = {'my_random.yaml': self.read_template('random_string')}
-
-        stack_identifier = self.create_stack(stack_name, template,
-                                             files=files)
-        self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
-
-        # random_string.yaml specifies a length of 10
-        random_value = self.get_stack_output(stack_identifier, 'random_value')
-        random_string_template = self.load_template('random_string')
-        expected_length = random_string_template['parameters'][
-            'random_length']['default']
-        self.assertEqual(expected_length, len(random_value))
diff --git a/tempest/api/orchestration/stacks/test_limits.py b/tempest/api/orchestration/stacks/test_limits.py
deleted file mode 100644
index b079435..0000000
--- a/tempest/api/orchestration/stacks/test_limits.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-CONF = config.CONF
-
-
-class TestServerStackLimits(base.BaseOrchestrationTest):
-
-    @decorators.idempotent_id('ec9bed71-c460-45c9-ab98-295caa9fd76b')
-    def test_exceed_max_template_size_fails(self):
-        stack_name = data_utils.rand_name('heat')
-        fill = 'A' * CONF.orchestration.max_template_size
-        template = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Description: '%s'
-Outputs:
-  Foo: bar''' % fill
-        ex = self.assertRaises(lib_exc.BadRequest, self.create_stack,
-                               stack_name, template)
-        self.assertIn('exceeds maximum allowed size', str(ex))
-
-    @decorators.idempotent_id('d1b83e73-7cad-4a22-9839-036548c5387c')
-    def test_exceed_max_resources_per_stack(self):
-        stack_name = data_utils.rand_name('heat')
-        # Create a big template, one resource more than the limit
-        template = 'heat_template_version: \'2013-05-23\'\nresources:\n'
-        rsrc_snippet = '  random%s:\n    type: \'OS::Heat::RandomString\'\n'
-        num_resources = CONF.orchestration.max_resources_per_stack + 1
-        for i in range(num_resources):
-            template += rsrc_snippet % i
-
-        ex = self.assertRaises(lib_exc.BadRequest, self.create_stack,
-                               stack_name, template)
-        self.assertIn('Maximum resources per stack exceeded', str(ex))
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
deleted file mode 100644
index 3a52108..0000000
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ /dev/null
@@ -1,193 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import netaddr
-from oslo_log import log as logging
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest.lib import decorators
-from tempest.lib import exceptions
-from tempest import test
-
-CONF = config.CONF
-
-LOG = logging.getLogger(__name__)
-
-
-class NeutronResourcesTestJSON(base.BaseOrchestrationTest):
-
-    @classmethod
-    def skip_checks(cls):
-        super(NeutronResourcesTestJSON, cls).skip_checks()
-        if not CONF.service_available.neutron:
-            raise cls.skipException("Neutron support is required")
-
-    @classmethod
-    def setup_credentials(cls):
-        cls.set_network_resources()
-        super(NeutronResourcesTestJSON, cls).setup_credentials()
-
-    @classmethod
-    def setup_clients(cls):
-        super(NeutronResourcesTestJSON, cls).setup_clients()
-        cls.subnets_client = cls.os.subnets_client
-        cls.ports_client = cls.os.ports_client
-        cls.routers_client = cls.os.routers_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(NeutronResourcesTestJSON, cls).resource_setup()
-        cls.neutron_basic_template = cls.load_template('neutron_basic')
-        cls.stack_name = data_utils.rand_name('heat')
-        template = cls.read_template('neutron_basic')
-        cls.keypair_name = (CONF.orchestration.keypair_name or
-                            cls._create_keypair()['name'])
-        cls.external_network_id = CONF.network.public_network_id
-
-        tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
-        mask_bits = CONF.network.project_network_mask_bits
-        cls.subnet_cidr = tenant_cidr.subnet(mask_bits).next()
-
-        # create the stack
-        cls.stack_identifier = cls.create_stack(
-            cls.stack_name,
-            template,
-            parameters={
-                'KeyName': cls.keypair_name,
-                'InstanceType': CONF.orchestration.instance_type,
-                'ImageId': CONF.compute.image_ref,
-                'ExternalNetworkId': cls.external_network_id,
-                'timeout': CONF.orchestration.build_timeout,
-                'DNSServers': CONF.network.dns_servers,
-                'SubNetCidr': str(cls.subnet_cidr)
-            })
-        cls.stack_id = cls.stack_identifier.split('/')[1]
-        try:
-            cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
-            resources = (cls.client.list_resources(cls.stack_identifier)
-                         ['resources'])
-        except exceptions.TimeoutException:
-            if CONF.compute_feature_enabled.console_output:
-                # attempt to log the server console to help with debugging
-                # the cause of the server not signalling the waitcondition
-                # to heat.
-                body = cls.client.show_resource(cls.stack_identifier,
-                                                'Server')
-                server_id = body['physical_resource_id']
-                LOG.debug('Console output for %s', server_id)
-                output = cls.servers_client.get_console_output(
-                    server_id)['output']
-                LOG.debug(output)
-            raise
-
-        cls.test_resources = {}
-        for resource in resources:
-            cls.test_resources[resource['logical_resource_id']] = resource
-
-    @decorators.idempotent_id('f9e2664c-bc44-4eef-98b6-495e4f9d74b3')
-    def test_created_resources(self):
-        """Verifies created neutron resources."""
-        resources = [('Network', self.neutron_basic_template['resources'][
-                      'Network']['type']),
-                     ('Subnet', self.neutron_basic_template['resources'][
-                      'Subnet']['type']),
-                     ('RouterInterface', self.neutron_basic_template[
-                      'resources']['RouterInterface']['type']),
-                     ('Server', self.neutron_basic_template['resources'][
-                      'Server']['type'])]
-        for resource_name, resource_type in resources:
-            resource = self.test_resources.get(resource_name, None)
-            self.assertIsInstance(resource, dict)
-            self.assertEqual(resource_name, resource['logical_resource_id'])
-            self.assertEqual(resource_type, resource['resource_type'])
-            self.assertEqual('CREATE_COMPLETE', resource['resource_status'])
-
-    @decorators.idempotent_id('c572b915-edb1-4e90-b196-c7199a6848c0')
-    @test.services('network')
-    def test_created_network(self):
-        """Verifies created network."""
-        network_id = self.test_resources.get('Network')['physical_resource_id']
-        body = self.networks_client.show_network(network_id)
-        network = body['network']
-        self.assertIsInstance(network, dict)
-        self.assertEqual(network_id, network['id'])
-        self.assertEqual(self.neutron_basic_template['resources'][
-            'Network']['properties']['name'], network['name'])
-
-    @decorators.idempotent_id('e8f84b96-f9d7-4684-ad5f-340203e9f2c2')
-    @test.services('network')
-    def test_created_subnet(self):
-        """Verifies created subnet."""
-        subnet_id = self.test_resources.get('Subnet')['physical_resource_id']
-        body = self.subnets_client.show_subnet(subnet_id)
-        subnet = body['subnet']
-        network_id = self.test_resources.get('Network')['physical_resource_id']
-        self.assertEqual(subnet_id, subnet['id'])
-        self.assertEqual(network_id, subnet['network_id'])
-        self.assertEqual(self.neutron_basic_template['resources'][
-            'Subnet']['properties']['name'], subnet['name'])
-        self.assertEqual(sorted(CONF.network.dns_servers),
-                         sorted(subnet['dns_nameservers']))
-        self.assertEqual(self.neutron_basic_template['resources'][
-            'Subnet']['properties']['ip_version'], subnet['ip_version'])
-        self.assertEqual(str(self.subnet_cidr), subnet['cidr'])
-
-    @decorators.idempotent_id('96af4c7f-5069-44bc-bdcf-c0390f8a67d1')
-    @test.services('network')
-    def test_created_router(self):
-        """Verifies created router."""
-        router_id = self.test_resources.get('Router')['physical_resource_id']
-        body = self.routers_client.show_router(router_id)
-        router = body['router']
-        self.assertEqual(self.neutron_basic_template['resources'][
-            'Router']['properties']['name'], router['name'])
-        self.assertEqual(self.external_network_id,
-                         router['external_gateway_info']['network_id'])
-        self.assertEqual(True, router['admin_state_up'])
-
-    @decorators.idempotent_id('89f605bd-153e-43ee-a0ed-9919b63423c5')
-    @test.services('network')
-    def test_created_router_interface(self):
-        """Verifies created router interface."""
-        router_id = self.test_resources.get('Router')['physical_resource_id']
-        network_id = self.test_resources.get('Network')['physical_resource_id']
-        subnet_id = self.test_resources.get('Subnet')['physical_resource_id']
-        body = self.ports_client.list_ports()
-        ports = body['ports']
-        router_ports = filter(lambda port: port['device_id'] ==
-                              router_id, ports)
-        created_network_ports = filter(lambda port: port['network_id'] ==
-                                       network_id, router_ports)
-        self.assertEqual(1, len(created_network_ports))
-        router_interface = created_network_ports[0]
-        fixed_ips = router_interface['fixed_ips']
-        subnet_fixed_ips = filter(lambda port: port['subnet_id'] ==
-                                  subnet_id, fixed_ips)
-        self.assertEqual(1, len(subnet_fixed_ips))
-        router_interface_ip = subnet_fixed_ips[0]['ip_address']
-        self.assertEqual(str(self.subnet_cidr.iter_hosts().next()),
-                         router_interface_ip)
-
-    @decorators.idempotent_id('75d85316-4ac2-4c0e-a1a9-edd2148fc10e')
-    @test.services('compute', 'network')
-    def test_created_server(self):
-        """Verifies created sever."""
-        server_id = self.test_resources.get('Server')['physical_resource_id']
-        server = self.servers_client.show_server(server_id)['server']
-        self.assertEqual(self.keypair_name, server['key_name'])
-        self.assertEqual('ACTIVE', server['status'])
-        network = server['addresses'][self.neutron_basic_template['resources'][
-                                      'Network']['properties']['name']][0]
-        self.assertEqual(4, network['version'])
-        self.assertIn(netaddr.IPAddress(network['addr']), self.subnet_cidr)
diff --git a/tempest/api/orchestration/stacks/test_non_empty_stack.py b/tempest/api/orchestration/stacks/test_non_empty_stack.py
deleted file mode 100644
index f106349..0000000
--- a/tempest/api/orchestration/stacks/test_non_empty_stack.py
+++ /dev/null
@@ -1,150 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest.lib import decorators
-
-CONF = config.CONF
-
-
-class StacksTestJSON(base.BaseOrchestrationTest):
-
-    @classmethod
-    def resource_setup(cls):
-        super(StacksTestJSON, cls).resource_setup()
-        cls.stack_name = data_utils.rand_name('heat')
-        template = cls.read_template('non_empty_stack')
-        image_id = (CONF.compute.image_ref or
-                    cls._create_image()['id'])
-        flavor = CONF.orchestration.instance_type
-        # create the stack
-        cls.stack_identifier = cls.create_stack(
-            cls.stack_name,
-            template,
-            parameters={
-                'trigger': 'start',
-                'image': image_id,
-                'flavor': flavor
-            })
-        cls.stack_id = cls.stack_identifier.split('/')[1]
-        cls.resource_name = 'fluffy'
-        cls.resource_type = 'AWS::AutoScaling::LaunchConfiguration'
-        cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
-
-    def _list_stacks(self, expected_num=None, **filter_kwargs):
-        stacks = self.client.list_stacks(params=filter_kwargs)['stacks']
-        self.assertIsInstance(stacks, list)
-        if expected_num is not None:
-            self.assertEqual(expected_num, len(stacks))
-        return stacks
-
-    @decorators.idempotent_id('065c652a-720d-4760-9132-06aedeb8e3ab')
-    def test_stack_list(self):
-        """Created stack should be in the list of existing stacks."""
-        stacks = self._list_stacks()
-        stacks_names = map(lambda stack: stack['stack_name'], stacks)
-        self.assertIn(self.stack_name, stacks_names)
-
-    @decorators.idempotent_id('992f96e3-41ee-4ff6-91c7-bcfb670c0919')
-    def test_stack_show(self):
-        """Getting details about created stack should be possible."""
-        stack = self.client.show_stack(self.stack_name)['stack']
-        self.assertIsInstance(stack, dict)
-        self.assert_fields_in_dict(stack, 'stack_name', 'id', 'links',
-                                   'parameters', 'outputs', 'disable_rollback',
-                                   'stack_status_reason', 'stack_status',
-                                   'creation_time', 'updated_time',
-                                   'capabilities', 'notification_topics',
-                                   'timeout_mins', 'template_description')
-        self.assert_fields_in_dict(stack['parameters'], 'AWS::StackId',
-                                   'trigger', 'AWS::Region', 'AWS::StackName')
-        self.assertEqual(True, stack['disable_rollback'],
-                         'disable_rollback should default to True')
-        self.assertEqual(self.stack_name, stack['stack_name'])
-        self.assertEqual(self.stack_id, stack['id'])
-        self.assertEqual('fluffy', stack['outputs'][0]['output_key'])
-
-    @decorators.idempotent_id('fe719f7a-305a-44d8-bbb5-c91e93d9da17')
-    def test_suspend_resume_stack(self):
-        """Suspend and resume a stack."""
-        self.client.suspend_stack(self.stack_identifier)
-        self.client.wait_for_stack_status(self.stack_identifier,
-                                          'SUSPEND_COMPLETE')
-        self.client.resume_stack(self.stack_identifier)
-        self.client.wait_for_stack_status(self.stack_identifier,
-                                          'RESUME_COMPLETE')
-
-    @decorators.idempotent_id('c951d55e-7cce-4c1f-83a0-bad735437fa6')
-    def test_list_resources(self):
-        """Get list of created resources for the stack should be possible."""
-        resources = self.list_resources(self.stack_identifier)
-        self.assertEqual({self.resource_name: self.resource_type}, resources)
-
-    @decorators.idempotent_id('2aba03b3-392f-4237-900b-1f5a5e9bd962')
-    def test_show_resource(self):
-        """Getting details about created resource should be possible."""
-        resource = self.client.show_resource(self.stack_identifier,
-                                             self.resource_name)['resource']
-        self.assertIsInstance(resource, dict)
-        self.assert_fields_in_dict(resource, 'resource_name', 'description',
-                                   'links', 'logical_resource_id',
-                                   'resource_status', 'updated_time',
-                                   'required_by', 'resource_status_reason',
-                                   'physical_resource_id', 'resource_type')
-        self.assertEqual(self.resource_name, resource['logical_resource_id'])
-        self.assertEqual(self.resource_type, resource['resource_type'])
-
-    @decorators.idempotent_id('898070a9-eba5-4fae-b7d6-cf3ffa03090f')
-    def test_resource_metadata(self):
-        """Getting metadata for created resources should be possible."""
-        metadata = self.client.show_resource_metadata(
-            self.stack_identifier,
-            self.resource_name)['metadata']
-        self.assertIsInstance(metadata, dict)
-        self.assertEqual(['Tom', 'Stinky'], metadata.get('kittens', None))
-
-    @decorators.idempotent_id('46567533-0a7f-483b-8942-fa19e0f17839')
-    def test_list_events(self):
-        """Getting list of created events for the stack should be possible."""
-        events = self.client.list_events(self.stack_identifier)['events']
-        self.assertIsInstance(events, list)
-
-        for event in events:
-            self.assert_fields_in_dict(event, 'logical_resource_id', 'id',
-                                       'resource_status_reason',
-                                       'resource_status', 'event_time')
-
-        resource_statuses = [event['resource_status'] for event in events]
-        self.assertIn('CREATE_IN_PROGRESS', resource_statuses)
-        self.assertIn('CREATE_COMPLETE', resource_statuses)
-
-    @decorators.idempotent_id('92465723-1673-400a-909d-4773757a3f21')
-    def test_show_event(self):
-        """Getting details about an event should be possible."""
-        events = self.client.list_resource_events(self.stack_identifier,
-                                                  self.resource_name)['events']
-        self.assertNotEqual([], events)
-        events.sort(key=lambda event: event['event_time'])
-        event_id = events[0]['id']
-        event = self.client.show_event(self.stack_identifier,
-                                       self.resource_name, event_id)['event']
-        self.assertIsInstance(event, dict)
-        self.assert_fields_in_dict(event, 'resource_name', 'event_time',
-                                   'links', 'logical_resource_id',
-                                   'resource_status', 'resource_status_reason',
-                                   'physical_resource_id', 'id',
-                                   'resource_properties', 'resource_type')
-        self.assertEqual(self.resource_name, event['resource_name'])
-        self.assertEqual('state changed', event['resource_status_reason'])
-        self.assertEqual(self.resource_name, event['logical_resource_id'])
diff --git a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
deleted file mode 100644
index 4d1db6d..0000000
--- a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest.lib import decorators
-
-
-class NovaKeyPairResourcesYAMLTest(base.BaseOrchestrationTest):
-    _tpl_type = 'yaml'
-    _resource = 'resources'
-    _type = 'type'
-
-    @classmethod
-    def resource_setup(cls):
-        super(NovaKeyPairResourcesYAMLTest, cls).resource_setup()
-        cls.stack_name = data_utils.rand_name('heat')
-        template = cls.read_template('nova_keypair', ext=cls._tpl_type)
-
-        # create the stack, avoid any duplicated key.
-        cls.stack_identifier = cls.create_stack(
-            cls.stack_name,
-            template,
-            parameters={
-                'KeyPairName1': cls.stack_name + '_1',
-                'KeyPairName2': cls.stack_name + '_2'
-            })
-
-        cls.stack_id = cls.stack_identifier.split('/')[1]
-        cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
-        resources = (cls.client.list_resources(cls.stack_identifier)
-                     ['resources'])
-        cls.test_resources = {}
-        for resource in resources:
-            cls.test_resources[resource['logical_resource_id']] = resource
-
-    @decorators.idempotent_id('b476eac2-a302-4815-961f-18c410a2a537')
-    def test_created_resources(self):
-        """Verifies created keypair resource."""
-
-        nova_keypair_template = self.load_template('nova_keypair',
-                                                   ext=self._tpl_type)
-        resources = [('KeyPairSavePrivate',
-                      nova_keypair_template[self._resource][
-                          'KeyPairSavePrivate'][self._type]),
-                     ('KeyPairDontSavePrivate',
-                      nova_keypair_template[self._resource][
-                          'KeyPairDontSavePrivate'][self._type])]
-
-        for resource_name, resource_type in resources:
-            resource = self.test_resources.get(resource_name, None)
-            self.assertIsInstance(resource, dict)
-            self.assertEqual(resource_name, resource['logical_resource_id'])
-            self.assertEqual(resource_type, resource['resource_type'])
-            self.assertEqual('CREATE_COMPLETE', resource['resource_status'])
-
-    @decorators.idempotent_id('8d77dec7-91fd-45a6-943d-5abd45e338a4')
-    def test_stack_keypairs_output(self):
-        stack = self.client.show_stack(self.stack_name)['stack']
-        self.assertIsInstance(stack, dict)
-
-        output_map = {}
-        for outputs in stack['outputs']:
-            output_map[outputs['output_key']] = outputs['output_value']
-        # Test that first key generated public and private keys
-        self.assertIn('KeyPair_PublicKey', output_map)
-        self.assertIn("Generated", output_map['KeyPair_PublicKey'])
-        self.assertIn('KeyPair_PrivateKey', output_map)
-        self.assertIn('-----BEGIN', output_map['KeyPair_PrivateKey'])
-        # Test that second key generated public key, and private key is not
-        # in the output due to save_private_key = false
-        self.assertIn('KeyPairDontSavePrivate_PublicKey', output_map)
-        self.assertIn('Generated',
-                      output_map['KeyPairDontSavePrivate_PublicKey'])
-        self.assertIn(u'KeyPairDontSavePrivate_PrivateKey', output_map)
-        private_key = output_map['KeyPairDontSavePrivate_PrivateKey']
-        self.assertEqual(0, len(private_key))
-
-
-class NovaKeyPairResourcesAWSTest(NovaKeyPairResourcesYAMLTest):
-    _tpl_type = 'json'
-    _resource = 'Resources'
-    _type = 'Type'
diff --git a/tempest/api/orchestration/stacks/test_resource_types.py b/tempest/api/orchestration/stacks/test_resource_types.py
deleted file mode 100644
index 63376d5..0000000
--- a/tempest/api/orchestration/stacks/test_resource_types.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.orchestration import base
-from tempest.lib import decorators
-from tempest import test
-
-
-class ResourceTypesTest(base.BaseOrchestrationTest):
-
-    @test.attr(type='smoke')
-    @decorators.idempotent_id('7123d082-3577-4a30-8f00-f805327c4ffd')
-    def test_resource_type_list(self):
-        """Verify it is possible to list resource types."""
-        resource_types = self.client.list_resource_types()['resource_types']
-        self.assertIsInstance(resource_types, list)
-        self.assertIn('OS::Nova::Server', resource_types)
-
-    @test.attr(type='smoke')
-    @decorators.idempotent_id('0e85a483-828b-4a28-a0e3-f0a21809192b')
-    def test_resource_type_show(self):
-        """Verify it is possible to get schema about resource types."""
-        resource_types = self.client.list_resource_types()['resource_types']
-        self.assertNotEmpty(resource_types)
-
-        for resource_type in resource_types:
-            type_schema = self.client.show_resource_type(resource_type)
-            self.assert_fields_in_dict(type_schema, 'properties',
-                                       'attributes', 'resource_type')
-            self.assertEqual(resource_type, type_schema['resource_type'])
-
-    @test.attr(type='smoke')
-    @decorators.idempotent_id('8401821d-65fe-4d43-9fa3-57d5ce3a35c7')
-    def test_resource_type_template(self):
-        """Verify it is possible to get template about resource types."""
-        type_template = self.client.show_resource_type_template(
-            'OS::Nova::Server')
-        self.assert_fields_in_dict(
-            type_template,
-            'Outputs',
-            'Parameters', 'Resources')
diff --git a/tempest/api/orchestration/stacks/test_soft_conf.py b/tempest/api/orchestration/stacks/test_soft_conf.py
deleted file mode 100644
index 89b10cc..0000000
--- a/tempest/api/orchestration/stacks/test_soft_conf.py
+++ /dev/null
@@ -1,170 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-from tempest import test
-
-
-class TestSoftwareConfig(base.BaseOrchestrationTest):
-
-    def setUp(self):
-        super(TestSoftwareConfig, self).setUp()
-        self.configs = []
-        # Add 2 sets of software configuration
-        self.configs.append(self._config_create('a'))
-        self.configs.append(self._config_create('b'))
-        # Create a deployment using config a's id
-        self._deployment_create(self.configs[0]['id'])
-
-    def _config_create(self, suffix):
-        configuration = {'group': 'script',
-                         'inputs': [],
-                         'outputs': [],
-                         'options': {}}
-        configuration['name'] = 'heat_soft_config_%s' % suffix
-        configuration['config'] = '#!/bin/bash echo init-%s' % suffix
-        api_config = self.client.create_software_config(**configuration)
-        configuration['id'] = api_config['software_config']['id']
-        self.addCleanup(self._config_delete, configuration['id'])
-        self._validate_config(configuration, api_config)
-        return configuration
-
-    def _validate_config(self, configuration, api_config):
-        # Assert all expected keys are present with matching data
-        for k in configuration:
-            self.assertEqual(configuration[k],
-                             api_config['software_config'][k])
-
-    def _deployment_create(self, config_id):
-        self.server_id = data_utils.rand_name('dummy-server')
-        self.action = 'ACTION_0'
-        self.status = 'STATUS_0'
-        self.input_values = {}
-        self.output_values = []
-        self.status_reason = 'REASON_0'
-        self.signal_transport = 'NO_SIGNAL'
-        self.deployment = self.client.create_software_deploy(
-            self.server_id, config_id, self.action, self.status,
-            self.input_values, self.output_values, self.status_reason,
-            self.signal_transport)
-        self.deployment_id = self.deployment['software_deployment']['id']
-        self.addCleanup(self._deployment_delete, self.deployment_id)
-
-    def _deployment_delete(self, deploy_id):
-        self.client.delete_software_deploy(deploy_id)
-        # Testing that it is really gone
-        self.assertRaises(
-            lib_exc.NotFound, self.client.show_software_deployment,
-            self.deployment_id)
-
-    def _config_delete(self, config_id):
-        self.client.delete_software_config(config_id)
-        # Testing that it is really gone
-        self.assertRaises(
-            lib_exc.NotFound, self.client.show_software_config, config_id)
-
-    @test.attr(type='smoke')
-    @decorators.idempotent_id('136162ed-9445-4b9c-b7fc-306af8b5da99')
-    def test_get_software_config(self):
-        """Testing software config get."""
-        for conf in self.configs:
-            api_config = self.client.show_software_config(conf['id'])
-            self._validate_config(conf, api_config)
-
-    @test.attr(type='smoke')
-    @decorators.idempotent_id('1275c835-c967-4a2c-8d5d-ad533447ed91')
-    def test_get_deployment_list(self):
-        """Getting a list of all deployments"""
-        deploy_list = self.client.list_software_deployments()
-        deploy_ids = [deploy['id'] for deploy in
-                      deploy_list['software_deployments']]
-        self.assertIn(self.deployment_id, deploy_ids)
-
-    @test.attr(type='smoke')
-    @decorators.idempotent_id('fe7cd9f9-54b1-429c-a3b7-7df8451db913')
-    def test_get_deployment_metadata(self):
-        """Testing deployment metadata get"""
-        metadata = self.client.show_software_deployment_metadata(
-            self.server_id)
-        conf_ids = [conf['id'] for conf in metadata['metadata']]
-        self.assertIn(self.configs[0]['id'], conf_ids)
-
-    def _validate_deployment(self, action, status, reason, config_id):
-        deployment = self.client.show_software_deployment(self.deployment_id)
-        self.assertEqual(action, deployment['software_deployment']['action'])
-        self.assertEqual(status, deployment['software_deployment']['status'])
-        self.assertEqual(reason,
-                         deployment['software_deployment']['status_reason'])
-        self.assertEqual(config_id,
-                         deployment['software_deployment']['config_id'])
-
-    @test.attr(type='smoke')
-    @decorators.idempotent_id('f29d21f3-ed75-47cf-8cdc-ef1bdeb4c674')
-    def test_software_deployment_create_validate(self):
-        """Testing software deployment was created as expected."""
-        # Asserting that all fields were created
-        self.assert_fields_in_dict(
-            self.deployment['software_deployment'], 'action', 'config_id',
-            'id', 'input_values', 'output_values', 'server_id', 'status',
-            'status_reason')
-        # Testing get for this deployment and verifying parameters
-        self._validate_deployment(self.action, self.status,
-                                  self.status_reason, self.configs[0]['id'])
-
-    @test.attr(type='smoke')
-    @decorators.idempotent_id('2ac43ab3-34f2-415d-be2e-eabb4d14ee32')
-    def test_software_deployment_update_no_metadata_change(self):
-        """Testing software deployment update without metadata change."""
-        metadata = self.client.show_software_deployment_metadata(
-            self.server_id)
-        # Updating values without changing the configuration ID
-        new_action = 'ACTION_1'
-        new_status = 'STATUS_1'
-        new_reason = 'REASON_1'
-        self.client.update_software_deploy(
-            self.deployment_id, self.server_id, self.configs[0]['id'],
-            new_action, new_status, self.input_values, self.output_values,
-            new_reason, self.signal_transport)
-        # Verifying get and that the deployment was updated as expected
-        self._validate_deployment(new_action, new_status,
-                                  new_reason, self.configs[0]['id'])
-
-        # Metadata should not be changed at this point
-        test_metadata = self.client.show_software_deployment_metadata(
-            self.server_id)
-        for key in metadata['metadata'][0]:
-            self.assertEqual(
-                metadata['metadata'][0][key],
-                test_metadata['metadata'][0][key])
-
-    @test.attr(type='smoke')
-    @decorators.idempotent_id('92c48944-d79d-4595-a840-8e1a581c1a72')
-    def test_software_deployment_update_with_metadata_change(self):
-        """Testing software deployment update with metadata change."""
-        metadata = self.client.show_software_deployment_metadata(
-            self.server_id)
-        self.client.update_software_deploy(
-            self.deployment_id, self.server_id, self.configs[1]['id'],
-            self.action, self.status, self.input_values,
-            self.output_values, self.status_reason, self.signal_transport)
-        self._validate_deployment(self.action, self.status,
-                                  self.status_reason, self.configs[1]['id'])
-        # Metadata should now be changed
-        new_metadata = self.client.show_software_deployment_metadata(
-            self.server_id)
-        # Its enough to test the ID in this case
-        meta_id = metadata['metadata'][0]['id']
-        test_id = new_metadata['metadata'][0]['id']
-        self.assertNotEqual(meta_id, test_id)
diff --git a/tempest/api/orchestration/stacks/test_stacks.py b/tempest/api/orchestration/stacks/test_stacks.py
deleted file mode 100644
index 7b5f161..0000000
--- a/tempest/api/orchestration/stacks/test_stacks.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest.lib import decorators
-from tempest import test
-
-
-class StacksTestJSON(base.BaseOrchestrationTest):
-    empty_template = "HeatTemplateFormatVersion: '2012-12-12'\n"
-
-    @test.attr(type='smoke')
-    @decorators.idempotent_id('d35d628c-07f6-4674-85a1-74db9919e986')
-    def test_stack_list_responds(self):
-        stacks = self.client.list_stacks()['stacks']
-        self.assertIsInstance(stacks, list)
-
-    @test.attr(type='smoke')
-    @decorators.idempotent_id('10498bd5-a83e-4b62-a817-ce24afe938fe')
-    def test_stack_crud_no_resources(self):
-        stack_name = data_utils.rand_name('heat')
-
-        # create the stack
-        stack_identifier = self.create_stack(
-            stack_name, self.empty_template)
-        stack_id = stack_identifier.split('/')[1]
-
-        # wait for create complete (with no resources it should be instant)
-        self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
-
-        # check for stack in list
-        stacks = self.client.list_stacks()['stacks']
-        list_ids = list([stack['id'] for stack in stacks])
-        self.assertIn(stack_id, list_ids)
-
-        # fetch the stack
-        stack = self.client.show_stack(stack_identifier)['stack']
-        self.assertEqual('CREATE_COMPLETE', stack['stack_status'])
-
-        # fetch the stack by name
-        stack = self.client.show_stack(stack_name)['stack']
-        self.assertEqual('CREATE_COMPLETE', stack['stack_status'])
-
-        # fetch the stack by id
-        stack = self.client.show_stack(stack_id)['stack']
-        self.assertEqual('CREATE_COMPLETE', stack['stack_status'])
-
-        # delete the stack
-        self.client.delete_stack(stack_identifier)
-        self.client.wait_for_stack_status(stack_identifier, 'DELETE_COMPLETE')
diff --git a/tempest/api/orchestration/stacks/test_swift_resources.py b/tempest/api/orchestration/stacks/test_swift_resources.py
deleted file mode 100644
index 505abe7..0000000
--- a/tempest/api/orchestration/stacks/test_swift_resources.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest.lib import decorators
-from tempest import test
-
-
-CONF = config.CONF
-
-
-class SwiftResourcesTestJSON(base.BaseOrchestrationTest):
-    @classmethod
-    def skip_checks(cls):
-        super(SwiftResourcesTestJSON, cls).skip_checks()
-        if not CONF.service_available.swift:
-            raise cls.skipException("Swift support is required")
-
-    @classmethod
-    def setup_credentials(cls):
-        super(SwiftResourcesTestJSON, cls).setup_credentials()
-        stack_owner_role = CONF.orchestration.stack_owner_role
-        operator_role = CONF.object_storage.operator_role
-        cls.os = cls.get_client_manager(
-            roles=[stack_owner_role, operator_role])
-
-    @classmethod
-    def setup_clients(cls):
-        super(SwiftResourcesTestJSON, cls).setup_clients()
-        cls.account_client = cls.os.account_client
-        cls.container_client = cls.os.container_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(SwiftResourcesTestJSON, cls).resource_setup()
-        cls.stack_name = data_utils.rand_name('heat')
-        template = cls.read_template('swift_basic')
-        # create the stack
-        cls.stack_identifier = cls.create_stack(
-            cls.stack_name,
-            template)
-        cls.stack_id = cls.stack_identifier.split('/')[1]
-        cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
-        cls.test_resources = {}
-        resources = (cls.client.list_resources(cls.stack_identifier)
-                     ['resources'])
-        for resource in resources:
-            cls.test_resources[resource['logical_resource_id']] = resource
-
-    @decorators.idempotent_id('1a6fe69e-4be4-4990-9a7a-84b6f18019cb')
-    def test_created_resources(self):
-        """Created stack should be in the list of existing stacks."""
-        swift_basic_template = self.load_template('swift_basic')
-        resources = [('SwiftContainer', swift_basic_template['resources'][
-                      'SwiftContainer']['type']),
-                     ('SwiftContainerWebsite', swift_basic_template[
-                      'resources']['SwiftContainerWebsite']['type'])]
-        for resource_name, resource_type in resources:
-            resource = self.test_resources.get(resource_name)
-            self.assertIsInstance(resource, dict)
-            self.assertEqual(resource_type, resource['resource_type'])
-            self.assertEqual(resource_name, resource['logical_resource_id'])
-            self.assertEqual('CREATE_COMPLETE', resource['resource_status'])
-
-    @decorators.idempotent_id('bd438b18-5494-4d5a-9ce6-d2a942ec5060')
-    @test.services('object_storage')
-    def test_created_containers(self):
-        params = {'format': 'json'}
-        _, container_list = \
-            self.account_client.list_account_containers(params=params)
-        created_containers = [cont for cont in container_list
-                              if cont['name'].startswith(self.stack_name)]
-        self.assertEqual(2, len(created_containers))
-
-    @decorators.idempotent_id('73d0c093-9922-44a0-8b1d-1fc092dee367')
-    @test.services('object_storage')
-    def test_acl(self):
-        acl_headers = ('x-container-meta-web-index', 'x-container-read')
-
-        swcont = self.test_resources.get(
-            'SwiftContainer')['physical_resource_id']
-        swcont_website = self.test_resources.get(
-            'SwiftContainerWebsite')['physical_resource_id']
-
-        headers, _ = self.container_client.list_container_metadata(swcont)
-        for h in acl_headers:
-            self.assertNotIn(h, headers)
-        headers, _ = self.container_client.list_container_metadata(
-            swcont_website)
-        for h in acl_headers:
-            self.assertIn(h, headers)
-
-    @decorators.idempotent_id('fda06135-6777-4594-aefa-0f6107169698')
-    @test.services('object_storage')
-    def test_metadata(self):
-        swift_basic_template = self.load_template('swift_basic')
-        metadatas = swift_basic_template['resources']['SwiftContainerWebsite'][
-            'properties']['X-Container-Meta']
-        swcont_website = self.test_resources.get(
-            'SwiftContainerWebsite')['physical_resource_id']
-        headers, _ = self.container_client.list_container_metadata(
-            swcont_website)
-
-        for meta in metadatas:
-            header_meta = "x-container-meta-%s" % meta
-            self.assertIn(header_meta, headers)
-            self.assertEqual(headers[header_meta], metadatas[meta])
diff --git a/tempest/api/orchestration/stacks/test_templates.py b/tempest/api/orchestration/stacks/test_templates.py
deleted file mode 100644
index 21548a0..0000000
--- a/tempest/api/orchestration/stacks/test_templates.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest.lib import decorators
-
-
-class TemplateYAMLTestJSON(base.BaseOrchestrationTest):
-    template = """
-HeatTemplateFormatVersion: '2012-12-12'
-Description: |
-  Template which creates only a new user
-Resources:
-  CfnUser:
-    Type: AWS::IAM::User
-"""
-
-    @classmethod
-    def resource_setup(cls):
-        super(TemplateYAMLTestJSON, cls).resource_setup()
-        cls.stack_name = data_utils.rand_name('heat')
-        cls.stack_identifier = cls.create_stack(cls.stack_name, cls.template)
-        cls.client.wait_for_stack_status(cls.stack_identifier,
-                                         'CREATE_COMPLETE')
-        cls.stack_id = cls.stack_identifier.split('/')[1]
-        cls.parameters = {}
-
-    @decorators.idempotent_id('47430699-c368-495e-a1db-64c26fd967d7')
-    def test_show_template(self):
-        """Getting template used to create the stack."""
-        self.client.show_template(self.stack_identifier)
-
-    @decorators.idempotent_id('ed53debe-8727-46c5-ab58-eba6090ec4de')
-    def test_validate_template(self):
-        """Validating template passing it content."""
-        self.client.validate_template(self.template,
-                                      self.parameters)
-
-
-class TemplateAWSTestJSON(TemplateYAMLTestJSON):
-    template = """
-{
-  "AWSTemplateFormatVersion" : "2010-09-09",
-  "Description" : "Template which creates only a new user",
-  "Resources" : {
-    "CfnUser" : {
-      "Type" : "AWS::IAM::User"
-    }
-  }
-}
-"""
diff --git a/tempest/api/orchestration/stacks/test_templates_negative.py b/tempest/api/orchestration/stacks/test_templates_negative.py
deleted file mode 100644
index a90abe2..0000000
--- a/tempest/api/orchestration/stacks/test_templates_negative.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright 2014 NEC Corporation.  All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.orchestration import base
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-from tempest import test
-
-
-class TemplateYAMLNegativeTestJSON(base.BaseOrchestrationTest):
-    template = """
-HeatTemplateFormatVersion: '2012-12-12'
-Description: |
-  Template which creates only a new user
-Resources:
-  CfnUser:
-    Type: AWS::IAM::User
-"""
-
-    invalid_template_url = 'http:///template.yaml'
-
-    @classmethod
-    def resource_setup(cls):
-        super(TemplateYAMLNegativeTestJSON, cls).resource_setup()
-        cls.parameters = {}
-
-    @test.attr(type=['negative'])
-    @decorators.idempotent_id('5586cbca-ddc4-4152-9db8-fa1ce5fc1876')
-    def test_validate_template_url(self):
-        """Validating template passing url to it."""
-        self.assertRaises(lib_exc.BadRequest,
-                          self.client.validate_template_url,
-                          template_url=self.invalid_template_url,
-                          parameters=self.parameters)
-
-
-class TemplateAWSNegativeTestJSON(TemplateYAMLNegativeTestJSON):
-    template = """
-{
-  "AWSTemplateFormatVersion" : "2010-09-09",
-  "Description" : "Template which creates only a new user",
-  "Resources" : {
-    "CfnUser" : {
-      "Type" : "AWS::IAM::User"
-    }
-  }
-}
-"""
-
-    invalid_template_url = 'http:///template.template'
diff --git a/tempest/api/orchestration/stacks/test_volumes.py b/tempest/api/orchestration/stacks/test_volumes.py
deleted file mode 100644
index d34eb89..0000000
--- a/tempest/api/orchestration/stacks/test_volumes.py
+++ /dev/null
@@ -1,124 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-from tempest import test
-
-
-CONF = config.CONF
-
-
-class CinderResourcesTest(base.BaseOrchestrationTest):
-
-    @classmethod
-    def skip_checks(cls):
-        super(CinderResourcesTest, cls).skip_checks()
-        if not CONF.service_available.cinder:
-            raise cls.skipException('Cinder support is required')
-
-    def _cinder_verify(self, volume_id, template):
-        self.assertIsNotNone(volume_id)
-        volume = self.volumes_client.show_volume(volume_id)['volume']
-        self.assertEqual('available', volume.get('status'))
-        self.assertEqual(CONF.volume.volume_size, volume.get('size'))
-
-        # Some volume properties have been renamed with Cinder v2
-        if CONF.volume_feature_enabled.api_v2:
-            description_field = 'description'
-            name_field = 'name'
-        else:
-            description_field = 'display_description'
-            name_field = 'display_name'
-
-        self.assertEqual(template['resources']['volume']['properties'][
-            'description'], volume.get(description_field))
-        self.assertEqual(template['resources']['volume']['properties'][
-            'name'], volume.get(name_field))
-
-    def _outputs_verify(self, stack_identifier, template):
-        self.assertEqual('available',
-                         self.get_stack_output(stack_identifier, 'status'))
-        self.assertEqual(str(CONF.volume.volume_size),
-                         self.get_stack_output(stack_identifier, 'size'))
-        self.assertEqual(template['resources']['volume']['properties'][
-            'description'], self.get_stack_output(stack_identifier,
-                                                  'display_description'))
-        self.assertEqual(template['resources']['volume']['properties'][
-            'name'], self.get_stack_output(stack_identifier, 'display_name'))
-
-    @decorators.idempotent_id('c3243329-7bdd-4730-b402-4d19d50c41d8')
-    @test.services('volume')
-    def test_cinder_volume_create_delete(self):
-        """Create and delete a volume via OS::Cinder::Volume."""
-        stack_name = data_utils.rand_name('heat')
-        template = self.read_template('cinder_basic')
-        stack_identifier = self.create_stack(
-            stack_name,
-            template,
-            parameters={
-                'volume_size': CONF.volume.volume_size
-            })
-        self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
-
-        # Verify with cinder that the volume exists, with matching details
-        volume_id = self.get_stack_output(stack_identifier, 'volume_id')
-        cinder_basic_template = self.load_template('cinder_basic')
-        self._cinder_verify(volume_id, cinder_basic_template)
-
-        # Verify the stack outputs are as expected
-        self._outputs_verify(stack_identifier, cinder_basic_template)
-
-        # Delete the stack and ensure the volume is gone
-        self.client.delete_stack(stack_identifier)
-        self.client.wait_for_stack_status(stack_identifier, 'DELETE_COMPLETE')
-        self.assertRaises(lib_exc.NotFound,
-                          self.volumes_client.show_volume,
-                          volume_id)
-
-    def _cleanup_volume(self, volume_id):
-        """Cleanup the volume direct with cinder."""
-        self.volumes_client.delete_volume(volume_id)
-        self.volumes_client.wait_for_resource_deletion(volume_id)
-
-    @decorators.idempotent_id('ea8b3a46-b932-4c18-907a-fe23f00b33f8')
-    @test.services('volume')
-    def test_cinder_volume_create_delete_retain(self):
-        """Ensure the 'Retain' deletion policy is respected."""
-        stack_name = data_utils.rand_name('heat')
-        template = self.read_template('cinder_basic_delete_retain')
-        stack_identifier = self.create_stack(
-            stack_name,
-            template,
-            parameters={
-                'volume_size': CONF.volume.volume_size
-            })
-        self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
-
-        # Verify with cinder that the volume exists, with matching details
-        volume_id = self.get_stack_output(stack_identifier, 'volume_id')
-        self.addCleanup(self._cleanup_volume, volume_id)
-        retain_template = self.load_template('cinder_basic_delete_retain')
-        self._cinder_verify(volume_id, retain_template)
-
-        # Verify the stack outputs are as expected
-        self._outputs_verify(stack_identifier, retain_template)
-
-        # Delete the stack and ensure the volume is *not* gone
-        self.client.delete_stack(stack_identifier)
-        self.client.wait_for_stack_status(stack_identifier, 'DELETE_COMPLETE')
-        self._cinder_verify(volume_id, retain_template)
-
-        # Volume cleanup happens via addCleanup calling _cleanup_volume
diff --git a/tempest/api/volume/admin/v2/test_backends_capabilities.py b/tempest/api/volume/admin/test_backends_capabilities.py
similarity index 94%
rename from tempest/api/volume/admin/v2/test_backends_capabilities.py
rename to tempest/api/volume/admin/test_backends_capabilities.py
index 1060f2b..607fc43 100644
--- a/tempest/api/volume/admin/v2/test_backends_capabilities.py
+++ b/tempest/api/volume/admin/test_backends_capabilities.py
@@ -19,7 +19,7 @@
 from tempest.lib import decorators
 
 
-class BackendsCapabilitiesAdminV2TestsJSON(base.BaseVolumeAdminTest):
+class BackendsCapabilitiesAdminTestsJSON(base.BaseVolumeAdminTest):
 
     CAPABILITIES = ('namespace',
                     'vendor_name',
@@ -34,7 +34,7 @@
 
     @classmethod
     def resource_setup(cls):
-        super(BackendsCapabilitiesAdminV2TestsJSON, cls).resource_setup()
+        super(BackendsCapabilitiesAdminTestsJSON, cls).resource_setup()
         # Get host list, formation: host@backend-name
         cls.hosts = [
             pool['name'] for pool in
diff --git a/tempest/api/volume/admin/test_group_types.py b/tempest/api/volume/admin/test_group_types.py
new file mode 100644
index 0000000..0df5fbd
--- /dev/null
+++ b/tempest/api/volume/admin/test_group_types.py
@@ -0,0 +1,54 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES 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.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+
+class GroupTypesTest(base.BaseVolumeAdminTest):
+    _api_version = 3
+    min_microversion = '3.11'
+    max_microversion = 'latest'
+
+    @decorators.idempotent_id('dd71e5f9-393e-4d4f-90e9-fa1b8d278864')
+    def test_group_type_create_list_show(self):
+        # Create/list/show group type.
+        name = data_utils.rand_name(self.__class__.__name__ + '-group-type')
+        description = data_utils.rand_name("group-type-description")
+        group_specs = {"consistent_group_snapshot_enabled": "<is> False"}
+        params = {'name': name,
+                  'description': description,
+                  'group_specs': group_specs,
+                  'is_public': True}
+        body = self.create_group_type(**params)
+        self.assertIn('name', body)
+        err_msg = ("The created group_type %(var)s is not equal to the "
+                   "requested %(var)s")
+        self.assertEqual(name, body['name'], err_msg % {"var": "name"})
+        self.assertEqual(description, body['description'],
+                         err_msg % {"var": "description"})
+
+        group_list = (
+            self.admin_group_types_client.list_group_types()['group_types'])
+        self.assertIsInstance(group_list, list)
+        self.assertNotEmpty(group_list)
+
+        fetched_group_type = self.admin_group_types_client.show_group_type(
+            body['id'])['group_type']
+        for key in params.keys():
+            self.assertEqual(params[key], fetched_group_type[key],
+                             '%s of the fetched group_type is different '
+                             'from the created group_type' % key)
diff --git a/tempest/api/volume/admin/test_groups.py b/tempest/api/volume/admin/test_groups.py
new file mode 100644
index 0000000..baea37b
--- /dev/null
+++ b/tempest/api/volume/admin/test_groups.py
@@ -0,0 +1,322 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES 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 import waiters
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class GroupsTest(base.BaseVolumeAdminTest):
+    _api_version = 3
+    min_microversion = '3.14'
+    max_microversion = 'latest'
+
+    def _delete_group(self, grp_id, delete_volumes=True):
+        self.groups_client.delete_group(grp_id, delete_volumes)
+        vols = self.volumes_client.list_volumes(detail=True)['volumes']
+        for vol in vols:
+            if vol['group_id'] == grp_id:
+                self.volumes_client.wait_for_resource_deletion(vol['id'])
+        self.groups_client.wait_for_resource_deletion(grp_id)
+
+    def _delete_group_snapshot(self, group_snapshot_id, grp_id):
+        self.group_snapshots_client.delete_group_snapshot(
+            group_snapshot_id)
+        vols = self.volumes_client.list_volumes(detail=True)['volumes']
+        snapshots = self.snapshots_client.list_snapshots(
+            detail=True)['snapshots']
+        for vol in vols:
+            for snap in snapshots:
+                if (vol['group_id'] == grp_id and
+                        vol['id'] == snap['volume_id']):
+                    self.snapshots_client.wait_for_resource_deletion(
+                        snap['id'])
+        self.group_snapshots_client.wait_for_resource_deletion(
+            group_snapshot_id)
+
+    def _create_group(self, group_type, volume_type, grp_name=None):
+        if not grp_name:
+            grp_name = data_utils.rand_name('Group')
+        grp = self.groups_client.create_group(
+            group_type=group_type['id'],
+            volume_types=[volume_type['id']],
+            name=grp_name)['group']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self._delete_group, grp['id'])
+        waiters.wait_for_volume_resource_status(
+            self.groups_client, grp['id'], 'available')
+        self.assertEqual(grp_name, grp['name'])
+        return grp
+
+    @decorators.idempotent_id('4b111d28-b73d-4908-9bd2-03dc2992e4d4')
+    def test_group_create_show_list_delete(self):
+        # Create volume type
+        volume_type = self.create_volume_type()
+
+        # Create group type
+        group_type = self.create_group_type()
+
+        # Create group
+        grp1_name = data_utils.rand_name('Group1')
+        grp1 = self._create_group(group_type, volume_type,
+                                  grp_name=grp1_name)
+        grp1_id = grp1['id']
+
+        grp2_name = data_utils.rand_name('Group2')
+        grp2 = self._create_group(group_type, volume_type,
+                                  grp_name=grp2_name)
+        grp2_id = grp2['id']
+
+        # Create volume
+        vol1_name = data_utils.rand_name("volume")
+        params = {'name': vol1_name,
+                  'volume_type': volume_type['id'],
+                  'group_id': grp1['id'],
+                  'size': CONF.volume.volume_size}
+        vol1 = self.volumes_client.create_volume(**params)['volume']
+        self.assertEqual(grp1['id'], vol1['group_id'])
+        waiters.wait_for_volume_resource_status(
+            self.volumes_client, vol1['id'], 'available')
+        vol1_id = vol1['id']
+
+        # Get a given group
+        grp1 = self.groups_client.show_group(grp1['id'])['group']
+        self.assertEqual(grp1_name, grp1['name'])
+        self.assertEqual(grp1_id, grp1['id'])
+
+        grp2 = self.groups_client.show_group(grp2['id'])['group']
+        self.assertEqual(grp2_name, grp2['name'])
+        self.assertEqual(grp2_id, grp2['id'])
+
+        # Get all groups with detail
+        grps = self.groups_client.list_groups(
+            detail=True)['groups']
+        filtered_grps = [g for g in grps if g['id'] in [grp1_id, grp2_id]]
+        self.assertEqual(2, len(filtered_grps))
+        for grp in filtered_grps:
+            self.assertEqual([volume_type['id']], grp['volume_types'])
+            self.assertEqual(group_type['id'], grp['group_type'])
+
+        vols = self.volumes_client.list_volumes(
+            detail=True)['volumes']
+        filtered_vols = [v for v in vols if v['id'] in [vol1_id]]
+        self.assertEqual(1, len(filtered_vols))
+        for vol in filtered_vols:
+            self.assertEqual(grp1_id, vol['group_id'])
+
+        # Delete group
+        # grp1 has a volume so delete_volumes flag is set to True by default
+        self._delete_group(grp1_id)
+        # grp2 is empty so delete_volumes flag can be set to False
+        self._delete_group(grp2_id, delete_volumes=False)
+        grps = self.groups_client.list_groups(
+            detail=True)['groups']
+        self.assertEmpty(grps)
+
+    @decorators.idempotent_id('1298e537-f1f0-47a3-a1dd-8adec8168897')
+    def test_group_snapshot_create_show_list_delete(self):
+        # Create volume type
+        volume_type = self.create_volume_type()
+
+        # Create group type
+        group_type = self.create_group_type()
+
+        # Create group
+        grp = self._create_group(group_type, volume_type)
+
+        # Create volume
+        vol = self.create_volume(volume_type=volume_type['id'],
+                                 group_id=grp['id'])
+
+        # Create group snapshot
+        group_snapshot_name = data_utils.rand_name('group_snapshot')
+        group_snapshot = (
+            self.group_snapshots_client.create_group_snapshot(
+                group_id=grp['id'],
+                name=group_snapshot_name)['group_snapshot'])
+        snapshots = self.snapshots_client.list_snapshots(
+            detail=True)['snapshots']
+        for snap in snapshots:
+            if vol['id'] == snap['volume_id']:
+                waiters.wait_for_volume_resource_status(
+                    self.snapshots_client, snap['id'], 'available')
+        waiters.wait_for_volume_resource_status(
+            self.group_snapshots_client,
+            group_snapshot['id'], 'available')
+        self.assertEqual(group_snapshot_name, group_snapshot['name'])
+
+        # Get a given group snapshot
+        group_snapshot = self.group_snapshots_client.show_group_snapshot(
+            group_snapshot['id'])['group_snapshot']
+        self.assertEqual(group_snapshot_name, group_snapshot['name'])
+
+        # Get all group snapshots with detail
+        group_snapshots = (
+            self.group_snapshots_client.list_group_snapshots(
+                detail=True)['group_snapshots'])
+        self.assertIn((group_snapshot['name'], group_snapshot['id']),
+                      [(m['name'], m['id']) for m in group_snapshots])
+
+        # Delete group snapshot
+        self._delete_group_snapshot(group_snapshot['id'], grp['id'])
+        group_snapshots = (
+            self.group_snapshots_client.list_group_snapshots(
+                detail=True)['group_snapshots'])
+        self.assertEmpty(group_snapshots)
+
+    @decorators.idempotent_id('eff52c70-efc7-45ed-b47a-4ad675d09b81')
+    def test_create_group_from_group_snapshot(self):
+        # Create volume type
+        volume_type = self.create_volume_type()
+
+        # Create group type
+        group_type = self.create_group_type()
+
+        # Create Group
+        grp = self._create_group(group_type, volume_type)
+
+        # Create volume
+        vol = self.create_volume(volume_type=volume_type['id'],
+                                 group_id=grp['id'])
+
+        # Create group_snapshot
+        group_snapshot_name = data_utils.rand_name('group_snapshot')
+        group_snapshot = (
+            self.group_snapshots_client.create_group_snapshot(
+                group_id=grp['id'],
+                name=group_snapshot_name)['group_snapshot'])
+        self.addCleanup(self._delete_group_snapshot,
+                        group_snapshot['id'], grp['id'])
+        self.assertEqual(group_snapshot_name, group_snapshot['name'])
+        snapshots = self.snapshots_client.list_snapshots(
+            detail=True)['snapshots']
+        for snap in snapshots:
+            if vol['id'] == snap['volume_id']:
+                waiters.wait_for_volume_resource_status(
+                    self.snapshots_client, snap['id'], 'available')
+        waiters.wait_for_volume_resource_status(
+            self.group_snapshots_client,
+            group_snapshot['id'], 'available')
+
+        # Create Group from Group snapshot
+        grp_name2 = data_utils.rand_name('Group_from_snap')
+        grp2 = self.groups_client.create_group_from_source(
+            group_snapshot_id=group_snapshot['id'],
+            name=grp_name2)['group']
+        self.addCleanup(self._delete_group, grp2['id'])
+        self.assertEqual(grp_name2, grp2['name'])
+        vols = self.volumes_client.list_volumes(detail=True)['volumes']
+        for vol in vols:
+            if vol['group_id'] == grp2['id']:
+                waiters.wait_for_volume_resource_status(
+                    self.volumes_client, vol['id'], 'available')
+        waiters.wait_for_volume_resource_status(
+            self.groups_client, grp2['id'], 'available')
+
+    @decorators.idempotent_id('2424af8c-7851-4888-986a-794b10c3210e')
+    def test_create_group_from_group(self):
+        # Create volume type
+        volume_type = self.create_volume_type()
+
+        # Create group type
+        group_type = self.create_group_type()
+
+        # Create Group
+        grp = self._create_group(group_type, volume_type)
+
+        # Create volume
+        self.create_volume(volume_type=volume_type['id'], group_id=grp['id'])
+
+        # Create Group from Group
+        grp_name2 = data_utils.rand_name('Group_from_grp')
+        grp2 = self.groups_client.create_group_from_source(
+            source_group_id=grp['id'], name=grp_name2)['group']
+        self.addCleanup(self._delete_group, grp2['id'])
+        self.assertEqual(grp_name2, grp2['name'])
+        vols = self.volumes_client.list_volumes(
+            detail=True)['volumes']
+        for vol in vols:
+            if vol['group_id'] == grp2['id']:
+                waiters.wait_for_volume_resource_status(
+                    self.volumes_client, vol['id'], 'available')
+        waiters.wait_for_volume_resource_status(
+            self.groups_client, grp2['id'], 'available')
+
+    @decorators.idempotent_id('4a8a6fd2-8b3b-4641-8f54-6a6f99320006')
+    def test_group_update(self):
+        # Create volume type
+        volume_type = self.create_volume_type()
+
+        # Create group type
+        group_type = self.create_group_type()
+
+        # Create Group
+        grp = self._create_group(group_type, volume_type)
+
+        # Create volumes
+        grp_vols = []
+        for _ in range(2):
+            vol = self.create_volume(volume_type=volume_type['id'],
+                                     group_id=grp['id'])
+            grp_vols.append(vol)
+        vol2 = grp_vols[1]
+
+        # Remove a volume from group and update name and description
+        new_grp_name = 'new_group'
+        new_desc = 'This is a new group'
+        grp_params = {'name': new_grp_name,
+                      'description': new_desc,
+                      'remove_volumes': vol2['id']}
+        self.groups_client.update_group(grp['id'], **grp_params)
+
+        # Wait for group status to become available
+        waiters.wait_for_volume_resource_status(
+            self.groups_client, grp['id'], 'available')
+
+        # Get the updated Group
+        grp = self.groups_client.show_group(grp['id'])['group']
+        self.assertEqual(new_grp_name, grp['name'])
+        self.assertEqual(new_desc, grp['description'])
+
+        # Get volumes in the group
+        vols = self.volumes_client.list_volumes(
+            detail=True)['volumes']
+        grp_vols = []
+        for vol in vols:
+            if vol['group_id'] == grp['id']:
+                grp_vols.append(vol)
+        self.assertEqual(1, len(grp_vols))
+
+        # Add a volume to the group
+        grp_params = {'add_volumes': vol2['id']}
+        self.groups_client.update_group(grp['id'], **grp_params)
+
+        # Wait for group status to become available
+        waiters.wait_for_volume_resource_status(
+            self.groups_client, grp['id'], 'available')
+
+        # Get volumes in the group
+        vols = self.volumes_client.list_volumes(
+            detail=True)['volumes']
+        grp_vols = []
+        for vol in vols:
+            if vol['group_id'] == grp['id']:
+                grp_vols.append(vol)
+        self.assertEqual(2, len(grp_vols))
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 72d71c7..c0891e4 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -12,41 +12,39 @@
 
 import six
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
 
 
-class VolumeMultiBackendV2Test(base.BaseVolumeAdminTest):
+class VolumeMultiBackendTest(base.BaseVolumeAdminTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumeMultiBackendV2Test, cls).skip_checks()
+        super(VolumeMultiBackendTest, cls).skip_checks()
 
         if not CONF.volume_feature_enabled.multi_backend:
             raise cls.skipException("Cinder multi-backend feature disabled")
 
     @classmethod
     def resource_setup(cls):
-        super(VolumeMultiBackendV2Test, cls).resource_setup()
+        super(VolumeMultiBackendTest, cls).resource_setup()
 
         # read backend name from a list .
-        cls.backend_names = set(CONF.volume.backend_names)
+        backend_names = set(CONF.volume.backend_names)
 
-        cls.name_field = cls.special_fields['name_field']
-        cls.volume_type_id_list = []
         cls.volume_id_list_with_prefix = []
         cls.volume_id_list_without_prefix = []
 
         # Volume/Type creation (uses volume_backend_name)
         # It is not allowed to create the same backend name twice
-        if len(cls.backend_names) < 2:
+        if len(backend_names) < 2:
             raise cls.skipException("Requires at least two different "
                                     "backend names")
-        for backend_name in cls.backend_names:
+        for backend_name in backend_names:
             # Volume/Type creation (uses backend_name)
             cls._create_type_and_volume(backend_name, False)
             # Volume/Type creation (uses capabilities:volume_backend_name)
@@ -63,35 +61,19 @@
             extra_specs = {spec_key_with_prefix: backend_name_key}
         else:
             extra_specs = {spec_key_without_prefix: backend_name_key}
-        cls.type = cls.create_volume_type(name=type_name,
-                                          extra_specs=extra_specs)
+        cls.create_volume_type(name=type_name,
+                               extra_specs=extra_specs)
 
-        params = {cls.name_field: vol_name, 'volume_type': type_name,
+        params = {'name': vol_name, 'volume_type': type_name,
                   'size': CONF.volume.volume_size}
-        cls.volume = cls.admin_volume_client.create_volume(
-            **params)['volume']
+        cls.volume = cls.create_volume(**params)
         if with_prefix:
             cls.volume_id_list_with_prefix.append(cls.volume['id'])
         else:
             cls.volume_id_list_without_prefix.append(
                 cls.volume['id'])
-        waiters.wait_for_volume_status(cls.admin_volume_client,
-                                       cls.volume['id'], 'available')
-
-    @classmethod
-    def resource_cleanup(cls):
-        # volumes deletion
-        vid_prefix = getattr(cls, 'volume_id_list_with_prefix', [])
-        for volume_id in vid_prefix:
-            cls.admin_volume_client.delete_volume(volume_id)
-            cls.admin_volume_client.wait_for_resource_deletion(volume_id)
-
-        vid_no_pre = getattr(cls, 'volume_id_list_without_prefix', [])
-        for volume_id in vid_no_pre:
-            cls.admin_volume_client.delete_volume(volume_id)
-            cls.admin_volume_client.wait_for_resource_deletion(volume_id)
-
-        super(VolumeMultiBackendV2Test, cls).resource_cleanup()
+        waiters.wait_for_volume_resource_status(cls.admin_volume_client,
+                                                cls.volume['id'], 'available')
 
     @decorators.idempotent_id('c1a41f3f-9dad-493e-9f09-3ff197d477cc')
     def test_backend_name_reporting(self):
@@ -141,7 +123,3 @@
         msg = ("volumes %s were created in the same backend" % ", "
                .join(volume_hosts))
         six.assertCountEqual(self, volume_hosts, set(volume_hosts), msg)
-
-
-class VolumeMultiBackendV1Test(VolumeMultiBackendV2Test):
-    _api_version = 1
diff --git a/tempest/api/volume/admin/test_qos.py b/tempest/api/volume/admin/test_qos.py
index e1cfb30..e31c0ef 100644
--- a/tempest/api/volume/admin/test_qos.py
+++ b/tempest/api/volume/admin/test_qos.py
@@ -13,21 +13,21 @@
 #    under the License.
 
 from tempest.api.volume import base
-from tempest.common.utils import data_utils as utils
 from tempest.common import waiters
+from tempest.lib.common.utils import data_utils as utils
 from tempest.lib import decorators
 
 
-class QosSpecsV2TestJSON(base.BaseVolumeAdminTest):
+class QosSpecsTestJSON(base.BaseVolumeAdminTest):
     """Test the Cinder QoS-specs.
 
     Tests for  create, list, delete, show, associate,
-    disassociate, set/unset key V2 APIs.
+    disassociate, set/unset key APIs.
     """
 
     @classmethod
     def resource_setup(cls):
-        super(QosSpecsV2TestJSON, cls).resource_setup()
+        super(QosSpecsTestJSON, cls).resource_setup()
         # Create admin qos client
         # Create a test shared qos-specs for tests
         cls.qos_name = utils.rand_name(cls.__name__ + '-QoS')
@@ -157,7 +157,3 @@
         operation = 'disassociate-all'
         waiters.wait_for_qos_operations(self.admin_volume_qos_client,
                                         self.created_qos['id'], operation)
-
-
-class QosSpecsV1TestJSON(QosSpecsV2TestJSON):
-    _api_version = 1
diff --git a/tempest/api/volume/admin/test_snapshot_manage.py b/tempest/api/volume/admin/test_snapshot_manage.py
new file mode 100644
index 0000000..9ff7160
--- /dev/null
+++ b/tempest/api/volume/admin/test_snapshot_manage.py
@@ -0,0 +1,98 @@
+# 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 import waiters
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions
+
+CONF = config.CONF
+
+
+class SnapshotManageAdminTest(base.BaseVolumeAdminTest):
+    """Unmanage & manage snapshots
+
+     This feature provides the ability to import/export volume snapshot
+     from one Cinder to another and to import snapshots that have not been
+     managed by Cinder from a storage back end to Cinder
+    """
+
+    @classmethod
+    def skip_checks(cls):
+        super(SnapshotManageAdminTest, cls).skip_checks()
+
+        if not CONF.volume_feature_enabled.manage_snapshot:
+            raise cls.skipException("Manage snapshot tests are disabled")
+
+        if len(CONF.volume.manage_snapshot_ref) != 2:
+            msg = ("Manage snapshot ref is not correctly configured, "
+                   "it should be a list of two elements")
+            raise exceptions.InvalidConfiguration(msg)
+
+    @decorators.idempotent_id('0132f42d-0147-4b45-8501-cc504bbf7810')
+    def test_unmanage_manage_snapshot(self):
+        # Create a volume
+        volume = self.create_volume()
+
+        # Create a snapshot
+        snapshot = self.create_snapshot(volume_id=volume['id'])
+
+        # Unmanage the snapshot
+        # Unmanage snapshot function works almost the same as delete snapshot,
+        # but it does not delete the snapshot data
+        self.admin_snapshots_client.unmanage_snapshot(snapshot['id'])
+        self.admin_snapshots_client.wait_for_resource_deletion(snapshot['id'])
+
+        # Verify the original snapshot does not exist in snapshot list
+        params = {'all_tenants': 1}
+        all_snapshots = self.admin_snapshots_client.list_snapshots(
+            detail=True, params=params)['snapshots']
+        self.assertNotIn(snapshot['id'], [v['id'] for v in all_snapshots])
+
+        # Manage the snapshot
+        name = data_utils.rand_name(self.__class__.__name__ +
+                                    '-Managed-Snapshot')
+        description = data_utils.rand_name(self.__class__.__name__ +
+                                           '-Managed-Snapshot-Description')
+        metadata = {"manage-snap-meta1": "value1",
+                    "manage-snap-meta2": "value2",
+                    "manage-snap-meta3": "value3"}
+        snapshot_ref = {
+            'volume_id': volume['id'],
+            'ref': {CONF.volume.manage_snapshot_ref[0]:
+                    CONF.volume.manage_snapshot_ref[1] % snapshot['id']},
+            'name': name,
+            'description': description,
+            'metadata': metadata
+        }
+        new_snapshot = self.admin_snapshot_manage_client.manage_snapshot(
+            **snapshot_ref)['snapshot']
+        self.addCleanup(self.delete_snapshot, new_snapshot['id'],
+                        self.admin_snapshots_client)
+
+        # Wait for the snapshot to be available after manage operation
+        waiters.wait_for_volume_resource_status(self.admin_snapshots_client,
+                                                new_snapshot['id'],
+                                                'available')
+
+        # Verify the managed snapshot has the expected parent volume
+        # and the expected field values.
+        new_snapshot_info = self.admin_snapshots_client.show_snapshot(
+            new_snapshot['id'])['snapshot']
+        self.assertEqual(snapshot['size'], new_snapshot_info['size'])
+        for key in ['volume_id', 'name', 'description', 'metadata']:
+            self.assertEqual(snapshot_ref[key], new_snapshot_info[key])
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index e1c9492..41849bc 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -14,22 +14,23 @@
 #    under the License.
 
 from tempest.api.volume import base
+from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
 
 CONF = config.CONF
 
 
-class SnapshotsActionsV2Test(base.BaseVolumeAdminTest):
+class SnapshotsActionsTest(base.BaseVolumeAdminTest):
     @classmethod
     def skip_checks(cls):
-        super(SnapshotsActionsV2Test, cls).skip_checks()
+        super(SnapshotsActionsTest, cls).skip_checks()
         if not CONF.volume_feature_enabled.snapshot:
             raise cls.skipException("Cinder snapshot feature disabled")
 
     @classmethod
     def resource_setup(cls):
-        super(SnapshotsActionsV2Test, cls).resource_setup()
+        super(SnapshotsActionsTest, cls).resource_setup()
 
         # Create a test shared volume for tests
         cls.volume = cls.create_volume()
@@ -43,17 +44,20 @@
         snapshot_id = self.snapshot['id']
         self.admin_snapshots_client.reset_snapshot_status(snapshot_id,
                                                           status)
-        super(SnapshotsActionsV2Test, self).tearDown()
+        waiters.wait_for_volume_resource_status(self.snapshots_client,
+                                                snapshot_id, status)
+        super(SnapshotsActionsTest, self).tearDown()
 
     def _create_reset_and_force_delete_temp_snapshot(self, status=None):
         # Create snapshot, reset snapshot status,
         # and force delete temp snapshot
         temp_snapshot = self.create_snapshot(volume_id=self.volume['id'])
         if status:
-            self.admin_snapshots_client.\
-                reset_snapshot_status(temp_snapshot['id'], status)
-        self.admin_snapshots_client.\
-            force_delete_snapshot(temp_snapshot['id'])
+            self.admin_snapshots_client.reset_snapshot_status(
+                temp_snapshot['id'], status)
+            waiters.wait_for_volume_resource_status(
+                self.snapshots_client, temp_snapshot['id'], status)
+        self.admin_snapshots_client.force_delete_snapshot(temp_snapshot['id'])
         self.snapshots_client.wait_for_resource_deletion(temp_snapshot['id'])
 
     def _get_progress_alias(self):
@@ -63,18 +67,19 @@
     def test_reset_snapshot_status(self):
         # Reset snapshot status to creating
         status = 'creating'
-        self.admin_snapshots_client.\
-            reset_snapshot_status(self.snapshot['id'], status)
-        snapshot_get = self.admin_snapshots_client.show_snapshot(
-            self.snapshot['id'])['snapshot']
-        self.assertEqual(status, snapshot_get['status'])
+        self.admin_snapshots_client.reset_snapshot_status(
+            self.snapshot['id'], status)
+        waiters.wait_for_volume_resource_status(self.snapshots_client,
+                                                self.snapshot['id'], status)
 
     @decorators.idempotent_id('41288afd-d463-485e-8f6e-4eea159413eb')
     def test_update_snapshot_status(self):
         # Reset snapshot status to creating
         status = 'creating'
-        self.admin_snapshots_client.\
-            reset_snapshot_status(self.snapshot['id'], status)
+        self.admin_snapshots_client.reset_snapshot_status(
+            self.snapshot['id'], status)
+        waiters.wait_for_volume_resource_status(self.snapshots_client,
+                                                self.snapshot['id'], status)
 
         # Update snapshot status to error
         progress = '80%'
@@ -107,7 +112,3 @@
     def test_snapshot_force_delete_when_snapshot_is_error_deleting(self):
         # test force delete when status of snapshot is error_deleting
         self._create_reset_and_force_delete_temp_snapshot('error_deleting')
-
-
-class SnapshotsActionsV1Test(SnapshotsActionsV2Test):
-    _api_version = 1
diff --git a/tempest/api/volume/admin/v3/test_user_messages.py b/tempest/api/volume/admin/test_user_messages.py
similarity index 95%
rename from tempest/api/volume/admin/v3/test_user_messages.py
rename to tempest/api/volume/admin/test_user_messages.py
index ed85d4d..20c3538 100755
--- a/tempest/api/volume/admin/v3/test_user_messages.py
+++ b/tempest/api/volume/admin/test_user_messages.py
@@ -13,9 +13,9 @@
 #    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.api.volume import base
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -33,7 +33,8 @@
     'links']
 
 
-class UserMessagesTest(base.VolumesV3AdminTest):
+class UserMessagesTest(base.BaseVolumeAdminTest):
+    _api_version = 3
     min_microversion = '3.3'
     max_microversion = 'latest'
 
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index 73c927a..ce0cbd2 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -17,14 +17,48 @@
 from tempest.lib import decorators
 
 
-class VolumeHostsAdminV2TestsJSON(base.BaseVolumeAdminTest):
+class VolumeHostsAdminTestsJSON(base.BaseVolumeAdminTest):
 
     @decorators.idempotent_id('d5f3efa2-6684-4190-9ced-1c2f526352ad')
     def test_list_hosts(self):
         hosts = self.admin_hosts_client.list_hosts()['hosts']
-        self.assertGreaterEqual(len(hosts), 2, "No. of hosts are < 2,"
-                                "response of list hosts is: % s" % hosts)
+        self.assertGreaterEqual(len(hosts), 2,
+                                "The count of volume hosts is < 2, "
+                                "response of list hosts is: %s" % hosts)
 
+        # Check elements in volume hosts list
+        host_list_keys = ['service', 'host_name', 'last-update',
+                          'zone', 'service-status', 'service-state']
+        for host in hosts:
+            for key in host_list_keys:
+                self.assertIn(key, host)
 
-class VolumeHostsAdminV1TestsJSON(VolumeHostsAdminV2TestsJSON):
-    _api_version = 1
+    @decorators.idempotent_id('21168d57-b373-4b71-a3ac-f2c88f0c5d31')
+    def test_show_host(self):
+        hosts = self.admin_hosts_client.list_hosts()['hosts']
+        self.assertGreaterEqual(len(hosts), 2,
+                                "The count of volume hosts is < 2, "
+                                "response of list hosts is: %s" % hosts)
+
+        # Note(jeremyZ): The show host API is to show volume usage info on the
+        # specified cinder-volume host. If the host does not run cinder-volume
+        # service, or the cinder-volume service is disabled on the host, the
+        # show host API should fail (return code: 404). The cinder-volume host
+        # is presented in format: <host-name>@driver-name.
+        c_vol_hosts = [host['host_name'] for host in hosts
+                       if (host['service'] == 'cinder-volume'
+                           and host['service-state'] == 'enabled')]
+        self.assertNotEmpty(c_vol_hosts,
+                            "No available cinder-volume host is found, "
+                            "all hosts that found are: %s" % hosts)
+
+        # Check each cinder-volume host.
+        host_detail_keys = ['project', 'volume_count', 'snapshot_count',
+                            'host', 'total_volume_gb', 'total_snapshot_gb']
+        for host in c_vol_hosts:
+            host_details = self.admin_hosts_client.show_host(host)['host']
+            self.assertNotEmpty(host_details)
+            for detail in host_details:
+                self.assertIn('resource', detail)
+                for key in host_detail_keys:
+                    self.assertIn(key, detail['resource'])
diff --git a/tempest/api/volume/admin/test_volume_manage.py b/tempest/api/volume/admin/test_volume_manage.py
new file mode 100644
index 0000000..4b352e0
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_manage.py
@@ -0,0 +1,83 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES 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 import waiters
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions
+
+CONF = config.CONF
+
+
+class VolumeManageAdminTest(base.BaseVolumeAdminTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumeManageAdminTest, cls).skip_checks()
+
+        if not CONF.volume_feature_enabled.manage_volume:
+            raise cls.skipException("Manage volume tests are disabled")
+
+        if len(CONF.volume.manage_volume_ref) != 2:
+            msg = ("Manage volume ref is not correctly configured, "
+                   "it should be a list of two elements")
+            raise exceptions.InvalidConfiguration(msg)
+
+    @decorators.idempotent_id('70076c71-0ce1-4208-a8ff-36a66e65cc1e')
+    def test_unmanage_manage_volume(self):
+        # Create original volume
+        org_vol_id = self.create_volume()['id']
+        org_vol_info = self.admin_volume_client.show_volume(
+            org_vol_id)['volume']
+
+        # Unmanage the original volume
+        self.admin_volume_client.unmanage_volume(org_vol_id)
+        self.admin_volume_client.wait_for_resource_deletion(org_vol_id)
+
+        # Verify the original volume does not exist in volume list
+        params = {'all_tenants': 1}
+        all_tenants_volumes = self.admin_volume_client.list_volumes(
+            detail=True, params=params)['volumes']
+        self.assertNotIn(org_vol_id, [v['id'] for v in all_tenants_volumes])
+
+        # Manage volume
+        new_vol_name = data_utils.rand_name(
+            self.__class__.__name__ + '-volume')
+        new_vol_ref = {
+            'name': new_vol_name,
+            'host': org_vol_info['os-vol-host-attr:host'],
+            'ref': {CONF.volume.manage_volume_ref[0]:
+                    CONF.volume.manage_volume_ref[1] % org_vol_id},
+            'volume_type': org_vol_info['volume_type'],
+            'availability_zone': org_vol_info['availability_zone']}
+        new_vol_id = self.admin_volume_manage_client.manage_volume(
+            **new_vol_ref)['volume']['id']
+        self.addCleanup(self.delete_volume,
+                        self.admin_volume_client, new_vol_id)
+        waiters.wait_for_volume_resource_status(self.admin_volume_client,
+                                                new_vol_id, 'available')
+
+        # Compare the managed volume with the original
+        new_vol_info = self.admin_volume_client.show_volume(
+            new_vol_id)['volume']
+        self.assertNotIn(new_vol_id, [org_vol_id])
+        self.assertEqual(new_vol_info['name'], new_vol_name)
+        for key in ['size',
+                    'volume_type',
+                    'availability_zone',
+                    'os-vol-host-attr:host']:
+            self.assertEqual(new_vol_info[key], org_vol_info[key])
diff --git a/tempest/api/volume/admin/test_volume_pools.py b/tempest/api/volume/admin/test_volume_pools.py
new file mode 100644
index 0000000..d389c26
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_pools.py
@@ -0,0 +1,39 @@
+# 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 decorators
+
+CONF = config.CONF
+
+
+class VolumePoolsAdminTestsJSON(base.BaseVolumeAdminTest):
+    def _assert_pools(self, with_detail=False):
+        cinder_pools = self.admin_scheduler_stats_client.list_pools(
+            detail=with_detail)['pools']
+        self.assertIn('name', cinder_pools[0])
+        if with_detail:
+            self.assertIn(CONF.volume.vendor_name,
+                          [pool['capabilities']['vendor_name']
+                           for pool in cinder_pools])
+
+    @decorators.idempotent_id('0248a46c-e226-4933-be10-ad6fca8227e7')
+    def test_get_pools_without_details(self):
+        self._assert_pools()
+
+    @decorators.idempotent_id('d4bb61f7-762d-4437-b8a4-5785759a0ced')
+    def test_get_pools_with_details(self):
+        self._assert_pools(with_detail=True)
diff --git a/tempest/api/volume/admin/test_volume_quota_classes.py b/tempest/api/volume/admin/test_volume_quota_classes.py
new file mode 100644
index 0000000..f551575
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_quota_classes.py
@@ -0,0 +1,101 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    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 random
+
+from oslo_log import log as logging
+from testtools import matchers
+
+from tempest.api.volume import base
+from tempest.common import tempest_fixtures as fixtures
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+LOG = logging.getLogger(__name__)
+QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes', 'backups',
+              'backup_gigabytes', 'per_volume_gigabytes']
+
+
+class VolumeQuotaClassesTest(base.BaseVolumeAdminTest):
+
+    def setUp(self):
+        # Note(jeremy.zhang): All test cases in this class need to externally
+        # lock on doing anything with default quota values.
+        self.useFixture(fixtures.LockFixture('volume_quotas'))
+        super(VolumeQuotaClassesTest, self).setUp()
+
+    def _restore_default_quotas(self, original_defaults):
+        LOG.debug("Restoring volume quota class defaults")
+        self.admin_quota_classes_client.update_quota_class_set(
+            'default', **original_defaults)
+
+    @decorators.idempotent_id('abb9198e-67d0-4b09-859f-4f4a1418f176')
+    def test_show_default_quota(self):
+        default_quotas = self.admin_quota_classes_client.show_quota_class_set(
+            'default')['quota_class_set']
+        self.assertIn('id', default_quotas)
+        self.assertEqual('default', default_quotas.pop('id'))
+        for key in QUOTA_KEYS:
+            self.assertIn(key, default_quotas)
+
+    @decorators.idempotent_id('a7644c63-2669-467a-b00e-452dd5c5397b')
+    def test_update_default_quota(self):
+        LOG.debug("Get the current default quota class values")
+        body = self.admin_quota_classes_client.show_quota_class_set(
+            'default')['quota_class_set']
+
+        # Note(jeremyZ) Only include specified quota keys to avoid the conflict
+        # that other tests may create/delete volume types or update volume
+        # type's default quotas in concurrency running.
+        update_kwargs = {key: body[key] for key in body if key in QUOTA_KEYS}
+
+        # Restore the defaults when the test is done.
+        self.addCleanup(self._restore_default_quotas, update_kwargs.copy())
+
+        # Note(jeremyZ) Increment some of the values for updating the default
+        # quota class. For safety, only items with value >= 0 will be updated,
+        # and items with value < 0 (-1 means unlimited) will be ignored.
+        for quota, default in update_kwargs.items():
+            if default >= 0:
+                update_kwargs[quota] = default + 1
+
+        # Create a volume type for updating default quotas class.
+        volume_type_name = self.create_volume_type()['name']
+        for key in ['volumes', 'snapshots', 'gigabytes']:
+            update_kwargs['%s_%s' % (key, volume_type_name)] = \
+                random.randint(1, 10)
+
+        LOG.debug("Update limits for the default quota class set")
+        update_body = self.admin_quota_classes_client.update_quota_class_set(
+            'default', **update_kwargs)['quota_class_set']
+        self.assertThat(update_body.items(),
+                        matchers.ContainsAll(update_kwargs.items()))
+
+        # Verify current project's default quotas.
+        default_quotas = self.admin_quotas_client.show_default_quota_set(
+            self.os_admin.credentials.tenant_id)['quota_set']
+        self.assertThat(default_quotas.items(),
+                        matchers.ContainsAll(update_kwargs.items()))
+
+        # Verify a new project's default quotas.
+        project_name = data_utils.rand_name('quota_class_tenant')
+        description = data_utils.rand_name('desc_')
+        project_id = self.identity_utils.create_project(
+            name=project_name, description=description)['id']
+        self.addCleanup(self.identity_utils.delete_project, project_id)
+        default_quotas = self.admin_quotas_client.show_default_quota_set(
+            project_id)['quota_set']
+        self.assertThat(default_quotas.items(),
+                        matchers.ContainsAll(update_kwargs.items()))
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 5a83ae3..754104e 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -13,24 +13,36 @@
 #    under the License.
 
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
+from tempest.common import tempest_fixtures as fixtures
 from tempest.common import waiters
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes', 'backups']
 QUOTA_USAGE_KEYS = ['reserved', 'limit', 'in_use']
 
 
-class BaseVolumeQuotasAdminV2TestJSON(base.BaseVolumeAdminTest):
+class BaseVolumeQuotasAdminTestJSON(base.BaseVolumeAdminTest):
     force_tenant_isolation = True
 
     credentials = ['primary', 'alt', 'admin']
 
+    def setUp(self):
+        # NOTE(jeremy.zhang): Avoid conflicts with volume quota class tests.
+        self.useFixture(fixtures.LockFixture('volume_quotas'))
+        super(BaseVolumeQuotasAdminTestJSON, self).setUp()
+
     @classmethod
     def setup_credentials(cls):
-        super(BaseVolumeQuotasAdminV2TestJSON, cls).setup_credentials()
-        cls.demo_tenant_id = cls.os.credentials.tenant_id
-        cls.alt_client = cls.os_alt.volumes_client
+        super(BaseVolumeQuotasAdminTestJSON, cls).setup_credentials()
+        cls.demo_tenant_id = cls.os_primary.credentials.tenant_id
+        cls.alt_client = cls.os_alt.volumes_client_latest
+
+    @classmethod
+    def setup_clients(cls):
+        super(BaseVolumeQuotasAdminTestJSON, cls).setup_clients()
+        cls.transfer_client = cls.os_primary.volume_transfers_v2_client
+        cls.alt_transfer_client = cls.os_alt.volume_transfers_v2_client
 
     @decorators.idempotent_id('59eada70-403c-4cef-a2a3-a8ce2f1b07a0')
     def test_list_quotas(self):
@@ -74,7 +86,7 @@
     @decorators.idempotent_id('18c51ae9-cb03-48fc-b234-14a19374dbed')
     def test_show_quota_usage(self):
         quota_usage = self.admin_quotas_client.show_quota_set(
-            self.os_adm.credentials.tenant_id,
+            self.os_admin.credentials.tenant_id,
             params={'usage': True})['quota_set']
         for key in QUOTA_KEYS:
             self.assertIn(key, quota_usage)
@@ -114,7 +126,7 @@
         volume_default = quota_set_default['volumes']
 
         self.admin_quotas_client.update_quota_set(
-            project_id, volumes=(int(volume_default) + 5))
+            project_id, volumes=(volume_default + 5))
 
         self.admin_quotas_client.delete_quota_set(project_id)
         quota_set_new = (self.admin_quotas_client.show_quota_set(project_id)
@@ -136,17 +148,17 @@
             self.alt_client.tenant_id, params={'usage': True})['quota_set']
 
         # Creates a volume transfer
-        transfer = self.volumes_client.create_volume_transfer(
+        transfer = self.transfer_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']
+        self.alt_transfer_client.accept_volume_transfer(
+            transfer_id, auth_key=auth_key)
 
         # Verify volume transferred is available
-        waiters.wait_for_volume_status(
+        waiters.wait_for_volume_resource_status(
             self.alt_client, volume['id'], 'available')
 
         # List of tenants quota usage post transfer
@@ -169,7 +181,3 @@
         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 8170626..d127b5f 100644
--- a/tempest/api/volume/admin/test_volume_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_quotas_negative.py
@@ -17,22 +17,21 @@
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
 
-class BaseVolumeQuotasNegativeV2TestJSON(base.BaseVolumeAdminTest):
+class BaseVolumeQuotasNegativeTestJSON(base.BaseVolumeAdminTest):
     force_tenant_isolation = True
 
     @classmethod
     def setup_credentials(cls):
-        super(BaseVolumeQuotasNegativeV2TestJSON, cls).setup_credentials()
-        cls.demo_tenant_id = cls.os.credentials.tenant_id
+        super(BaseVolumeQuotasNegativeTestJSON, cls).setup_credentials()
+        cls.demo_tenant_id = cls.os_primary.credentials.tenant_id
 
     @classmethod
     def resource_setup(cls):
-        super(BaseVolumeQuotasNegativeV2TestJSON, cls).resource_setup()
+        super(BaseVolumeQuotasNegativeTestJSON, cls).resource_setup()
         cls.shared_quota_set = {'gigabytes': 2 * CONF.volume.volume_size,
                                 'volumes': 1}
 
@@ -46,14 +45,14 @@
         # they are created using utility wrapper methods.
         cls.volume = cls.create_volume()
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('bf544854-d62a-47f2-a681-90f7a47d86b6')
     def test_quota_volumes(self):
         self.assertRaises(lib_exc.OverLimit,
                           self.volumes_client.create_volume,
                           size=CONF.volume.volume_size)
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('2dc27eee-8659-4298-b900-169d71a91374')
     def test_quota_volume_gigabytes(self):
         # NOTE(gfidente): quota set needs to be changed for this test
@@ -70,7 +69,3 @@
         self.assertRaises(lib_exc.OverLimit,
                           self.volumes_client.create_volume,
                           size=CONF.volume.volume_size)
-
-
-class VolumeQuotasNegativeV1TestJSON(BaseVolumeQuotasNegativeV2TestJSON):
-    _api_version = 1
diff --git a/tempest/api/volume/admin/test_volume_retype_with_migration.py b/tempest/api/volume/admin/test_volume_retype_with_migration.py
index dc509de..f0b3a4f 100644
--- a/tempest/api/volume/admin/test_volume_retype_with_migration.py
+++ b/tempest/api/volume/admin/test_volume_retype_with_migration.py
@@ -23,11 +23,11 @@
 LOG = logging.getLogger(__name__)
 
 
-class VolumeRetypeWithMigrationV2Test(base.BaseVolumeAdminTest):
+class VolumeRetypeWithMigrationTest(base.BaseVolumeAdminTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumeRetypeWithMigrationV2Test, cls).skip_checks()
+        super(VolumeRetypeWithMigrationTest, cls).skip_checks()
 
         if not CONF.volume_feature_enabled.multi_backend:
             raise cls.skipException("Cinder multi-backend feature disabled.")
@@ -38,18 +38,18 @@
 
     @classmethod
     def resource_setup(cls):
-        super(VolumeRetypeWithMigrationV2Test, cls).resource_setup()
+        super(VolumeRetypeWithMigrationTest, cls).resource_setup()
         # read backend name from a list.
-        cls.backend_src = CONF.volume.backend_names[0]
-        cls.backend_dst = CONF.volume.backend_names[1]
+        backend_src = CONF.volume.backend_names[0]
+        backend_dst = CONF.volume.backend_names[1]
 
-        extra_specs_src = {"volume_backend_name": cls.backend_src}
-        extra_specs_dst = {"volume_backend_name": cls.backend_dst}
+        extra_specs_src = {"volume_backend_name": backend_src}
+        extra_specs_dst = {"volume_backend_name": backend_dst}
 
-        cls.src_vol_type = cls.create_volume_type(extra_specs=extra_specs_src)
+        src_vol_type = cls.create_volume_type(extra_specs=extra_specs_src)
         cls.dst_vol_type = cls.create_volume_type(extra_specs=extra_specs_dst)
 
-        cls.src_vol = cls.create_volume(volume_type=cls.src_vol_type['name'])
+        cls.src_vol = cls.create_volume(volume_type=src_vol_type['name'])
 
     @classmethod
     def resource_cleanup(cls):
@@ -73,7 +73,7 @@
                     fetched_vol['id'])
                 break
 
-        super(VolumeRetypeWithMigrationV2Test, cls).resource_cleanup()
+        super(VolumeRetypeWithMigrationTest, cls).resource_cleanup()
 
     @decorators.idempotent_id('a1a41f3f-9dad-493e-9f09-3ff197d477cd')
     def test_available_volume_retype_with_migration(self):
@@ -85,9 +85,7 @@
         volume_source = self.admin_volume_client.show_volume(
             self.src_vol['id'])['volume']
 
-        # TODO(erlon): change this to volumes_client client after Bug
-        # #1657806 is fixed
-        self.admin_volume_client.retype_volume(
+        self.volumes_client.retype_volume(
             self.src_vol['id'],
             new_type=self.dst_vol_type['name'],
             migration_policy='on-demand')
diff --git a/tempest/api/volume/admin/test_volume_services.py b/tempest/api/volume/admin/test_volume_services.py
index 97a352b..293af81 100644
--- a/tempest/api/volume/admin/test_volume_services.py
+++ b/tempest/api/volume/admin/test_volume_services.py
@@ -13,20 +13,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 from tempest.api.volume import base
-from tempest import config
 from tempest.lib import decorators
 
 
-CONF = config.CONF
-
-
 def _get_host(host):
-    if CONF.volume_feature_enabled.volume_services:
-        host = host.split('@')[0]
-    return host
+    return host.split('@')[0]
 
 
-class VolumesServicesV2TestJSON(base.BaseVolumeAdminTest):
+class VolumesServicesTestJSON(base.BaseVolumeAdminTest):
     """Tests Volume Services API.
 
     volume service list requires admin privileges.
@@ -34,7 +28,7 @@
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesServicesV2TestJSON, cls).resource_setup()
+        super(VolumesServicesTestJSON, cls).resource_setup()
         cls.services = (cls.admin_volume_services_client.list_services()
                         ['services'])
         # NOTE: Cinder service-list API returns the list contains
@@ -47,13 +41,13 @@
     def test_list_services(self):
         services = (self.admin_volume_services_client.list_services()
                     ['services'])
-        self.assertNotEqual(0, len(services))
+        self.assertNotEmpty(services)
 
     @decorators.idempotent_id('63a3e1ca-37ee-4983-826d-83276a370d25')
     def test_get_service_by_service_binary_name(self):
         services = (self.admin_volume_services_client.list_services(
             binary=self.binary_name)['services'])
-        self.assertNotEqual(0, len(services))
+        self.assertNotEmpty(services)
         for service in services:
             self.assertEqual(self.binary_name, service['binary'])
 
@@ -73,16 +67,26 @@
         # on order.
         self.assertEqual(sorted(s1), sorted(s2))
 
+    @decorators.idempotent_id('67ec6902-f91d-4dec-91fa-338523208bbc')
+    def test_get_service_by_volume_host_name(self):
+        volume_id = self.create_volume()['id']
+        volume = self.admin_volume_client.show_volume(volume_id)['volume']
+        hostname = _get_host(volume['os-vol-host-attr:host'])
+
+        services = (self.admin_volume_services_client.list_services(
+            host=hostname, binary='cinder-volume')['services'])
+
+        self.assertNotEmpty(services,
+                            'cinder-volume not found on host %s' % hostname)
+        self.assertEqual(hostname, _get_host(services[0]['host']))
+        self.assertEqual('cinder-volume', services[0]['binary'])
+
     @decorators.idempotent_id('ffa6167c-4497-4944-a464-226bbdb53908')
     def test_get_service_by_service_and_host_name(self):
 
         services = (self.admin_volume_services_client.list_services(
             host=self.host_name, binary=self.binary_name))['services']
 
-        self.assertNotEqual(0, len(services))
+        self.assertNotEmpty(services)
         self.assertEqual(self.host_name, _get_host(services[0]['host']))
         self.assertEqual(self.binary_name, services[0]['binary'])
-
-
-class VolumesServicesV1TestJSON(VolumesServicesV2TestJSON):
-    _api_version = 1
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 90a0022..0f4e90f 100644
--- a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
@@ -17,28 +17,27 @@
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
 
-class VolumeSnapshotQuotasNegativeV2TestJSON(base.BaseVolumeAdminTest):
+class VolumeSnapshotQuotasNegativeTestJSON(base.BaseVolumeAdminTest):
     force_tenant_isolation = True
 
     @classmethod
     def skip_checks(cls):
-        super(VolumeSnapshotQuotasNegativeV2TestJSON, cls).skip_checks()
+        super(VolumeSnapshotQuotasNegativeTestJSON, cls).skip_checks()
         if not CONF.volume_feature_enabled.snapshot:
             raise cls.skipException('Cinder volume snapshots are disabled')
 
     @classmethod
     def setup_credentials(cls):
-        super(VolumeSnapshotQuotasNegativeV2TestJSON, cls).setup_credentials()
-        cls.demo_tenant_id = cls.os.credentials.tenant_id
+        super(VolumeSnapshotQuotasNegativeTestJSON, cls).setup_credentials()
+        cls.demo_tenant_id = cls.os_primary.credentials.tenant_id
 
     @classmethod
     def resource_setup(cls):
-        super(VolumeSnapshotQuotasNegativeV2TestJSON, cls).resource_setup()
+        super(VolumeSnapshotQuotasNegativeTestJSON, cls).resource_setup()
         cls.default_volume_size = CONF.volume.volume_size
         cls.shared_quota_set = {'gigabytes': 3 * cls.default_volume_size,
                                 'volumes': 1, 'snapshots': 1}
@@ -54,14 +53,14 @@
         cls.volume = cls.create_volume()
         cls.snapshot = cls.create_snapshot(volume_id=cls.volume['id'])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('02bbf63f-6c05-4357-9d98-2926a94064ff')
     def test_quota_volume_snapshots(self):
         self.assertRaises(lib_exc.OverLimit,
                           self.snapshots_client.create_snapshot,
                           volume_id=self.volume['id'])
 
-    @test.attr(type='negative')
+    @decorators.attr(type='negative')
     @decorators.idempotent_id('c99a1ca9-6cdf-498d-9fdf-25832babef27')
     def test_quota_volume_gigabytes_snapshots(self):
         self.addCleanup(self.admin_quotas_client.update_quota_set,
@@ -75,7 +74,3 @@
         self.assertRaises(lib_exc.OverLimit,
                           self.snapshots_client.create_snapshot,
                           volume_id=self.volume['id'])
-
-
-class VolumeSnapshotNegativeV1TestJSON(VolumeSnapshotQuotasNegativeV2TestJSON):
-    _api_version = 1
diff --git a/tempest/api/volume/admin/v2/test_volume_type_access.py b/tempest/api/volume/admin/test_volume_type_access.py
similarity index 95%
rename from tempest/api/volume/admin/v2/test_volume_type_access.py
rename to tempest/api/volume/admin/test_volume_type_access.py
index b60b33b..e93bcb5 100644
--- a/tempest/api/volume/admin/v2/test_volume_type_access.py
+++ b/tempest/api/volume/admin/test_volume_type_access.py
@@ -23,14 +23,14 @@
 CONF = config.CONF
 
 
-class VolumeTypesAccessV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesAccessTest(base.BaseVolumeAdminTest):
 
     credentials = ['primary', 'alt', 'admin']
 
     @classmethod
     def setup_clients(cls):
-        super(VolumeTypesAccessV2Test, cls).setup_clients()
-        cls.alt_client = cls.os_alt.volumes_client
+        super(VolumeTypesAccessTest, cls).setup_clients()
+        cls.alt_client = cls.os_alt.volumes_client_latest
 
     @decorators.idempotent_id('d4dd0027-835f-4554-a6e5-50903fb79184')
     def test_volume_type_access_add(self):
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 7938604..af1024c 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -14,15 +14,15 @@
 #    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.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
 
 
-class VolumeTypesV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesTest(base.BaseVolumeAdminTest):
 
     @decorators.idempotent_id('9d9b28e3-1b2e-4483-a2cc-24aa0ea1de54')
     def test_volume_type_list(self):
@@ -36,7 +36,6 @@
         # Create/update/get/delete volume with volume_type and extra spec.
         volume_types = list()
         vol_name = data_utils.rand_name(self.__class__.__name__ + '-volume')
-        self.name_field = self.special_fields['name_field']
         proto = CONF.volume.storage_protocol
         vendor = CONF.volume.vendor_name
         extra_specs = {"storage_protocol": proto,
@@ -46,26 +45,24 @@
             vol_type = self.create_volume_type(
                 extra_specs=extra_specs)
             volume_types.append(vol_type)
-        params = {self.name_field: vol_name,
+        params = {'name': vol_name,
                   'volume_type': volume_types[0]['id'],
                   'size': CONF.volume.volume_size}
 
         # Create volume
         volume = self.create_volume(**params)
         self.assertEqual(volume_types[0]['name'], volume["volume_type"])
-        self.assertEqual(volume[self.name_field], vol_name,
+        self.assertEqual(volume['name'], vol_name,
                          "The created volume name is not equal "
                          "to the requested name")
         self.assertIsNotNone(volume['id'],
                              "Field volume id is empty or not found.")
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume['id'], 'available')
 
         # Update volume with new volume_type
         self.volumes_client.retype_volume(volume['id'],
                                           new_type=volume_types[1]['id'])
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume['id'], 'available')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
 
         # Get volume details and Verify
         fetched_volume = self.volumes_client.show_volume(
@@ -74,7 +71,7 @@
                          fetched_volume['volume_type'],
                          'The fetched Volume type is different '
                          'from updated volume type')
-        self.assertEqual(vol_name, fetched_volume[self.name_field],
+        self.assertEqual(vol_name, fetched_volume['name'],
                          'The fetched Volume is different '
                          'from the created Volume')
         self.assertEqual(volume['id'], fetched_volume['id'],
@@ -123,43 +120,55 @@
             fetched_volume_type['os-volume-type-access:is_public'])
 
     @decorators.idempotent_id('7830abd0-ff99-4793-a265-405684a54d46')
-    def test_volume_type_encryption_create_get_delete(self):
-        # Create/get/delete encryption type.
-        provider = "LuksEncryptor"
-        control_location = "front-end"
-        body = self.create_volume_type()
+    def test_volume_type_encryption_create_get_update_delete(self):
+        # Create/get/update/delete encryption type.
+        create_kwargs = {'provider': 'LuksEncryptor',
+                         'control_location': 'front-end'}
+        volume_type_id = self.create_volume_type()['id']
+
         # Create encryption type
         encryption_type = \
             self.admin_encryption_types_client.create_encryption_type(
-                body['id'], provider=provider,
-                control_location=control_location)['encryption']
+                volume_type_id, **create_kwargs)['encryption']
         self.assertIn('volume_type_id', encryption_type)
-        self.assertEqual(provider, encryption_type['provider'],
-                         "The created encryption_type provider is not equal "
-                         "to the requested provider")
-        self.assertEqual(control_location, encryption_type['control_location'],
-                         "The created encryption_type control_location is not "
-                         "equal to the requested control_location")
+        for key in create_kwargs:
+            self.assertEqual(create_kwargs[key], encryption_type[key],
+                             'The created encryption_type %s is different '
+                             'from the requested encryption_type' % key)
 
         # Get encryption type
+        encrypt_type_id = encryption_type['volume_type_id']
         fetched_encryption_type = (
             self.admin_encryption_types_client.show_encryption_type(
-                encryption_type['volume_type_id']))
-        self.assertEqual(provider,
-                         fetched_encryption_type['provider'],
-                         'The fetched encryption_type provider is different '
-                         'from the created encryption_type')
-        self.assertEqual(control_location,
-                         fetched_encryption_type['control_location'],
-                         'The fetched encryption_type control_location is '
-                         'different from the created encryption_type')
+                encrypt_type_id))
+        for key in create_kwargs:
+            self.assertEqual(create_kwargs[key], fetched_encryption_type[key],
+                             'The fetched encryption_type %s is different '
+                             'from the created encryption_type' % key)
+
+        # Update encryption type
+        update_kwargs = {'key_size': 128,
+                         'provider': 'SomeProvider',
+                         'cipher': 'aes-xts-plain64',
+                         'control_location': 'back-end'}
+        self.admin_encryption_types_client.update_encryption_type(
+            encrypt_type_id, **update_kwargs)
+        updated_encryption_type = (
+            self.admin_encryption_types_client.show_encryption_type(
+                encrypt_type_id))
+        for key in update_kwargs:
+            self.assertEqual(update_kwargs[key], updated_encryption_type[key],
+                             'The fetched encryption_type %s is different '
+                             'from the updated encryption_type' % key)
 
         # Delete encryption type
-        type_id = encryption_type['volume_type_id']
-        self.admin_encryption_types_client.delete_encryption_type(type_id)
-        self.admin_encryption_types_client.wait_for_resource_deletion(type_id)
+        self.admin_encryption_types_client.delete_encryption_type(
+            encrypt_type_id)
+        self.admin_encryption_types_client.wait_for_resource_deletion(
+            encrypt_type_id)
         deleted_encryption_type = (
-            self.admin_encryption_types_client.show_encryption_type(type_id))
+            self.admin_encryption_types_client.show_encryption_type(
+                encrypt_type_id))
         self.assertEmpty(deleted_encryption_type)
 
     @decorators.idempotent_id('cf9f07c6-db9e-4462-a243-5933ad65e9c8')
@@ -183,7 +192,3 @@
         self.assertEqual(name, updated_vol_type['name'])
         self.assertEqual(description, updated_vol_type['description'])
         self.assertEqual(is_public, updated_vol_type['is_public'])
-
-
-class VolumeTypesV1Test(VolumeTypesV2Test):
-    _api_version = 1
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs.py b/tempest/api/volume/admin/test_volume_types_extra_specs.py
index 1729d06..b5a2fb7 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -18,11 +18,11 @@
 from tempest.lib import exceptions as lib_exc
 
 
-class VolumeTypesExtraSpecsV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesExtraSpecsTest(base.BaseVolumeAdminTest):
 
     @classmethod
     def resource_setup(cls):
-        super(VolumeTypesExtraSpecsV2Test, cls).resource_setup()
+        super(VolumeTypesExtraSpecsTest, cls).resource_setup()
         cls.volume_type = cls.create_volume_type()
 
     @decorators.idempotent_id('b42923e9-0452-4945-be5b-d362ae533e60')
@@ -77,7 +77,3 @@
             lib_exc.NotFound,
             self.admin_volume_types_client.show_volume_type_extra_specs,
             self.volume_type['id'], spec_key)
-
-
-class VolumeTypesExtraSpecsV1Test(VolumeTypesExtraSpecsV2Test):
-    _api_version = 1
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 933b6ad..fe249d6 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
@@ -14,19 +14,20 @@
 #    under the License.
 
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
 
-class ExtraSpecsNegativeV2Test(base.BaseVolumeAdminTest):
+class ExtraSpecsNegativeTest(base.BaseVolumeAdminTest):
 
     @classmethod
     def resource_setup(cls):
-        super(ExtraSpecsNegativeV2Test, cls).resource_setup()
-        cls.extra_specs = {"spec1": "val1"}
-        cls.volume_type = cls.create_volume_type(extra_specs=cls.extra_specs)
+        super(ExtraSpecsNegativeTest, cls).resource_setup()
+        extra_specs = {"spec1": "val1"}
+        cls.volume_type = cls.create_volume_type(extra_specs=extra_specs)
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('08961d20-5cbb-4910-ac0f-89ad6dbb2da1')
     def test_update_no_body(self):
         # Should not update volume type extra specs with no body
@@ -35,6 +36,7 @@
             self.admin_volume_types_client.update_volume_type_extra_specs,
             self.volume_type['id'], "spec1", None)
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('25e5a0ee-89b3-4c53-8310-236f76c75365')
     def test_update_nonexistent_extra_spec_id(self):
         # Should not update volume type extra specs with nonexistent id.
@@ -45,6 +47,7 @@
             self.volume_type['id'], data_utils.rand_uuid(),
             extra_spec)
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9bf7a657-b011-4aec-866d-81c496fbe5c8')
     def test_update_none_extra_spec_id(self):
         # Should not update volume type extra specs with none id.
@@ -54,10 +57,11 @@
             self.admin_volume_types_client.update_volume_type_extra_specs,
             self.volume_type['id'], None, extra_spec)
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a77dfda2-9100-448e-9076-ed1711f4bdfc')
     def test_update_multiple_extra_spec(self):
         # Should not update volume type extra specs with multiple specs as
-            # body.
+        # body.
         extra_spec = {"spec1": "val2", "spec2": "val1"}
         self.assertRaises(
             lib_exc.BadRequest,
@@ -65,16 +69,18 @@
             self.volume_type['id'], list(extra_spec)[0],
             extra_spec)
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('49d5472c-a53d-4eab-a4d3-450c4db1c545')
     def test_create_nonexistent_type_id(self):
         # Should not create volume type extra spec for nonexistent volume
-            # type id.
+        # type id.
         extra_specs = {"spec2": "val1"}
         self.assertRaises(
             lib_exc.NotFound,
             self.admin_volume_types_client.create_volume_type_extra_specs,
             data_utils.rand_uuid(), extra_specs)
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c821bdc8-43a4-4bf4-86c8-82f3858d5f7d')
     def test_create_none_body(self):
         # Should not create volume type extra spec for none POST body.
@@ -83,6 +89,7 @@
             self.admin_volume_types_client.create_volume_type_extra_specs,
             self.volume_type['id'], None)
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('bc772c71-1ed4-4716-b945-8b5ed0f15e87')
     def test_create_invalid_body(self):
         # Should not create volume type extra spec for invalid POST body.
@@ -91,6 +98,7 @@
             self.admin_volume_types_client.create_volume_type_extra_specs,
             self.volume_type['id'], extra_specs=['invalid'])
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('031cda8b-7d23-4246-8bf6-bbe73fd67074')
     def test_delete_nonexistent_volume_type_id(self):
         # Should not delete volume type extra spec for nonexistent
@@ -100,6 +108,7 @@
             self.admin_volume_types_client.delete_volume_type_extra_specs,
             data_utils.rand_uuid(), "spec1")
 
+    @decorators.attr(type=['negative'])
     @decorators.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.
@@ -108,6 +117,7 @@
             self.admin_volume_types_client.list_volume_types_extra_specs,
             data_utils.rand_uuid())
 
+    @decorators.attr(type=['negative'])
     @decorators.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.
@@ -116,15 +126,12 @@
             self.admin_volume_types_client.show_volume_type_extra_specs,
             data_utils.rand_uuid(), "spec1")
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c881797d-12ff-4f1a-b09d-9f6212159753')
-    def test_get_nonexistent_extra_spec_id(self):
+    def test_get_nonexistent_extra_spec_name(self):
         # Should not get volume type extra spec for nonexistent extra spec
-            # id.
+        # name.
         self.assertRaises(
             lib_exc.NotFound,
             self.admin_volume_types_client.show_volume_type_extra_specs,
-            self.volume_type['id'], data_utils.rand_uuid())
-
-
-class ExtraSpecsNegativeV1Test(ExtraSpecsNegativeV2Test):
-    _api_version = 1
+            self.volume_type['id'], "nonexistent_extra_spec_name")
diff --git a/tempest/api/volume/admin/test_volume_types_negative.py b/tempest/api/volume/admin/test_volume_types_negative.py
index b278127..4cad52a 100644
--- a/tempest/api/volume/admin/test_volume_types_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_negative.py
@@ -19,17 +19,18 @@
 from tempest.lib import exceptions as lib_exc
 
 
-class VolumeTypesNegativeV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesNegativeTest(base.BaseVolumeAdminTest):
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b48c98f2-e662-4885-9b71-032256906314')
     def test_create_with_nonexistent_volume_type(self):
         # Should not be able to create volume with nonexistent volume_type.
-        self.name_field = self.special_fields['name_field']
-        params = {self.name_field: data_utils.rand_uuid(),
+        params = {'name': data_utils.rand_uuid(),
                   'volume_type': data_utils.rand_uuid()}
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.create_volume, **params)
 
+    @decorators.attr(type=['negative'])
     @decorators.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.
@@ -37,6 +38,7 @@
             lib_exc.BadRequest,
             self.admin_volume_types_client.create_volume_type, name='')
 
+    @decorators.attr(type=['negative'])
     @decorators.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.
@@ -44,6 +46,7 @@
                           self.admin_volume_types_client.show_volume_type,
                           data_utils.rand_uuid())
 
+    @decorators.attr(type=['negative'])
     @decorators.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.
@@ -51,6 +54,7 @@
                           self.admin_volume_types_client.delete_volume_type,
                           data_utils.rand_uuid())
 
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8c09f849-f225-4d78-ba87-bffd9a5e0c6f')
     def test_create_volume_with_private_volume_type(self):
         # Should not be able to create volume with private volume type.
@@ -58,7 +62,3 @@
         volume_type = self.create_volume_type(**params)
         self.assertRaises(lib_exc.NotFound,
                           self.create_volume, volume_type=volume_type['id'])
-
-
-class VolumeTypesNegativeV1Test(VolumeTypesNegativeV2Test):
-    _api_version = 1
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index 693ffe7..8d09217 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -14,10 +14,15 @@
 #    under the License.
 
 from tempest.api.volume import base
+from tempest.common import utils
+from tempest.common import waiters
+from tempest import config
 from tempest.lib import decorators
 
+CONF = config.CONF
 
-class VolumesActionsV2Test(base.BaseVolumeAdminTest):
+
+class VolumesActionsTest(base.BaseVolumeAdminTest):
 
     def _create_reset_and_force_delete_temp_volume(self, status=None):
         # Create volume, reset volume status, and force delete temp volume
@@ -25,6 +30,8 @@
         if status:
             self.admin_volume_client.reset_volume_status(
                 temp_volume['id'], status=status)
+            waiters.wait_for_volume_resource_status(
+                self.volumes_client, temp_volume['id'], status)
         self.admin_volume_client.force_delete_volume(temp_volume['id'])
         self.volumes_client.wait_for_resource_deletion(temp_volume['id'])
 
@@ -32,12 +39,15 @@
     def test_volume_reset_status(self):
         # test volume reset status : available->error->available
         volume = self.create_volume()
-        for status in ['error', 'available']:
+        self.addCleanup(waiters.wait_for_volume_resource_status,
+                        self.volumes_client, volume['id'], 'available')
+        self.addCleanup(self.admin_volume_client.reset_volume_status,
+                        volume['id'], status='available')
+        for status in ['error', 'available', 'maintenance']:
             self.admin_volume_client.reset_volume_status(
                 volume['id'], status=status)
-            volume_get = self.admin_volume_client.show_volume(
-                volume['id'])['volume']
-            self.assertEqual(status, volume_get['status'])
+            waiters.wait_for_volume_resource_status(
+                self.volumes_client, volume['id'], status)
 
     @decorators.idempotent_id('21737d5a-92f2-46d7-b009-a0cc0ee7a570')
     def test_volume_force_delete_when_volume_is_creating(self):
@@ -54,6 +64,42 @@
         # test force delete when status of volume is error
         self._create_reset_and_force_delete_temp_volume('error')
 
+    @decorators.idempotent_id('b957cabd-1486-4e21-90cf-a9ed3c39dfb2')
+    def test_volume_force_delete_when_volume_is_maintenance(self):
+        # test force delete when status of volume is maintenance
+        self._create_reset_and_force_delete_temp_volume('maintenance')
 
-class VolumesActionsV1Test(VolumesActionsV2Test):
-    _api_version = 1
+    @decorators.idempotent_id('d38285d9-929d-478f-96a5-00e66a115b81')
+    @utils.services('compute')
+    def test_force_detach_volume(self):
+        # Create a server and a volume
+        server_id = self.create_server()['id']
+        volume_id = self.create_volume()['id']
+
+        # Attach volume
+        self.volumes_client.attach_volume(
+            volume_id,
+            instance_uuid=server_id,
+            mountpoint='/dev/%s' % CONF.compute.volume_device_name)
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume_id, 'in-use')
+        self.addCleanup(waiters.wait_for_volume_resource_status,
+                        self.volumes_client, volume_id, 'available')
+        self.addCleanup(self.volumes_client.detach_volume, volume_id)
+        attachment = self.volumes_client.show_volume(
+            volume_id)['volume']['attachments'][0]
+
+        # Reset volume's status to error
+        self.admin_volume_client.reset_volume_status(volume_id, status='error')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume_id, 'error')
+
+        # Force detach volume
+        self.admin_volume_client.force_detach_volume(
+            volume_id, connector=None,
+            attachment_id=attachment['attachment_id'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume_id, 'available')
+        vol_info = self.volumes_client.show_volume(volume_id)['volume']
+        self.assertIn('attachments', vol_info)
+        self.assertEmpty(vol_info['attachments'])
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 04d27ea..375aacb 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -17,19 +17,19 @@
 from oslo_serialization import jsonutils as json
 
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
 
 
-class VolumesBackupsAdminV2Test(base.BaseVolumeAdminTest):
+class VolumesBackupsAdminTest(base.BaseVolumeAdminTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesBackupsAdminV2Test, cls).skip_checks()
+        super(VolumesBackupsAdminTest, cls).skip_checks()
         if not CONF.volume_feature_enabled.backup:
             raise cls.skipException("Cinder backup feature disabled")
 
@@ -94,12 +94,12 @@
         self.addCleanup(self._delete_backup, new_id)
         self.assertIn("id", import_backup)
         self.assertEqual(new_id, import_backup['id'])
-        waiters.wait_for_backup_status(self.admin_backups_client,
-                                       import_backup['id'], 'available')
+        waiters.wait_for_volume_resource_status(self.admin_backups_client,
+                                                import_backup['id'],
+                                                'available')
 
         # Verify Import Backup
-        backups = self.admin_backups_client.list_backups(
-            detail=True)['backups']
+        backups = self.admin_backups_client.list_backups()['backups']
         self.assertIn(new_id, [b['id'] for b in backups])
 
         # Restore backup
@@ -108,14 +108,16 @@
         self.addCleanup(self.admin_volume_client.delete_volume,
                         restore['volume_id'])
         self.assertEqual(backup['id'], restore['backup_id'])
-        waiters.wait_for_volume_status(self.admin_volume_client,
-                                       restore['volume_id'], 'available')
+        waiters.wait_for_volume_resource_status(self.admin_volume_client,
+                                                restore['volume_id'],
+                                                'available')
 
         # Verify if restored volume is there in volume list
         volumes = self.admin_volume_client.list_volumes()['volumes']
         self.assertIn(restore['volume_id'], [v['id'] for v in volumes])
-        waiters.wait_for_backup_status(self.admin_backups_client,
-                                       import_backup['id'], 'available')
+        waiters.wait_for_volume_resource_status(self.admin_backups_client,
+                                                import_backup['id'],
+                                                'available')
 
     @decorators.idempotent_id('47a35425-a891-4e13-961c-c45deea21e94')
     def test_volume_backup_reset_status(self):
@@ -131,9 +133,5 @@
         # Reset backup status to error
         self.admin_backups_client.reset_backup_status(backup_id=backup['id'],
                                                       status="error")
-        waiters.wait_for_backup_status(self.admin_backups_client,
-                                       backup['id'], 'error')
-
-
-class VolumesBackupsAdminV1Test(VolumesBackupsAdminV2Test):
-    _api_version = 1
+        waiters.wait_for_volume_resource_status(self.admin_backups_client,
+                                                backup['id'], 'error')
diff --git a/tempest/api/volume/admin/v2/test_volumes_list.py b/tempest/api/volume/admin/test_volumes_list.py
similarity index 90%
rename from tempest/api/volume/admin/v2/test_volumes_list.py
rename to tempest/api/volume/admin/test_volumes_list.py
index b0a37fb..9d98b7a 100644
--- a/tempest/api/volume/admin/v2/test_volumes_list.py
+++ b/tempest/api/volume/admin/test_volumes_list.py
@@ -23,11 +23,11 @@
 CONF = config.CONF
 
 
-class VolumesListAdminV2TestJSON(base.BaseVolumeAdminTest):
+class VolumesListAdminTestJSON(base.BaseVolumeAdminTest):
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesListAdminV2TestJSON, cls).resource_setup()
+        super(VolumesListAdminTestJSON, cls).resource_setup()
         # Create 3 test volumes
         # NOTE(zhufl): When using pre-provisioned credentials, the project
         # may have volumes other than those created below.
@@ -45,8 +45,8 @@
         # Create a volume in admin tenant
         adm_vol = self.admin_volume_client.create_volume(
             size=CONF.volume.volume_size)['volume']
-        waiters.wait_for_volume_status(self.admin_volume_client,
-                                       adm_vol['id'], 'available')
+        waiters.wait_for_volume_resource_status(self.admin_volume_client,
+                                                adm_vol['id'], 'available')
         self.addCleanup(self.admin_volume_client.delete_volume, adm_vol['id'])
         params = {'all_tenants': 1,
                   'project_id': self.volumes_client.tenant_id}
diff --git a/tempest/api/volume/admin/v2/__init__.py b/tempest/api/volume/admin/v2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/volume/admin/v2/__init__.py
+++ /dev/null
diff --git a/tempest/api/volume/admin/v2/test_snapshot_manage.py b/tempest/api/volume/admin/v2/test_snapshot_manage.py
deleted file mode 100644
index 1114924..0000000
--- a/tempest/api/volume/admin/v2/test_snapshot_manage.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright 2016 Red Hat, Inc.
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import testtools
-
-from tempest.api.volume import base
-from tempest.common import waiters
-from tempest import config
-from tempest.lib import decorators
-
-CONF = config.CONF
-
-
-class SnapshotManageAdminV2Test(base.BaseVolumeAdminTest):
-    """Unmanage & manage snapshots
-
-     This feature provides the ability to import/export volume snapshot
-     from one Cinder to another and to import snapshots that have not been
-     managed by Cinder from a storage back end to Cinder
-    """
-
-    @decorators.idempotent_id('0132f42d-0147-4b45-8501-cc504bbf7810')
-    @testtools.skipUnless(CONF.volume_feature_enabled.manage_snapshot,
-                          "Manage snapshot tests are disabled")
-    def test_unmanage_manage_snapshot(self):
-        # Create a volume
-        volume = self.create_volume()
-
-        # Create a snapshot
-        snapshot = self.create_snapshot(volume_id=volume['id'])
-
-        # Unmanage the snapshot
-        # Unmanage snapshot function works almost the same as delete snapshot,
-        # but it does not delete the snapshot data
-        self.admin_snapshots_client.unmanage_snapshot(snapshot['id'])
-        self.admin_snapshots_client.wait_for_resource_deletion(snapshot['id'])
-
-        # Fetch snapshot ids
-        snapshot_list = [
-            snap['id'] for snap in
-            self.snapshots_client.list_snapshots()['snapshots']
-        ]
-
-        # Verify snapshot does not exist in snapshot list
-        self.assertNotIn(snapshot['id'], snapshot_list)
-
-        # Manage the snapshot
-        snapshot_ref = '_snapshot-%s' % snapshot['id']
-        new_snapshot = self.admin_snapshot_manage_client.manage_snapshot(
-            volume_id=volume['id'],
-            ref={'source-name': snapshot_ref})['snapshot']
-        self.addCleanup(self.delete_snapshot,
-                        self.admin_snapshots_client, new_snapshot['id'])
-
-        # Wait for the snapshot to be available after manage operation
-        waiters.wait_for_snapshot_status(self.admin_snapshots_client,
-                                         new_snapshot['id'],
-                                         'available')
-
-        # Verify the managed snapshot has the expected parent volume
-        self.assertEqual(new_snapshot['volume_id'], volume['id'])
diff --git a/tempest/api/volume/admin/v2/test_volume_pools.py b/tempest/api/volume/admin/v2/test_volume_pools.py
deleted file mode 100644
index 91d092d..0000000
--- a/tempest/api/volume/admin/v2/test_volume_pools.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# 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.lib import decorators
-
-
-class VolumePoolsAdminV2TestsJSON(base.BaseVolumeAdminTest):
-
-    @classmethod
-    def resource_setup(cls):
-        super(VolumePoolsAdminV2TestsJSON, cls).resource_setup()
-        # Create a test shared volume for tests
-        cls.volume = cls.create_volume()
-
-    def _assert_host_volume_in_pools(self, with_detail=False):
-        volume_info = self.admin_volume_client.show_volume(
-            self.volume['id'])['volume']
-        cinder_pools = self.admin_volume_client.show_pools(
-            detail=with_detail)['pools']
-        self.assertIn(volume_info['os-vol-host-attr:host'],
-                      [pool['name'] for pool in cinder_pools])
-
-    @decorators.idempotent_id('0248a46c-e226-4933-be10-ad6fca8227e7')
-    def test_get_pools_without_details(self):
-        self._assert_host_volume_in_pools()
-
-    @decorators.idempotent_id('d4bb61f7-762d-4437-b8a4-5785759a0ced')
-    def test_get_pools_with_details(self):
-        self._assert_host_volume_in_pools(with_detail=True)
diff --git a/tempest/api/volume/api_microversion_fixture.py b/tempest/api/volume/api_microversion_fixture.py
index 64bc537..7bbe674 100644
--- a/tempest/api/volume/api_microversion_fixture.py
+++ b/tempest/api/volume/api_microversion_fixture.py
@@ -13,7 +13,7 @@
 
 import fixtures
 
-from tempest.lib.services.volume.v3 import base_client
+from tempest.lib.services.volume import base_client
 
 
 class APIMicroversionFixture(fixtures.Fixture):
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 98e050e..63ef85b 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -13,10 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.api.volume import api_microversion_fixture
 from tempest.common import compute
-from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common import api_version_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions
 import tempest.test
@@ -24,7 +26,8 @@
 CONF = config.CONF
 
 
-class BaseVolumeTest(tempest.test.BaseTestCase):
+class BaseVolumeTest(api_version_utils.BaseMicroversionTest,
+                     tempest.test.BaseTestCase):
     """Base test case class for all Cinder API tests."""
 
     _api_version = 2
@@ -37,11 +40,7 @@
         if not CONF.service_available.cinder:
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
-        if cls._api_version == 1:
-            if not CONF.volume_feature_enabled.api_v1:
-                msg = "Volume API v1 is disabled"
-                raise cls.skipException(msg)
-        elif cls._api_version == 2:
+        if cls._api_version == 2:
             if not CONF.volume_feature_enabled.api_v2:
                 msg = "Volume API v2 is disabled"
                 raise cls.skipException(msg)
@@ -53,6 +52,10 @@
             msg = ("Invalid Cinder API version (%s)" % cls._api_version)
             raise exceptions.InvalidConfiguration(msg)
 
+        api_version_utils.check_skip_with_microversion(
+            cls.min_microversion, cls.max_microversion,
+            CONF.volume.min_microversion, CONF.volume.max_microversion)
+
     @classmethod
     def setup_credentials(cls):
         cls.set_network_resources()
@@ -61,31 +64,42 @@
     @classmethod
     def setup_clients(cls):
         super(BaseVolumeTest, cls).setup_clients()
-        cls.servers_client = cls.os.servers_client
-        cls.compute_networks_client = cls.os.compute_networks_client
-        cls.compute_images_client = cls.os.compute_images_client
+        cls.servers_client = cls.os_primary.servers_client
 
-        if cls._api_version == 1:
-            cls.snapshots_client = cls.os.snapshots_client
-            cls.volumes_client = cls.os.volumes_client
-            cls.backups_client = cls.os.backups_client
-            cls.volume_services_client = cls.os.volume_services_client
-            cls.volumes_extension_client = cls.os.volumes_extension_client
-            cls.availability_zone_client = (
-                cls.os.volume_availability_zone_client)
-            cls.volume_limits_client = cls.os.volume_limits_client
+        if CONF.service_available.glance:
+            cls.images_client = cls.os_primary.image_client_v2
+
+        if cls._api_version == 3:
+            cls.backups_client = cls.os_primary.backups_v3_client
+            cls.volumes_client = cls.os_primary.volumes_v3_client
+            cls.messages_client = cls.os_primary.volume_v3_messages_client
+            cls.versions_client = cls.os_primary.volume_v3_versions_client
+            cls.groups_client = cls.os_primary.groups_v3_client
+            cls.group_snapshots_client = (
+                cls.os_primary.group_snapshots_v3_client)
         else:
-            cls.snapshots_client = cls.os.snapshots_v2_client
-            cls.volumes_client = cls.os.volumes_v2_client
-            cls.backups_client = cls.os.backups_v2_client
-            cls.volumes_extension_client = cls.os.volumes_v2_extension_client
-            cls.availability_zone_client = (
-                cls.os.volume_v2_availability_zone_client)
-            cls.volume_limits_client = cls.os.volume_v2_limits_client
+            cls.backups_client = cls.os_primary.backups_v2_client
+            cls.volumes_client = cls.os_primary.volumes_v2_client
+
+        cls.snapshots_client = cls.os_primary.snapshots_v2_client
+        cls.volumes_extension_client =\
+            cls.os_primary.volumes_v2_extension_client
+        cls.availability_zone_client = (
+            cls.os_primary.volume_v2_availability_zone_client)
+        cls.volume_limits_client = cls.os_primary.volume_v2_limits_client
+
+    def setUp(self):
+        super(BaseVolumeTest, self).setUp()
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            self.request_microversion))
 
     @classmethod
     def resource_setup(cls):
         super(BaseVolumeTest, cls).resource_setup()
+        cls.request_microversion = (
+            api_version_utils.select_request_microversion(
+                cls.min_microversion,
+                CONF.volume.min_microversion))
 
         cls.snapshots = []
         cls.volumes = []
@@ -94,15 +108,6 @@
         cls.build_interval = CONF.volume.build_interval
         cls.build_timeout = CONF.volume.build_timeout
 
-        if cls._api_version == 1:
-            # Special fields and resp code for cinder v1
-            cls.special_fields = {'name_field': 'display_name',
-                                  'descrip_field': 'display_description'}
-        else:
-            # Special fields and resp code for cinder v2
-            cls.special_fields = {'name_field': 'name',
-                                  'descrip_field': 'description'}
-
     @classmethod
     def resource_cleanup(cls):
         cls.clear_snapshots()
@@ -119,47 +124,47 @@
             kwargs['size'] = CONF.volume.volume_size
 
         if 'imageRef' in kwargs:
-            image = cls.compute_images_client.show_image(
-                kwargs['imageRef'])['image']
-            min_disk = image.get('minDisk')
+            image = cls.images_client.show_image(kwargs['imageRef'])
+            min_disk = image['min_disk']
             kwargs['size'] = max(kwargs['size'], min_disk)
 
-        name_field = cls.special_fields['name_field']
-        if name_field not in kwargs:
+        if 'name' not in kwargs:
             name = data_utils.rand_name(cls.__name__ + '-Volume')
-            kwargs[name_field] = name
+            kwargs['name'] = name
 
         volume = cls.volumes_client.create_volume(**kwargs)['volume']
         cls.volumes.append(volume)
-        waiters.wait_for_volume_status(cls.volumes_client, volume['id'],
-                                       wait_until)
+        waiters.wait_for_volume_resource_status(cls.volumes_client,
+                                                volume['id'], wait_until)
         return volume
 
     @classmethod
     def create_snapshot(cls, volume_id=1, **kwargs):
         """Wrapper utility that returns a test snapshot."""
-        name_field = cls.special_fields['name_field']
-        if name_field not in kwargs:
+        if 'name' not in kwargs:
             name = data_utils.rand_name(cls.__name__ + '-Snapshot')
-            kwargs[name_field] = name
+            kwargs['name'] = name
 
         snapshot = cls.snapshots_client.create_snapshot(
             volume_id=volume_id, **kwargs)['snapshot']
-        cls.snapshots.append(snapshot)
-        waiters.wait_for_snapshot_status(cls.snapshots_client,
-                                         snapshot['id'], 'available')
+        cls.snapshots.append(snapshot['id'])
+        waiters.wait_for_volume_resource_status(cls.snapshots_client,
+                                                snapshot['id'], 'available')
         return snapshot
 
     def create_backup(self, volume_id, backup_client=None, **kwargs):
         """Wrapper utility that returns a test backup."""
         if backup_client is None:
             backup_client = self.backups_client
+        if 'name' not in kwargs:
+            name = data_utils.rand_name(self.__class__.__name__ + '-Backup')
+            kwargs['name'] = name
 
         backup = backup_client.create_backup(
             volume_id=volume_id, **kwargs)['backup']
         self.addCleanup(backup_client.delete_backup, backup['id'])
-        waiters.wait_for_backup_status(backup_client, backup['id'],
-                                       'available')
+        waiters.wait_for_volume_resource_status(backup_client, backup['id'],
+                                                'available')
         return backup
 
     # NOTE(afazekas): these create_* and clean_* could be defined
@@ -171,21 +176,24 @@
         client.delete_volume(volume_id)
         client.wait_for_resource_deletion(volume_id)
 
-    @staticmethod
-    def delete_snapshot(client, snapshot_id):
+    def delete_snapshot(self, snapshot_id, snapshots_client=None):
         """Delete snapshot by the given client"""
-        client.delete_snapshot(snapshot_id)
-        client.wait_for_resource_deletion(snapshot_id)
+        if snapshots_client is None:
+            snapshots_client = self.snapshots_client
+        snapshots_client.delete_snapshot(snapshot_id)
+        snapshots_client.wait_for_resource_deletion(snapshot_id)
+        if snapshot_id in self.snapshots:
+            self.snapshots.remove(snapshot_id)
 
     def attach_volume(self, server_id, volume_id):
         """Attach a volume to a server"""
         self.servers_client.attach_volume(
             server_id, volumeId=volume_id,
             device='/dev/%s' % CONF.compute.volume_device_name)
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume_id, 'in-use')
-        self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
-                        volume_id, 'available')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume_id, 'in-use')
+        self.addCleanup(waiters.wait_for_volume_resource_status,
+                        self.volumes_client, volume_id, 'available')
         self.addCleanup(self.servers_client.detach_volume, server_id,
                         volume_id)
 
@@ -207,23 +215,24 @@
     def clear_snapshots(cls):
         for snapshot in cls.snapshots:
             test_utils.call_and_ignore_notfound_exc(
-                cls.snapshots_client.delete_snapshot, snapshot['id'])
+                cls.snapshots_client.delete_snapshot, snapshot)
 
         for snapshot in cls.snapshots:
             test_utils.call_and_ignore_notfound_exc(
                 cls.snapshots_client.wait_for_resource_deletion,
-                snapshot['id'])
+                snapshot)
 
-    def create_server(self, **kwargs):
+    def create_server(self, wait_until='ACTIVE', **kwargs):
         name = kwargs.pop(
             'name',
             data_utils.rand_name(self.__class__.__name__ + '-instance'))
 
         tenant_network = self.get_tenant_network()
         body, _ = compute.create_test_server(
-            self.os,
+            self.os_primary,
             tenant_network=tenant_network,
             name=name,
+            wait_until=wait_until,
             **kwargs)
 
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
@@ -243,38 +252,34 @@
     def setup_clients(cls):
         super(BaseVolumeAdminTest, cls).setup_clients()
 
-        if cls._api_version == 1:
-            cls.admin_volume_qos_client = cls.os_adm.volume_qos_client
-            cls.admin_volume_services_client = \
-                cls.os_adm.volume_services_client
-            cls.admin_volume_types_client = cls.os_adm.volume_types_client
-            cls.admin_volume_client = cls.os_adm.volumes_client
-            cls.admin_hosts_client = cls.os_adm.volume_hosts_client
-            cls.admin_snapshots_client = cls.os_adm.snapshots_client
-            cls.admin_backups_client = cls.os_adm.backups_client
-            cls.admin_encryption_types_client = \
-                cls.os_adm.encryption_types_client
-            cls.admin_quotas_client = cls.os_adm.volume_quotas_client
-            cls.admin_volume_limits_client = cls.os_adm.volume_limits_client
-        elif cls._api_version == 2:
-            cls.admin_volume_qos_client = cls.os_adm.volume_qos_v2_client
-            cls.admin_volume_services_client = \
-                cls.os_adm.volume_services_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.admin_hosts_client = cls.os_adm.volume_hosts_v2_client
-            cls.admin_snapshot_manage_client = \
-                cls.os_adm.snapshot_manage_v2_client
-            cls.admin_snapshots_client = cls.os_adm.snapshots_v2_client
-            cls.admin_backups_client = cls.os_adm.backups_v2_client
-            cls.admin_encryption_types_client = \
-                cls.os_adm.encryption_types_v2_client
-            cls.admin_quotas_client = cls.os_adm.volume_quotas_v2_client
-            cls.admin_volume_limits_client = cls.os_adm.volume_v2_limits_client
-            cls.admin_capabilities_client = \
-                cls.os_adm.volume_capabilities_v2_client
-            cls.admin_scheduler_stats_client = \
-                cls.os_adm.volume_scheduler_stats_v2_client
+        cls.admin_volume_qos_client = cls.os_admin.volume_qos_v2_client
+        cls.admin_volume_services_client = \
+            cls.os_admin.volume_services_v2_client
+        cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
+        cls.admin_volume_manage_client = cls.os_admin.volume_manage_v2_client
+        cls.admin_volume_client = cls.os_admin.volumes_v2_client
+        if cls._api_version == 3:
+            cls.admin_volume_client = cls.os_admin.volumes_v3_client
+            cls.admin_groups_client = cls.os_admin.groups_v3_client
+            cls.admin_messages_client = cls.os_admin.volume_v3_messages_client
+            cls.admin_group_snapshots_client = \
+                cls.os_admin.group_snapshots_v3_client
+            cls.admin_group_types_client = cls.os_admin.group_types_v3_client
+        cls.admin_hosts_client = cls.os_admin.volume_hosts_v2_client
+        cls.admin_snapshot_manage_client = \
+            cls.os_admin.snapshot_manage_v2_client
+        cls.admin_snapshots_client = cls.os_admin.snapshots_v2_client
+        cls.admin_backups_client = cls.os_admin.backups_v2_client
+        cls.admin_encryption_types_client = \
+            cls.os_admin.encryption_types_v2_client
+        cls.admin_quota_classes_client = \
+            cls.os_admin.volume_quota_classes_v2_client
+        cls.admin_quotas_client = cls.os_admin.volume_quotas_v2_client
+        cls.admin_volume_limits_client = cls.os_admin.volume_v2_limits_client
+        cls.admin_capabilities_client = \
+            cls.os_admin.volume_capabilities_v2_client
+        cls.admin_scheduler_stats_client = \
+            cls.os_admin.volume_scheduler_stats_v2_client
 
     @classmethod
     def resource_setup(cls):
@@ -308,6 +313,16 @@
         cls.volume_types.append(volume_type['id'])
         return volume_type
 
+    def create_group_type(self, name=None, **kwargs):
+        """Create a test group-type"""
+        name = name or data_utils.rand_name(
+            self.__class__.__name__ + '-group-type')
+        group_type = self.admin_group_types_client.create_group_type(
+            name=name, **kwargs)['group_type']
+        self.addCleanup(self.admin_group_types_client.delete_group_type,
+                        group_type['id'])
+        return group_type
+
     @classmethod
     def clear_qos_specs(cls):
         for qos_id in cls.qos_specs:
diff --git a/tempest/api/volume/test_availability_zone.py b/tempest/api/volume/test_availability_zone.py
index 25fe5ad..d0a87db 100644
--- a/tempest/api/volume/test_availability_zone.py
+++ b/tempest/api/volume/test_availability_zone.py
@@ -17,12 +17,12 @@
 from tempest.lib import decorators
 
 
-class AvailabilityZoneV2TestJSON(base.BaseVolumeTest):
-    """Tests Availability Zone V2 API List"""
+class AvailabilityZoneTestJSON(base.BaseVolumeTest):
+    """Tests Availability Zone API List"""
 
     @classmethod
     def setup_clients(cls):
-        super(AvailabilityZoneV2TestJSON, cls).setup_clients()
+        super(AvailabilityZoneTestJSON, cls).setup_clients()
         cls.client = cls.availability_zone_client
 
     @decorators.idempotent_id('01f1ae88-eba9-4c6b-a011-6f7ace06b725')
@@ -30,8 +30,4 @@
         # List of availability zone
         availability_zone = (self.client.list_availability_zones()
                              ['availabilityZoneInfo'])
-        self.assertGreater(len(availability_zone), 0)
-
-
-class AvailabilityZoneV1TestJSON(AvailabilityZoneV2TestJSON):
-    _api_version = 1
+        self.assertNotEmpty(availability_zone)
diff --git a/tempest/api/volume/test_extensions.py b/tempest/api/volume/test_extensions.py
index ca86748..39ce00c 100644
--- a/tempest/api/volume/test_extensions.py
+++ b/tempest/api/volume/test_extensions.py
@@ -25,14 +25,14 @@
 LOG = logging.getLogger(__name__)
 
 
-class ExtensionsV2TestJSON(base.BaseVolumeTest):
+class ExtensionsTestJSON(base.BaseVolumeTest):
 
     @decorators.idempotent_id('94607eb0-43a5-47ca-82aa-736b41bd2e2c')
     def test_list_extensions(self):
         # List of all extensions
         extensions = (self.volumes_extension_client.list_extensions()
                       ['extensions'])
-        if len(CONF.volume_feature_enabled.api_extensions) == 0:
+        if not CONF.volume_feature_enabled.api_extensions:
             raise self.skipException('There are not any extensions configured')
         extension_list = [extension.get('alias') for extension in extensions]
         LOG.debug("Cinder extensions: %s", ','.join(extension_list))
@@ -43,7 +43,3 @@
             self.assertIn(ext, map(lambda x: x['alias'], extensions))
         else:
             raise self.skipException('There are not any extensions configured')
-
-
-class ExtensionsV1TestJSON(ExtensionsV2TestJSON):
-    _api_version = 1
diff --git a/tempest/api/volume/v2/test_image_metadata.py b/tempest/api/volume/test_image_metadata.py
similarity index 74%
rename from tempest/api/volume/v2/test_image_metadata.py
rename to tempest/api/volume/test_image_metadata.py
index 9c082b3..53b3acc 100644
--- a/tempest/api/volume/v2/test_image_metadata.py
+++ b/tempest/api/volume/test_image_metadata.py
@@ -16,24 +16,31 @@
 from testtools import matchers
 
 from tempest.api.volume import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
 
-class VolumesV2ImageMetadata(base.BaseVolumeTest):
+class VolumesImageMetadata(base.BaseVolumeTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesImageMetadata, cls).skip_checks()
+        if not CONF.service_available.glance:
+            skip_msg = ("%s skipped as Glance is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesV2ImageMetadata, cls).resource_setup()
+        super(VolumesImageMetadata, cls).resource_setup()
         # Create a volume from image ID
         cls.volume = cls.create_volume(imageRef=CONF.compute.image_ref)
 
     @decorators.idempotent_id('03efff0b-5c75-4822-8f10-8789ac15b13e')
-    @test.services('image')
-    def test_update_image_metadata(self):
+    @utils.services('image')
+    def test_update_show_delete_image_metadata(self):
         # Update image metadata
         image_metadata = {'image_id': '5137a025-3c5f-43c1-bc64-5f41270040a5',
                           'image_name': 'image',
@@ -42,7 +49,7 @@
         self.volumes_client.update_volume_image_metadata(self.volume['id'],
                                                          **image_metadata)
 
-        # Fetch image metadata from the volume
+        # Fetch volume's image metadata by show_volume method
         volume_image_metadata = self.volumes_client.show_volume(
             self.volume['id'])['volume']['volume_image_metadata']
 
@@ -55,9 +62,9 @@
                                                          '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']
+        # Fetch volume's image metadata by show_volume_image_metadata method
+        volume_image_metadata = self.volumes_client.show_volume_image_metadata(
+            self.volume['id'])['metadata']
 
         # Verify image metadata was updated after item deletion
         self.assertThat(volume_image_metadata.items(),
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index d5ad8d2..e6fe25d 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -22,16 +22,16 @@
 CONF = config.CONF
 
 
-class SnapshotV2MetadataTestJSON(base.BaseVolumeTest):
+class SnapshotMetadataTestJSON(base.BaseVolumeTest):
     @classmethod
     def skip_checks(cls):
-        super(SnapshotV2MetadataTestJSON, cls).skip_checks()
+        super(SnapshotMetadataTestJSON, cls).skip_checks()
         if not CONF.volume_feature_enabled.snapshot:
             raise cls.skipException("Cinder snapshot feature disabled")
 
     @classmethod
     def resource_setup(cls):
-        super(SnapshotV2MetadataTestJSON, cls).resource_setup()
+        super(SnapshotMetadataTestJSON, cls).resource_setup()
         # Create a volume
         cls.volume = cls.create_volume()
         # Create a snapshot
@@ -41,7 +41,7 @@
         # Update the metadata to {}
         self.snapshots_client.update_snapshot_metadata(
             self.snapshot['id'], metadata={})
-        super(SnapshotV2MetadataTestJSON, self).tearDown()
+        super(SnapshotMetadataTestJSON, self).tearDown()
 
     @decorators.idempotent_id('a2f20f99-e363-4584-be97-bc33afb1a56c')
     def test_crud_snapshot_metadata(self):
@@ -55,6 +55,7 @@
         # Create metadata
         body = self.snapshots_client.create_snapshot_metadata(
             self.snapshot['id'], metadata)['metadata']
+        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
 
         # Get the metadata of the snapshot
         body = self.snapshots_client.show_snapshot_metadata(
@@ -65,6 +66,7 @@
         # Update metadata
         body = self.snapshots_client.update_snapshot_metadata(
             self.snapshot['id'], metadata=update)['metadata']
+        self.assertEqual(update, body)
         body = self.snapshots_client.show_snapshot_metadata(
             self.snapshot['id'])['metadata']
         self.assertEqual(update, body, 'Update snapshot metadata failed')
@@ -79,7 +81,7 @@
         self.assertNotIn("key3", body)
 
     @decorators.idempotent_id('e8ff85c5-8f97-477f-806a-3ac364a949ed')
-    def test_update_snapshot_metadata_item(self):
+    def test_update_show_snapshot_metadata_item(self):
         # Update metadata item for the snapshot
         metadata = {"key1": "value1",
                     "key2": "value2",
@@ -89,8 +91,8 @@
                   "key2": "value2",
                   "key3": "value3_update"}
         # Create metadata for the snapshot
-        body = self.snapshots_client.create_snapshot_metadata(
-            self.snapshot['id'], metadata)['metadata']
+        self.snapshots_client.create_snapshot_metadata(
+            self.snapshot['id'], metadata)
         # Get the metadata of the snapshot
         body = self.snapshots_client.show_snapshot_metadata(
             self.snapshot['id'])['metadata']
@@ -98,11 +100,14 @@
         # Update metadata item
         body = self.snapshots_client.update_snapshot_metadata_item(
             self.snapshot['id'], "key3", meta=update_item)['meta']
+        self.assertEqual(update_item, body)
+
+        # Get a specific metadata item of the snapshot
+        body = self.snapshots_client.show_snapshot_metadata_item(
+            self.snapshot['id'], "key3")['meta']
+        self.assertEqual({"key3": expect['key3']}, body)
+
         # Get the metadata of the snapshot
         body = self.snapshots_client.show_snapshot_metadata(
             self.snapshot['id'])['metadata']
         self.assertThat(body.items(), matchers.ContainsAll(expect.items()))
-
-
-class SnapshotV1MetadataTestJSON(SnapshotV2MetadataTestJSON):
-    _api_version = 1
diff --git a/tempest/api/volume/test_versions.py b/tempest/api/volume/test_versions.py
new file mode 100644
index 0000000..b4d48db
--- /dev/null
+++ b/tempest/api/volume/test_versions.py
@@ -0,0 +1,29 @@
+# Copyright 2017 NEC Corporation.  All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.volume import base
+from tempest.lib import decorators
+
+
+class VersionsTest(base.BaseVolumeTest):
+
+    _api_version = 3
+
+    @decorators.idempotent_id('77838fc4-b49b-4c64-9533-166762517369')
+    @decorators.attr(type='smoke')
+    def test_list_versions(self):
+        # NOTE: The version data is checked on service client side
+        #       with JSON-Schema validation. It is enough to just call
+        #       the API here.
+        self.versions_client.list_versions()
diff --git a/tempest/api/volume/test_volume_absolute_limits.py b/tempest/api/volume/test_volume_absolute_limits.py
index 36445b9..4018468 100644
--- a/tempest/api/volume/test_volume_absolute_limits.py
+++ b/tempest/api/volume/test_volume_absolute_limits.py
@@ -21,14 +21,17 @@
 CONF = config.CONF
 
 
-class AbsoluteLimitsV2Tests(base.BaseVolumeTest):
+# NOTE(zhufl): This inherits from BaseVolumeAdminTest because
+# it requires force_tenant_isolation=True, which need admin
+# credentials to create non-admin users for the tests.
+class AbsoluteLimitsTests(base.BaseVolumeAdminTest):  # noqa
 
     # avoid existing volumes of pre-defined tenant
     force_tenant_isolation = True
 
     @classmethod
     def resource_setup(cls):
-        super(AbsoluteLimitsV2Tests, cls).resource_setup()
+        super(AbsoluteLimitsTests, cls).resource_setup()
         # Create a shared volume for tests
         cls.volume = cls.create_volume()
 
@@ -46,7 +49,3 @@
         self.assertEqual(absolute_limits['totalSnapshotsUsed'], 0)
         self.assertEqual(absolute_limits['totalBackupsUsed'], 0)
         self.assertEqual(absolute_limits['totalBackupGigabytesUsed'], 0)
-
-
-class AbsoluteLimitsV1Tests(AbsoluteLimitsV2Tests):
-    _api_version = 1
diff --git a/tempest/api/volume/test_volume_delete_cascade.py b/tempest/api/volume/test_volume_delete_cascade.py
new file mode 100644
index 0000000..bb32c11
--- /dev/null
+++ b/tempest/api/volume/test_volume_delete_cascade.py
@@ -0,0 +1,101 @@
+# 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
+
+import testtools
+
+from tempest.api.volume import base
+from tempest import config
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class VolumesDeleteCascade(base.BaseVolumeTest):
+    """Delete a volume with associated snapshots.
+
+    Cinder provides the ability to delete a volume with its
+    associated snapshots.
+    It is allow a volume and its snapshots to be removed in one operation
+    both for usability and performance reasons.
+    """
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesDeleteCascade, cls).skip_checks()
+        if not CONF.volume_feature_enabled.snapshot:
+            raise cls.skipException("Cinder snapshot feature disabled")
+
+    def _assert_cascade_delete(self, volume_id):
+        # Fetch volume ids
+        volume_list = [
+            vol['id'] for vol in
+            self.volumes_client.list_volumes()['volumes']
+            ]
+
+        # Verify the parent volume was deleted
+        self.assertNotIn(volume_id, volume_list)
+
+        # List snapshots
+        snapshot_list = self.snapshots_client.list_snapshots()['snapshots']
+
+        # Verify snapshots were deleted
+        self.assertNotIn(volume_id, map(operator.itemgetter('volume_id'),
+                                        snapshot_list))
+
+    @decorators.idempotent_id('994e2d40-de37-46e8-b328-a58fba7e4a95')
+    def test_volume_delete_cascade(self):
+        # The case validates the ability to delete a volume
+        # with associated snapshots.
+
+        # Create a volume
+        volume = self.create_volume()
+
+        for _ in range(2):
+            self.create_snapshot(volume['id'])
+
+        # Delete the parent volume with associated snapshots
+        self.volumes_client.delete_volume(volume['id'], cascade=True)
+        self.volumes_client.wait_for_resource_deletion(volume['id'])
+
+        # Verify volume parent was deleted with its associated snapshots
+        self._assert_cascade_delete(volume['id'])
+
+    @decorators.idempotent_id('59a77ede-609b-4ee8-9f68-fc3c6ffe97b5')
+    @testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
+                      'Skip because of Bug#1677525')
+    def test_volume_from_snapshot_cascade_delete(self):
+        # The case validates the ability to delete a volume with
+        # associated snapshot while there is another volume created
+        # from that snapshot.
+
+        # Create a volume
+        volume = self.create_volume()
+
+        snapshot = self.create_snapshot(volume['id'])
+
+        # Create volume from snapshot
+        volume_snap = self.create_volume(snapshot_id=snapshot['id'])
+        volume_details = self.volumes_client.show_volume(
+            volume_snap['id'])['volume']
+        self.assertEqual(snapshot['id'], volume_details['snapshot_id'])
+
+        # Delete the parent volume with associated snapshot
+        self.volumes_client.delete_volume(volume['id'], cascade=True)
+        self.volumes_client.wait_for_resource_deletion(volume['id'])
+
+        # Verify volume parent was deleted with its associated snapshot
+        self._assert_cascade_delete(volume['id'])
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
index 9e7b0a7..d203b2d 100644
--- a/tempest/api/volume/test_volume_metadata.py
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -19,18 +19,18 @@
 from tempest.lib import decorators
 
 
-class VolumesV2MetadataTest(base.BaseVolumeTest):
+class VolumesMetadataTest(base.BaseVolumeTest):
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesV2MetadataTest, cls).resource_setup()
+        super(VolumesMetadataTest, cls).resource_setup()
         # Create a volume
         cls.volume = cls.create_volume()
 
     def tearDown(self):
         # Update the metadata to {}
         self.volumes_client.update_volume_metadata(self.volume['id'], {})
-        super(VolumesV2MetadataTest, self).tearDown()
+        super(VolumesMetadataTest, self).tearDown()
 
     @decorators.idempotent_id('6f5b125b-f664-44bf-910f-751591fe5769')
     def test_crud_volume_metadata(self):
@@ -45,6 +45,7 @@
 
         body = self.volumes_client.create_volume_metadata(self.volume['id'],
                                                           metadata)['metadata']
+        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
         # Get the metadata of the volume
         body = self.volumes_client.show_volume_metadata(
             self.volume['id'])['metadata']
@@ -54,6 +55,7 @@
         # Update metadata
         body = self.volumes_client.update_volume_metadata(
             self.volume['id'], update)['metadata']
+        self.assertEqual(update, body)
         body = self.volumes_client.show_volume_metadata(
             self.volume['id'])['metadata']
         self.assertEqual(update, body, 'Update metadata failed')
@@ -68,7 +70,7 @@
                         'Delete one item metadata of the volume failed')
 
     @decorators.idempotent_id('862261c5-8df4-475a-8c21-946e50e36a20')
-    def test_update_volume_metadata_item(self):
+    def test_update_show_volume_metadata_item(self):
         # Update metadata item for the volume
         metadata = {"key1": "value1",
                     "key2": "value2",
@@ -85,11 +87,14 @@
         # Update metadata item
         body = self.volumes_client.update_volume_metadata_item(
             self.volume['id'], "key3", update_item)['meta']
+        self.assertEqual(update_item, body)
+
+        # Get a specific metadata item of the volume
+        body = self.volumes_client.show_volume_metadata_item(
+            self.volume['id'], "key3")['meta']
+        self.assertEqual({"key3": expect['key3']}, body)
+
         # Get the metadata of the volume
         body = self.volumes_client.show_volume_metadata(
             self.volume['id'])['metadata']
         self.assertThat(body.items(), matchers.ContainsAll(expect.items()))
-
-
-class VolumesV1MetadataTest(VolumesV2MetadataTest):
-    _api_version = 1
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 8d94cd2..4108da5 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -13,39 +13,39 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from testtools import matchers
-
 from tempest.api.volume import base
 from tempest.common import waiters
 from tempest.lib import decorators
 
 
-class VolumesV2TransfersTest(base.BaseVolumeTest):
+class VolumesTransfersTest(base.BaseVolumeTest):
 
     credentials = ['primary', 'alt', 'admin']
 
     @classmethod
     def setup_clients(cls):
-        super(VolumesV2TransfersTest, cls).setup_clients()
+        super(VolumesTransfersTest, cls).setup_clients()
 
-        cls.client = cls.volumes_client
-        cls.alt_client = cls.os_alt.volumes_client
-        cls.alt_tenant_id = cls.alt_client.tenant_id
-        cls.adm_client = cls.os_adm.volumes_client
+        cls.client = cls.os_primary.volume_transfers_v2_client
+        cls.alt_client = cls.os_alt.volume_transfers_v2_client
+        cls.alt_volumes_client = cls.os_alt.volumes_v2_client
+        cls.adm_volumes_client = cls.os_admin.volumes_v2_client
 
     @decorators.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, self.adm_client, volume['id'])
+        self.addCleanup(self.delete_volume,
+                        self.adm_volumes_client,
+                        volume['id'])
 
         # Create a volume transfer
         transfer = self.client.create_volume_transfer(
             volume_id=volume['id'])['transfer']
         transfer_id = transfer['id']
         auth_key = transfer['auth_key']
-        waiters.wait_for_volume_status(self.client,
-                                       volume['id'], 'awaiting-transfer')
+        waiters.wait_for_volume_resource_status(
+            self.volumes_client, volume['id'], 'awaiting-transfer')
 
         # Get a volume transfer
         body = self.client.show_volume_transfer(transfer_id)['transfer']
@@ -54,39 +54,47 @@
         # List volume transfers, the result should be greater than
         # or equal to 1
         body = self.client.list_volume_transfers()['transfers']
-        self.assertThat(len(body), matchers.GreaterThan(0))
+        self.assertNotEmpty(body)
 
         # Accept a volume transfer by alt_tenant
         body = self.alt_client.accept_volume_transfer(
             transfer_id, auth_key=auth_key)['transfer']
-        waiters.wait_for_volume_status(self.alt_client,
-                                       volume['id'], 'available')
+        for key in ['id', 'name', 'links', 'volume_id']:
+            self.assertIn(key, body)
+        waiters.wait_for_volume_resource_status(self.alt_volumes_client,
+                                                volume['id'], 'available')
+        accepted_volume = self.alt_volumes_client.show_volume(
+            volume['id'])['volume']
+        self.assertEqual(self.os_alt.credentials.user_id,
+                         accepted_volume['user_id'])
+        self.assertEqual(self.os_alt.credentials.project_id,
+                         accepted_volume['os-vol-tenant-attr:tenant_id'])
 
     @decorators.idempotent_id('ab526943-b725-4c07-b875-8e8ef87a2c30')
     def test_create_list_delete_volume_transfer(self):
         # Create a volume first
         volume = self.create_volume()
-        self.addCleanup(self.delete_volume, self.adm_client, volume['id'])
+        self.addCleanup(self.delete_volume,
+                        self.adm_volumes_client,
+                        volume['id'])
 
         # Create a volume transfer
-        body = self.client.create_volume_transfer(
-            volume_id=volume['id'])['transfer']
-        transfer_id = body['id']
-        waiters.wait_for_volume_status(self.client,
-                                       volume['id'], 'awaiting-transfer')
+        transfer_id = self.client.create_volume_transfer(
+            volume_id=volume['id'])['transfer']['id']
+        waiters.wait_for_volume_resource_status(
+            self.volumes_client, volume['id'], 'awaiting-transfer')
 
-        # List all volume transfers (looking for the one we created)
-        body = self.client.list_volume_transfers()['transfers']
-        for transfer in body:
-            if volume['id'] == transfer['volume_id']:
-                break
-        else:
-            self.fail('Transfer not found for volume %s' % volume['id'])
+        # List all volume transfers with details, check the detail-specific
+        # elements, and look for the created transfer.
+        transfers = self.client.list_volume_transfers(detail=True)['transfers']
+        self.assertNotEmpty(transfers)
+        for transfer in transfers:
+            self.assertIn('created_at', transfer)
+        volume_list = [transfer['volume_id'] for transfer in transfers]
+        self.assertIn(volume['id'], volume_list,
+                      'Transfer not found for volume %s' % volume['id'])
 
         # Delete a volume transfer
         self.client.delete_volume_transfer(transfer_id)
-        waiters.wait_for_volume_status(self.client, volume['id'], 'available')
-
-
-class VolumesV1TransfersTest(VolumesV2TransfersTest):
-    _api_version = 1
+        waiters.wait_for_volume_resource_status(
+            self.volumes_client, volume['id'], 'available')
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index c0cc74d..be5638e 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -14,65 +14,49 @@
 #    under the License.
 
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
-from tempest.lib import exceptions
-from tempest import test
 
 CONF = config.CONF
 
 
-class VolumesV2ActionsTest(base.BaseVolumeTest):
-
-    @classmethod
-    def setup_clients(cls):
-        super(VolumesV2ActionsTest, cls).setup_clients()
-        cls.client = cls.volumes_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].')
+class VolumesActionsTest(base.BaseVolumeTest):
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesV2ActionsTest, cls).resource_setup()
+        super(VolumesActionsTest, cls).resource_setup()
 
         # Create a test shared volume for attach/detach tests
         cls.volume = cls.create_volume()
 
     @decorators.idempotent_id('fff42874-7db5-4487-a8e1-ddda5fb5288d')
-    @test.attr(type='smoke')
-    @test.services('compute')
+    @decorators.attr(type='smoke')
+    @utils.services('compute')
     def test_attach_detach_volume_to_instance(self):
         # Create a server
-        server = self.create_server(wait_until='ACTIVE')
+        server = self.create_server()
         # Volume is attached and detached successfully from an instance
-        self.client.attach_volume(self.volume['id'],
-                                  instance_uuid=server['id'],
-                                  mountpoint='/dev/%s' %
-                                             CONF.compute.volume_device_name)
-        waiters.wait_for_volume_status(self.client,
-                                       self.volume['id'], 'in-use')
-        self.client.detach_volume(self.volume['id'])
-        waiters.wait_for_volume_status(self.client,
-                                       self.volume['id'], 'available')
+        self.volumes_client.attach_volume(self.volume['id'],
+                                          instance_uuid=server['id'],
+                                          mountpoint='/dev/%s' %
+                                          CONF.compute.volume_device_name)
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                self.volume['id'], 'in-use')
+        self.volumes_client.detach_volume(self.volume['id'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                self.volume['id'], 'available')
 
     @decorators.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
     def test_volume_bootable(self):
         # Verify that a volume bootable flag is retrieved
         for bool_bootable in [True, False]:
-            self.client.set_bootable_volume(self.volume['id'],
-                                            bootable=bool_bootable)
-            fetched_volume = self.client.show_volume(
+            self.volumes_client.set_bootable_volume(self.volume['id'],
+                                                    bootable=bool_bootable)
+            fetched_volume = self.volumes_client.show_volume(
                 self.volume['id'])['volume']
             # Get Volume information
             # NOTE(masayukig): 'bootable' is "true" or "false" in the current
@@ -82,22 +66,23 @@
                              fetched_volume['bootable'])
 
     @decorators.idempotent_id('9516a2c8-9135-488c-8dd6-5677a7e5f371')
-    @test.services('compute')
+    @utils.services('compute')
     def test_get_volume_attachment(self):
         # Create a server
-        server = self.create_server(wait_until='ACTIVE')
+        server = self.create_server()
         # Verify that a volume's attachment information is retrieved
-        self.client.attach_volume(self.volume['id'],
-                                  instance_uuid=server['id'],
-                                  mountpoint='/dev/%s' %
-                                             CONF.compute.volume_device_name)
-        waiters.wait_for_volume_status(self.client,
-                                       self.volume['id'], 'in-use')
-        self.addCleanup(waiters.wait_for_volume_status, self.client,
-                        self.volume['id'],
-                        'available')
-        self.addCleanup(self.client.detach_volume, self.volume['id'])
-        volume = self.client.show_volume(self.volume['id'])['volume']
+        self.volumes_client.attach_volume(self.volume['id'],
+                                          instance_uuid=server['id'],
+                                          mountpoint='/dev/%s' %
+                                          CONF.compute.volume_device_name)
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                self.volume['id'],
+                                                'in-use')
+        self.addCleanup(waiters.wait_for_volume_resource_status,
+                        self.volumes_client,
+                        self.volume['id'], 'available')
+        self.addCleanup(self.volumes_client.detach_volume, self.volume['id'])
+        volume = self.volumes_client.show_volume(self.volume['id'])['volume']
         self.assertIn('attachments', volume)
         attachment = volume['attachments'][0]
 
@@ -109,52 +94,52 @@
         self.assertEqual(self.volume['id'], attachment['volume_id'])
 
     @decorators.idempotent_id('d8f1ca95-3d5b-44a3-b8ca-909691c9532d')
-    @test.services('image')
+    @utils.services('image')
     def test_volume_upload(self):
         # NOTE(gfidente): the volume uploaded in Glance comes from setUpClass,
         # it is shared with the other tests. After it is uploaded in Glance,
         # there is no way to delete it from Cinder, so we delete it from Glance
-        # using the Glance image_client and from Cinder via tearDownClass.
+        # using the Glance images_client and from Cinder via tearDownClass.
         image_name = data_utils.rand_name(self.__class__.__name__ + '-Image')
-        body = self.client.upload_volume(
+        body = self.volumes_client.upload_volume(
             self.volume['id'], image_name=image_name,
             disk_format=CONF.volume.disk_format)['os-volume_upload_image']
         image_id = body["image_id"]
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.image_client.delete_image,
+                        self.images_client.delete_image,
                         image_id)
-        waiters.wait_for_image_status(self.image_client, image_id, 'active')
-        waiters.wait_for_volume_status(self.client,
-                                       self.volume['id'], 'available')
+        waiters.wait_for_image_status(self.images_client, image_id, 'active')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                self.volume['id'], 'available')
+
+        image_info = self.images_client.show_image(image_id)
+        self.assertEqual(image_name, image_info['name'])
+        self.assertEqual(CONF.volume.disk_format, image_info['disk_format'])
 
     @decorators.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
     def test_reserve_unreserve_volume(self):
         # Mark volume as reserved.
-        body = self.client.reserve_volume(self.volume['id'])
+        self.volumes_client.reserve_volume(self.volume['id'])
         # To get the volume info
-        body = self.client.show_volume(self.volume['id'])['volume']
+        body = self.volumes_client.show_volume(self.volume['id'])['volume']
         self.assertIn('attaching', body['status'])
         # Unmark volume as reserved.
-        body = self.client.unreserve_volume(self.volume['id'])
+        self.volumes_client.unreserve_volume(self.volume['id'])
         # To get the volume info
-        body = self.client.show_volume(self.volume['id'])['volume']
+        body = self.volumes_client.show_volume(self.volume['id'])['volume']
         self.assertIn('available', body['status'])
 
     @decorators.idempotent_id('fff74e1e-5bd3-4b33-9ea9-24c103bc3f59')
     def test_volume_readonly_update(self):
         for readonly in [True, False]:
             # Update volume readonly
-            self.client.update_volume_readonly(self.volume['id'],
-                                               readonly=readonly)
+            self.volumes_client.update_volume_readonly(self.volume['id'],
+                                                       readonly=readonly)
             # Get Volume information
-            fetched_volume = self.client.show_volume(
+            fetched_volume = self.volumes_client.show_volume(
                 self.volume['id'])['volume']
             # NOTE(masayukig): 'readonly' is "True" or "False" in the current
             # cinder implementation. So we need to cast boolean values to str
             # to compare here.
             self.assertEqual(str(readonly),
                              fetched_volume['metadata']['readonly'])
-
-
-class VolumesV1ActionsTest(VolumesV2ActionsTest):
-    _api_version = 1
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index 939f1ac..1e240b8 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -13,21 +13,24 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+from testtools import matchers
+
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
 
-class VolumesBackupsV2Test(base.BaseVolumeTest):
+class VolumesBackupsTest(base.BaseVolumeTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesBackupsV2Test, cls).skip_checks()
+        super(VolumesBackupsTest, cls).skip_checks()
         if not CONF.volume_feature_enabled.backup:
             raise cls.skipException("Cinder backup feature disabled")
 
@@ -40,44 +43,64 @@
         self.addCleanup(self.volumes_client.delete_volume,
                         restored_volume['volume_id'])
         self.assertEqual(backup_id, restored_volume['backup_id'])
-        waiters.wait_for_backup_status(self.backups_client,
-                                       backup_id, 'available')
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       restored_volume['volume_id'],
-                                       'available')
+        waiters.wait_for_volume_resource_status(self.backups_client,
+                                                backup_id, 'available')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                restored_volume['volume_id'],
+                                                'available')
         return restored_volume
 
+    @testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
+                      'ceph does not support arbitrary container names')
     @decorators.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
     def test_volume_backup_create_get_detailed_list_restore_delete(self):
-        # Create backup
-        volume = self.create_volume()
+        # Create a volume with metadata
+        metadata = {"vol-meta1": "value1",
+                    "vol-meta2": "value2",
+                    "vol-meta3": "value3"}
+        volume = self.create_volume(metadata=metadata)
         self.addCleanup(self.volumes_client.delete_volume,
                         volume['id'])
+
+        # Create a backup
         backup_name = data_utils.rand_name(
             self.__class__.__name__ + '-Backup')
         description = data_utils.rand_name("volume-backup-description")
         backup = self.create_backup(volume_id=volume['id'],
                                     name=backup_name,
-                                    description=description)
+                                    description=description,
+                                    container='container')
         self.assertEqual(backup_name, backup['name'])
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume['id'], 'available')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
 
         # Get a given backup
         backup = self.backups_client.show_backup(backup['id'])['backup']
         self.assertEqual(backup_name, backup['name'])
         self.assertEqual(description, backup['description'])
+        self.assertEqual('container', backup['container'])
 
         # Get all backups with detail
         backups = self.backups_client.list_backups(
             detail=True)['backups']
+        for backup_info in backups:
+            self.assertIn('created_at', backup_info)
+            self.assertIn('links', backup_info)
         self.assertIn((backup['name'], backup['id']),
                       [(m['name'], m['id']) for m in backups])
 
-        self.restore_backup(backup['id'])
+        restored_volume = self.restore_backup(backup['id'])
+
+        restored_volume_metadata = self.volumes_client.show_volume(
+            restored_volume['volume_id'])['volume']['metadata']
+
+        # Verify the backups has been restored successfully
+        # with the metadata of the source volume.
+        self.assertThat(restored_volume_metadata.items(),
+                        matchers.ContainsAll(metadata.items()))
 
     @decorators.idempotent_id('07af8f6d-80af-44c9-a5dc-c8427b1b62e6')
-    @test.services('compute')
+    @utils.services('compute')
     def test_backup_create_attached_volume(self):
         """Test backup create using force flag.
 
@@ -88,7 +111,7 @@
         volume = self.create_volume()
         self.addCleanup(self.volumes_client.delete_volume,
                         volume['id'])
-        server = self.create_server(wait_until='ACTIVE')
+        server = self.create_server()
         # Attach volume to instance
         self.attach_volume(server['id'], volume['id'])
         # Create backup using force flag
@@ -99,6 +122,7 @@
         self.assertEqual(backup_name, backup['name'])
 
     @decorators.idempotent_id('2a8ba340-dff2-4511-9db7-646f07156b15')
+    @utils.services('image')
     def test_bootable_volume_backup_and_restore(self):
         # Create volume from image
         img_uuid = CONF.compute.image_ref
@@ -121,5 +145,37 @@
         self.assertEqual('true', restored_volume_info['bootable'])
 
 
-class VolumesBackupsV1Test(VolumesBackupsV2Test):
-    _api_version = 1
+class VolumesBackupsV39Test(base.BaseVolumeTest):
+
+    _api_version = 3
+    min_microversion = '3.9'
+    max_microversion = 'latest'
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesBackupsV39Test, cls).skip_checks()
+        if not CONF.volume_feature_enabled.backup:
+            raise cls.skipException("Cinder backup feature disabled")
+
+    @decorators.idempotent_id('9b374cbc-be5f-4d37-8848-7efb8a873dcc')
+    def test_update_backup(self):
+        # Create volume and backup
+        volume = self.create_volume()
+        backup = self.create_backup(volume_id=volume['id'])
+
+        # Update backup and assert response body for update_backup method
+        update_kwargs = {
+            'name': data_utils.rand_name(self.__class__.__name__ + '-Backup'),
+            'description': data_utils.rand_name("volume-backup-description")
+        }
+        update_backup = self.backups_client.update_backup(
+            backup['id'], **update_kwargs)['backup']
+        self.assertEqual(backup['id'], update_backup['id'])
+        self.assertEqual(update_kwargs['name'], update_backup['name'])
+        self.assertIn('links', update_backup)
+
+        # Assert response body for show_backup method
+        retrieved_backup = self.backups_client.show_backup(
+            backup['id'])['backup']
+        for key in update_kwargs:
+            self.assertEqual(update_kwargs[key], retrieved_backup[key])
diff --git a/tempest/api/volume/test_volumes_clone.py b/tempest/api/volume/test_volumes_clone.py
index 79a1a0a..ea39a21 100644
--- a/tempest/api/volume/test_volumes_clone.py
+++ b/tempest/api/volume/test_volumes_clone.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from tempest.api.volume import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
 
@@ -21,14 +22,26 @@
 CONF = config.CONF
 
 
-class VolumesV2CloneTest(base.BaseVolumeTest):
+class VolumesCloneTest(base.BaseVolumeTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesV2CloneTest, cls).skip_checks()
+        super(VolumesCloneTest, cls).skip_checks()
         if not CONF.volume_feature_enabled.clone:
             raise cls.skipException("Cinder volume clones are disabled")
 
+    def _verify_volume_clone(self, source_volume, cloned_volume,
+                             bootable='false', extra_size=0):
+
+        cloned_vol_details = self.volumes_client.show_volume(
+            cloned_volume['id'])['volume']
+
+        self.assertEqual(source_volume['id'],
+                         cloned_vol_details['source_volid'])
+        self.assertEqual(source_volume['size'] + extra_size,
+                         cloned_vol_details['size'])
+        self.assertEqual(bootable, cloned_vol_details['bootable'])
+
     @decorators.idempotent_id('9adae371-a257-43a5-9555-dc7c88e66e0e')
     def test_create_from_volume(self):
         # Creates a volume from another volume passing a size different from
@@ -40,12 +53,10 @@
         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)
+        self._verify_volume_clone(src_vol, dst_vol, extra_size=1)
 
     @decorators.idempotent_id('cbbcd7c6-5a6c-481a-97ac-ca55ab715d16')
+    @utils.services('image')
     def test_create_from_bootable_volume(self):
         # Create volume from image
         img_uuid = CONF.compute.image_ref
@@ -53,14 +64,5 @@
 
         # Create a volume from the bootable volume
         cloned_vol = self.create_volume(source_volid=src_vol['id'])
-        cloned_vol_details = self.volumes_client.show_volume(
-            cloned_vol['id'])['volume']
 
-        # Verify cloned volume creation as expected
-        self.assertEqual('true', cloned_vol_details['bootable'])
-        self.assertEqual(src_vol['id'], cloned_vol_details['source_volid'])
-        self.assertEqual(src_vol['size'], cloned_vol_details['size'])
-
-
-class VolumesV1CloneTest(VolumesV2CloneTest):
-    _api_version = 1
+        self._verify_volume_clone(src_vol, cloned_vol, bootable='true')
diff --git a/tempest/api/volume/test_volumes_clone_negative.py b/tempest/api/volume/test_volumes_clone_negative.py
index fa827cd..bba7a0b 100644
--- a/tempest/api/volume/test_volumes_clone_negative.py
+++ b/tempest/api/volume/test_volumes_clone_negative.py
@@ -21,14 +21,15 @@
 CONF = config.CONF
 
 
-class VolumesV2CloneNegativeTest(base.BaseVolumeTest):
+class VolumesCloneNegativeTest(base.BaseVolumeTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesV2CloneNegativeTest, cls).skip_checks()
+        super(VolumesCloneNegativeTest, cls).skip_checks()
         if not CONF.volume_feature_enabled.clone:
             raise cls.skipException("Cinder volume clones are disabled")
 
+    @decorators.attr(type=['negative'])
     @decorators.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
@@ -41,7 +42,3 @@
                           self.volumes_client.create_volume,
                           size=src_size - 1,
                           source_volid=src_vol['id'])
-
-
-class VolumesV1CloneNegativeTest(VolumesV2CloneNegativeTest):
-    _api_version = 1
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 20118df..1eb76a0 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -13,25 +13,43 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.volume import base
 from tempest.common import waiters
+from tempest import config
 from tempest.lib import decorators
 
+CONF = config.CONF
 
-class VolumesV2ExtendTest(base.BaseVolumeTest):
+
+class VolumesExtendTest(base.BaseVolumeTest):
 
     @decorators.idempotent_id('9a36df71-a257-43a5-9555-dc7c88e66e0e')
     def test_volume_extend(self):
         # Extend Volume Test.
-        self.volume = self.create_volume()
-        extend_size = int(self.volume['size']) + 1
-        self.volumes_client.extend_volume(self.volume['id'],
+        volume = self.create_volume()
+        extend_size = volume['size'] + 1
+        self.volumes_client.extend_volume(volume['id'],
                                           new_size=extend_size)
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       self.volume['id'], 'available')
-        volume = self.volumes_client.show_volume(self.volume['id'])['volume']
-        self.assertEqual(int(volume['size']), extend_size)
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
+        volume = self.volumes_client.show_volume(volume['id'])['volume']
+        self.assertEqual(volume['size'], extend_size)
 
+    @decorators.idempotent_id('86be1cba-2640-11e5-9c82-635fb964c912')
+    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+                          "Cinder volume snapshots are disabled")
+    @decorators.skip_because(bug='1687044')
+    def test_volume_extend_when_volume_has_snapshot(self):
+        volume = self.create_volume()
+        self.create_snapshot(volume['id'])
 
-class VolumesV1ExtendTest(VolumesV2ExtendTest):
-    _api_version = 1
+        extend_size = volume['size'] + 1
+        self.volumes_client.extend_volume(volume['id'], new_size=extend_size)
+
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
+        resized_volume = self.volumes_client.show_volume(
+            volume['id'])['volume']
+        self.assertEqual(extend_size, resized_volume['size'])
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index d1a1c2f..71db95c 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -17,34 +17,31 @@
 from testtools import matchers
 
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
 
-class VolumesV2GetTest(base.BaseVolumeTest):
+class VolumesGetTest(base.BaseVolumeTest):
 
     def _volume_create_get_update_delete(self, **kwargs):
-        name_field = self.special_fields['name_field']
-        descrip_field = self.special_fields['descrip_field']
-
         # Create a volume, Get it's details and Delete the volume
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'Test'}
         # Create a volume
-        kwargs[name_field] = v_name
+        kwargs['name'] = v_name
         kwargs['metadata'] = metadata
         volume = self.volumes_client.create_volume(**kwargs)['volume']
         self.assertIn('id', volume)
         self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
-        waiters.wait_for_volume_status(self.volumes_client, volume['id'],
-                                       'available')
-        self.assertIn(name_field, volume)
-        self.assertEqual(volume[name_field], v_name,
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
+        self.assertIn('name', volume)
+        self.assertEqual(volume['name'], v_name,
                          "The created volume name is not equal "
                          "to the requested name")
 
@@ -52,7 +49,7 @@
         fetched_volume = self.volumes_client.show_volume(
             volume['id'])['volume']
         self.assertEqual(v_name,
-                         fetched_volume[name_field],
+                         fetched_volume['name'],
                          'The fetched Volume name is different '
                          'from the created Volume')
         self.assertEqual(volume['id'],
@@ -71,25 +68,25 @@
 
         # Update Volume
         # Test volume update when display_name is same with original value
-        params = {name_field: v_name}
+        params = {'name': v_name}
         self.volumes_client.update_volume(volume['id'], **params)
         # Test volume update when display_name is new
         new_v_name = data_utils.rand_name(
             self.__class__.__name__ + '-new-Volume')
         new_desc = 'This is the new description of volume'
-        params = {name_field: new_v_name,
-                  descrip_field: new_desc}
+        params = {'name': new_v_name,
+                  'description': new_desc}
         update_volume = self.volumes_client.update_volume(
             volume['id'], **params)['volume']
         # Assert response body for update_volume method
-        self.assertEqual(new_v_name, update_volume[name_field])
-        self.assertEqual(new_desc, update_volume[descrip_field])
+        self.assertEqual(new_v_name, update_volume['name'])
+        self.assertEqual(new_desc, update_volume['description'])
         # Assert response body for show_volume method
         updated_volume = self.volumes_client.show_volume(
             volume['id'])['volume']
         self.assertEqual(volume['id'], updated_volume['id'])
-        self.assertEqual(new_v_name, updated_volume[name_field])
-        self.assertEqual(new_desc, updated_volume[descrip_field])
+        self.assertEqual(new_v_name, updated_volume['name'])
+        self.assertEqual(new_desc, updated_volume['description'])
         self.assertThat(updated_volume['metadata'].items(),
                         matchers.ContainsAll(metadata.items()),
                         'The fetched Volume metadata misses data '
@@ -99,18 +96,18 @@
         # contains specific characters,
         # then test volume update if display_name is duplicated
         new_v_desc = data_utils.rand_name('@#$%^* description')
-        params = {descrip_field: new_v_desc,
+        params = {'description': new_v_desc,
                   'availability_zone': volume['availability_zone'],
                   'size': CONF.volume.volume_size}
         new_volume = self.volumes_client.create_volume(**params)['volume']
         self.assertIn('id', new_volume)
         self.addCleanup(self.delete_volume, self.volumes_client,
                         new_volume['id'])
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       new_volume['id'], 'available')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                new_volume['id'], 'available')
 
-        params = {name_field: volume[name_field],
-                  descrip_field: volume[descrip_field]}
+        params = {'name': volume['name'],
+                  'description': volume['description']}
         self.volumes_client.update_volume(new_volume['id'], **params)
 
         if 'imageRef' in kwargs:
@@ -118,18 +115,17 @@
         else:
             self.assertEqual('false', updated_volume['bootable'])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('27fb0e9f-fb64-41dd-8bdb-1ffa762f0d51')
     def test_volume_create_get_update_delete(self):
         self._volume_create_get_update_delete(size=CONF.volume.volume_size)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('54a01030-c7fc-447c-86ee-c1182beae638')
-    @test.services('image')
+    @utils.services('image')
     def test_volume_create_get_update_delete_from_image(self):
-        image = self.compute_images_client.show_image(
-            CONF.compute.image_ref)['image']
-        min_disk = image.get('minDisk')
+        image = self.images_client.show_image(CONF.compute.image_ref)
+        min_disk = image['min_disk']
         disk_size = max(min_disk, CONF.volume.volume_size)
         self._volume_create_get_update_delete(
             imageRef=CONF.compute.image_ref, size=disk_size)
@@ -143,5 +139,15 @@
                                               size=CONF.volume.volume_size)
 
 
-class VolumesV1GetTest(VolumesV2GetTest):
-    _api_version = 1
+class VolumesSummaryTest(base.BaseVolumeTest):
+
+    _api_version = 3
+    min_microversion = '3.12'
+    max_microversion = 'latest'
+
+    @decorators.idempotent_id('c4f2431e-4920-4736-9e00-4040386b6feb')
+    def test_show_volume_summary(self):
+        volume_summary = \
+            self.volumes_client.show_volume_summary()['volume-summary']
+        for key in ['total_size', 'total_count']:
+            self.assertIn(key, volume_summary)
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index d1da95d..b5f98ea 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -13,17 +13,19 @@
 #    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
 
+import operator
+import random
+
+from six.moves.urllib import parse
 from testtools import matchers
 
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
-class VolumesV2ListTestJSON(base.BaseVolumeTest):
+class VolumesListTestJSON(base.BaseVolumeTest):
     # NOTE: This test creates a number of 1G volumes. To run successfully,
     # ensure that the backing file for the volume group that Nova uses
     # has space for at least 3 1G volumes!
@@ -44,11 +46,11 @@
             fetched_list = [fieldsgetter(item) for item in fetched_list]
 
         missing_vols = [v for v in expected_list if v not in fetched_list]
-        if len(missing_vols) == 0:
+        if not missing_vols:
             return
 
         def str_vol(vol):
-            return "%s:%s" % (vol['id'], vol[self.name])
+            return "%s:%s" % (vol['id'], vol['name'])
 
         raw_msg = "Could not find volumes %s in expected list %s; fetched %s"
         self.fail(raw_msg % ([str_vol(v) for v in missing_vols],
@@ -57,8 +59,11 @@
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesV2ListTestJSON, cls).resource_setup()
-        cls.name = cls.VOLUME_FIELDS[1]
+        super(VolumesListTestJSON, cls).resource_setup()
+
+        existing_volumes = cls.volumes_client.list_volumes()['volumes']
+        cls.volume_id_list = [vol['id'] for vol in existing_volumes]
+
         # Create 3 test volumes
         cls.volume_list = []
         cls.metadata = {'Type': 'work'}
@@ -66,6 +71,7 @@
             volume = cls.create_volume(metadata=cls.metadata)
             volume = cls.volumes_client.show_volume(volume['id'])['volume']
             cls.volume_list.append(volume)
+            cls.volume_id_list.append(volume['id'])
 
     def _list_by_param_value_and_assert(self, params, with_detail=False):
         """list or list_details with given params and validates result"""
@@ -78,11 +84,7 @@
                 params=params)['volumes']
 
         # Validating params of fetched volumes
-        # In v2, only list detail view includes items in params.
-        # In v1, list view and list detail view are same. So the
-        # following check should be run when 'with_detail' is True
-        # or v1 tests.
-        if with_detail or self._api_version == 1:
+        if with_detail:
             for volume in fetched_vol_list:
                 for key in params:
                     msg = "Failed to list volumes %s by %s" % \
@@ -95,7 +97,7 @@
                     else:
                         self.assertEqual(params[key], volume[key], msg)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('0b6ddd39-b948-471f-8038-4787978747c4')
     def test_volume_list(self):
         # Get a list of Volumes
@@ -114,22 +116,20 @@
     @decorators.idempotent_id('a28e8da4-0b56-472f-87a8-0f4d3f819c02')
     def test_volume_list_by_name(self):
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
-        params = {self.name: volume[self.name]}
+        params = {'name': volume['name']}
         fetched_vol = self.volumes_client.list_volumes(
             params=params)['volumes']
         self.assertEqual(1, len(fetched_vol), str(fetched_vol))
-        self.assertEqual(fetched_vol[0][self.name],
-                         volume[self.name])
+        self.assertEqual(fetched_vol[0]['name'], volume['name'])
 
     @decorators.idempotent_id('2de3a6d4-12aa-403b-a8f2-fdeb42a89623')
     def test_volume_list_details_by_name(self):
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
-        params = {self.name: volume[self.name]}
+        params = {'name': volume['name']}
         fetched_vol = self.volumes_client.list_volumes(
             detail=True, params=params)['volumes']
         self.assertEqual(1, len(fetched_vol), str(fetched_vol))
-        self.assertEqual(fetched_vol[0][self.name],
-                         volume[self.name])
+        self.assertEqual(fetched_vol[0]['name'], volume['name'])
 
     @decorators.idempotent_id('39654e13-734c-4dab-95ce-7613bf8407ce')
     def test_volumes_list_by_status(self):
@@ -210,7 +210,7 @@
     def test_volume_list_param_display_name_and_status(self):
         # Test to list volume when display name and status param is given
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
-        params = {self.name: volume[self.name],
+        params = {'name': volume['name'],
                   'status': 'available'}
         self._list_by_param_value_and_assert(params)
 
@@ -218,11 +218,160 @@
     def test_volume_list_with_detail_param_display_name_and_status(self):
         # Test to list volume when name and status param is given
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
-        params = {self.name: volume[self.name],
+        params = {'name': volume['name'],
                   'status': 'available'}
         self._list_by_param_value_and_assert(params, with_detail=True)
 
+    @decorators.idempotent_id('2a7064eb-b9c3-429b-b888-33928fc5edd3')
+    def test_volume_list_details_with_multiple_params(self):
+        # List volumes detail using combined condition
+        def _list_details_with_multiple_params(limit=2,
+                                               status='available',
+                                               sort_dir='asc',
+                                               sort_key='id'):
+            params = {'limit': limit,
+                      'status': status,
+                      'sort_dir': sort_dir,
+                      'sort_key': sort_key
+                      }
+            fetched_volume = self.volumes_client.list_volumes(
+                detail=True, params=params)['volumes']
+            self.assertEqual(limit, len(fetched_volume),
+                             "The count of volumes is %s, expected:%s " %
+                             (len(fetched_volume), limit))
+            self.assertEqual(status, fetched_volume[0]['status'])
+            self.assertEqual(status, fetched_volume[1]['status'])
+            val0 = fetched_volume[0][sort_key]
+            val1 = fetched_volume[1][sort_key]
+            if sort_dir == 'asc':
+                self.assertLess(val0, val1,
+                                "list is not in asc order with sort_key: %s."
+                                " %s" % (sort_key, fetched_volume))
+            elif sort_dir == 'desc':
+                self.assertGreater(val0, val1,
+                                   "list is not in desc order with sort_key: "
+                                   "%s. %s" % (sort_key, fetched_volume))
 
-class VolumesV1ListTestJSON(VolumesV2ListTestJSON):
-    _api_version = 1
-    VOLUME_FIELDS = ('id', 'display_name')
+        _list_details_with_multiple_params()
+        _list_details_with_multiple_params(sort_dir='desc')
+
+    def _test_pagination(self, resource, ids=None, limit=1, **kwargs):
+        """Check list pagination functionality for a resource.
+
+        This method requests the list of resources and follows pagination
+        links.
+
+        If an iterable is supplied in ids it will check that all ids are
+        retrieved and that only those are listed, that we will get a next
+        link for an empty page if the number of items is divisible by used
+        limit (this is expected behavior).
+
+        We can specify number of items per request using limit argument.
+        """
+
+        # Get list method for the type of resource from the client
+        client = getattr(self, resource + '_client')
+        method = getattr(client, 'list_' + resource)
+
+        # Include limit in params for list request
+        params = kwargs.pop('params', {})
+        params['limit'] = limit
+
+        # Store remaining items we are expecting from list
+        if ids is not None:
+            remaining = list(ids)
+        else:
+            remaining = None
+
+        # Mark that the current iteration is not from a 'next' link
+        next = None
+
+        while True:
+            # Get a list page
+            response = method(params=params, **kwargs)
+
+            # If we have to check ids
+            if remaining is not None:
+                # Confirm we receive expected number of elements
+                num_expected = min(len(remaining), limit)
+                self.assertEqual(num_expected, len(response[resource]),
+                                 'Requested %(#expect)d but got %(#received)d '
+                                 % {'#expect': num_expected,
+                                    '#received': len(response[resource])})
+
+                # For each received element
+                for element in response[resource]:
+                    element_id = element['id']
+                    # Check it's one of expected ids
+                    self.assertIn(element_id,
+                                  ids,
+                                  'Id %(id)s is not in expected ids %(ids)s' %
+                                  {'id': element_id, 'ids': ids})
+                    # If not in remaining, we have received it twice
+                    self.assertIn(element_id,
+                                  remaining,
+                                  'Id %s was received twice' % element_id)
+                    # We no longer expect it
+                    remaining.remove(element_id)
+
+            # If the current iteration is from a 'next' link, check that the
+            # absolute url is the same as the one used for this request
+            if next:
+                self.assertEqual(next, response.response['content-location'])
+
+            # Get next from response
+            next = None
+            for link in response.get(resource + '_links', ()):
+                if link['rel'] == 'next':
+                    next = link['href']
+                    break
+
+            # Check if we have next and we shouldn't or the other way around
+            if remaining is not None:
+                if remaining or (num_expected and len(ids) % limit == 0):
+                    self.assertIsNotNone(next, 'Missing link to next page')
+                else:
+                    self.assertIsNone(next, 'Unexpected link to next page')
+
+            # If we can follow to the next page, get params from url to make
+            # request in the form of a relative URL
+            if next:
+                params = parse.urlparse(next).query
+
+            # If cannot follow make sure it's because we have finished
+            else:
+                self.assertEmpty(remaining or [],
+                                 'No more pages reported, but still '
+                                 'missing ids %s' % remaining)
+                break
+
+    @decorators.idempotent_id('e9138a2c-f67b-4796-8efa-635c196d01de')
+    def test_volume_list_details_pagination(self):
+        self._test_pagination('volumes', ids=self.volume_id_list, detail=True)
+
+    @decorators.idempotent_id('af55e775-8e4b-4feb-8719-215c43b0238c')
+    def test_volume_list_pagination(self):
+        self._test_pagination('volumes', ids=self.volume_id_list, detail=False)
+
+    @decorators.idempotent_id('46eff077-100b-427f-914e-3db2abcdb7e2')
+    def test_volume_list_with_detail_param_marker(self):
+        # Choosing a random volume from a list of volumes for 'marker'
+        # parameter
+        marker = random.choice(self.volume_id_list)
+
+        # Though Cinder volumes are returned sorted by ID by default
+        # this is implicit. Let make this explicit in case Cinder
+        # folks change their minds.
+        params = {'marker': marker, 'sort': 'id:asc'}
+
+        # Running volume list using marker parameter
+        vol_with_marker = self.volumes_client.list_volumes(
+            detail=True, params=params)['volumes']
+
+        expected_volumes_id = {
+            id for id in self.volume_id_list if id > marker
+        }
+
+        self.assertEqual(
+            expected_volumes_id, {v['id'] for v in vol_with_marker}
+        )
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 0a095a9..7d745f2 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -13,169 +13,164 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import six
+
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.common import waiters
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
+
+CONF = config.CONF
 
 
-class VolumesV2NegativeTest(base.BaseVolumeTest):
+class VolumesNegativeTest(base.BaseVolumeTest):
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesV2NegativeTest, cls).resource_setup()
-
-        cls.name_field = cls.special_fields['name_field']
+        super(VolumesNegativeTest, cls).resource_setup()
 
         # Create a test shared instance and volume for attach/detach tests
         cls.volume = cls.create_volume()
         cls.mountpoint = "/dev/vdc"
 
-    @test.attr(type=['negative'])
+    def create_image(self):
+        # Create image
+        image_name = data_utils.rand_name(self.__class__.__name__ + "-image")
+        image = self.images_client.create_image(
+            name=image_name,
+            container_format=CONF.image.container_formats[0],
+            disk_format=CONF.image.disk_formats[0],
+            visibility='private',
+            min_disk=CONF.volume.volume_size + 1)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.images_client.delete_image, image['id'])
+
+        # Upload image with 1KB data
+        image_file = six.BytesIO(data_utils.random_bytes())
+        self.images_client.store_image_file(image['id'], image_file)
+        waiters.wait_for_image_status(self.images_client,
+                                      image['id'], 'active')
+        return image
+
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f131c586-9448-44a4-a8b0-54ca838aa43e')
     def test_volume_get_nonexistent_volume_id(self):
         # Should not be able to get a non-existent volume
         self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
                           data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('555efa6e-efcd-44ef-8a3b-4a7ca4837a29')
     def test_volume_delete_nonexistent_volume_id(self):
         # Should not be able to delete a non-existent Volume
         self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
                           data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1ed83a8a-682d-4dfb-a30e-ee63ffd6c049')
     def test_create_volume_with_invalid_size(self):
-        # Should not be able to create volume with invalid size
-        # in request
-        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
-        params = {self.name_field: v_name}
+        # Should not be able to create volume with invalid size in request
         self.assertRaises(lib_exc.BadRequest,
-                          self.volumes_client.create_volume,
-                          size='#$%', params=params)
+                          self.volumes_client.create_volume, size='#$%')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9387686f-334f-4d31-a439-33494b9e2683')
     def test_create_volume_without_passing_size(self):
         # Should not be able to create volume without passing size
         # in request
-        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
-        params = {self.name_field: v_name}
         self.assertRaises(lib_exc.BadRequest,
-                          self.volumes_client.create_volume,
-                          size='', params=params)
+                          self.volumes_client.create_volume, size='')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('41331caa-eaf4-4001-869d-bc18c1869360')
     def test_create_volume_with_size_zero(self):
         # Should not be able to create volume with size zero
-        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
-        params = {self.name_field: v_name}
         self.assertRaises(lib_exc.BadRequest,
-                          self.volumes_client.create_volume,
-                          size='0', params=params)
+                          self.volumes_client.create_volume, size='0')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8b472729-9eba-446e-a83b-916bdb34bef7')
     def test_create_volume_with_size_negative(self):
         # Should not be able to create volume with size negative
-        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
-        params = {self.name_field: v_name}
         self.assertRaises(lib_exc.BadRequest,
-                          self.volumes_client.create_volume,
-                          size='-1', params=params)
+                          self.volumes_client.create_volume, size='-1')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('10254ed8-3849-454e-862e-3ab8e6aa01d2')
     def test_create_volume_with_nonexistent_volume_type(self):
         # Should not be able to create volume with non-existent volume type
-        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
-        params = {self.name_field: v_name}
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
-                          size='1', volume_type=data_utils.rand_uuid(),
-                          params=params)
+                          size='1', volume_type=data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0c36f6ae-4604-4017-b0a9-34fdc63096f9')
     def test_create_volume_with_nonexistent_snapshot_id(self):
         # Should not be able to create volume with non-existent snapshot
-        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
-        params = {self.name_field: v_name}
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
-                          size='1', snapshot_id=data_utils.rand_uuid(),
-                          params=params)
+                          size='1', snapshot_id=data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('47c73e08-4be8-45bb-bfdf-0c4e79b88344')
     def test_create_volume_with_nonexistent_source_volid(self):
         # Should not be able to create volume with non-existent source volume
-        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
-        params = {self.name_field: v_name}
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
-                          size='1', source_volid=data_utils.rand_uuid(),
-                          params=params)
+                          size='1', source_volid=data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0186422c-999a-480e-a026-6a665744c30c')
     def test_update_volume_with_nonexistent_volume_id(self):
-        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
-        params = {self.name_field: v_name}
         self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
-                          volume_id=data_utils.rand_uuid(), params=params)
+                          volume_id=data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e66e40d6-65e6-4e75-bdc7-636792fa152d')
     def test_update_volume_with_invalid_volume_id(self):
-        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
-        params = {self.name_field: v_name}
         self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
-                          volume_id=data_utils.rand_name('invalid'),
-                          params=params)
+                          volume_id=data_utils.rand_name('invalid'))
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('72aeca85-57a5-4c1f-9057-f320f9ea575b')
     def test_update_volume_with_empty_volume_id(self):
-        v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
-        params = {self.name_field: v_name}
         self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
-                          volume_id='', params=params)
+                          volume_id='')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('30799cfd-7ee4-446c-b66c-45b383ed211b')
     def test_get_invalid_volume_id(self):
         # Should not be able to get volume with invalid id
         self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
                           data_utils.rand_name('invalid'))
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c6c3db06-29ad-4e91-beb0-2ab195fe49e3')
     def test_get_volume_without_passing_volume_id(self):
         # Should not be able to get volume when empty ID is passed
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.show_volume, '')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1f035827-7c32-4019-9240-b4ec2dbd9dfd')
     def test_delete_invalid_volume_id(self):
         # Should not be able to delete volume when invalid ID is passed
         self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
                           data_utils.rand_name('invalid'))
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('441a1550-5d44-4b30-af0f-a6d402f52026')
     def test_delete_volume_without_passing_volume_id(self):
         # Should not be able to delete volume when empty ID is passed
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.delete_volume, '')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f5e56b0a-5d02-43c1-a2a7-c9b792c2e3f6')
-    @test.services('compute')
+    @utils.services('compute')
     def test_attach_volumes_with_nonexistent_volume_id(self):
-        server = self.create_server(wait_until='ACTIVE')
+        server = self.create_server()
 
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.attach_volume,
@@ -183,14 +178,14 @@
                           instance_uuid=server['id'],
                           mountpoint=self.mountpoint)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9f9c24e4-011d-46b5-b992-952140ce237a')
     def test_detach_volumes_with_invalid_volume_id(self):
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.detach_volume,
                           'xxx')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e0c75c74-ee34-41a9-9288-2a2051452854')
     def test_volume_extend_with_size_smaller_than_original_size(self):
         # Extend volume with smaller size than original size.
@@ -199,7 +194,7 @@
                           self.volumes_client.extend_volume,
                           self.volume['id'], new_size=extend_size)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5d0b480d-e833-439f-8a5a-96ad2ed6f22f')
     def test_volume_extend_with_non_number_size(self):
         # Extend volume when size is non number.
@@ -208,7 +203,7 @@
                           self.volumes_client.extend_volume,
                           self.volume['id'], new_size=extend_size)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('355218f1-8991-400a-a6bb-971239287d92')
     def test_volume_extend_with_None_size(self):
         # Extend volume with None size.
@@ -217,37 +212,37 @@
                           self.volumes_client.extend_volume,
                           self.volume['id'], new_size=extend_size)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8f05a943-013c-4063-ac71-7baf561e82eb')
     def test_volume_extend_with_nonexistent_volume_id(self):
         # Extend volume size when volume is nonexistent.
-        extend_size = int(self.volume['size']) + 1
+        extend_size = self.volume['size'] + 1
         self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
                           data_utils.rand_uuid(), new_size=extend_size)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('aff8ba64-6d6f-4f2e-bc33-41a08ee9f115')
     def test_volume_extend_without_passing_volume_id(self):
         # Extend volume size when passing volume id is None.
-        extend_size = int(self.volume['size']) + 1
+        extend_size = self.volume['size'] + 1
         self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
                           None, new_size=extend_size)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ac6084c0-0546-45f9-b284-38a367e0e0e2')
     def test_reserve_volume_with_nonexistent_volume_id(self):
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.reserve_volume,
                           data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('eb467654-3dc1-4a72-9b46-47c29d22654c')
     def test_unreserve_volume_with_nonexistent_volume_id(self):
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.unreserve_volume,
                           data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('449c4ed2-ecdd-47bb-98dc-072aeccf158c')
     def test_reserve_volume_with_negative_volume_status(self):
         # Mark volume as reserved.
@@ -259,42 +254,69 @@
         # Unmark volume as reserved.
         self.volumes_client.unreserve_volume(self.volume['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0f4aa809-8c7b-418f-8fb3-84c7a5dfc52f')
     def test_list_volumes_with_nonexistent_name(self):
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
-        params = {self.name_field: v_name}
+        params = {'name': v_name}
         fetched_volume = self.volumes_client.list_volumes(
             params=params)['volumes']
-        self.assertEqual(0, len(fetched_volume))
+        self.assertEmpty(fetched_volume)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9ca17820-a0e7-4cbd-a7fa-f4468735e359')
     def test_list_volumes_detail_with_nonexistent_name(self):
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
-        params = {self.name_field: v_name}
+        params = {'name': v_name}
         fetched_volume = \
             self.volumes_client.list_volumes(
                 detail=True, params=params)['volumes']
-        self.assertEqual(0, len(fetched_volume))
+        self.assertEmpty(fetched_volume)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('143b279b-7522-466b-81be-34a87d564a7c')
     def test_list_volumes_with_invalid_status(self):
         params = {'status': 'null'}
         fetched_volume = self.volumes_client.list_volumes(
             params=params)['volumes']
-        self.assertEqual(0, len(fetched_volume))
+        self.assertEmpty(fetched_volume)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ba94b27b-be3f-496c-a00e-0283b373fa75')
     def test_list_volumes_detail_with_invalid_status(self):
         params = {'status': 'null'}
         fetched_volume = \
             self.volumes_client.list_volumes(detail=True,
                                              params=params)['volumes']
-        self.assertEqual(0, len(fetched_volume))
+        self.assertEmpty(fetched_volume)
 
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('5b810c91-0ad1-47ce-aee8-615f789be78f')
+    @utils.services('image')
+    def test_create_volume_from_image_with_decreasing_size(self):
+        # Create image
+        image = self.create_image()
 
-class VolumesV1NegativeTest(VolumesV2NegativeTest):
-    _api_version = 1
+        # Note(jeremyZ): To shorten the test time (uploading a big size image
+        # is time-consuming), here just consider the scenario that volume size
+        # is smaller than the min_disk of image.
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.create_volume,
+                          size=CONF.volume.volume_size,
+                          imageRef=image['id'])
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('d15e7f35-2cfc-48c8-9418-c8223a89bcbb')
+    @utils.services('image')
+    def test_create_volume_from_deactivated_image(self):
+        # Create image
+        image = self.create_image()
+
+        # Deactivate the image
+        self.images_client.deactivate_image(image['id'])
+        body = self.images_client.show_image(image['id'])
+        self.assertEqual("deactivated", body['status'])
+        # Try creating a volume from deactivated image
+        self.assertRaises(lib_exc.BadRequest,
+                          self.create_volume,
+                          imageRef=image['id'])
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index f1ca722..dcd3518 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -10,57 +10,43 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+from testtools import matchers
+
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
+from tempest.lib import exceptions as lib_exc
 
 CONF = config.CONF
 
 
-class VolumesV2SnapshotTestJSON(base.BaseVolumeTest):
+class VolumesSnapshotTestJSON(base.BaseVolumeTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesV2SnapshotTestJSON, cls).skip_checks()
+        super(VolumesSnapshotTestJSON, cls).skip_checks()
         if not CONF.volume_feature_enabled.snapshot:
             raise cls.skipException("Cinder volume snapshots are disabled")
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesV2SnapshotTestJSON, cls).resource_setup()
+        super(VolumesSnapshotTestJSON, cls).resource_setup()
         cls.volume_origin = cls.create_volume()
-        cls.name_field = cls.special_fields['name_field']
-        cls.descrip_field = cls.special_fields['descrip_field']
-
-    def cleanup_snapshot(self, snapshot):
-        # Delete the snapshot
-        self.snapshots_client.delete_snapshot(snapshot['id'])
-        self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
-        self.snapshots.remove(snapshot)
-
-    @decorators.idempotent_id('b467b54c-07a4-446d-a1cf-651dedcc3ff1')
-    @test.services('compute')
-    def test_snapshot_create_with_volume_in_use(self):
-        # Create a snapshot when volume status is in-use
-        # Create a test instance
-        server = self.create_server(wait_until='ACTIVE')
-        self.attach_volume(server['id'], self.volume_origin['id'])
-
-        # Snapshot a volume even if it's attached to an instance
-        snapshot = self.create_snapshot(self.volume_origin['id'],
-                                        force=True)
-        # Delete the snapshot
-        self.cleanup_snapshot(snapshot)
 
     @decorators.idempotent_id('8567b54c-4455-446d-a1cf-651ddeaa3ff2')
-    @test.services('compute')
-    def test_snapshot_delete_with_volume_in_use(self):
+    @utils.services('compute')
+    def test_snapshot_create_delete_with_volume_in_use(self):
         # Create a test instance
-        server = self.create_server(wait_until='ACTIVE')
+        server = self.create_server()
         self.attach_volume(server['id'], self.volume_origin['id'])
 
+        # Snapshot a volume which attached to an instance with force=False
+        self.assertRaises(lib_exc.BadRequest, self.create_snapshot,
+                          self.volume_origin['id'], force=False)
+
         # Snapshot a volume attached to an instance
         snapshot1 = self.create_snapshot(self.volume_origin['id'], force=True)
         snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
@@ -68,19 +54,19 @@
 
         # Delete the snapshots. Some snapshot implementations can take
         # different paths according to order they are deleted.
-        self.cleanup_snapshot(snapshot1)
-        self.cleanup_snapshot(snapshot3)
-        self.cleanup_snapshot(snapshot2)
+        self.delete_snapshot(snapshot1['id'])
+        self.delete_snapshot(snapshot3['id'])
+        self.delete_snapshot(snapshot2['id'])
 
     @decorators.idempotent_id('5210a1de-85a0-11e6-bb21-641c676a5d61')
-    @test.services('compute')
+    @utils.services('compute')
     def test_snapshot_create_offline_delete_online(self):
 
         # Create a snapshot while it is not attached
         snapshot1 = self.create_snapshot(self.volume_origin['id'])
 
         # Create a server and attach it
-        server = self.create_server(wait_until='ACTIVE')
+        server = self.create_server()
         self.attach_volume(server['id'], self.volume_origin['id'])
 
         # Now that the volume is attached, create another snapshots
@@ -89,14 +75,18 @@
 
         # Delete the snapshots. Some snapshot implementations can take
         # different paths according to order they are deleted.
-        self.cleanup_snapshot(snapshot3)
-        self.cleanup_snapshot(snapshot1)
-        self.cleanup_snapshot(snapshot2)
+        self.delete_snapshot(snapshot3['id'])
+        self.delete_snapshot(snapshot1['id'])
+        self.delete_snapshot(snapshot2['id'])
 
     @decorators.idempotent_id('2a8abbe4-d871-46db-b049-c41f5af8216e')
     def test_snapshot_create_get_list_update_delete(self):
-        # Create a snapshot
-        snapshot = self.create_snapshot(self.volume_origin['id'])
+        # Create a snapshot with metadata
+        metadata = {"snap-meta1": "value1",
+                    "snap-meta2": "value2",
+                    "snap-meta3": "value3"}
+        snapshot = self.create_snapshot(self.volume_origin['id'],
+                                        metadata=metadata)
 
         # Get the snap and check for some of its details
         snap_get = self.snapshots_client.show_snapshot(
@@ -104,36 +94,42 @@
         self.assertEqual(self.volume_origin['id'],
                          snap_get['volume_id'],
                          "Referred volume origin mismatch")
+        self.assertEqual(self.volume_origin['size'], snap_get['size'])
+
+        # Verify snapshot metadata
+        self.assertThat(snap_get['metadata'].items(),
+                        matchers.ContainsAll(metadata.items()))
 
         # Compare also with the output from the list action
-        tracking_data = (snapshot['id'], snapshot[self.name_field])
+        tracking_data = (snapshot['id'], snapshot['name'])
         snaps_list = self.snapshots_client.list_snapshots()['snapshots']
-        snaps_data = [(f['id'], f[self.name_field]) for f in snaps_list]
+        snaps_data = [(f['id'], f['name']) for f in snaps_list]
         self.assertIn(tracking_data, snaps_data)
 
         # Updates snapshot with new values
         new_s_name = data_utils.rand_name(
             self.__class__.__name__ + '-new-snap')
         new_desc = 'This is the new description of snapshot.'
-        params = {self.name_field: new_s_name,
-                  self.descrip_field: new_desc}
+        params = {'name': new_s_name,
+                  'description': new_desc}
         update_snapshot = self.snapshots_client.update_snapshot(
             snapshot['id'], **params)['snapshot']
         # Assert response body for update_snapshot method
-        self.assertEqual(new_s_name, update_snapshot[self.name_field])
-        self.assertEqual(new_desc, update_snapshot[self.descrip_field])
+        self.assertEqual(new_s_name, update_snapshot['name'])
+        self.assertEqual(new_desc, update_snapshot['description'])
         # Assert response body for show_snapshot method
         updated_snapshot = self.snapshots_client.show_snapshot(
             snapshot['id'])['snapshot']
-        self.assertEqual(new_s_name, updated_snapshot[self.name_field])
-        self.assertEqual(new_desc, updated_snapshot[self.descrip_field])
+        self.assertEqual(new_s_name, updated_snapshot['name'])
+        self.assertEqual(new_desc, updated_snapshot['description'])
 
         # Delete the snapshot
-        self.cleanup_snapshot(snapshot)
+        self.delete_snapshot(snapshot['id'])
 
     @decorators.idempotent_id('677863d1-3142-456d-b6ac-9924f667a7f4')
     def test_volume_from_snapshot(self):
-        # Creates a volume a snapshot passing a size different from the source
+        # Creates a volume from a snapshot passing a size
+        # different from the source
         src_size = CONF.volume.volume_size
 
         src_vol = self.create_volume(size=src_size)
@@ -153,8 +149,17 @@
         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)
+        self.assertEqual(volume['size'], src_size + 1)
 
+    @decorators.idempotent_id('bbcfa285-af7f-479e-8c1a-8c34fc16543c')
+    @testtools.skipUnless(CONF.volume_feature_enabled.backup,
+                          "Cinder backup is disabled")
+    def test_snapshot_backup(self):
+        # Create a snapshot
+        snapshot = self.create_snapshot(volume_id=self.volume_origin['id'])
 
-class VolumesV1SnapshotTestJSON(VolumesV2SnapshotTestJSON):
-    _api_version = 1
+        backup = self.create_backup(volume_id=self.volume_origin['id'],
+                                    snapshot_id=snapshot['id'])
+        backup_info = self.backups_client.show_backup(backup['id'])['backup']
+        self.assertEqual(self.volume_origin['id'], backup_info['volume_id'])
+        self.assertEqual(snapshot['id'], backup_info['snapshot_id'])
diff --git a/tempest/api/volume/test_volumes_snapshots_list.py b/tempest/api/volume/test_volumes_snapshots_list.py
index ff390ea..507df1f 100644
--- a/tempest/api/volume/test_volumes_snapshots_list.py
+++ b/tempest/api/volume/test_volumes_snapshots_list.py
@@ -17,22 +17,25 @@
 CONF = config.CONF
 
 
-class VolumesV2SnapshotListTestJSON(base.BaseVolumeTest):
+class VolumesSnapshotListTestJSON(base.BaseVolumeTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesV2SnapshotListTestJSON, cls).skip_checks()
+        super(VolumesSnapshotListTestJSON, cls).skip_checks()
         if not CONF.volume_feature_enabled.snapshot:
             raise cls.skipException("Cinder volume snapshots are disabled")
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesV2SnapshotListTestJSON, cls).resource_setup()
-        cls.volume_origin = cls.create_volume()
-        cls.name_field = cls.special_fields['name_field']
+        super(VolumesSnapshotListTestJSON, cls).resource_setup()
+        cls.snapshot_id_list = []
+        volume_origin = cls.create_volume()
+
         # Create snapshots with params
-        for _ in range(2):
-            cls.snapshot = cls.create_snapshot(cls.volume_origin['id'])
+        for _ in range(3):
+            snapshot = cls.create_snapshot(volume_origin['id'])
+            cls.snapshot_id_list.append(snapshot['id'])
+        cls.snapshot = snapshot
 
     def _list_by_param_values_and_assert(self, with_detail=False, **params):
         """list or list_details with given params and validates result."""
@@ -59,7 +62,7 @@
     def test_snapshots_list_with_params(self):
         """list snapshots with params."""
         # Verify list snapshots by display_name filter
-        params = {self.name_field: self.snapshot[self.name_field]}
+        params = {'name': self.snapshot['name']}
         self._list_by_param_values_and_assert(**params)
 
         # Verify list snapshots by status filter
@@ -68,21 +71,21 @@
 
         # Verify list snapshots by status and display name filter
         params = {'status': 'available',
-                  self.name_field: self.snapshot[self.name_field]}
+                  'name': self.snapshot['name']}
         self._list_by_param_values_and_assert(**params)
 
     @decorators.idempotent_id('220a1022-1fcd-4a74-a7bd-6b859156cda2')
     def test_snapshots_list_details_with_params(self):
         """list snapshot details with params."""
         # Verify list snapshot details by display_name filter
-        params = {self.name_field: self.snapshot[self.name_field]}
+        params = {'name': self.snapshot['name']}
         self._list_by_param_values_and_assert(with_detail=True, **params)
         # Verify list snapshot details by status filter
         params = {'status': 'available'}
         self._list_by_param_values_and_assert(with_detail=True, **params)
         # Verify list snapshot details by status and display name filter
         params = {'status': 'available',
-                  self.name_field: self.snapshot[self.name_field]}
+                  'name': self.snapshot['name']}
         self._list_by_param_values_and_assert(with_detail=True, **params)
 
     @decorators.idempotent_id('db4d8e0a-7a2e-41cc-a712-961f6844e896')
@@ -98,12 +101,60 @@
         self._list_snapshots_by_param_limit(limit=100000,
                                             expected_elements=len(snap_list))
 
-    @decorators.skip_because(bug='1540893')
     @decorators.idempotent_id('e3b44b7f-ae87-45b5-8a8c-66110eb24d0a')
     def test_snapshot_list_param_limit_equals_zero(self):
         # List returns zero elements
         self._list_snapshots_by_param_limit(limit=0, expected_elements=0)
 
+    def _list_snapshots_param_sort(self, sort_key, sort_dir):
+        """list snapshots by sort param"""
+        snap_list = self.snapshots_client.list_snapshots(
+            sort_key=sort_key, sort_dir=sort_dir)['snapshots']
+        self.assertNotEmpty(snap_list)
+        if sort_key is 'display_name':
+            sort_key = 'name'
+        # Note: On Cinder API, 'display_name' works as a sort key
+        # on a request, a volume name appears as 'name' on the response.
+        # So Tempest needs to change the key name here for this inconsistent
+        # API behavior.
+        sorted_list = [snapshot[sort_key] for snapshot in snap_list]
+        msg = 'The list of snapshots was not sorted correctly.'
+        self.assertEqual(sorted(sorted_list, reverse=(sort_dir == 'desc')),
+                         sorted_list, msg)
 
-class VolumesV1SnapshotLimitTestJSON(VolumesV2SnapshotListTestJSON):
-    _api_version = 1
+    @decorators.idempotent_id('c5513ada-64c1-4d28-83b9-af3307ec1388')
+    def test_snapshot_list_param_sort_id_asc(self):
+        self._list_snapshots_param_sort(sort_key='id', sort_dir='asc')
+
+    @decorators.idempotent_id('8a7fe058-0b41-402a-8afd-2dbc5a4a718b')
+    def test_snapshot_list_param_sort_id_desc(self):
+        self._list_snapshots_param_sort(sort_key='id', sort_dir='desc')
+
+    @decorators.idempotent_id('4052c3a0-2415-440a-a8cc-305a875331b0')
+    def test_snapshot_list_param_sort_created_at_asc(self):
+        self._list_snapshots_param_sort(sort_key='created_at', sort_dir='asc')
+
+    @decorators.idempotent_id('dcbbe24a-f3c0-4ec8-9274-55d48db8d1cf')
+    def test_snapshot_list_param_sort_created_at_desc(self):
+        self._list_snapshots_param_sort(sort_key='created_at', sort_dir='desc')
+
+    @decorators.idempotent_id('d58b5fed-0c37-42d3-8c5d-39014ac13c00')
+    def test_snapshot_list_param_sort_name_asc(self):
+        self._list_snapshots_param_sort(sort_key='display_name',
+                                        sort_dir='asc')
+
+    @decorators.idempotent_id('96ba6f4d-1f18-47e1-b4bc-76edc6c21250')
+    def test_snapshot_list_param_sort_name_desc(self):
+        self._list_snapshots_param_sort(sort_key='display_name',
+                                        sort_dir='desc')
+
+    @decorators.idempotent_id('05489dde-44bc-4961-a1f5-3ce7ee7824f7')
+    def test_snapshot_list_param_marker(self):
+        # The list of snapshots should end before the provided marker
+        params = {'marker': self.snapshot_id_list[1]}
+        snap_list = self.snapshots_client.list_snapshots(**params)['snapshots']
+        fetched_list_id = [snap['id'] for snap in snap_list]
+        # Verify the list of snapshots ends before the provided
+        # marker(second snapshot), therefore only the first snapshot
+        # should displayed.
+        self.assertEqual(self.snapshot_id_list[:1], fetched_list_id)
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index 1e68848..ea5f036 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -11,24 +11,23 @@
 #    under the License.
 
 from tempest.api.volume import base
-from tempest.common.utils import data_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 CONF = config.CONF
 
 
-class VolumesV2SnapshotNegativeTestJSON(base.BaseVolumeTest):
+class VolumesSnapshotNegativeTestJSON(base.BaseVolumeTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesV2SnapshotNegativeTestJSON, cls).skip_checks()
+        super(VolumesSnapshotNegativeTestJSON, cls).skip_checks()
         if not CONF.volume_feature_enabled.snapshot:
             raise cls.skipException("Cinder volume snapshots are disabled")
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e3e466af-70ab-4f4b-a967-ab04e3532ea7')
     def test_create_snapshot_with_nonexistent_volume_id(self):
         # Create a snapshot with nonexistent volume id
@@ -38,7 +37,7 @@
                           volume_id=data_utils.rand_uuid(),
                           display_name=s_name)
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('bb9da53e-d335-4309-9c15-7e76fd5e4d6d')
     def test_create_snapshot_without_passing_volume_id(self):
         # Create a snapshot without passing volume id
@@ -47,6 +46,7 @@
                           self.snapshots_client.create_snapshot,
                           volume_id=None, display_name=s_name)
 
+    @decorators.attr(type=['negative'])
     @decorators.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
@@ -61,6 +61,23 @@
                           size=src_size - 1,
                           snapshot_id=src_snap['id'])
 
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('8fd92339-e22f-4591-86b4-1e2215372a40')
+    def test_list_snapshot_invalid_param_limit(self):
+        self.assertRaises(lib_exc.BadRequest,
+                          self.snapshots_client.list_snapshots,
+                          limit='invalid')
 
-class VolumesV1SnapshotNegativeTestJSON(VolumesV2SnapshotNegativeTestJSON):
-    _api_version = 1
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('27b5f37f-bf69-4e8c-986e-c44f3d6819b8')
+    def test_list_snapshots_invalid_param_sort(self):
+        self.assertRaises(lib_exc.BadRequest,
+                          self.snapshots_client.list_snapshots,
+                          sort_key='invalid')
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('b68deeda-ca79-4a32-81af-5c51179e553a')
+    def test_list_snapshots_invalid_param_marker(self):
+        self.assertRaises(lib_exc.NotFound,
+                          self.snapshots_client.list_snapshots,
+                          marker=data_utils.rand_uuid())
diff --git a/tempest/api/volume/v2/__init__.py b/tempest/api/volume/v2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/volume/v2/__init__.py
+++ /dev/null
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
deleted file mode 100644
index 9b17515..0000000
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ /dev/null
@@ -1,203 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# Copyright 2013 IBM Corp.
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import random
-from six.moves.urllib import parse
-
-from tempest.api.volume import base
-from tempest.lib import decorators
-
-
-class VolumesV2ListTestJSON(base.BaseVolumeTest):
-    """volumes v2 specific tests.
-
-    This test creates a number of 1G volumes. To run successfully,
-    ensure that the backing file for the volume group that Nova uses
-    has space for at least 3 1G volumes!
-    If you are running a Devstack environment, ensure that the
-    VOLUME_BACKING_FILE_SIZE is at least 4G in your localrc
-    """
-
-    @classmethod
-    def resource_setup(cls):
-        super(VolumesV2ListTestJSON, cls).resource_setup()
-
-        # Create 3 test volumes
-        cls.metadata = {'Type': 'work'}
-        # NOTE(zhufl): When using pre-provisioned credentials, the project
-        # may have volumes other than those created below.
-        existing_volumes = cls.volumes_client.list_volumes()['volumes']
-        cls.volume_id_list = [vol['id'] for vol in existing_volumes]
-        for _ in range(3):
-            volume = cls.create_volume(metadata=cls.metadata)
-            cls.volume_id_list.append(volume['id'])
-
-    @decorators.idempotent_id('2a7064eb-b9c3-429b-b888-33928fc5edd3')
-    def test_volume_list_details_with_multiple_params(self):
-        # List volumes detail using combined condition
-        def _list_details_with_multiple_params(limit=2,
-                                               status='available',
-                                               sort_dir='asc',
-                                               sort_key='id'):
-            params = {'limit': limit,
-                      'status': status,
-                      'sort_dir': sort_dir,
-                      'sort_key': sort_key
-                      }
-            fetched_volume = self.volumes_client.list_volumes(
-                detail=True, params=params)['volumes']
-            self.assertEqual(limit, len(fetched_volume),
-                             "The count of volumes is %s, expected:%s " %
-                             (len(fetched_volume), limit))
-            self.assertEqual(status, fetched_volume[0]['status'])
-            self.assertEqual(status, fetched_volume[1]['status'])
-            val0 = fetched_volume[0][sort_key]
-            val1 = fetched_volume[1][sort_key]
-            if sort_dir == 'asc':
-                self.assertLess(val0, val1,
-                                "list is not in asc order with sort_key: %s."
-                                " %s" % (sort_key, fetched_volume))
-            elif sort_dir == 'desc':
-                self.assertGreater(val0, val1,
-                                   "list is not in desc order with sort_key: "
-                                   "%s. %s" % (sort_key, fetched_volume))
-
-        _list_details_with_multiple_params()
-        _list_details_with_multiple_params(sort_dir='desc')
-
-    def _test_pagination(self, resource, ids=None, limit=1, **kwargs):
-        """Check list pagination functionality for a resource.
-
-        This method requests the list of resources and follows pagination
-        links.
-
-        If an iterable is supplied in ids it will check that all ids are
-        retrieved and that only those are listed, that we will get a next
-        link for an empty page if the number of items is divisible by used
-        limit (this is expected behavior).
-
-        We can specify number of items per request using limit argument.
-        """
-
-        # Get list method for the type of resource from the client
-        client = getattr(self, resource + '_client')
-        method = getattr(client, 'list_' + resource)
-
-        # Include limit in params for list request
-        params = kwargs.pop('params', {})
-        params['limit'] = limit
-
-        # Store remaining items we are expecting from list
-        if ids is not None:
-            remaining = list(ids)
-        else:
-            remaining = None
-
-        # Mark that the current iteration is not from a 'next' link
-        next = None
-
-        while True:
-            # Get a list page
-            response = method(params=params, **kwargs)
-
-            # If we have to check ids
-            if remaining is not None:
-                # Confirm we receive expected number of elements
-                num_expected = min(len(remaining), limit)
-                self.assertEqual(num_expected, len(response[resource]),
-                                 'Requested %(#expect)d but got %(#received)d '
-                                 % {'#expect': num_expected,
-                                    '#received': len(response[resource])})
-
-                # For each received element
-                for element in response[resource]:
-                    element_id = element['id']
-                    # Check it's one of expected ids
-                    self.assertIn(element_id,
-                                  ids,
-                                  'Id %(id)s is not in expected ids %(ids)s' %
-                                  {'id': element_id, 'ids': ids})
-                    # If not in remaining, we have received it twice
-                    self.assertIn(element_id,
-                                  remaining,
-                                  'Id %s was received twice' % element_id)
-                    # We no longer expect it
-                    remaining.remove(element_id)
-
-            # If the current iteration is from a 'next' link, check that the
-            # absolute url is the same as the one used for this request
-            if next:
-                self.assertEqual(next, response.response['content-location'])
-
-            # Get next from response
-            next = None
-            for link in response.get(resource + '_links', ()):
-                if link['rel'] == 'next':
-                    next = link['href']
-                    break
-
-            # Check if we have next and we shouldn't or the other way around
-            if remaining is not None:
-                if remaining or (num_expected and len(ids) % limit == 0):
-                    self.assertIsNotNone(next, 'Missing link to next page')
-                else:
-                    self.assertIsNone(next, 'Unexpected link to next page')
-
-            # If we can follow to the next page, get params from url to make
-            # request in the form of a relative URL
-            if next:
-                params = parse.urlparse(next).query
-
-            # If cannot follow make sure it's because we have finished
-            else:
-                self.assertEqual([], remaining or [],
-                                 'No more pages reported, but still '
-                                 'missing ids %s' % remaining)
-                break
-
-    @decorators.idempotent_id('e9138a2c-f67b-4796-8efa-635c196d01de')
-    def test_volume_list_details_pagination(self):
-        self._test_pagination('volumes', ids=self.volume_id_list, detail=True)
-
-    @decorators.idempotent_id('af55e775-8e4b-4feb-8719-215c43b0238c')
-    def test_volume_list_pagination(self):
-        self._test_pagination('volumes', ids=self.volume_id_list, detail=False)
-
-    @decorators.idempotent_id('46eff077-100b-427f-914e-3db2abcdb7e2')
-    @decorators.skip_because(bug='1572765')
-    def test_volume_list_with_detail_param_marker(self):
-        # Choosing a random volume from a list of volumes for 'marker'
-        # parameter
-        random_volume = random.choice(self.volume_id_list)
-
-        params = {'marker': random_volume}
-
-        # Running volume list using marker parameter
-        vol_with_marker = self.volumes_client.list_volumes(
-            detail=True, params=params)['volumes']
-
-        # Fetching the index of the random volume from volume_id_list
-        index_marker = self.volume_id_list.index(random_volume)
-
-        # The expected list with marker parameter
-        verify_volume_list = self.volume_id_list[:index_marker]
-
-        failed_msg = "Failed to list volume details by marker"
-
-        # Validating the expected list is the same like the observed list
-        self.assertEqual(verify_volume_list,
-                         map(lambda x: x['id'],
-                             vol_with_marker[::-1]), failed_msg)
diff --git a/tempest/api/volume/v2/test_volumes_snapshots_list.py b/tempest/api/volume/v2/test_volumes_snapshots_list.py
deleted file mode 100644
index f389b59..0000000
--- a/tempest/api/volume/v2/test_volumes_snapshots_list.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# 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 import config
-from tempest import test
-
-CONF = config.CONF
-
-
-class VolumesV2SnapshotListTestJSON(base.BaseVolumeTest):
-
-    @classmethod
-    def skip_checks(cls):
-        super(VolumesV2SnapshotListTestJSON, cls).skip_checks()
-        if not CONF.volume_feature_enabled.snapshot:
-            raise cls.skipException("Cinder volume snapshots are disabled")
-
-    @classmethod
-    def resource_setup(cls):
-        super(VolumesV2SnapshotListTestJSON, cls).resource_setup()
-        cls.snapshot_id_list = []
-        # Create a volume
-        cls.volume_origin = cls.create_volume()
-        cls.name_field = cls.special_fields['name_field']
-        # Create 3 snapshots
-        for _ in range(3):
-            snapshot = cls.create_snapshot(cls.volume_origin['id'])
-            cls.snapshot_id_list.append(snapshot['id'])
-
-    def _list_snapshots_param_sort(self, sort_key, sort_dir):
-        """list snapshots by sort param"""
-        snap_list = self.snapshots_client.list_snapshots(
-            sort_key=sort_key, sort_dir=sort_dir)['snapshots']
-        self.assertNotEmpty(snap_list)
-        if sort_key is 'display_name':
-            sort_key = 'name'
-        # Note: On Cinder V2 API, 'display_name' works as a sort key
-        # on a request, a volume name appears as 'name' on the response.
-        # So Tempest needs to change the key name here for this inconsistent
-        # API behavior.
-        sorted_list = [snapshot[sort_key] for snapshot in snap_list]
-        msg = 'The list of snapshots was not sorted correctly.'
-        self.assertEqual(sorted(sorted_list, reverse=(sort_dir == 'desc')),
-                         sorted_list, msg)
-
-    @test.idempotent_id('c5513ada-64c1-4d28-83b9-af3307ec1388')
-    def test_snapshot_list_param_sort_id_asc(self):
-        self._list_snapshots_param_sort(sort_key='id', sort_dir='asc')
-
-    @test.idempotent_id('8a7fe058-0b41-402a-8afd-2dbc5a4a718b')
-    def test_snapshot_list_param_sort_id_desc(self):
-        self._list_snapshots_param_sort(sort_key='id', sort_dir='desc')
-
-    @test.idempotent_id('4052c3a0-2415-440a-a8cc-305a875331b0')
-    def test_snapshot_list_param_sort_created_at_asc(self):
-        self._list_snapshots_param_sort(sort_key='created_at', sort_dir='asc')
-
-    @test.idempotent_id('dcbbe24a-f3c0-4ec8-9274-55d48db8d1cf')
-    def test_snapshot_list_param_sort_created_at_desc(self):
-        self._list_snapshots_param_sort(sort_key='created_at', sort_dir='desc')
-
-    @test.idempotent_id('d58b5fed-0c37-42d3-8c5d-39014ac13c00')
-    def test_snapshot_list_param_sort_name_asc(self):
-        self._list_snapshots_param_sort(sort_key='display_name',
-                                        sort_dir='asc')
-
-    @test.idempotent_id('96ba6f4d-1f18-47e1-b4bc-76edc6c21250')
-    def test_snapshot_list_param_sort_name_desc(self):
-        self._list_snapshots_param_sort(sort_key='display_name',
-                                        sort_dir='desc')
-
-    @test.idempotent_id('05489dde-44bc-4961-a1f5-3ce7ee7824f7')
-    def test_snapshot_list_param_marker(self):
-        # The list of snapshots should end before the provided marker
-        params = {'marker': self.snapshot_id_list[1]}
-        snap_list = self.snapshots_client.list_snapshots(**params)['snapshots']
-        fetched_list_id = [snap['id'] for snap in snap_list]
-        # Verify the list of snapshots ends before the provided
-        # marker(second snapshot), therefore only the first snapshot
-        # should displayed.
-        self.assertEqual(self.snapshot_id_list[:1], fetched_list_id)
diff --git a/tempest/api/volume/v3/__init__.py b/tempest/api/volume/v3/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/volume/v3/__init__.py
+++ /dev/null
diff --git a/tempest/api/volume/v3/base.py b/tempest/api/volume/v3/base.py
deleted file mode 100644
index 31fc1eb..0000000
--- a/tempest/api/volume/v3/base.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright 2016 Andrew Kerr
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-from tempest.api.volume import api_microversion_fixture
-from tempest.api.volume import base
-from tempest import config
-from tempest.lib.common import api_version_utils
-
-CONF = config.CONF
-
-
-class VolumesV3Test(api_version_utils.BaseMicroversionTest,
-                    base.BaseVolumeTest):
-    """Base test case class for all v3 Cinder API tests."""
-
-    _api_version = 3
-
-    @classmethod
-    def skip_checks(cls):
-        super(VolumesV3Test, cls).skip_checks()
-        api_version_utils.check_skip_with_microversion(
-            cls.min_microversion, cls.max_microversion,
-            CONF.volume.min_microversion, CONF.volume.max_microversion)
-
-    @classmethod
-    def resource_setup(cls):
-        super(VolumesV3Test, cls).resource_setup()
-        cls.request_microversion = (
-            api_version_utils.select_request_microversion(
-                cls.min_microversion,
-                CONF.volume.min_microversion))
-
-    @classmethod
-    def setup_clients(cls):
-        super(VolumesV3Test, cls).setup_clients()
-        cls.messages_client = cls.os.volume_v3_messages_client
-
-    def setUp(self):
-        super(VolumesV3Test, self).setUp()
-        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
-            self.request_microversion))
-
-
-class VolumesV3AdminTest(VolumesV3Test,
-                         base.BaseVolumeAdminTest):
-    """Base test case class for all v3 Volume Admin API tests."""
-
-    credentials = ['primary', 'admin']
-
-    @classmethod
-    def setup_clients(cls):
-        super(VolumesV3AdminTest, cls).setup_clients()
-        cls.admin_messages_client = cls.os_adm.volume_v3_messages_client
-        cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
diff --git a/tempest/clients.py b/tempest/clients.py
index cdd6925..e617c3c 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -13,17 +13,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_log import log as logging
-
 from tempest import config
 from tempest.lib import auth
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services import clients
 from tempest.services import object_storage
-from tempest.services import orchestration
 
 CONF = config.CONF
-LOG = logging.getLogger(__name__)
 
 
 class Manager(clients.ServiceClients):
@@ -41,8 +37,7 @@
         _, 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())
+            region=CONF.identity.region)
         # 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.
@@ -53,44 +48,6 @@
         self._set_image_clients()
         self._set_network_clients()
 
-        self.orchestration_client = orchestration.OrchestrationClient(
-            self.auth_provider,
-            CONF.orchestration.catalog_type,
-            CONF.orchestration.region or CONF.identity.region,
-            endpoint_type=CONF.orchestration.endpoint_type,
-            build_interval=CONF.orchestration.build_interval,
-            build_timeout=CONF.orchestration.build_timeout,
-            **self.default_params)
-
-    def _prepare_configuration(self):
-        """Map values from CONF into Manager parameters
-
-        This uses `config.service_client_config` for all services to collect
-        most configuration items needed to init the clients.
-        """
-        # NOTE(andreaf) Once all service clients in Tempest are migrated
-        # to tempest.lib, their configuration will be picked up from the
-        # registry, and this method will become redundant.
-
-        configuration = {}
-
-        # Setup the parameters for all Tempest services which are not in lib.
-        # NOTE(andreaf) Since client.py is an internal module of Tempest,
-        # it doesn't have to consider plugin configuration.
-        for service in clients._tempest_internal_modules():
-            try:
-                # NOTE(andreaf) Use the unversioned service name to fetch
-                # the configuration since configuration is not versioned.
-                service_for_config = service.split('.')[0]
-                if service_for_config not in configuration:
-                    configuration[service_for_config] = (
-                        config.service_client_config(service_for_config))
-            except lib_exc.UnknownServiceClient:
-                LOG.warning(
-                    'Could not load configuration for service %s', service)
-
-        return configuration
-
     def _set_network_clients(self):
         self.network_agents_client = self.network.AgentsClient()
         self.network_extensions_client = self.network.ExtensionsClient()
@@ -109,6 +66,7 @@
         self.security_groups_client = self.network.SecurityGroupsClient()
         self.network_versions_client = self.network.NetworkVersionsClient()
         self.service_providers_client = self.network.ServiceProvidersClient()
+        self.tags_client = self.network.TagsClient()
 
     def _set_image_clients(self):
         if CONF.service_available.glance:
@@ -125,6 +83,8 @@
                 self.image_v2.NamespacePropertiesClient()
             self.namespace_tags_client = \
                 self.image_v2.NamespaceTagsClient()
+            self.image_versions_client = \
+                self.image_v2.VersionsClient()
 
     def _set_compute_clients(self):
         self.agents_client = self.compute.AgentsClient()
@@ -157,7 +117,6 @@
         self.aggregates_client = self.compute.AggregatesClient()
         self.services_client = self.compute.ServicesClient()
         self.tenant_usages_client = self.compute.TenantUsagesClient()
-        self.baremetal_nodes_client = self.compute.BaremetalNodesClient()
         self.hosts_client = self.compute.HostsClient()
         self.hypervisor_client = self.compute.HypervisorClient()
         self.instance_usages_audit_log_client = (
@@ -166,11 +125,10 @@
 
         # NOTE: The following client needs special timeout values because
         # the API is a proxy for the other component.
-        params_volume = {}
-        for _key in ('build_interval', 'build_timeout'):
-            _value = self.parameters['volume'].get(_key)
-            if _value:
-                params_volume[_key] = _value
+        params_volume = {
+            'build_interval': CONF.volume.build_interval,
+            'build_timeout': CONF.volume.build_timeout
+        }
         self.volumes_extensions_client = self.compute.VolumesClient(
             **params_volume)
         self.compute_versions_client = self.compute.VersionsClient(
@@ -225,6 +183,19 @@
         self.credentials_client = self.identity_v3.CredentialsClient(
             **params_v3)
         self.groups_client = self.identity_v3.GroupsClient(**params_v3)
+        self.identity_versions_v3_client = self.identity_v3.VersionsClient(
+            **params_v3)
+        self.oauth_consumers_client = self.identity_v3.OAUTHConsumerClient(
+            **params_v3)
+        self.oauth_token_client = self.identity_v3.OAUTHTokenClient(
+            **params_v3)
+        self.domain_config_client = self.identity_v3.DomainConfigurationClient(
+            **params_v3)
+        self.endpoint_filter_client = \
+            self.identity_v3.EndPointsFilterClient(**params_v3)
+        self.endpoint_groups_client = self.identity_v3.EndPointGroupsClient(
+            **params_v3)
+        self.catalog_client = self.identity_v3.CatalogClient(**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
@@ -246,46 +217,79 @@
 
     def _set_volume_clients(self):
 
-        self.volume_qos_client = self.volume_v1.QosSpecsClient()
-        self.volume_qos_v2_client = self.volume_v2.QosSpecsClient()
-        self.volume_services_client = self.volume_v1.ServicesClient()
-        self.volume_services_v2_client = self.volume_v2.ServicesClient()
-        self.backups_client = self.volume_v1.BackupsClient()
-        self.backups_v2_client = self.volume_v2.BackupsClient()
-        self.encryption_types_client = self.volume_v1.EncryptionTypesClient()
-        self.encryption_types_v2_client = \
-            self.volume_v2.EncryptionTypesClient()
-        self.snapshot_manage_v2_client = self.volume_v2.SnapshotManageClient()
-        self.snapshots_client = self.volume_v1.SnapshotsClient()
-        self.snapshots_v2_client = self.volume_v2.SnapshotsClient()
-        self.volumes_client = self.volume_v1.VolumesClient()
-        self.volumes_v2_client = self.volume_v2.VolumesClient()
-        self.volume_v3_messages_client = self.volume_v3.MessagesClient()
-        self.volume_types_client = self.volume_v1.TypesClient()
-        self.volume_types_v2_client = self.volume_v2.TypesClient()
-        self.volume_hosts_client = self.volume_v1.HostsClient()
-        self.volume_hosts_v2_client = self.volume_v2.HostsClient()
-        self.volume_quotas_client = self.volume_v1.QuotasClient()
-        self.volume_quotas_v2_client = self.volume_v2.QuotasClient()
-        self.volumes_extension_client = self.volume_v1.ExtensionsClient()
-        self.volumes_v2_extension_client = self.volume_v2.ExtensionsClient()
-        self.volume_availability_zone_client = \
-            self.volume_v1.AvailabilityZoneClient()
-        self.volume_v2_availability_zone_client = \
-            self.volume_v2.AvailabilityZoneClient()
-        self.volume_limits_client = self.volume_v1.LimitsClient()
-        self.volume_v2_limits_client = self.volume_v2.LimitsClient()
-        self.volume_capabilities_v2_client = \
-            self.volume_v2.CapabilitiesClient()
-        self.volume_scheduler_stats_v2_client = \
-            self.volume_v2.SchedulerStatsClient()
+        if CONF.volume_feature_enabled.api_v1:
+            self.backups_client = self.volume_v1.BackupsClient()
+            self.encryption_types_client = \
+                self.volume_v1.EncryptionTypesClient()
+            self.snapshots_client = self.volume_v1.SnapshotsClient()
+            self.volume_availability_zone_client = \
+                self.volume_v1.AvailabilityZoneClient()
+            self.volume_hosts_client = self.volume_v1.HostsClient()
+            self.volume_limits_client = self.volume_v1.LimitsClient()
+            self.volume_qos_client = self.volume_v1.QosSpecsClient()
+            self.volume_quotas_client = self.volume_v1.QuotasClient()
+            self.volume_services_client = self.volume_v1.ServicesClient()
+            self.volume_types_client = self.volume_v1.TypesClient()
+            self.volumes_client = self.volume_v1.VolumesClient()
+            self.volumes_extension_client = self.volume_v1.ExtensionsClient()
+
+        if CONF.volume_feature_enabled.api_v2:
+            self.backups_v2_client = self.volume_v2.BackupsClient()
+            self.encryption_types_v2_client = \
+                self.volume_v2.EncryptionTypesClient()
+            self.snapshot_manage_v2_client = \
+                self.volume_v2.SnapshotManageClient()
+            self.snapshots_v2_client = self.volume_v2.SnapshotsClient()
+            self.volume_capabilities_v2_client = \
+                self.volume_v2.CapabilitiesClient()
+            self.volume_manage_v2_client = self.volume_v2.VolumeManageClient()
+            self.volume_qos_v2_client = self.volume_v2.QosSpecsClient()
+            self.volume_services_v2_client = self.volume_v2.ServicesClient()
+            self.volume_types_v2_client = self.volume_v2.TypesClient()
+            self.volume_hosts_v2_client = self.volume_v2.HostsClient()
+            self.volume_quotas_v2_client = self.volume_v2.QuotasClient()
+            self.volume_quota_classes_v2_client = \
+                self.volume_v2.QuotaClassesClient()
+            self.volume_scheduler_stats_v2_client = \
+                self.volume_v2.SchedulerStatsClient()
+            self.volume_transfers_v2_client = \
+                self.volume_v2.TransfersClient()
+            self.volume_v2_availability_zone_client = \
+                self.volume_v2.AvailabilityZoneClient()
+            self.volume_v2_limits_client = self.volume_v2.LimitsClient()
+            self.volumes_v2_client = self.volume_v2.VolumesClient()
+            self.volumes_v2_extension_client = \
+                self.volume_v2.ExtensionsClient()
+
+            # Set default client for users that don't need explicit version
+            self.volumes_client_latest = self.volumes_v2_client
+            self.snapshots_client_latest = self.snapshots_v2_client
+
+        if CONF.volume_feature_enabled.api_v3:
+            self.backups_v3_client = self.volume_v3.BackupsClient()
+            self.group_types_v3_client = self.volume_v3.GroupTypesClient()
+            self.groups_v3_client = self.volume_v3.GroupsClient()
+            self.group_snapshots_v3_client = \
+                self.volume_v3.GroupSnapshotsClient()
+            self.snapshots_v3_client = self.volume_v3.SnapshotsClient()
+            self.volume_v3_messages_client = self.volume_v3.MessagesClient()
+            self.volume_v3_versions_client = self.volume_v3.VersionsClient()
+            self.volumes_v3_client = self.volume_v3.VolumesClient()
+
+            # Set default client for users that don't need explicit version
+            self.volumes_client_latest = self.volumes_v3_client
+            self.snapshots_client_latest = self.snapshots_v3_client
 
     def _set_object_storage_clients(self):
-        # Mandatory parameters (always defined)
-        params = self.parameters['object-storage']
+        # NOTE(andreaf) Load configuration from config. Once object storage
+        # is in lib, configuration will be pulled directly from the registry
+        # and this will not be required anymore.
+        params = config.service_client_config('object-storage')
 
         self.account_client = object_storage.AccountClient(self.auth_provider,
                                                            **params)
+        self.bulk_client = object_storage.BulkMiddlewareClient(
+            self.auth_provider, **params)
         self.capabilities_client = object_storage.CapabilitiesClient(
             self.auth_provider, **params)
         self.container_client = object_storage.ContainerClient(
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 172d9e1..8636405 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -102,8 +102,8 @@
 import yaml
 
 from tempest.common import credentials_factory
-from tempest.common import dynamic_creds
 from tempest import config
+from tempest.lib.common import dynamic_creds
 
 
 LOG = None
@@ -141,18 +141,10 @@
     admin_creds = credentials_factory.get_credentials(
         fill_in=False, identity_version=identity_version, **admin_creds_dict)
     return dynamic_creds.DynamicCredentialProvider(
-        identity_version=identity_version,
         name=opts.tag,
         network_resources=network_resources,
-        neutron_available=CONF.service_available.neutron,
-        create_networks=CONF.auth.create_isolated_networks,
-        identity_admin_role=CONF.identity.admin_role,
-        identity_admin_domain_scope=CONF.identity.admin_domain_scope,
-        project_network_cidr=CONF.network.project_network_cidr,
-        project_network_mask_bits=CONF.network.project_network_mask_bits,
-        public_network_id=CONF.network.public_network_id,
-        admin_creds=admin_creds,
-        **credentials_factory.get_dynamic_provider_params())
+        **credentials_factory.get_dynamic_provider_params(
+            identity_version, admin_creds=admin_creds))
 
 
 def generate_resources(cred_provider, admin):
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index ec76103..a128b3f 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -14,41 +14,61 @@
 # under the License.
 
 """
-Utility for cleaning up environment after Tempest run
+Utility for cleaning up environment after Tempest test run
+
+**Usage:** ``tempest cleanup [--help] [OPTIONS]``
+
+If run with no arguments, ``tempest cleanup`` will query your OpenStack
+deployment and build a list of resources to delete and destroy them. This list
+will exclude the resources from ``saved_state.json`` and will include the
+configured admin account if the ``--delete-tempest-conf-objects`` flag is
+specified. By default the admin project is not deleted and the admin user
+specified in ``tempest.conf`` is never deleted.
+
+Example Run
+-----------
+
+**WARNING: If step 1 is skipped in the example below, the cleanup procedure
+may delete resources that existed in the cloud before the test run. This
+may cause an unwanted destruction of cloud resources, so use caution with
+this command.**
+
+``$ tempest cleanup --init-saved-state``
+
+``$ # Actual running of Tempest tests``
+
+``$ tempest cleanup``
 
 Runtime Arguments
 -----------------
 
-**--init-saved-state**: Before you can execute cleanup you must initialize
-the saved state by running it with the **--init-saved-state** flag
-(creating ./saved_state.json), which protects your deployment from
-cleanup deleting objects you want to keep.  Typically you would run
-cleanup with **--init-saved-state** prior to a tempest run. If this is not
-the case saved_state.json must be edited, removing objects you want
-cleanup to delete.
+**--init-saved-state**: Initializes the saved state of the OpenStack deployment
+and will output a ``saved_state.json`` file containing resources from your
+deployment that will be preserved from the cleanup command. This should be
+done prior to running Tempest tests.
 
-**--dry-run**: Creates a report (dry_run.json) of the tenants that will be
-cleaned up (in the "_tenants_to_clean" array), and the global objects
-that will be removed (tenants, users, flavors and images).  Once
-cleanup is executed in normal mode, running it again with **--dry-run**
-should yield an empty report.
+**--delete-tempest-conf-objects**: If option is present, then the command will
+delete the admin project in addition to the resources associated with them on
+clean up. If option is not present, the command will delete the resources
+associated with the Tempest and alternate Tempest users and projects but will
+not delete the projects themselves.
 
-**NOTE**: The _tenants_to_clean array in dry-run.json lists the
-tenants that cleanup will loop through and delete child objects, not
-delete the tenant itself. This may differ from the tenants array as you
-can clean the tempest and alternate tempest tenants but by default,
-cleanup deletes the objects in the tempest and alternate tempest tenants
-but does not delete those tenants unless the **--delete-tempest-conf-objects**
-flag is used to force their deletion.
+**--dry-run**: Creates a report (``./dry_run.json``) of the projects that will
+be cleaned up (in the ``_tenants_to_clean`` dictionary [1]_) and the global
+objects that will be removed (domains, flavors, images, roles, projects,
+and users). Once the cleanup command is executed (e.g. run without
+parameters), running it again with **--dry-run** should yield an empty report.
 
-**Normal mode**: running with no arguments, will query your deployment and
-build a list of objects to delete after filtering out the objects found in
-saved_state.json and based on the **--delete-tempest-conf-objects** flag.
+**--help**: Print the help text for the command and parameters.
 
-By default the tempest and alternate tempest users and tenants are not
-deleted and the admin user specified in tempest.conf is never deleted.
+.. [1] The ``_tenants_to_clean`` dictionary in ``dry_run.json`` lists the
+    projects that ``tempest cleanup`` will loop through to delete child
+    objects, but the command will, by default, not delete the projects
+    themselves. This may differ from the ``tenants`` list as you can clean
+    the Tempest and alternate Tempest users and projects but they will not be
+    deleted unless the **--delete-tempest-conf-objects** flag is used to
+    force their deletion.
 
-Please run with **--help** to see full list of options.
 """
 import sys
 import traceback
@@ -84,7 +104,8 @@
     def init(self, parsed_args):
         cleanup_service.init_conf()
         self.options = parsed_args
-        self.admin_mgr = credentials.AdminManager()
+        self.admin_mgr = clients.Manager(
+            credentials.get_configured_admin_credentials())
         self.dry_run_data = {}
         self.json_data = {}
 
@@ -243,9 +264,10 @@
 
     def _remove_admin_role(self, tenant_id):
         LOG.debug("Remove admin user role for tenant: %s", tenant_id)
-        # Must initialize AdminManager for each user role
+        # Must initialize Admin Manager for each user role
         # Otherwise authentication exception is thrown, weird
-        id_cl = credentials.AdminManager().identity_client
+        id_cl = clients.Manager(
+            credentials.get_configured_admin_credentials()).identity_client
         if (self._tenant_exists(tenant_id)):
             try:
                 id_cl.delete_role_from_user_on_project(tenant_id,
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index a632726..c75bc85 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -16,11 +16,12 @@
 
 from oslo_log import log as logging
 
+from tempest import clients
 from tempest.common import credentials_factory as credentials
 from tempest.common import identity
+from tempest.common import utils
 from tempest.common.utils import net_info
 from tempest import config
-from tempest import test
 
 LOG = logging.getLogger(__name__)
 CONF = config.CONF
@@ -78,7 +79,8 @@
 
 
 def _get_network_id(net_name, project_name):
-    am = credentials.AdminManager()
+    am = clients.Manager(
+        credentials.get_configured_admin_credentials())
     net_cl = am.networks_client
     tn_cl = am.tenants_client
 
@@ -105,7 +107,7 @@
 
     def _filter_by_tenant_id(self, item_list):
         if (item_list is None
-                or len(item_list) == 0
+                or not item_list
                 or not hasattr(self, 'tenant_id')
                 or self.tenant_id is None
                 or 'tenant_id' not in item_list[0]):
@@ -213,7 +215,9 @@
 class StackService(BaseService):
     def __init__(self, manager, **kwargs):
         super(StackService, self).__init__(kwargs)
-        self.client = manager.orchestration_client
+        params = config.service_client_config('orchestration')
+        self.client = manager.orchestration.OrchestrationClient(
+            manager.auth_provider, **params)
 
     def list(self):
         client = self.client
@@ -960,7 +964,7 @@
         tenant_services.append(StackService)
     if IS_NEUTRON:
         tenant_services.append(NetworkFloatingIpService)
-        if test.is_extension_enabled('metering', 'network'):
+        if utils.is_extension_enabled('metering', 'network'):
             tenant_services.append(NetworkMeteringLabelRuleService)
             tenant_services.append(NetworkMeteringLabelService)
         tenant_services.append(NetworkRouterService)
diff --git a/tempest/cmd/config-generator.tempest.conf b/tempest/cmd/config-generator.tempest.conf
index d718f93..b8f16d9 100644
--- a/tempest/cmd/config-generator.tempest.conf
+++ b/tempest/cmd/config-generator.tempest.conf
@@ -2,7 +2,4 @@
 output_file = etc/tempest.conf.sample
 namespace = tempest.config
 namespace = oslo.concurrency
-namespace = oslo.i18n
 namespace = oslo.log
-namespace = oslo.serialization
-namespace = oslo.utils
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index 54b844a..e71032a 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -78,25 +78,39 @@
 subunit-trace output filter. But, if you would prefer a subunit v2 stream be
 output to STDOUT use the **--subunit** flag
 
+Combining Runs
+==============
+
+There are certain situations in which you want to split a single run of tempest
+across 2 executions of tempest run. (for example to run part of the tests
+serially and others in parallel) To accomplish this but still treat the results
+as a single run you can leverage the **--combine** option which will append
+the current run's results with the previous runs.
 """
 
 import io
 import os
 import sys
+import tempfile
 import threading
 
 from cliff import command
 from os_testr import regex_builder
 from os_testr import subunit_trace
+from oslo_serialization import jsonutils as json
 import six
 from testrepository.commands import run_argv
 
+from tempest import clients
+from tempest.cmd import cleanup_service
 from tempest.cmd import init
 from tempest.cmd import workspace
+from tempest.common import credentials_factory as credentials
 from tempest import config
 
 
 CONF = config.CONF
+SAVED_STATE_JSON = "saved_state.json"
 
 
 class TempestRun(command.Command):
@@ -165,6 +179,17 @@
         else:
             print("No .testr.conf file was found for local execution")
             sys.exit(2)
+        if parsed_args.state:
+            self._init_state()
+        else:
+            pass
+
+        if parsed_args.combine:
+            temp_stream = tempfile.NamedTemporaryFile()
+            return_code = run_argv(['tempest', 'last', '--subunit'], sys.stdin,
+                                   temp_stream, sys.stderr)
+            if return_code > 0:
+                sys.exit(return_code)
 
         regex = self._build_regex(parsed_args)
         if parsed_args.list_tests:
@@ -173,11 +198,41 @@
         else:
             options = self._build_options(parsed_args)
             returncode = self._run(regex, options)
+            if returncode > 0:
+                sys.exit(returncode)
+
+        if parsed_args.combine:
+            return_code = run_argv(['tempest', 'last', '--subunit'], sys.stdin,
+                                   temp_stream, sys.stderr)
+            if return_code > 0:
+                sys.exit(return_code)
+            returncode = run_argv(['tempest', 'load', temp_stream.name],
+                                  sys.stdin, sys.stdout, sys.stderr)
         sys.exit(returncode)
 
     def get_description(self):
         return 'Run tempest'
 
+    def _init_state(self):
+        print("Initializing saved state.")
+        data = {}
+        self.global_services = cleanup_service.get_global_cleanup_services()
+        self.admin_mgr = clients.Manager(
+            credentials.get_configured_admin_credentials())
+        admin_mgr = self.admin_mgr
+        kwargs = {'data': data,
+                  'is_dry_run': False,
+                  'saved_state_json': data,
+                  'is_preserve': False,
+                  'is_save_state': True}
+        for service in self.global_services:
+            svc = service(admin_mgr, **kwargs)
+            svc.run()
+
+        with open(SAVED_STATE_JSON, 'w+') as f:
+            f.write(json.dumps(data,
+                    sort_keys=True, indent=2, separators=(',', ': ')))
+
     def get_parser(self, prog_name):
         parser = super(TempestRun, self).get_parser(prog_name)
         parser = self._add_args(parser)
@@ -228,9 +283,17 @@
         parallel.add_argument('--serial', '-t', dest='parallel',
                               action='store_false',
                               help='Run tests serially')
+        parser.add_argument('--save-state', dest='state',
+                            action='store_true',
+                            help="To save the state of the cloud before "
+                                 "running tempest.")
         # output args
         parser.add_argument("--subunit", action='store_true',
                             help='Enable subunit v2 output')
+        parser.add_argument("--combine", action='store_true',
+                            help='Combine the output of this run with the '
+                                 "previous run's as a combined stream in the "
+                                 "testr repository after it finish")
 
         parser.set_defaults(parallel=True)
         return parser
diff --git a/tempest/cmd/subunit_describe_calls.py b/tempest/cmd/subunit_describe_calls.py
index 0f868a9..f9ebe20 100644
--- a/tempest/cmd/subunit_describe_calls.py
+++ b/tempest/cmd/subunit_describe_calls.py
@@ -102,8 +102,8 @@
     response_re = re.compile(r'.* Response - Headers: (?P<headers>.*)')
     body_re = re.compile(r'.*Body: (?P<body>.*)')
 
-    # Based on mitaka defaults:
-    # http://docs.openstack.org/mitaka/config-reference/
+    # Based on newton defaults:
+    # http://docs.openstack.org/newton/config-reference/
     # firewalls-default-ports.html
     services = {
         "8776": "Block Storage",
@@ -122,7 +122,8 @@
         "873": "rsync",
         "3260": "iSCSI",
         "3306": "MySQL",
-        "5672": "AMQP"}
+        "5672": "AMQP",
+        "8082": "murano"}
 
     def __init__(self, services=None):
         super(UrlParser, self).__init__()
@@ -294,7 +295,8 @@
             outfile.write(json.dumps(url_parser.test_logs))
         return
 
-    for test_name, items in url_parser.test_logs.iteritems():
+    for test_name in url_parser.test_logs:
+        items = url_parser.test_logs[test_name]
         sys.stdout.write('{0}\n'.format(test_name))
         if not items:
             sys.stdout.write('\n')
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 0a1881c..a72493d 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -14,6 +14,51 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+"""
+Verifies user's current tempest configuration.
+
+This command is used for updating or user's tempest configuration file based on
+api queries or replacing all option in a tempest configuration file for a full
+list of extensions.
+
+General Options
+===============
+
+-u, --update
+------------
+Update the config file with results from api queries. This assumes whatever is
+set in the config file is incorrect.
+
+-o FILE, --output=FILE
+----------------------
+Output file to write an updated config file to. This has to be a separate file
+from the original one. If one isn't specified with -u the values which should
+be changed will be printed to STDOUT.
+
+-r, --replace-ext
+-----------------
+If specified the all option will be replaced with a full list of extensions.
+
+Environment Variables
+=====================
+
+The command is workspace aware - it uses tempest config file tempest.conf
+located in ./etc/ directory.
+The path to the config file and it's name can be changed through environment
+variables.
+
+TEMPEST_CONFIG_DIR
+------------------
+Path to a directory where tempest configuration file is stored. If the variable
+is set, the default path (./etc/) is overridden.
+
+TEMPEST_CONFIG
+--------------
+Name of a tempest configuration file. If the variable is specified, the default
+name (tempest.conf) is overridden.
+
+"""
+
 import argparse
 import os
 import re
@@ -30,6 +75,8 @@
 from tempest.common import credentials_factory as credentials
 from tempest import config
 import tempest.lib.common.http
+from tempest.lib import exceptions as lib_exc
+from tempest.services import object_storage
 
 
 CONF = config.CONF
@@ -39,8 +86,8 @@
 
 
 def _get_config_file():
-    default_config_dir = os.path.join(os.path.abspath(
-        os.path.dirname(os.path.dirname(os.path.dirname(__file__)))), "etc")
+    config_dir = os.getcwd()
+    default_config_dir = os.path.join(config_dir, "etc")
     default_config_file = "tempest.conf"
 
     conf_dir = os.environ.get('TEMPEST_CONFIG_DIR', default_config_dir)
@@ -69,7 +116,25 @@
 
 def verify_glance_api_versions(os, update):
     # Check glance api versions
-    _, versions = os.image_client.get_versions()
+    # Since we want to verify that the configuration is correct, we cannot
+    # rely on a specific version of the API being available.
+    try:
+        _, versions = os.image_v1.ImagesClient().get_versions()
+    except lib_exc.NotFound:
+        # If not found, we use v2. The assumption is that either v1 or v2
+        # are available, since glance is marked as available in the catalog.
+        # If not, glance should be disabled in Tempest conf.
+        try:
+            versions = os.image_v2.VersionsClient().list_versions()['versions']
+            versions = [x['id'] for x in versions]
+        except lib_exc.NotFound:
+            msg = ('Glance is available in the catalog, but no known version, '
+                   '(v1.x or v2.x) of Glance could be found, so Glance should '
+                   'be configured as not available')
+            LOG.warn(msg)
+            print_and_or_update('glance', 'service-available', False, update)
+            return
+
     if CONF.image_feature_enabled.api_v1 != contains_version('v1.', versions):
         print_and_or_update('api_v1', 'image-feature-enabled',
                             not CONF.image_feature_enabled.api_v1, update)
@@ -92,13 +157,19 @@
 
 
 def _get_api_versions(os, service):
+    # Clients are used to obtain the base_url. Each client applies the
+    # appropriate filters to the catalog to extract a base_url which
+    # matches the configured region and endpoint_type.
+    # The base URL is used to obtain the list of versions available.
     client_dict = {
-        'nova': os.servers_client,
-        'keystone': os.identity_client,
-        'cinder': os.volumes_client,
+        'nova': os.compute.ServersClient(),
+        'keystone': os.identity_v3.IdentityClient(
+            endpoint_type=CONF.identity.v3_endpoint_type),
+        'cinder': os.volume_v3.VolumesClient(),
     }
-    if service != 'keystone':
-        # Since keystone may be listening on a path, do not remove the path.
+    if service != 'keystone' and service != 'cinder':
+        # Since keystone and cinder may be listening on a path,
+        # do not remove the path.
         client_dict[service].skip_path()
     endpoint = _get_unversioned_endpoint(client_dict[service].base_url)
 
@@ -165,17 +236,16 @@
 
 
 def get_extension_client(os, service):
+    params = config.service_client_config('object-storage')
     extensions_client = {
-        'nova': os.extensions_client,
-        'cinder': os.volumes_extension_client,
-        'neutron': os.network_extensions_client,
-        'swift': os.capabilities_client,
+        'nova': os.compute.ExtensionsClient(),
+        'neutron': os.network.ExtensionsClient(),
+        'swift': object_storage.CapabilitiesClient(os.auth_provider, **params),
+        # NOTE: Cinder v3 API is current and v2 and v1 are deprecated.
+        # V3 extension API is the same as v2, so we reuse the v2 client
+        # for v3 API also.
+        'cinder': os.volume_v2.ExtensionsClient(),
     }
-    # NOTE (e0ne): Use Cinder API v2 by default because v1 is deprecated
-    if CONF.volume_feature_enabled.api_v2:
-        extensions_client['cinder'] = os.volumes_v2_extension_client
-    else:
-        extensions_client['cinder'] = os.volumes_extension_client
 
     if service not in extensions_client:
         print('No tempest extensions client for %s' % service)
@@ -201,7 +271,7 @@
     if service != 'swift':
         resp = extensions_client.list_extensions()
     else:
-        __, resp = extensions_client.list_capabilities()
+        resp = extensions_client.list_capabilities()
     # For Nova, Cinder and Neutron we use the alias name rather than the
     # 'name' field because the alias is considered to be the canonical
     # name.
@@ -345,8 +415,8 @@
                         help="Output file to write an updated config file to. "
                              "This has to be a separate file from the "
                              "original config file. If one isn't specified "
-                             "with -u the new config file will be printed to "
-                             "STDOUT")
+                             "with -u the values which should be changed "
+                             "will be printed to STDOUT")
     parser.add_argument('-r', '--replace-ext', action='store_true',
                         help="If specified the all option will be replaced "
                              "with a full list of extensions")
diff --git a/tempest/cmd/workspace.py b/tempest/cmd/workspace.py
index d2dc00d..8166b4f 100644
--- a/tempest/cmd/workspace.py
+++ b/tempest/cmd/workspace.py
@@ -40,6 +40,8 @@
 ------
 Deletes the entry for a given tempest workspace --name
 
+--rmdir Deletes the given tempest workspace directory
+
 General Options
 ===============
 
@@ -49,11 +51,12 @@
 """
 
 import os
+import shutil
 import sys
 
 from cliff import command
+from cliff import lister
 from oslo_concurrency import lockutils
-import prettytable
 import yaml
 
 from tempest import config
@@ -102,11 +105,16 @@
             sys.exit(1)
 
     @lockutils.synchronized('workspaces', external=True)
-    def remove_workspace(self, name):
+    def remove_workspace_entry(self, name):
         self._populate()
         self._name_exists(name)
-        self.workspaces.pop(name)
+        workspace_path = self.workspaces.pop(name)
         self._write_file()
+        return workspace_path
+
+    @lockutils.synchronized('workspaces', external=True)
+    def remove_workspace_directory(self, workspace_path):
+        shutil.rmtree(workspace_path)
 
     @lockutils.synchronized('workspaces', external=True)
     def list_workspaces(self):
@@ -154,76 +162,101 @@
             self.workspaces = yaml.safe_load(f) or {}
 
 
-class TempestWorkspace(command.Command):
-    def take_action(self, parsed_args):
-        self.manager = WorkspaceManager(parsed_args.workspace_path)
-        if getattr(parsed_args, 'register', None):
-            self.manager.register_new_workspace(
-                parsed_args.name, parsed_args.path)
-        elif getattr(parsed_args, 'rename', None):
-            self.manager.rename_workspace(
-                parsed_args.old_name, parsed_args.new_name)
-        elif getattr(parsed_args, 'move', None):
-            self.manager.move_workspace(
-                parsed_args.name, parsed_args.path)
-        elif getattr(parsed_args, 'remove', None):
-            self.manager.remove_workspace(
-                parsed_args.name)
-        else:
-            self._print_workspaces()
-        sys.exit(0)
+def add_global_arguments(parser):
+    parser.add_argument(
+        '--workspace-path', required=False, default=None,
+        help="The path to the workspace file, the default is "
+             "~/.tempest/workspace.yaml")
+    return parser
 
+
+class TempestWorkspaceRegister(command.Command):
     def get_description(self):
-        return 'Tempest workspace actions'
+        return ('Registers a new tempest workspace via a given '
+                '--name and --path')
 
     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)
+        parser = super(TempestWorkspaceRegister, self).get_parser(prog_name)
+        add_global_arguments(parser)
+        parser.add_argument('--name', required=True)
+        parser.add_argument('--path', required=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])
+    def take_action(self, parsed_args):
+        self.manager = WorkspaceManager(parsed_args.workspace_path)
+        self.manager.register_new_workspace(parsed_args.name, parsed_args.path)
+        sys.exit(0)
 
-        print(output)
+
+class TempestWorkspaceRename(command.Command):
+    def get_description(self):
+        return 'Renames a tempest workspace from --old-name to --new-name'
+
+    def get_parser(self, prog_name):
+        parser = super(TempestWorkspaceRename, self).get_parser(prog_name)
+        add_global_arguments(parser)
+        parser.add_argument('--old-name', required=True)
+        parser.add_argument('--new-name', required=True)
+
+        return parser
+
+    def take_action(self, parsed_args):
+        self.manager = WorkspaceManager(parsed_args.workspace_path)
+        self.manager.rename_workspace(
+            parsed_args.old_name, parsed_args.new_name)
+        sys.exit(0)
+
+
+class TempestWorkspaceMove(command.Command):
+    def get_description(self):
+        return 'Changes the path of a given tempest workspace --name to --path'
+
+    def get_parser(self, prog_name):
+        parser = super(TempestWorkspaceMove, self).get_parser(prog_name)
+        add_global_arguments(parser)
+        parser.add_argument('--name', required=True)
+        parser.add_argument('--path', required=True)
+
+        return parser
+
+    def take_action(self, parsed_args):
+        self.manager = WorkspaceManager(parsed_args.workspace_path)
+        self.manager.move_workspace(parsed_args.name, parsed_args.path)
+        sys.exit(0)
+
+
+class TempestWorkspaceRemove(command.Command):
+    def get_description(self):
+        return 'Deletes the entry for a given tempest workspace --name'
+
+    def get_parser(self, prog_name):
+        parser = super(TempestWorkspaceRemove, self).get_parser(prog_name)
+        add_global_arguments(parser)
+        parser.add_argument('--name', required=True)
+        parser.add_argument('--rmdir', action='store_true',
+                            help='Deletes the given workspace directory')
+
+        return parser
+
+    def take_action(self, parsed_args):
+        self.manager = WorkspaceManager(parsed_args.workspace_path)
+        workspace_path = self.manager.remove_workspace_entry(parsed_args.name)
+        if parsed_args.rmdir:
+            self.manager.remove_workspace_directory(workspace_path)
+        sys.exit(0)
+
+
+class TempestWorkspaceList(lister.Lister):
+    def get_description(self):
+        return 'Outputs the name and path of all known tempest workspaces'
+
+    def get_parser(self, prog_name):
+        parser = super(TempestWorkspaceList, self).get_parser(prog_name)
+        add_global_arguments(parser)
+        return parser
+
+    def take_action(self, parsed_args):
+        self.manager = WorkspaceManager(parsed_args.workspace_path)
+        return (("Name", "Path"),
+                ((n, p) for n, p in self.manager.list_workspaces().items()))
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 01de704..df0f5a5 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -13,20 +13,55 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 
+import base64
+import socket
+import ssl
+import struct
+import textwrap
+
+import six
+from six.moves.urllib import parse as urlparse
+
 from oslo_log import log as logging
 from oslo_utils import excutils
 
-from tempest.common import fixed_network
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common import fixed_network
 from tempest.lib.common import rest_client
 from tempest.lib.common.utils import data_utils
 
+if six.PY2:
+    ord_func = ord
+else:
+    ord_func = int
+
 CONF = config.CONF
 
 LOG = logging.getLogger(__name__)
 
 
+def is_scheduler_filter_enabled(filter_name):
+    """Check the list of enabled compute scheduler filters from config.
+
+    This function checks whether the given compute scheduler filter is
+    available and configured in the config file. If the
+    scheduler_available_filters option is set to 'all' (Default value. which
+    means default filters are configured in nova) in tempest.conf then, this
+    function returns True with assumption that requested filter 'filter_name'
+    is one of available filter in nova ("nova.scheduler.filters.all_filters").
+    """
+
+    filters = CONF.compute_feature_enabled.scheduler_available_filters
+    if not filters:
+        return False
+    if 'all' in filters:
+        return True
+    if filter_name in filters:
+        return True
+    return False
+
+
 def create_test_server(clients, validatable=False, validation_resources=None,
                        tenant_network=None, wait_until=None,
                        volume_backed=False, name=None, flavor=None,
@@ -113,19 +148,29 @@
             if wait_until is None:
                 wait_until = 'ACTIVE'
 
+        if 'user_data' not in kwargs:
+            # If nothing overrides the default user data script then run
+            # a simple script on the host to print networking info. This is
+            # to aid in debugging ssh failures.
+            script = '''
+                     #!/bin/sh
+                     echo "Printing {user} user authorized keys"
+                     cat ~{user}/.ssh/authorized_keys || true
+                     '''.format(user=CONF.validation.image_ssh_user)
+            script_clean = textwrap.dedent(script).lstrip().encode('utf8')
+            script_b64 = base64.b64encode(script_clean)
+            kwargs['user_data'] = script_b64
+
     if volume_backed:
         volume_name = data_utils.rand_name(__name__ + '-volume')
         volumes_client = clients.volumes_v2_client
-        name_field = 'name'
-        if not CONF.volume_feature_enabled.api_v2:
-            volumes_client = clients.volumes_client
-            name_field = 'display_name'
-        params = {name_field: volume_name,
+        params = {'name': volume_name,
                   'imageRef': image_id,
                   'size': CONF.volume.volume_size}
         volume = volumes_client.create_volume(**params)
-        waiters.wait_for_volume_status(volumes_client,
-                                       volume['volume']['id'], 'available')
+        waiters.wait_for_volume_resource_status(volumes_client,
+                                                volume['volume']['id'],
+                                                'available')
 
         bd_map_v2 = [{
             'uuid': volume['volume']['id'],
@@ -153,9 +198,27 @@
         body = rest_client.ResponseBody(body.response, body['server'])
         servers = [body]
 
-    # The name of the method to associate a floating IP to as server is too
-    # long for PEP8 compliance so:
-    assoc = clients.compute_floating_ips_client.associate_floating_ip_to_server
+    def _setup_validation_fip():
+        if CONF.service_available.neutron:
+            ifaces = clients.interfaces_client.list_interfaces(server['id'])
+            validation_port = None
+            for iface in ifaces['interfaceAttachments']:
+                if iface['net_id'] == tenant_network['id']:
+                    validation_port = iface['port_id']
+                    break
+            if not validation_port:
+                # NOTE(artom) This will get caught by the catch-all clause in
+                # the wait_until loop below
+                raise ValueError('Unable to setup floating IP for validation: '
+                                 'port not found on tenant network')
+            clients.floating_ips_client.update_floatingip(
+                validation_resources['floating_ip']['id'],
+                port_id=validation_port)
+        else:
+            fip_client = clients.compute_floating_ips_client
+            fip_client.associate_floating_ip_to_server(
+                floating_ip=validation_resources['floating_ip']['ip'],
+                server_id=servers[0]['id'])
 
     if wait_until:
         for server in servers:
@@ -167,9 +230,7 @@
                 # creation will fail with the condition above (l.58).
                 if CONF.validation.run_validation and validatable:
                     if CONF.validation.connect_method == 'floating':
-                        assoc(floating_ip=validation_resources[
-                              'floating_ip']['ip'],
-                              server_id=servers[0]['id'])
+                        _setup_validation_fip()
 
             except Exception:
                 with excutils.save_and_reraise_exception():
@@ -180,6 +241,19 @@
                         except Exception:
                             LOG.exception('Deleting server %s failed',
                                           server['id'])
+                    for server in servers:
+                        # NOTE(artom) If the servers were booted with volumes
+                        # and with delete_on_termination=False we need to wait
+                        # for the servers to go away before proceeding with
+                        # cleanup, otherwise we'll attempt to delete the
+                        # volumes while they're still attached to servers that
+                        # are in the process of being deleted.
+                        try:
+                            waiters.wait_for_server_termination(
+                                clients.servers_client, server['id'])
+                        except Exception:
+                            LOG.exception('Server %s failed to delete in time',
+                                          server['id'])
 
     return body, servers
 
@@ -209,3 +283,118 @@
             servers_client.shelve_offload_server(server_id)
             waiters.wait_for_server_status(servers_client, server_id,
                                            'SHELVED_OFFLOADED')
+
+
+def create_websocket(url):
+    url = urlparse.urlparse(url)
+    if url.scheme == 'https':
+        client_socket = ssl.wrap_socket(socket.socket(socket.AF_INET,
+                                                      socket.SOCK_STREAM))
+    else:
+        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    client_socket.connect((url.hostname, url.port))
+    # Turn the Socket into a WebSocket to do the communication
+    return _WebSocket(client_socket, url)
+
+
+class _WebSocket(object):
+    def __init__(self, client_socket, url):
+        """Contructor for the WebSocket wrapper to the socket."""
+        self._socket = client_socket
+        # cached stream for early frames.
+        self.cached_stream = b''
+        # Upgrade the HTTP connection to a WebSocket
+        self._upgrade(url)
+
+    def _recv(self, recv_size):
+        """Wrapper to receive data from the cached stream or socket."""
+        if recv_size <= 0:
+            return None
+
+        data_from_cached = b''
+        data_from_socket = b''
+        if len(self.cached_stream) > 0:
+            read_from_cached = min(len(self.cached_stream), recv_size)
+            data_from_cached += self.cached_stream[:read_from_cached]
+            self.cached_stream = self.cached_stream[read_from_cached:]
+            recv_size -= read_from_cached
+        if recv_size > 0:
+            data_from_socket = self._socket.recv(recv_size)
+        return data_from_cached + data_from_socket
+
+    def receive_frame(self):
+        """Wrapper for receiving data to parse the WebSocket frame format"""
+        # We need to loop until we either get some bytes back in the frame
+        # or no data was received (meaning the socket was closed).  This is
+        # done to handle the case where we get back some empty frames
+        while True:
+            header = self._recv(2)
+            # If we didn't receive any data, just return None
+            if not header:
+                return None
+            # We will make the assumption that we are only dealing with
+            # frames less than 125 bytes here (for the negotiation) and
+            # that only the 2nd byte contains the length, and since the
+            # server doesn't do masking, we can just read the data length
+            if ord_func(header[1]) & 127 > 0:
+                return self._recv(ord_func(header[1]) & 127)
+
+    def send_frame(self, data):
+        """Wrapper for sending data to add in the WebSocket frame format."""
+        frame_bytes = list()
+        # For the first byte, want to say we are sending binary data (130)
+        frame_bytes.append(130)
+        # Only sending negotiation data so don't need to worry about > 125
+        # We do need to add the bit that says we are masking the data
+        frame_bytes.append(len(data) | 128)
+        # We don't really care about providing a random mask for security
+        # So we will just hard-code a value since a test program
+        mask = [7, 2, 1, 9]
+        for i in range(len(mask)):
+            frame_bytes.append(mask[i])
+        # Mask each of the actual data bytes that we are going to send
+        for i in range(len(data)):
+            frame_bytes.append(ord_func(data[i]) ^ mask[i % 4])
+        # Convert our integer list to a binary array of bytes
+        frame_bytes = struct.pack('!%iB' % len(frame_bytes), * frame_bytes)
+        self._socket.sendall(frame_bytes)
+
+    def close(self):
+        """Helper method to close the connection."""
+        # Close down the real socket connection and exit the test program
+        if self._socket is not None:
+            self._socket.shutdown(1)
+            self._socket.close()
+            self._socket = None
+
+    def _upgrade(self, url):
+        """Upgrade the HTTP connection to a WebSocket and verify."""
+        # The real request goes to the /websockify URI always
+        reqdata = 'GET /websockify HTTP/1.1\r\n'
+        reqdata += 'Host: %s:%s\r\n' % (url.hostname, url.port)
+        # Tell the HTTP Server to Upgrade the connection to a WebSocket
+        reqdata += 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
+        # The token=xxx is sent as a Cookie not in the URI
+        reqdata += 'Cookie: %s\r\n' % url.query
+        # Use a hard-coded WebSocket key since a test program
+        reqdata += 'Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n'
+        reqdata += 'Sec-WebSocket-Version: 13\r\n'
+        # We are choosing to use binary even though browser may do Base64
+        reqdata += 'Sec-WebSocket-Protocol: binary\r\n\r\n'
+        # Send the HTTP GET request and get the response back
+        self._socket.sendall(reqdata.encode('utf8'))
+        self.response = data = self._socket.recv(4096)
+        # Loop through & concatenate all of the data in the response body
+        end_loc = self.response.find(b'\r\n\r\n')
+        while data and end_loc < 0:
+            data = self._socket.recv(4096)
+            self.response += data
+            end_loc = self.response.find(b'\r\n\r\n')
+
+        if len(self.response) > end_loc + 4:
+            # In case some frames (e.g. the first RFP negotiation) have
+            # arrived, cache it for next reading.
+            self.cached_stream = self.response[end_loc + 4:]
+            # ensure response ends with '\r\n\r\n'.
+            self.response = self.response[:end_loc + 4]
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index e6b46ed..a340531 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -14,10 +14,10 @@
 from oslo_concurrency import lockutils
 
 from tempest import clients
-from tempest.common import dynamic_creds
-from tempest.common import preprov_creds
 from tempest import config
 from tempest.lib import auth
+from tempest.lib.common import dynamic_creds
+from tempest.lib.common import preprov_creds
 from tempest.lib import exceptions
 
 CONF = config.CONF
@@ -39,19 +39,69 @@
 
 
 # Subset of the parameters of credential providers that depend on configuration
-def get_common_provider_params():
+def _get_common_provider_params(identity_version):
+    if identity_version == 'v3':
+        identity_uri = CONF.identity.uri_v3
+    elif identity_version == 'v2':
+        identity_uri = CONF.identity.uri
+    else:
+        raise exceptions.InvalidIdentityVersion(
+            identity_version=identity_version)
     return {
+        'identity_version': identity_version,
+        'identity_uri': identity_uri,
         '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(identity_version, admin_creds=None):
+    """Dynamic provider parameters setup from config
+
+    This helper returns a dict of parameter that can be used to initialise
+    a `DynamicCredentialProvider` according to tempest configuration.
+    Parameters that are not configuration specific (name, network_resources)
+    are not returned.
+
+    :param identity_version: 'v2' or 'v3'
+    :param admin_creds: An object of type `auth.Credentials`. If None, it
+                        is built from the configuration file as well.
+    :return: A dict with the parameters
+    """
+    _common_params = _get_common_provider_params(identity_version)
+    admin_creds = admin_creds or get_configured_admin_credentials(
+        fill_in=True, identity_version=identity_version)
+    if identity_version == 'v3':
+        endpoint_type = CONF.identity.v3_endpoint_type
+    elif identity_version == 'v2':
+        endpoint_type = CONF.identity.v2_admin_endpoint_type
+    return dict(_common_params, **dict([
+        ('admin_creds', admin_creds),
+        ('identity_admin_domain_scope', CONF.identity.admin_domain_scope),
+        ('identity_admin_role', CONF.identity.admin_role),
+        ('extra_roles', CONF.auth.tempest_roles),
+        ('neutron_available', CONF.service_available.neutron),
+        ('project_network_cidr', CONF.network.project_network_cidr),
+        ('project_network_mask_bits', CONF.network.project_network_mask_bits),
+        ('public_network_id', CONF.network.public_network_id),
+        ('create_networks', (CONF.auth.create_isolated_networks and not
+                             CONF.network.shared_physical_network)),
+        ('resource_prefix', CONF.resources_prefix),
+        ('identity_admin_endpoint_type', endpoint_type)
+    ]))
 
 
-def get_preprov_provider_params():
-    _common_params = get_common_provider_params()
+def get_preprov_provider_params(identity_version):
+    """Pre-provisioned provider parameters setup from config
+
+    This helper returns a dict of parameter that can be used to initialise
+    a `PreProvisionedCredentialProvider` according to tempest configuration.
+    Parameters that are not configuration specific (name) are not returned.
+
+    :param identity_version: 'v2' or 'v3'
+    :return: A dict with the parameters
+    """
+    _common_params = _get_common_provider_params(identity_version)
     reseller_admin_role = CONF.object_storage.reseller_admin_role
     return dict(_common_params, **dict([
         ('accounts_lock_dir', lockutils.get_lock_path(CONF)),
@@ -61,53 +111,55 @@
     ]))
 
 
-# Return the right implementation of CredentialProvider based on config
-# Dropping interface and password, as they are never used anyways
-# TODO(andreaf) Drop them from the CredentialsProvider interface completely
 def get_credentials_provider(name, network_resources=None,
                              force_tenant_isolation=False,
                              identity_version=None):
+    """Return the right implementation of CredentialProvider based on config
+
+    This helper returns the right implementation of CredentialProvider based on
+    config and on the value of force_tenant_isolation.
+
+    :param name: When provided, it makes it possible to associate credential
+                 artifacts back to the owner (test class).
+    :param network_resources: Dictionary of network resources to be allocated
+                              for each test account. Only valid for the dynamic
+                              credentials provider.
+    :param force_tenant_isolation: Always return a `DynamicCredentialProvider`,
+                                   regardless of the configuration.
+    :param identity_version: Use the specified identity API version, regardless
+                             of the configuration. Valid values are 'v2', 'v3'.
+    """
     # If a test requires a new account to work, it can have it via forcing
     # dynamic credentials. A new account will be produced only for that test.
     # In case admin credentials are not available for the account creation,
     # the test should be skipped else it would fail.
     identity_version = identity_version or CONF.identity.auth_version
     if CONF.auth.use_dynamic_credentials or force_tenant_isolation:
-        admin_creds = get_configured_admin_credentials(
-            fill_in=True, identity_version=identity_version)
         return dynamic_creds.DynamicCredentialProvider(
             name=name,
             network_resources=network_resources,
-            identity_version=identity_version,
-            admin_creds=admin_creds,
-            identity_admin_domain_scope=CONF.identity.admin_domain_scope,
-            identity_admin_role=CONF.identity.admin_role,
-            extra_roles=CONF.auth.tempest_roles,
-            neutron_available=CONF.service_available.neutron,
-            project_network_cidr=CONF.network.project_network_cidr,
-            project_network_mask_bits=CONF.network.project_network_mask_bits,
-            public_network_id=CONF.network.public_network_id,
-            create_networks=(CONF.auth.create_isolated_networks and not
-                             CONF.network.shared_physical_network),
-            resource_prefix=CONF.resources_prefix,
-            **get_dynamic_provider_params())
+            **get_dynamic_provider_params(identity_version))
     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())
+                name=name,
+                **get_preprov_provider_params(identity_version))
         else:
             raise exceptions.InvalidConfiguration(
                 'A valid credential provider is needed')
 
 
-# We want a helper function here to check and see if admin credentials
-# are available so we can do a single call from skip_checks if admin
-# creds area available.
-# This depends on identity_version as there may be admin credentials
-# available for v2 but not for v3.
 def is_admin_available(identity_version):
+    """Helper to check for admin credentials
+
+    Helper function to check if a set of admin credentials is available so we
+    can do a single call from skip_checks.
+    This helper depends on identity_version as there may be admin credentials
+    available for v2 but not for v3.
+
+    :param identity_version: 'v2' or 'v3'
+    """
     is_admin = True
     # If dynamic credentials is enabled admin will be available
     if CONF.auth.use_dynamic_credentials:
@@ -115,8 +167,8 @@
     # Check whether test accounts file has the admin specified or not
     elif CONF.auth.test_accounts_file:
         check_accounts = preprov_creds.PreProvisionedCredentialProvider(
-            identity_version=identity_version, name='check_admin',
-            **get_preprov_provider_params())
+            name='check_admin',
+            **get_preprov_provider_params(identity_version))
         if not check_accounts.admin_available():
             is_admin = False
     else:
@@ -128,20 +180,24 @@
     return is_admin
 
 
-# We want a helper function here to check and see if alt credentials
-# are available so we can do a single call from skip_checks if alt
-# creds area available.
-# This depends on identity_version as there may be alt credentials
-# available for v2 but not for v3.
 def is_alt_available(identity_version):
+    """Helper to check for alt credentials
+
+    Helper function to check if a second set of credentials is available (aka
+    alt credentials) so we can do a single call from skip_checks.
+    This helper depends on identity_version as there may be alt credentials
+    available for v2 but not for v3.
+
+    :param identity_version: 'v2' or 'v3'
+    """
     # If dynamic credentials is enabled alt will be available
     if CONF.auth.use_dynamic_credentials:
         return True
     # Check whether test accounts file has the admin specified or not
     if CONF.auth.test_accounts_file:
         check_accounts = preprov_creds.PreProvisionedCredentialProvider(
-            identity_version=identity_version, name='check_alt',
-            **get_preprov_provider_params())
+            name='check_alt',
+            **get_preprov_provider_params(identity_version))
     else:
         raise exceptions.InvalidConfiguration(
             'A valid credential provider is needed')
@@ -171,9 +227,19 @@
 }
 
 
-# Read credentials from configuration, builds a Credentials object
-# based on the specified or configured version
 def get_configured_admin_credentials(fill_in=True, identity_version=None):
+    """Get admin credentials from the config file
+
+    Read credentials from configuration, builds a Credentials object based on
+    the specified or configured version
+
+    :param fill_in: If True, a request to the Token API is submitted, and the
+                    credential object is filled in with all names and IDs from
+                    the token API response.
+    :param identity_version: The identity version to talk to and the type of
+                             credentials object to be created. 'v2' or 'v3'.
+    :returns: An object of a sub-type of `auth.Credentials`
+    """
     identity_version = identity_version or CONF.identity.auth_version
 
     if identity_version not in ('v2', 'v3'):
@@ -202,9 +268,20 @@
     return credentials
 
 
-# Wrapper around auth.get_credentials to use the configured identity version
-# if none is specified
 def get_credentials(fill_in=True, identity_version=None, **kwargs):
+    """Get credentials from dict based on config
+
+    Wrapper around auth.get_credentials to use the configured identity version
+    if none is specified.
+
+    :param fill_in: If True, a request to the Token API is submitted, and the
+                    credential object is filled in with all names and IDs from
+                    the token API response.
+    :param identity_version: The identity version to talk to and the type of
+                             credentials object to be created. 'v2' or 'v3'.
+    :param kwargs: Attributes to be used to build the Credentials object.
+    :returns: An object of a sub-type of `auth.Credentials`
+    """
     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
diff --git a/tempest/common/utils/__init__.py b/tempest/common/utils/__init__.py
index b6565d1..5a86caa 100644
--- a/tempest/common/utils/__init__.py
+++ b/tempest/common/utils/__init__.py
@@ -12,17 +12,19 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import functools
 from functools import partial
 
+import testtools
+
 from tempest import config
+from tempest.exceptions import InvalidServiceTag
 from tempest.lib.common.utils import data_utils as lib_data_utils
+from tempest.lib import decorators
+
 
 CONF = config.CONF
 
-PING_IPV4_COMMAND = 'ping -c 3 '
-PING_IPV6_COMMAND = 'ping6 -c 3 '
-PING_PACKET_LOSS_REGEX = '(\d{1,3})\.?\d*\% packet loss'
-
 
 class DataUtils(object):
     def __getattr__(self, attr):
@@ -40,3 +42,89 @@
         return attr_obj
 
 data_utils = DataUtils()
+
+
+def get_service_list():
+    service_list = {
+        'compute': CONF.service_available.nova,
+        'image': CONF.service_available.glance,
+        'volume': CONF.service_available.cinder,
+        # NOTE(masayukig): We have two network services which are neutron and
+        # nova-network. And we have no way to know whether nova-network is
+        # available or not. After the pending removal of nova-network from
+        # nova, we can treat the network/neutron case in the same manner as
+        # the other services.
+        'network': True,
+        # NOTE(masayukig): Tempest tests always require the identity service.
+        # So we should set this True here.
+        'identity': True,
+        'object_storage': CONF.service_available.swift,
+    }
+    return service_list
+
+
+def services(*args):
+    """A decorator used to set an attr for each service used in a test case
+
+    This decorator applies a testtools attr for each service that gets
+    exercised by a test case.
+    """
+    def decorator(f):
+        known_services = get_service_list()
+
+        for service in args:
+            if service not in known_services:
+                raise InvalidServiceTag('%s is not a valid service' % service)
+        decorators.attr(type=list(args))(f)
+
+        @functools.wraps(f)
+        def wrapper(self, *func_args, **func_kwargs):
+            service_list = get_service_list()
+
+            for service in args:
+                if not service_list[service]:
+                    msg = 'Skipped because the %s service is not available' % (
+                        service)
+                    raise testtools.TestCase.skipException(msg)
+            return f(self, *func_args, **func_kwargs)
+        return wrapper
+    return decorator
+
+
+def requires_ext(**kwargs):
+    """A decorator to skip tests if an extension is not enabled
+
+    @param extension
+    @param service
+    """
+    def decorator(func):
+        @functools.wraps(func)
+        def wrapper(*func_args, **func_kwargs):
+            if not is_extension_enabled(kwargs['extension'],
+                                        kwargs['service']):
+                msg = "Skipped because %s extension: %s is not enabled" % (
+                    kwargs['service'], kwargs['extension'])
+                raise testtools.TestCase.skipException(msg)
+            return func(*func_args, **func_kwargs)
+        return wrapper
+    return decorator
+
+
+def is_extension_enabled(extension_name, service):
+    """A function that will check the list of enabled extensions from config
+
+    """
+    config_dict = {
+        'compute': CONF.compute_feature_enabled.api_extensions,
+        'volume': CONF.volume_feature_enabled.api_extensions,
+        'network': CONF.network_feature_enabled.api_extensions,
+        'object': CONF.object_storage_feature_enabled.discoverable_apis,
+        'identity': CONF.identity_feature_enabled.api_extensions
+    }
+    if not config_dict[service]:
+        return False
+    if config_dict[service][0] == 'all':
+        return True
+    if extension_name in config_dict[service]:
+        return True
+    return False
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 009812e..52ccfa9 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -10,17 +10,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import netaddr
 import re
-import six
-import sys
 import time
 
 from oslo_log import log as logging
 
 from tempest import config
-from tempest.lib.common import ssh
-from tempest.lib.common.utils import test_utils
+from tempest.lib.common.utils.linux import remote_client
 import tempest.lib.exceptions
 
 CONF = config.CONF
@@ -28,39 +24,11 @@
 LOG = logging.getLogger(__name__)
 
 
-def debug_ssh(function):
-    """Decorator to generate extra debug info in case off SSH failure"""
-    def wrapper(self, *args, **kwargs):
-        try:
-            return function(self, *args, **kwargs)
-        except tempest.lib.exceptions.SSHTimeout:
-            try:
-                original_exception = sys.exc_info()
-                caller = test_utils.find_test_caller() or "not found"
-                if self.server:
-                    msg = 'Caller: %s. Timeout trying to ssh to server %s'
-                    LOG.debug(msg, caller, self.server)
-                    if self.log_console and self.servers_client:
-                        try:
-                            msg = 'Console log for server %s: %s'
-                            console_log = (
-                                self.servers_client.get_console_output(
-                                    self.server['id'])['output'])
-                            LOG.debug(msg, self.server['id'], console_log)
-                        except Exception:
-                            msg = 'Could not get console_log for server %s'
-                            LOG.debug(msg, self.server['id'])
-                # re-raise the original ssh timeout exception
-                six.reraise(*original_exception)
-            finally:
-                # Delete the traceback to avoid circular references
-                _, _, trace = original_exception
-                del trace
-    return wrapper
+class RemoteClient(remote_client.RemoteClient):
 
-
-class RemoteClient(object):
-
+    # TODO(oomichi): Make this class deprecated after migrating
+    #                necessary methods to tempest.lib and cleaning
+    #                unnecessary methods up from this class.
     def __init__(self, ip_address, username, password=None, pkey=None,
                  server=None, servers_client=None):
         """Executes commands in a VM over ssh
@@ -72,46 +40,18 @@
         :param server: server dict, used for debugging purposes
         :param servers_client: servers client, used for debugging purposes
         """
-        self.server = server
-        self.servers_client = servers_client
-        ssh_timeout = CONF.validation.ssh_timeout
-        connect_timeout = CONF.validation.connect_timeout
-        self.log_console = CONF.compute_feature_enabled.console_output
+        super(RemoteClient, self).__init__(
+            ip_address, username, password=password, pkey=pkey,
+            server=server, servers_client=servers_client,
+            ssh_timeout=CONF.validation.ssh_timeout,
+            connect_timeout=CONF.validation.connect_timeout,
+            console_output_enabled=CONF.compute_feature_enabled.console_output,
+            ssh_shell_prologue=CONF.validation.ssh_shell_prologue,
+            ping_count=CONF.validation.ping_count,
+            ping_size=CONF.validation.ping_size)
 
-        self.ssh_client = ssh.Client(ip_address, username, password,
-                                     ssh_timeout, pkey=pkey,
-                                     channel_timeout=connect_timeout)
-
-    @debug_ssh
-    def exec_command(self, cmd):
-        # Shell options below add more clearness on failures,
-        # path is extended for some non-cirros guest oses (centos7)
-        cmd = CONF.validation.ssh_shell_prologue + " " + cmd
-        LOG.debug("Remote command: %s", cmd)
-        return self.ssh_client.exec_command(cmd)
-
-    @debug_ssh
-    def validate_authentication(self):
-        """Validate ssh connection and authentication
-
-           This method raises an Exception when the validation fails.
-        """
-        self.ssh_client.test_connection_auth()
-
-    def get_hostname(self):
-        # Get host name using command "hostname"
-        actual_hostname = self.exec_command("hostname").rstrip()
-        return actual_hostname
-
-    def get_ram_size_in_mb(self):
-        output = self.exec_command('free -m | grep Mem')
-        if output:
-            return output.split()[1]
-
-    def get_number_of_vcpus(self):
-        output = self.exec_command('grep -c ^processor /proc/cpuinfo')
-        return int(output)
-
+    # Note that this method will not work on SLES11 guests, as they do
+    # not support the TYPE column on lsblk
     def get_disks(self):
         # Select root disk devices as shown by lsblk
         command = 'lsblk -lb --nodeps'
@@ -124,10 +64,14 @@
                 # Show header line too
                 selected.append(l)
             # lsblk lists disk type in a column right-aligned with TYPE
-            elif pos > 0 and l[pos:pos + 4] == "disk":
+            elif pos is not None and pos > 0 and l[pos:pos + 4] == "disk":
                 selected.append(l)
 
-        return "\n".join(selected)
+        if selected:
+            return "\n".join(selected)
+        else:
+            msg = "'TYPE' column is required but the output doesn't have it: "
+            raise tempest.lib.exceptions.TempestException(msg + output)
 
     def get_boot_time(self):
         cmd = 'cut -f1 -d. /proc/uptime'
@@ -141,21 +85,6 @@
         cmd = 'sudo sh -c "echo \\"%s\\" >/dev/console"' % message
         return self.exec_command(cmd)
 
-    def ping_host(self, host, count=CONF.validation.ping_count,
-                  size=CONF.validation.ping_size, nic=None):
-        addr = netaddr.IPAddress(host)
-        cmd = 'ping6' if addr.version == 6 else 'ping'
-        if nic:
-            cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
-        cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
-        return self.exec_command(cmd)
-
-    def set_mac_address(self, nic, address):
-        self.set_nic_state(nic=nic, state="down")
-        cmd = "sudo ip link set dev {0} address {1}".format(nic, address)
-        self.exec_command(cmd)
-        self.set_nic_state(nic=nic, state="up")
-
     def get_mac_address(self, nic=""):
         show_nic = "show {nic} ".format(nic=nic) if nic else ""
         cmd = "ip addr %s| awk '/ether/ {print $2}'" % show_nic
@@ -164,32 +93,12 @@
     def get_nic_name_by_mac(self, address):
         cmd = "ip -o link | awk '/%s/ {print $2}'" % address
         nic = self.exec_command(cmd)
-        return nic.strip().strip(":").lower()
+        return nic.strip().strip(":").split('@')[0].lower()
 
     def get_nic_name_by_ip(self, address):
         cmd = "ip -o addr | awk '/%s/ {print $2}'" % address
         nic = self.exec_command(cmd)
-        return nic.strip().strip(":").lower()
-
-    def get_ip_list(self):
-        cmd = "ip address"
-        return self.exec_command(cmd)
-
-    def assign_static_ip(self, nic, addr):
-        cmd = "sudo ip addr add {ip}/{mask} dev {nic}".format(
-            ip=addr, mask=CONF.network.project_network_mask_bits,
-            nic=nic
-        )
-        return self.exec_command(cmd)
-
-    def set_nic_state(self, nic, state="up"):
-        cmd = "sudo ip link set {nic} {state}".format(nic=nic, state=state)
-        return self.exec_command(cmd)
-
-    def get_pids(self, pr_name):
-        # Get pid(s) of a process/program
-        cmd = "ps -ef | grep %s | grep -v 'grep' | awk {'print $1'}" % pr_name
-        return self.exec_command(cmd).split('\n')
+        return nic.strip().strip(":").split('@')[0].lower()
 
     def get_dns_servers(self):
         cmd = 'cat /etc/resolv.conf'
@@ -199,10 +108,6 @@
                        if len(l) and l[0] == 'nameserver']
         return dns_servers
 
-    def send_signal(self, pid, signum):
-        cmd = 'sudo /bin/kill -{sig} {pid}'.format(pid=pid, sig=signum)
-        return self.exec_command(cmd)
-
     def _renew_lease_udhcpc(self, fixed_ip=None):
         """Renews DHCP lease via udhcpc client. """
         file_path = '/var/run/udhcpc.'
@@ -210,14 +115,15 @@
         pid = self.exec_command('cat {path}{nic}.pid'.
                                 format(path=file_path, nic=nic_name))
         pid = pid.strip()
-        self.send_signal(pid, 'USR1')
+        cmd = 'sudo /bin/kill -{sig} {pid}'.format(pid=pid, sig='USR1')
+        self.exec_command(cmd)
 
     def _renew_lease_dhclient(self, fixed_ip=None):
         """Renews DHCP lease via dhclient client. """
         cmd = "sudo /sbin/dhclient -r && sudo /sbin/dhclient"
         self.exec_command(cmd)
 
-    def renew_lease(self, fixed_ip=None):
+    def renew_lease(self, fixed_ip=None, dhcp_client='udhcpc'):
         """Wrapper method for renewing DHCP lease via given client
 
         Supporting:
@@ -226,7 +132,6 @@
         """
         # TODO(yfried): add support for dhcpcd
         supported_clients = ['udhcpc', 'dhclient']
-        dhcp_client = CONF.scenario.dhcp_client
         if dhcp_client not in supported_clients:
             raise tempest.lib.exceptions.InvalidConfiguration(
                 '%s DHCP client unsupported' % dhcp_client)
diff --git a/tempest/common/validation_resources.py b/tempest/common/validation_resources.py
index 88697c4..ae9d584 100644
--- a/tempest/common/validation_resources.py
+++ b/tempest/common/validation_resources.py
@@ -13,45 +13,78 @@
 
 from oslo_log import log as logging
 
-from tempest import config
-
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import exceptions as lib_exc
 
-CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
-def _create_neutron_sec_group_rules(os, sec_group):
-    sec_group_rules_client = os.security_group_rules_client
-    ethertype = 'IPv4'
-    if CONF.validation.ip_version_for_ssh == 6:
-        ethertype = 'IPv6'
-
-    sec_group_rules_client.create_security_group_rule(
-        security_group_id=sec_group['id'],
-        protocol='tcp',
-        ethertype=ethertype,
-        port_range_min=22,
-        port_range_max=22,
-        direction='ingress')
-    sec_group_rules_client.create_security_group_rule(
-        security_group_id=sec_group['id'],
-        protocol='icmp',
-        ethertype=ethertype,
-        direction='ingress')
+def _network_service(clients, use_neutron):
+    # Internal helper to select the right network clients
+    if use_neutron:
+        return clients.network
+    else:
+        return clients.compute
 
 
-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
+def create_ssh_security_group(clients, add_rule=False, ethertype='IPv4',
+                              use_neutron=True):
+    """Create a security group for ping/ssh testing
+
+    Create a security group to be attached to a VM using the nova or neutron
+    clients. If rules are added, the group can be attached to a VM to enable
+    connectivity validation over ICMP and further testing over SSH.
+
+    :param clients: Instance of `tempest.lib.services.clients.ServiceClients`
+        or of a subclass of it. Resources are provisioned using clients from
+        `clients`.
+    :param add_rule: Whether security group rules are provisioned or not.
+        Defaults to `False`.
+    :param ethertype: 'IPv4' or 'IPv6'. Honoured only in case neutron is used.
+    :param use_neutron: When True resources are provisioned via neutron, when
+        False resources are provisioned via nova.
+    :returns: A dictionary with the security group as returned by the API.
+
+    Examples::
+
+        from tempest.common import validation_resources as vr
+        from tempest.lib import auth
+        from tempest.lib.services import clients
+
+        creds = auth.get_credentials('http://mycloud/identity/v3',
+                                     username='me', project_name='me',
+                                     password='secret', domain_name='Default')
+        osclients = clients.ServiceClients(creds, 'http://mycloud/identity/v3')
+        # Security group for IPv4 tests
+        sg4 = vr.create_ssh_security_group(osclients, add_rule=True)
+        # Security group for IPv6 tests
+        sg6 = vr.create_ssh_security_group(osclients, ethertype='IPv6',
+                                           add_rule=True)
+    """
+    network_service = _network_service(clients, use_neutron)
+    security_groups_client = network_service.SecurityGroupsClient()
+    security_group_rules_client = network_service.SecurityGroupRulesClient()
+    # Security Group clients for nova and neutron behave the same
     sg_name = data_utils.rand_name('securitygroup-')
     sg_description = data_utils.rand_name('description-')
     security_group = security_groups_client.create_security_group(
         name=sg_name, description=sg_description)['security_group']
+    # Security Group Rules clients require different parameters depending on
+    # the network service in use
     if add_rule:
-        if CONF.service_available.neutron:
-            _create_neutron_sec_group_rules(os, security_group)
+        if use_neutron:
+            security_group_rules_client.create_security_group_rule(
+                security_group_id=security_group['id'],
+                protocol='tcp',
+                ethertype=ethertype,
+                port_range_min=22,
+                port_range_max=22,
+                direction='ingress')
+            security_group_rules_client.create_security_group_rule(
+                security_group_id=security_group['id'],
+                protocol='icmp',
+                ethertype=ethertype,
+                direction='ingress')
         else:
             security_group_rules_client.create_security_group_rule(
                 parent_group_id=security_group['id'], ip_protocol='tcp',
@@ -64,78 +97,196 @@
     return security_group
 
 
-def create_validation_resources(os, validation_resources=None):
+def create_validation_resources(clients, keypair=False, floating_ip=False,
+                                security_group=False,
+                                security_group_rules=False,
+                                ethertype='IPv4', use_neutron=True,
+                                floating_network_id=None,
+                                floating_network_name=None):
+    """Provision resources for VM ping/ssh testing
+
+    Create resources required to be able to ping / ssh a virtual machine:
+    keypair, security group, security group rules and a floating IP.
+    Which of those resources are required may depend on the cloud setup and on
+    the specific test and it can be controlled via the corresponding
+    arguments.
+
+    Provisioned resources are returned in a dictionary.
+
+    :param clients: Instance of `tempest.lib.services.clients.ServiceClients`
+        or of a subclass of it. Resources are provisioned using clients from
+        `clients`.
+    :param keypair: Whether to provision a keypair. Defaults to False.
+    :param floating_ip: Whether to provision a floating IP. Defaults to False.
+    :param security_group: Whether to provision a security group. Defaults to
+        False.
+    :param security_group_rules: Whether to provision security group rules.
+        Defaults to False.
+    :param ethertype: 'IPv4' or 'IPv6'. Honoured only in case neutron is used.
+    :param use_neutron: When True resources are provisioned via neutron, when
+        False resources are provisioned via nova.
+    :param floating_network_id: The id of the network used to provision a
+        floating IP. Only used if a floating IP is requested and with neutron.
+    :param floating_network_name: The name of the floating IP pool used to
+        provision the floating IP. Only used if a floating IP is requested and
+        with nova-net.
+    :returns: A dictionary with the same keys as the input
+        `validation_resources` and the resources for values in the format
+         they are returned by the API.
+
+    Examples::
+
+        from tempest.common import validation_resources as vr
+        from tempest.lib import auth
+        from tempest.lib.services import clients
+
+        creds = auth.get_credentials('http://mycloud/identity/v3',
+                                     username='me', project_name='me',
+                                     password='secret', domain_name='Default')
+        osclients = clients.ServiceClients(creds, 'http://mycloud/identity/v3')
+        # Request keypair and floating IP
+        resources = dict(keypair=True, security_group=False,
+                         security_group_rules=False, floating_ip=True)
+        resources = vr.create_validation_resources(
+            osclients, use_neutron=True,
+            floating_network_id='4240E68E-23DA-4C82-AC34-9FEFAA24521C',
+            **resources)
+
+        # The floating IP to be attached to the VM
+        floating_ip = resources['floating_ip']['ip']
+    """
     # Create and Return the validation resources required to validate a VM
     validation_data = {}
-    if validation_resources:
-        if validation_resources['keypair']:
-            keypair_name = data_utils.rand_name('keypair')
-            validation_data.update(os.keypairs_client.create_keypair(
+    if keypair:
+        keypair_name = data_utils.rand_name('keypair')
+        validation_data.update(
+            clients.compute.KeyPairsClient().create_keypair(
                 name=keypair_name))
-            LOG.debug("Validation resource key %s created", keypair_name)
-        add_rule = False
-        if validation_resources['security_group']:
-            if validation_resources['security_group_rules']:
-                add_rule = True
-            validation_data['security_group'] = \
-                create_ssh_security_group(os, add_rule)
-        if validation_resources['floating_ip']:
-            floating_client = os.compute_floating_ips_client
-            validation_data.update(
-                floating_client.create_floating_ip(
-                    pool=CONF.network.floating_network_name))
+        LOG.debug("Validation resource key %s created", keypair_name)
+    if security_group:
+        validation_data['security_group'] = create_ssh_security_group(
+            clients, add_rule=security_group_rules,
+            use_neutron=use_neutron, ethertype=ethertype)
+    if floating_ip:
+        floating_ip_client = _network_service(
+            clients, use_neutron).FloatingIPsClient()
+        if use_neutron:
+            floatingip = floating_ip_client.create_floatingip(
+                floating_network_id=floating_network_id)
+            # validation_resources['floating_ip'] has historically looked
+            # like a compute API POST /os-floating-ips response, so we need
+            # to mangle it a bit for a Neutron response with different
+            # fields.
+            validation_data['floating_ip'] = floatingip['floatingip']
+            validation_data['floating_ip']['ip'] = (
+                floatingip['floatingip']['floating_ip_address'])
+        else:
+            # NOTE(mriedem): The os-floating-ips compute API was deprecated
+            # in the 2.36 microversion. Any tests for CRUD operations on
+            # floating IPs using the compute API should be capped at 2.35.
+            validation_data.update(floating_ip_client.create_floating_ip(
+                pool=floating_network_name))
     return validation_data
 
 
-def clear_validation_resources(os, validation_data=None):
-    # Cleanup the vm validation resources
+def clear_validation_resources(clients, keypair=None, floating_ip=None,
+                               security_group=None, use_neutron=True):
+    """Cleanup resources for VM ping/ssh testing
+
+    Cleanup a set of resources provisioned via `create_validation_resources`.
+    In case of errors during cleanup, the exception is logged and the cleanup
+    process is continued. The first exception that was raised is re-raised
+    after the cleanup is complete.
+
+    :param clients: Instance of `tempest.lib.services.clients.ServiceClients`
+        or of a subclass of it. Resources are provisioned using clients from
+        `clients`.
+    :param keypair: A dictionary with the keypair to be deleted. Defaults to
+        None.
+    :param floating_ip: A dictionary with the floating_ip to be deleted.
+        Defaults to None.
+    :param security_group: A dictionary with the security_group to be deleted.
+        Defaults to None.
+    :param use_neutron: When True resources are provisioned via neutron, when
+        False resources are provisioned via nova.
+    :returns: A dictionary with the same keys as the input
+        `validation_resources` and the resources for values in the format
+         they are returned by the API.
+
+    Examples::
+
+        from tempest.common import validation_resources as vr
+        from tempest.lib import auth
+        from tempest.lib.services import clients
+
+        creds = auth.get_credentials('http://mycloud/identity/v3',
+                                     username='me', project_name='me',
+                                     password='secret', domain_name='Default')
+        osclients = clients.ServiceClients(creds, 'http://mycloud/identity/v3')
+        # Request keypair and floating IP
+        resources = dict(keypair=True, security_group=False,
+                         security_group_rules=False, floating_ip=True)
+        resources = vr.create_validation_resources(
+            osclients, validation_resources=resources, use_neutron=True,
+            floating_network_id='4240E68E-23DA-4C82-AC34-9FEFAA24521C')
+
+        # Now cleanup the resources
+        try:
+            vr.clear_validation_resources(osclients, use_neutron=True,
+                                          **resources)
+        except Exception as e:
+            LOG.exception('Something went wrong during cleanup, ignoring')
+    """
     has_exception = None
-    if validation_data:
-        if 'keypair' in validation_data:
-            keypair_client = os.keypairs_client
-            keypair_name = validation_data['keypair']['name']
-            try:
-                keypair_client.delete_keypair(keypair_name)
-            except lib_exc.NotFound:
-                LOG.warning(
-                    "Keypair %s is not found when attempting to delete",
-                    keypair_name
-                )
-            except Exception as exc:
-                LOG.exception('Exception raised while deleting key %s',
-                              keypair_name)
-                if not has_exception:
-                    has_exception = exc
-        if 'security_group' in validation_data:
-            security_group_client = os.compute_security_groups_client
-            sec_id = validation_data['security_group']['id']
-            try:
-                security_group_client.delete_security_group(sec_id)
-                security_group_client.wait_for_resource_deletion(sec_id)
-            except lib_exc.NotFound:
-                LOG.warning("Security group %s is not found when attempting "
-                            "to delete", sec_id)
-            except lib_exc.Conflict as exc:
-                LOG.exception('Conflict while deleting security '
-                              'group %s VM might not be deleted', sec_id)
-                if not has_exception:
-                    has_exception = exc
-            except Exception as exc:
-                LOG.exception('Exception raised while deleting security '
-                              'group %s', sec_id)
-                if not has_exception:
-                    has_exception = exc
-        if 'floating_ip' in validation_data:
-            floating_client = os.compute_floating_ips_client
-            fip_id = validation_data['floating_ip']['id']
-            try:
-                floating_client.delete_floating_ip(fip_id)
-            except lib_exc.NotFound:
-                LOG.warning('Floating ip %s not found while attempting to '
-                            'delete', fip_id)
-            except Exception as exc:
-                LOG.exception('Exception raised while deleting ip %s', fip_id)
-                if not has_exception:
-                    has_exception = exc
+    if keypair:
+        keypair_client = clients.compute.KeyPairsClient()
+        keypair_name = keypair['name']
+        try:
+            keypair_client.delete_keypair(keypair_name)
+        except lib_exc.NotFound:
+            LOG.warning(
+                "Keypair %s is not found when attempting to delete",
+                keypair_name
+            )
+        except Exception as exc:
+            LOG.exception('Exception raised while deleting key %s',
+                          keypair_name)
+            if not has_exception:
+                has_exception = exc
+    network_service = _network_service(clients, use_neutron)
+    if security_group:
+        security_group_client = network_service.SecurityGroupsClient()
+        sec_id = security_group['id']
+        try:
+            security_group_client.delete_security_group(sec_id)
+            security_group_client.wait_for_resource_deletion(sec_id)
+        except lib_exc.NotFound:
+            LOG.warning("Security group %s is not found when attempting "
+                        "to delete", sec_id)
+        except lib_exc.Conflict as exc:
+            LOG.exception('Conflict while deleting security '
+                          'group %s VM might not be deleted', sec_id)
+            if not has_exception:
+                has_exception = exc
+        except Exception as exc:
+            LOG.exception('Exception raised while deleting security '
+                          'group %s', sec_id)
+            if not has_exception:
+                has_exception = exc
+    if floating_ip:
+        floating_ip_client = network_service.FloatingIPsClient()
+        fip_id = floating_ip['id']
+        try:
+            if use_neutron:
+                floating_ip_client.delete_floatingip(fip_id)
+            else:
+                floating_ip_client.delete_floating_ip(fip_id)
+        except lib_exc.NotFound:
+            LOG.warning('Floating ip %s not found while attempting to '
+                        'delete', fip_id)
+        except Exception as exc:
+            LOG.exception('Exception raised while deleting ip %s', fip_id)
+            if not has_exception:
+                has_exception = exc
     if has_exception:
         raise has_exception
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 865db39..10afee0 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -10,7 +10,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-
+import re
 import time
 
 from oslo_log import log as logging
@@ -121,7 +121,7 @@
                      '/'.join((server_status, str(task_state))),
                      time.time() - start_time)
         if server_status == 'ERROR' and not ignore_error:
-            raise exceptions.BuildErrorException(server_id=server_id)
+            raise lib_exc.DeleteErrorException(resource_id=server_id)
 
         if int(time.time()) - start_time >= client.build_timeout:
             raise lib_exc.TimeoutException
@@ -179,27 +179,40 @@
     raise lib_exc.TimeoutException(message)
 
 
-def wait_for_volume_status(client, volume_id, status):
-    """Waits for a Volume to reach a given status."""
-    body = client.show_volume(volume_id)['volume']
-    volume_status = body['status']
+def wait_for_volume_resource_status(client, resource_id, statuses):
+    """Waits for a volume resource to reach any of the specified statuses.
+
+    This function is a common function for volume, snapshot and backup
+    resources. The function extracts the name of the desired resource from
+    the client class name of the resource.
+    """
+    if not isinstance(statuses, list):
+        statuses = [statuses]
+    resource_name = re.findall(
+        r'(volume|group-snapshot|snapshot|backup|group)',
+        client.resource_type)[-1].replace('-', '_')
+    show_resource = getattr(client, 'show_' + resource_name)
+    resource_status = show_resource(resource_id)[resource_name]['status']
     start = int(time.time())
 
-    while volume_status != status:
+    while resource_status not in statuses:
         time.sleep(client.build_interval)
-        body = client.show_volume(volume_id)['volume']
-        volume_status = body['status']
-        if volume_status == 'error' and status != 'error':
-            raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
-        if volume_status == 'error_restoring':
-            raise exceptions.VolumeRestoreErrorException(volume_id=volume_id)
+        resource_status = show_resource(resource_id)[
+            '{}'.format(resource_name)]['status']
+        if resource_status == 'error' and resource_status not in statuses:
+            raise exceptions.VolumeResourceBuildErrorException(
+                resource_name=resource_name, resource_id=resource_id)
+        if resource_name == 'volume' and resource_status == 'error_restoring':
+            raise exceptions.VolumeRestoreErrorException(volume_id=resource_id)
 
         if int(time.time()) - start >= client.build_timeout:
-            message = ('Volume %s failed to reach %s status (current %s) '
+            message = ('%s %s failed to reach %s status (current %s) '
                        'within the required time (%s s).' %
-                       (volume_id, status, volume_status,
+                       (resource_name, resource_id, statuses, resource_status,
                         client.build_timeout))
             raise lib_exc.TimeoutException(message)
+    LOG.info('%s %s reached %s after waiting for %f seconds',
+             resource_name, resource_id, statuses, time.time() - start)
 
 
 def wait_for_volume_retype(client, volume_id, new_volume_type):
@@ -218,48 +231,6 @@
                        'within the required time (%s s).' %
                        (volume_id, new_volume_type, current_volume_type,
                         client.build_timeout))
-            raise exceptions.TimeoutException(message)
-
-
-def wait_for_snapshot_status(client, snapshot_id, status):
-    """Waits for a Snapshot to reach a given status."""
-    body = client.show_snapshot(snapshot_id)['snapshot']
-    snapshot_status = body['status']
-    start = int(time.time())
-
-    while snapshot_status != status:
-        time.sleep(client.build_interval)
-        body = client.show_snapshot(snapshot_id)['snapshot']
-        snapshot_status = body['status']
-        if snapshot_status == 'error':
-            raise exceptions.SnapshotBuildErrorException(
-                snapshot_id=snapshot_id)
-        if int(time.time()) - start >= client.build_timeout:
-            message = ('Snapshot %s failed to reach %s status (current %s) '
-                       'within the required time (%s s).' %
-                       (snapshot_id, status, snapshot_status,
-                        client.build_timeout))
-            raise lib_exc.TimeoutException(message)
-
-
-def wait_for_backup_status(client, backup_id, status):
-    """Waits for a Backup to reach a given status."""
-    body = client.show_backup(backup_id)['backup']
-    backup_status = body['status']
-    start = int(time.time())
-
-    while backup_status != status:
-        time.sleep(client.build_interval)
-        body = client.show_backup(backup_id)['backup']
-        backup_status = body['status']
-        if backup_status == 'error' and backup_status != status:
-            raise lib_exc.VolumeBackupException(backup_id=backup_id)
-
-        if int(time.time()) - start >= client.build_timeout:
-            message = ('Volume backup %s failed to reach %s status '
-                       '(current %s) within the required time (%s s).' %
-                       (backup_id, status, backup_status,
-                        client.build_timeout))
             raise lib_exc.TimeoutException(message)
 
 
@@ -295,16 +266,16 @@
         time.sleep(client.build_interval)
 
 
-def wait_for_interface_status(client, server, port_id, status):
+def wait_for_interface_status(client, server_id, port_id, status):
     """Waits for an interface to reach a given status."""
-    body = (client.show_interface(server, port_id)
+    body = (client.show_interface(server_id, port_id)
             ['interfaceAttachment'])
     interface_status = body['port_state']
     start = int(time.time())
 
     while(interface_status != status):
         time.sleep(client.build_interval)
-        body = (client.show_interface(server, port_id)
+        body = (client.show_interface(server_id, port_id)
                 ['interfaceAttachment'])
         interface_status = body['port_state']
 
diff --git a/tempest/config.py b/tempest/config.py
index fe8c175..e78a07f 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -19,6 +19,7 @@
 import os
 import tempfile
 
+import debtcollector.removals
 from oslo_concurrency import lockutils
 from oslo_config import cfg
 from oslo_log import log as logging
@@ -132,7 +133,7 @@
     cfg.StrOpt('uri_v3',
                help='Full URI of the OpenStack Identity API (Keystone), v3'),
     cfg.StrOpt('auth_version',
-               default='v2',
+               default='v3',
                help="Identity API version to be used for authentication "
                     "for API tests."),
     cfg.StrOpt('region',
@@ -160,7 +161,9 @@
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
                help="The endpoint type to use for OpenStack Identity "
-                    "(Keystone) API v3"),
+                    "(Keystone) API v3. The default value adminURL is "
+                    "deprecated and will be modified to publicURL in "
+                    "the next release."),
     cfg.StrOpt('admin_role',
                default='admin',
                help="Role required to administrate keystone."),
@@ -207,6 +210,10 @@
     cfg.BoolOpt('api_v2',
                 default=True,
                 help='Is the v2 identity API enabled'),
+    cfg.BoolOpt('api_v2_admin',
+                default=True,
+                help="Is the v2 identity admin API available? This setting "
+                     "only applies if api_v2 is set to True."),
     cfg.BoolOpt('api_v3',
                 default=True,
                 help='Is the v3 identity API enabled'),
@@ -216,14 +223,23 @@
                      "entry all which indicates every extension is enabled. "
                      "Empty list indicates all extensions are disabled. "
                      "To get the list of extensions run: 'keystone discover'"),
-    # TODO(rodrigods): Remove the reseller flag when Kilo and Liberty is end
-    # of life.
-    cfg.BoolOpt('reseller',
-                default=True,
-                help='Does the environment support reseller?',
+    # TODO(rodrigods): This is a feature flag for bug 1590578 which is fixed
+    # in Newton and Ocata. This option can be removed after Mitaka is end of
+    # life.
+    cfg.BoolOpt('forbid_global_implied_dsr',
+                default=False,
+                help='Does the environment forbid global roles implying '
+                     'domain specific ones?',
                 deprecated_for_removal=True,
-                deprecated_reason="All supported version of OpenStack now "
-                                  "supports the 'reseller' feature"),
+                deprecated_reason="This feature flag was introduced to "
+                                  "support testing of old OpenStack versions, "
+                                  "which are not supported anymore"),
+    cfg.BoolOpt('domain_specific_drivers',
+                default=False,
+                help='Are domain specific drivers enabled? '
+                     'This configuration value should be same as '
+                     '[identity]->domain_specific_drivers_enabled '
+                     'in keystone.conf.'),
     cfg.BoolOpt('security_compliance',
                 default=False,
                 help='Does the environment have the security compliance '
@@ -283,7 +299,9 @@
     cfg.StrOpt('volume_device_name',
                default='vdb',
                help="Expected device name when a volume is attached to "
-                    "an instance"),
+                    "an instance. Not all hypervisors guarantee that they "
+                    "will respect the user defined device name, tests may "
+                    "fail if inappropriate device name is set."),
     cfg.IntOpt('shelved_offload_time',
                default=0,
                help='Time in seconds before a shelved instance is eligible '
@@ -310,8 +328,7 @@
                     "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'",
-                    deprecated_group='compute-feature-enabled'),
+                    "with format 'X.Y' or string 'latest'"),
     cfg.StrOpt('max_microversion',
                default=None,
                help="Upper version of the test target microversion range. "
@@ -320,23 +337,13 @@
                     "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'",
-                    deprecated_group='compute-feature-enabled'),
+                    "with format 'X.Y' or string 'latest'"),
 ]
 
 compute_features_group = cfg.OptGroup(name='compute-feature-enabled',
                                       title="Enabled Compute Service Features")
 
 ComputeFeaturesGroup = [
-    # NOTE(mriedem): This is a feature toggle for bug 1175464 which is fixed in
-    # mitaka and newton. This option can be removed after liberty-eol.
-    cfg.BoolOpt('allow_port_security_disabled',
-                default=True,
-                help='Does the test environment support creating ports in a '
-                     'network where port security is disabled?',
-                deprecated_for_removal=True,
-                deprecated_reason='This config switch was added for Liberty '
-                                  'which is not supported anymore.'),
     cfg.BoolOpt('disk_config',
                 default=True,
                 help="If false, skip disk config tests"),
@@ -362,7 +369,10 @@
                      "serial console output?"),
     cfg.BoolOpt('resize',
                 default=False,
-                help="Does the test environment support resizing?"),
+                help="Does the test environment support resizing? When you "
+                     "enable this feature, 'flavor_ref_alt' should be set and "
+                     "it should refer to a larger flavor than 'flavor_ref' "
+                     "one."),
     cfg.BoolOpt('pause',
                 default=True,
                 help="Does the test environment support pausing?"),
@@ -378,6 +388,11 @@
     cfg.BoolOpt('live_migration',
                 default=True,
                 help="Does the test environment support live migration?"),
+    cfg.BoolOpt('live_migrate_back_and_forth',
+                default=False,
+                help="Does the test environment support live migrating "
+                     "VM back and forth between different versions of "
+                     "nova-compute?"),
     cfg.BoolOpt('metadata_service',
                 default=True,
                 help="Does the test environment support metadata service? "
@@ -388,9 +403,9 @@
                      "migration"),
     cfg.BoolOpt('block_migrate_cinder_iscsi',
                 default=False,
-                help="Does the test environment block migration support "
-                "cinder iSCSI volumes. Note, libvirt doesn't support this, "
-                "see https://bugs.launchpad.net/nova/+bug/1398999"),
+                help="Does the test environment support block migration with "
+                "Cinder iSCSI volumes. Note: libvirt >= 1.2.17 is required "
+                "to support this if using the libvirt compute driver."),
     cfg.BoolOpt('vnc_console',
                 default=False,
                 help='Enable VNC console. This configuration value should '
@@ -403,6 +418,11 @@
                 default=False,
                 help='Enable RDP console. This configuration value should '
                      'be same as [nova.rdp]->enabled in nova.conf'),
+    cfg.BoolOpt('serial_console',
+                default=False,
+                help='Enable serial console. This configuration value '
+                     'should be the same as [nova.serial_console]->enabled '
+                     'in nova.conf'),
     cfg.BoolOpt('rescue',
                 default=True,
                 help='Does the test environment support instance rescue '
@@ -497,7 +517,7 @@
                      "users can specify."),
     cfg.ListOpt('disk_formats',
                 default=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2',
-                         'vdi', 'iso'],
+                         'vdi', 'iso', 'vhdx'],
                 help="A list of image's disk formats "
                      "users can specify.")
 ]
@@ -508,14 +528,27 @@
 ImageFeaturesGroup = [
     cfg.BoolOpt('api_v2',
                 default=True,
-                help="Is the v2 image API enabled"),
+                help="Is the v2 image API enabled",
+                deprecated_for_removal=True,
+                deprecated_reason='Glance v1 APIs are deprecated and v2 APIs '
+                                  'are current one. In future, Tempest will '
+                                  'test v2 APIs only so this config option '
+                                  'will be removed.'),
     cfg.BoolOpt('api_v1',
                 default=True,
-                help="Is the v1 image API enabled"),
+                help="Is the v1 image API enabled",
+                deprecated_for_removal=True,
+                deprecated_reason='Glance v1 APIs are deprecated and v2 APIs '
+                                  'are current one. In future, Tempest will '
+                                  'test v2 APIs only so this config option '
+                                  'will be removed.'),
     cfg.BoolOpt('deactivate_image',
                 default=False,
                 help="Is the deactivate-image feature enabled."
-                     " The feature has been integrated since Kilo."),
+                     " The feature has been integrated since Kilo.",
+                deprecated_for_removal=True,
+                deprecated_reason="All supported versions of OpenStack now "
+                                  "support the 'deactivate_image' feature"),
 ]
 
 network_group = cfg.OptGroup(name='network',
@@ -537,23 +570,18 @@
                         'publicURL', 'adminURL', 'internalURL'],
                help="The endpoint type to use for the network service."),
     cfg.StrOpt('project_network_cidr',
-               deprecated_name='tenant_network_cidr',
                default="10.100.0.0/16",
                help="The cidr block to allocate project ipv4 subnets from"),
     cfg.IntOpt('project_network_mask_bits',
-               deprecated_name='tenant_network_mask_bits',
                default=28,
                help="The mask bits for project ipv4 subnets"),
     cfg.StrOpt('project_network_v6_cidr',
-               deprecated_name='tenant_network_v6_cidr',
                default="2003::/48",
                help="The cidr block to allocate project ipv6 subnets from"),
     cfg.IntOpt('project_network_v6_mask_bits',
-               deprecated_name='tenant_network_v6_mask_bits',
                default=64,
                help="The mask bits for project ipv6 subnets"),
     cfg.BoolOpt('project_networks_reachable',
-                deprecated_name='tenant_networks_reachable',
                 default=False,
                 help="Whether project networks can be reached directly from "
                      "the test client. This must be set to True when the "
@@ -597,15 +625,6 @@
                 default=False,
                 help="The environment does not support network separation "
                      "between tenants."),
-    # TODO(ylobankov): Delete this option once the Liberty release is EOL.
-    cfg.BoolOpt('dvr_extra_resources',
-                default=True,
-                help="Whether or not to create internal network, subnet, "
-                     "port and add network interface to distributed router "
-                     "in L3 agent scheduler test. Extra resources need to be "
-                     "provisioned in order to bind router to L3 agent in the "
-                     "Liberty release or older, and are not required since "
-                     "the Mitaka release.")
 ]
 
 network_feature_group = cfg.OptGroup(name='network-feature-enabled',
@@ -634,6 +653,9 @@
     cfg.BoolOpt('port_security',
                 default=False,
                 help="Does the test environment support port security?"),
+    cfg.BoolOpt('floating_ips',
+                default=True,
+                help='Does the test environment support floating_ips')
 ]
 
 validation_group = cfg.OptGroup(name='validation',
@@ -655,25 +677,19 @@
                choices=['fixed', 'floating'],
                help='Default IP type used for validation: '
                     '-fixed: uses the first IP belonging to the fixed network '
-                    '-floating: creates and uses a floating IP',
-               deprecated_opts=[cfg.DeprecatedOpt('use_floatingip_for_ssh',
-                                                  group='compute')]),
+                    '-floating: creates and uses a floating IP'),
     cfg.StrOpt('auth_method',
                default='keypair',
                choices=['keypair'],
                help='Default authentication method to the instance. '
                     'Only ssh via keypair is supported for now. '
-                    'Additional methods will be handled in a separate spec.',
-               deprecated_opts=[cfg.DeprecatedOpt('ssh_auth_method',
-                                                  group='compute')]),
+                    'Additional methods will be handled in a separate spec.'),
     cfg.IntOpt('ip_version_for_ssh',
                default=4,
                help='Default IP version for ssh connections.'),
     cfg.IntOpt('ping_timeout',
                default=120,
-               help='Timeout in seconds to wait for ping to succeed.',
-               deprecated_opts=[cfg.DeprecatedOpt('ping_timeout',
-                                                  group='compute')]),
+               help='Timeout in seconds to wait for ping to succeed.'),
     cfg.IntOpt('connect_timeout',
                default=60,
                help='Timeout in seconds to wait for the TCP connection to be '
@@ -683,50 +699,32 @@
                help='Timeout in seconds to wait for the ssh banner.'),
     cfg.StrOpt('image_ssh_user',
                default="root",
-               help="User name used to authenticate to an instance.",
-               deprecated_opts=[cfg.DeprecatedOpt('image_ssh_user',
-                                                  group='compute'),
-                                cfg.DeprecatedOpt('ssh_user',
-                                                  group='compute'),
-                                cfg.DeprecatedOpt('ssh_user',
-                                                  group='scenario')]),
+               help="User name used to authenticate to an instance."),
     cfg.StrOpt('image_ssh_password',
                default="password",
-               help="Password used to authenticate to an instance.",
-               deprecated_opts=[cfg.DeprecatedOpt('image_ssh_password',
-                                                  group='compute')]),
+               help="Password used to authenticate to an instance."),
     cfg.StrOpt('ssh_shell_prologue',
                default="set -eu -o pipefail; PATH=$$PATH:/sbin;",
                help="Shell fragments to use before executing a command "
-                    "when sshing to a guest.",
-               deprecated_opts=[cfg.DeprecatedOpt('ssh_shell_prologue',
-                                                  group='compute')]),
+                    "when sshing to a guest."),
     cfg.IntOpt('ping_size',
                default=56,
                help="The packet size for ping packets originating "
-                    "from remote linux hosts",
-               deprecated_opts=[cfg.DeprecatedOpt('ping_size',
-                                                  group='compute')]),
+                    "from remote linux hosts"),
     cfg.IntOpt('ping_count',
                default=1,
                help="The number of ping packets originating from remote "
-                    "linux hosts",
-               deprecated_opts=[cfg.DeprecatedOpt('ping_count',
-                                                  group='compute')]),
+                    "linux hosts"),
     cfg.StrOpt('floating_ip_range',
                default='10.0.0.0/29',
                help='Unallocated floating IP range, which will be used to '
                     'test the floating IP bulk feature for CRUD operation. '
                     'This block must not overlap an existing floating IP '
-                    'pool.',
-               deprecated_opts=[cfg.DeprecatedOpt('floating_ip_range',
-                                                  group='compute')]),
+                    'pool.'),
     cfg.StrOpt('network_for_ssh',
                default='public',
                help="Network used for SSH connections. Ignored if "
-                    "connect_method=floating.",
-               deprecated_opts=[cfg.DeprecatedOpt('network_for_ssh',
-                                                  group='compute')]),
+                    "connect_method=floating."),
 ]
 
 volume_group = cfg.OptGroup(name='volume',
@@ -770,6 +768,18 @@
     cfg.IntOpt('volume_size',
                default=1,
                help='Default size in GB for volumes created by volumes tests'),
+    cfg.ListOpt('manage_volume_ref',
+                default=['source-name', 'volume-%s'],
+                help="A reference to existing volume for volume manage. "
+                     "It contains two elements, the first is ref type "
+                     "(like 'source-name', 'source-id', etc), the second is "
+                     "volume name template used in storage backend"),
+    cfg.ListOpt('manage_snapshot_ref',
+                default=['source-name', '_snapshot-%s'],
+                help="A reference to existing snapshot for snapshot manage. "
+                     "It contains two elements, the first is ref type "
+                     "(like 'source-name', 'source-id', etc), the second is "
+                     "snapshot name template used in storage backend"),
     cfg.StrOpt('min_microversion',
                default=None,
                help="Lower version of the test target microversion range. "
@@ -809,27 +819,27 @@
     cfg.BoolOpt('manage_snapshot',
                 default=False,
                 help='Runs Cinder manage snapshot tests'),
+    cfg.BoolOpt('manage_volume',
+                default=False,
+                help='Runs Cinder manage volume tests'),
     cfg.ListOpt('api_extensions',
                 default=['all'],
                 help='A list of enabled volume extensions with a special '
                      'entry all which indicates every extension is enabled. '
                      'Empty list indicates all extensions are disabled'),
     cfg.BoolOpt('api_v1',
-                default=True,
-                help="Is the v1 volume API enabled"),
+                default=False,
+                help="Is the v1 volume API enabled",
+                deprecated_for_removal=True,
+                deprecated_reason="The v1 volume API has been deprecated "
+                                  "since Juno release, and the API will be "
+                                  "removed."),
     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"),
-    # TODO(ynesenenko): Remove volume_services once liberty-eol happens.
-    cfg.BoolOpt('volume_services',
                 default=True,
-                help='Extract correct host info from host@backend',
-                deprecated_for_removal=True,
-                deprecated_reason='This config switch was added for Liberty '
-                                  'which is not supported anymore.')
+                help="Is the v3 volume API enabled")
 ]
 
 
@@ -906,38 +916,58 @@
 OrchestrationGroup = [
     cfg.StrOpt('catalog_type',
                default='orchestration',
-               help="Catalog type of the Orchestration service."),
+               help="Catalog type of the Orchestration service.",
+               deprecated_for_removal=True,
+               deprecated_reason='Heat support will be removed from Tempest'),
     cfg.StrOpt('region',
                default='',
                help="The orchestration region name to use. If empty, the "
                     "value of identity.region is used instead. If no such "
                     "region is found in the service catalog, the first found "
-                    "one is used."),
+                    "one is used.",
+               deprecated_for_removal=True,
+               deprecated_reason='Heat support will be removed from Tempest'),
     cfg.StrOpt('endpoint_type',
                default='publicURL',
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
-               help="The endpoint type to use for the orchestration service."),
+               help="The endpoint type to use for the orchestration service.",
+               deprecated_for_removal=True,
+               deprecated_reason='Heat support will be removed from Tempest'),
     cfg.StrOpt('stack_owner_role', default='heat_stack_owner',
-               help='Role required for users to be able to manage stacks'),
+               help='Role required for users to be able to manage stacks',
+               deprecated_for_removal=True,
+               deprecated_reason='Heat support will be removed from Tempest'),
     cfg.IntOpt('build_interval',
                default=1,
-               help="Time in seconds between build status checks."),
+               help="Time in seconds between build status checks.",
+               deprecated_for_removal=True,
+               deprecated_reason='Heat support will be removed from Tempest'),
     cfg.IntOpt('build_timeout',
                default=1200,
-               help="Timeout in seconds to wait for a stack to build."),
+               help="Timeout in seconds to wait for a stack to build.",
+               deprecated_for_removal=True,
+               deprecated_reason='Heat support will be removed from Tempest'),
     cfg.StrOpt('instance_type',
                default='m1.micro',
                help="Instance type for tests. Needs to be big enough for a "
-                    "full OS plus the test workload"),
+                    "full OS plus the test workload",
+               deprecated_for_removal=True,
+               deprecated_reason='Heat support will be removed from Tempest'),
     cfg.StrOpt('keypair_name',
-               help="Name of existing keypair to launch servers with."),
+               help="Name of existing keypair to launch servers with.",
+               deprecated_for_removal=True,
+               deprecated_reason='Heat support will be removed from Tempest'),
     cfg.IntOpt('max_template_size',
                default=524288,
-               help="Value must match heat configuration of the same name."),
+               help="Value must match heat configuration of the same name.",
+               deprecated_for_removal=True,
+               deprecated_reason='Heat support will be removed from Tempest'),
     cfg.IntOpt('max_resources_per_stack',
                default=1000,
-               help="Value must match heat configuration of the same name."),
+               help="Value must match heat configuration of the same name.",
+               deprecated_for_removal=True,
+               deprecated_reason='Heat support will be removed from Tempest'),
 ]
 
 
@@ -1003,10 +1033,9 @@
                 help="Whether or not nova is expected to be available"),
     cfg.BoolOpt('heat',
                 default=False,
-                help="Whether or not Heat is expected to be available"),
-    cfg.BoolOpt('sahara',
-                default=False,
-                help="Whether or not Sahara is expected to be available"),
+                help="Whether or not Heat is expected to be available",
+                deprecated_for_removal=True,
+                deprecated_reason='Heat support will be removed from Tempest'),
 ]
 
 debug_group = cfg.OptGroup(name="debug",
@@ -1035,39 +1064,27 @@
 """)
 ]
 
-input_scenario_group = cfg.OptGroup(name="input-scenario",
-                                    title="Filters and values for"
-                                          " input scenarios[DEPRECATED]")
-
-
-InputScenarioGroup = [
-    cfg.StrOpt('image_regex',
-               default='^cirros-0.3.1-x86_64-uec$',
-               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",
-               deprecated_for_removal=True),
-    cfg.StrOpt('non_ssh_image_regex',
-               default='^.*[Ww]in.*$',
-               help="SSH verification in tests is skipped"
-                    "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.",
-               deprecated_for_removal=True),
-]
-
 DefaultGroup = [
     cfg.StrOpt('resources_prefix',
                default='tempest',
                help="Prefix to be added when generating the name for "
                     "test resources. It can be used to discover all "
                     "resources associated with a specific test run when "
-                    "running tempest on a real-life cloud"),
+                    "running tempest on a real-life cloud",
+               deprecated_for_removal=True,
+               deprecated_reason="It is enough to add 'tempest' as this "
+                                 "prefix to ideintify resources which are "
+                                 "created by Tempest and no projects set "
+                                 "this option on OpenStack dev community."),
+    cfg.BoolOpt('pause_teardown',
+                default=False,
+                help="""Whether to pause a test in global teardown.
+
+The best use case is investigating used resources of one test.
+A test can be run as follows:
+ $ ostestr --pdb TEST_ID
+or
+ $ python -m testtools.run TEST_ID"""),
 ]
 
 _opts = [
@@ -1090,7 +1107,6 @@
     (scenario_group, ScenarioGroup),
     (service_available_group, ServiceAvailableGroup),
     (debug_group, DebugGroup),
-    (input_scenario_group, InputScenarioGroup),
     (None, DefaultGroup)
 ]
 
@@ -1132,6 +1148,12 @@
         return getattr(_CONF, attr)
 
     def _set_attrs(self):
+        # This methods ensures that config options in Tempest as well as
+        # in Tempest plugins can be accessed via:
+        #     CONF.<normalised_group_name>.<key_name>
+        # where:
+        #     normalised_group_name = group_name.replace('-', '_')
+        # Attributes are set at __init__ time *only* for known option groups
         self.auth = _CONF.auth
         self.compute = _CONF.compute
         self.compute_feature_enabled = _CONF['compute-feature-enabled']
@@ -1152,8 +1174,24 @@
         self.scenario = _CONF.scenario
         self.service_available = _CONF.service_available
         self.debug = _CONF.debug
-        self.input_scenario = _CONF['input-scenario']
         logging.tempest_set_log_file('tempest.log')
+        # Setting attributes for plugins
+        # NOTE(andreaf) Plugins have no access to the TempestConfigPrivate
+        # instance at discovery time, so they have no way of setting these
+        # aliases themselves.
+        ext_plugins = plugins.TempestTestPluginManager()
+        for group, _ in ext_plugins.get_plugin_options_list():
+            if isinstance(group, cfg.OptGroup):
+                # If we have an OptGroup
+                group_name = group.name
+                group_dest = group.dest
+            else:
+                # If we have a group name as a string
+                group_name = group
+                group_dest = group.replace('-', '_')
+            # NOTE(andreaf) We can set the attribute safely here since in
+            # case of name conflict we would not have reached this point.
+            setattr(self, group_dest, _CONF[group_name])
 
     def __init__(self, parse_conf=True, config_path=None):
         """Initialize a configuration from a conf directory and conf file."""
@@ -1231,6 +1269,12 @@
             # loaded, options registered, and _config is set.
             _register_tempest_service_clients()
 
+            # Registering service clients and pushing their configuration to
+            # the service clients register. Doing this in the config module
+            # ensures that the configuration is available by the time we
+            # discover tests from plugins.
+            plugins.TempestTestPluginManager()._register_service_clients()
+
         return getattr(self._config, attr)
 
     def set_config_path(self, path):
@@ -1240,6 +1284,8 @@
 CONF = TempestConfigProxy()
 
 
+@debtcollector.removals.remove(
+    message='use testtools.skipUnless instead', removal_version='Queens')
 def skip_unless_config(*args):
     """Decorator to raise a skip if a config opt doesn't exist or is False
 
@@ -1278,6 +1324,8 @@
     return decorator
 
 
+@debtcollector.removals.remove(
+    message='use testtools.skipIf instead', removal_version='Queens')
 def skip_if_config(*args):
     """Raise a skipException if a config exists and is True
 
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 45bbc11..a8a6ff0 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -17,10 +17,6 @@
 from tempest.lib import exceptions
 
 
-class InvalidServiceTag(exceptions.TempestException):
-    message = "Invalid service tag"
-
-
 class BuildErrorException(exceptions.TempestException):
     message = "Server %(server_id)s failed to build and is in ERROR status"
 
@@ -37,18 +33,15 @@
     message = "Image %(image_id)s failed to become ACTIVE in the allotted time"
 
 
-class VolumeBuildErrorException(exceptions.TempestException):
-    message = "Volume %(volume_id)s failed to build and is in ERROR status"
+class VolumeResourceBuildErrorException(exceptions.TempestException):
+    message = ("%(resource_name)s %(resource_id)s failed to build and is in "
+               "ERROR status")
 
 
 class VolumeRestoreErrorException(exceptions.TempestException):
     message = "Volume %(volume_id)s failed to restore and is in ERROR status"
 
 
-class SnapshotBuildErrorException(exceptions.TempestException):
-    message = "Snapshot %(snapshot_id)s failed to build and is in ERROR status"
-
-
 class StackBuildErrorException(exceptions.TempestException):
     message = ("Stack %(stack_identifier)s is in %(stack_status)s status "
                "due to '%(stack_status_reason)s'")
@@ -59,12 +52,9 @@
                "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(exceptions.TempestException):
-    message = "%(name)s is not a valid %(type)s, or the name is ambiguous"
-
-
 class RFCViolation(exceptions.RestClientException):
     message = "RFC Violation"
+
+
+class InvalidServiceTag(exceptions.TempestException):
+    message = "Invalid service tag"
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 4123ae5..aae685c 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -33,6 +33,7 @@
 METHOD_GET_RESOURCE = re.compile(r"^\s*def (list|show)\_.+")
 METHOD_DELETE_RESOURCE = re.compile(r"^\s*def delete_.+")
 CLASS = re.compile(r"^class .+")
+EX_ATTRIBUTE = re.compile(r'(\s+|\()(e|ex|exc|exception).message(\s+|\))')
 
 
 def import_no_clients_in_api_and_scenario_tests(physical_line, filename):
@@ -273,6 +274,38 @@
         yield(0, msg)
 
 
+def dont_put_admin_tests_on_nonadmin_path(logical_line, physical_line,
+                                          filename):
+    """Check admin tests should exist under admin path
+
+    T115
+    """
+
+    if 'tempest/api/' not in filename:
+        return
+
+    if pep8.noqa(physical_line):
+        return
+
+    if not re.match('class .*Test.*\(.*Admin.*\):', logical_line):
+        return
+
+    if not re.match('.\/tempest\/api\/.*\/admin\/.*', filename):
+        msg = 'T115: All admin tests should exist under admin path.'
+        yield(0, msg)
+
+
+def unsupported_exception_attribute_PY3(logical_line):
+    """Check Unsupported 'message' exception attribute in PY3
+
+    T116
+    """
+    result = EX_ATTRIBUTE.search(logical_line)
+    msg = ("[T116] Unsupported 'message' Exception attribute in PY3")
+    if result:
+        yield(0, msg)
+
+
 def factory(register):
     register(import_no_clients_in_api_and_scenario_tests)
     register(scenario_tests_need_service_tags)
@@ -287,3 +320,5 @@
     register(dont_import_local_tempest_into_lib)
     register(dont_use_config_in_tempest_lib)
     register(use_rand_uuid_instead_of_uuid4)
+    register(dont_put_admin_tests_on_nonadmin_path)
+    register(unsupported_exception_attribute_PY3)
diff --git a/tempest/hacking/ignored_list_T110.txt b/tempest/hacking/ignored_list_T110.txt
index be875ee..0e7e894 100644
--- a/tempest/hacking/ignored_list_T110.txt
+++ b/tempest/hacking/ignored_list_T110.txt
@@ -1,4 +1 @@
 ./tempest/services/object_storage/object_client.py
-./tempest/services/volume/base/base_qos_client.py
-./tempest/services/volume/base/base_backups_client.py
-./tempest/services/baremetal/base.py
diff --git a/tempest/hacking/ignored_list_T111.txt b/tempest/hacking/ignored_list_T111.txt
deleted file mode 100644
index 8017e76..0000000
--- a/tempest/hacking/ignored_list_T111.txt
+++ /dev/null
@@ -1 +0,0 @@
-./tempest/services/baremetal/base.py
diff --git a/tempest/lib/api_schema/response/compute/v2_1/keypairs.py b/tempest/lib/api_schema/response/compute/v2_1/keypairs.py
index 2828097..e7dcf79 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/keypairs.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/keypairs.py
@@ -34,12 +34,9 @@
 
                 },
                 'additionalProperties': False,
-                # When we run the get keypair API, response body includes
-                # all the above mentioned attributes.
-                # But in Nova API sample file, response body includes only
-                # 'public_key', 'name' & 'fingerprint'. So only 'public_key',
-                # 'name' & 'fingerprint' are defined as 'required'.
-                'required': ['public_key', 'name', 'fingerprint']
+                'required': ['public_key', 'name', 'fingerprint', 'user_id',
+                             'deleted', 'created_at', 'updated_at',
+                             'deleted_at', 'id']
             }
         },
         'additionalProperties': False,
diff --git a/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py b/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
index a3c9099..28ed816 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
@@ -65,7 +65,7 @@
             'items': {
                 'type': 'object',
                 'properties': {
-                    'version': {'type': 'integer'},
+                    'version': {'enum': [4, 6]},
                     'addr': {
                         'type': 'string',
                         'oneOf': [
diff --git a/tempest/lib/api_schema/response/compute/v2_1/quotas.py b/tempest/lib/api_schema/response/compute/v2_1/quotas.py
index 7953983..44f5bdf 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/quotas.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/quotas.py
@@ -60,6 +60,124 @@
 get_quota_set['response_body']['properties']['quota_set']['required'].extend([
     'id'])
 
+get_quota_set_details = copy.deepcopy(get_quota_set)
+get_quota_set_details['response_body']['properties']['quota_set'][
+    'properties'] = {
+    'id': {'type': 'string'},
+    'instances': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'cores': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'ram': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'floating_ips': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'fixed_ips': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'metadata_items': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'key_pairs': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'security_groups': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'security_group_rules': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'server_group_members': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'server_groups': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'injected_files': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'injected_file_content_bytes': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    },
+    'injected_file_path_bytes': {
+        'type': 'object',
+        'properties': {
+            'reserved': {'type': 'integer'},
+            'limit': {'type': 'integer'},
+            'in_use': {'type': 'integer'}
+        }
+    }
+}
+
 delete_quota = {
     'status_code': [202]
 }
diff --git a/tempest/lib/api_schema/response/compute/v2_1/servers.py b/tempest/lib/api_schema/response/compute/v2_1/servers.py
index 4ccca6f..2954de0 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -100,8 +100,10 @@
                 'id': {'type': 'string'},
                 'links': parameter_types.links
             },
-            'additionalProperties': False,
-            'required': ['id', 'links']
+            # NOTE(gmann): This will be empty object if there is no
+            # flavor info present in DB. This can happen when flavor info is
+            # deleted after server creation.
+            'additionalProperties': False
         },
         'fault': {
             'type': 'object',
@@ -418,8 +420,13 @@
         'properties': {
             'event': {'type': 'string'},
             'start_time': parameter_types.date_time,
-            'finish_time': parameter_types.date_time,
-            'result': {'type': 'string'},
+            # The finish_time, result and optionally traceback are all
+            # possibly None (null) until the event is actually finished.
+            # The traceback would only be set if there was an error, but
+            # when the event is complete both finish_time and result will
+            # be set.
+            'finish_time': parameter_types.date_time_or_null,
+            'result': {'type': ['string', 'null']},
             'traceback': {'type': ['string', 'null']}
         },
         'additionalProperties': False,
@@ -564,3 +571,26 @@
 update_attached_volume = {
     'status_code': [202]
 }
+
+evacuate_server = {
+    'status_code': [200]
+}
+
+evacuate_server_with_admin_pass = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'adminPass': {'type': 'string'}
+        },
+        'additionalProperties': False,
+        'required': ['adminPass']
+    }
+}
+
+show_server_diagnostics = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object'
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/services.py b/tempest/lib/api_schema/response/compute/v2_1/services.py
index 6949f86..3b58ece 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/services.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/services.py
@@ -65,3 +65,25 @@
         'required': ['service']
     }
 }
+
+disable_log_reason = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'service': {
+                'type': 'object',
+                'properties': {
+                    'disabled_reason': {'type': 'string'},
+                    'binary': {'type': 'string'},
+                    'host': {'type': 'string'},
+                    'status': {'type': 'string'}
+                },
+                'additionalProperties': False,
+                'required': ['disabled_reason', 'binary', 'host', 'status']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['service']
+    }
+}
diff --git a/tempest/api/image/admin/__init__.py b/tempest/lib/api_schema/response/compute/v2_11/__init__.py
similarity index 100%
copy from tempest/api/image/admin/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_11/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_11/services.py b/tempest/lib/api_schema/response/compute/v2_11/services.py
new file mode 100644
index 0000000..18b833b
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_11/services.py
@@ -0,0 +1,46 @@
+# Copyright 2017 AT&T Corporation.
+# All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import services
+
+
+list_services = copy.deepcopy(services.list_services)
+list_services['response_body']['properties']['services']['items'][
+    'properties']['forced_down'] = {'type': 'boolean'}
+list_services['response_body']['properties']['services']['items'][
+    'required'].append('forced_down')
+
+update_forced_down = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'service': {
+                'type': 'object',
+                'properties': {
+                    'binary': {'type': 'string'},
+                    'host': {'type': 'string'},
+                    'forced_down': {'type': 'boolean'}
+                },
+                'additionalProperties': False,
+                'required': ['binary', 'host', 'forced_down']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['service']
+    }
+}
diff --git a/tempest/api/volume/admin/v3/__init__.py b/tempest/lib/api_schema/response/compute/v2_13/__init__.py
similarity index 100%
rename from tempest/api/volume/admin/v3/__init__.py
rename to tempest/lib/api_schema/response/compute/v2_13/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_13/servers.py b/tempest/lib/api_schema/response/compute/v2_13/servers.py
new file mode 100644
index 0000000..a90f3e4
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_13/servers.py
@@ -0,0 +1,34 @@
+# Copyright 2017 NTT Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import servers
+
+
+common_server_group = copy.deepcopy(servers.common_server_group)
+common_server_group['properties']['project_id'] = {'type': 'string'}
+common_server_group['properties']['user_id'] = {'type': 'string'}
+common_server_group['required'].append('project_id')
+common_server_group['required'].append('user_id')
+
+create_show_server_group = copy.deepcopy(servers.create_show_server_group)
+create_show_server_group['response_body']['properties'][
+    'server_group'] = common_server_group
+
+delete_server_group = copy.deepcopy(servers.delete_server_group)
+
+list_server_groups = copy.deepcopy(servers.list_server_groups)
+list_server_groups['response_body']['properties']['server_groups'][
+    'items'] = common_server_group
diff --git a/tempest/lib/api_schema/response/compute/v2_26/servers.py b/tempest/lib/api_schema/response/compute/v2_26/servers.py
index bc5d18e..b03bdf6 100644
--- a/tempest/lib/api_schema/response/compute/v2_26/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_26/servers.py
@@ -1,4 +1,5 @@
 # Copyright 2016 IBM Corp.
+# Copyright 2017 AT&T Corp.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
 #    not use this file except in compliance with the License. You may obtain
@@ -45,3 +46,36 @@
 # list response schema wasn't changed for v2.26 so use v2.1
 
 list_servers = copy.deepcopy(servers21.list_servers)
+
+list_tags = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'tags': tag_items,
+        },
+        'additionalProperties': False,
+        'required': ['tags']
+    }
+}
+
+update_all_tags = copy.deepcopy(list_tags)
+
+delete_all_tags = {'status_code': [204]}
+
+check_tag_existence = {'status_code': [204]}
+
+update_tag = {
+    'status_code': [201, 204],
+    'response_header': {
+        'type': 'object',
+        'properties': {
+            'location': {
+                'type': 'string'
+            }
+        },
+        'required': ['location']
+    }
+}
+
+delete_tag = {'status_code': [204]}
diff --git a/tempest/api/image/admin/__init__.py b/tempest/lib/api_schema/response/compute/v2_47/__init__.py
similarity index 100%
copy from tempest/api/image/admin/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_47/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_47/servers.py b/tempest/lib/api_schema/response/compute/v2_47/servers.py
new file mode 100644
index 0000000..37a084f
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_47/servers.py
@@ -0,0 +1,39 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_26 import servers as servers226
+
+flavor = {
+    'type': 'object',
+    'properties': {
+        'original_name': {'type': 'string'},
+        'disk': {'type': 'integer'},
+        'ephemeral': {'type': 'integer'},
+        'ram': {'type': 'integer'},
+        'swap': {'type': 'integer'},
+        'vcpus': {'type': 'integer'},
+        'extra_specs': {
+            'type': 'object',
+            'patternProperties': {
+                '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
+            }
+        }
+    },
+    'additionalProperties': False,
+    'required': ['original_name', 'disk', 'ephemeral', 'ram', 'swap', 'vcpus']
+}
+
+get_server = copy.deepcopy(servers226.get_server)
+get_server['response_body']['properties']['server'][
+    'properties'].update({'flavor': flavor})
diff --git a/tempest/api/image/admin/__init__.py b/tempest/lib/api_schema/response/compute/v2_48/__init__.py
similarity index 100%
copy from tempest/api/image/admin/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_48/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_48/servers.py b/tempest/lib/api_schema/response/compute/v2_48/servers.py
new file mode 100644
index 0000000..5904758
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_48/servers.py
@@ -0,0 +1,115 @@
+# Copyright 2017 Mirantis Inc.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.compute.v2_47 import servers as servers247
+
+
+show_server_diagnostics = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'state': {
+                'type': 'string', 'enum': [
+                    'pending', 'running', 'paused', 'shutdown', 'crashed',
+                    'suspended']
+            },
+            'driver': {
+                'type': 'string', 'enum': [
+                    'libvirt', 'xenapi', 'vmwareapi', 'ironic', 'hyperv']
+            },
+            'hypervisor': {'type': ['string', 'null']},
+            'hypervisor_os': {'type': ['string', 'null']},
+            'uptime': {'type': ['integer', 'null']},
+            'config_drive': {'type': 'boolean'},
+            'num_cpus': {'type': 'integer'},
+            'num_nics': {'type': 'integer'},
+            'num_disks': {'type': 'integer'},
+            'memory_details': {
+                'type': 'object',
+                'properties': {
+                    'maximum': {'type': ['integer', 'null']},
+                    'used': {'type': ['integer', 'null']}
+                },
+                'additionalProperties': False,
+                'required': ['maximum', 'used']
+            },
+            'cpu_details': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'id': {'type': ['integer', 'null']},
+                        'time': {'type': ['integer', 'null']},
+                        'utilisation': {'type': ['integer', 'null']}
+                    },
+                    'additionalProperties': False,
+                    'required': ['id', 'time', 'utilisation']
+                }
+            },
+            'nic_details': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'mac_address': {'oneOf': [parameter_types.mac_address,
+                                                  {'type': 'null'}]},
+                        'rx_octets': {'type': ['integer', 'null']},
+                        'rx_errors': {'type': ['integer', 'null']},
+                        'rx_drop': {'type': ['integer', 'null']},
+                        'rx_packets': {'type': ['integer', 'null']},
+                        'rx_rate': {'type': ['integer', 'null']},
+                        'tx_octets': {'type': ['integer', 'null']},
+                        'tx_errors': {'type': ['integer', 'null']},
+                        'tx_drop': {'type': ['integer', 'null']},
+                        'tx_packets': {'type': ['integer', 'null']},
+                        'tx_rate': {'type': ['integer', 'null']}
+                    },
+                    'additionalProperties': False,
+                    'required': ['mac_address', 'rx_octets', 'rx_errors',
+                                 'rx_drop',
+                                 'rx_packets', 'rx_rate', 'tx_octets',
+                                 'tx_errors',
+                                 'tx_drop', 'tx_packets', 'tx_rate']
+                }
+            },
+            'disk_details': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'read_bytes': {'type': ['integer', 'null']},
+                        'read_requests': {'type': ['integer', 'null']},
+                        'write_bytes': {'type': ['integer', 'null']},
+                        'write_requests': {'type': ['integer', 'null']},
+                        'errors_count': {'type': ['integer', 'null']}
+                    },
+                    'additionalProperties': False,
+                    'required': ['read_bytes', 'read_requests', 'write_bytes',
+                                 'write_requests', 'errors_count']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': [
+            'state', 'driver', 'hypervisor', 'hypervisor_os', 'uptime',
+            'config_drive', 'num_cpus', 'num_nics', 'num_disks',
+            'memory_details', 'cpu_details', 'nic_details', 'disk_details'],
+    }
+}
+
+get_server = copy.deepcopy(servers247.get_server)
diff --git a/tempest/api/image/admin/__init__.py b/tempest/lib/api_schema/response/compute/v2_6/__init__.py
similarity index 100%
copy from tempest/api/image/admin/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_6/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_6/servers.py b/tempest/lib/api_schema/response/compute/v2_6/servers.py
new file mode 100644
index 0000000..29b3e86
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_6/servers.py
@@ -0,0 +1,48 @@
+# Copyright 2016 IBM Corp.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_3 import servers
+
+list_servers = copy.deepcopy(servers.list_servers)
+get_server = copy.deepcopy(servers.get_server)
+list_servers_detail = copy.deepcopy(servers.list_servers_detail)
+
+# NOTE: The consolidated remote console API got introduced with v2.6
+# with bp/consolidate-console-api. See Nova commit 578bafeda
+get_remote_consoles = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'remote_console': {
+                'type': 'object',
+                'properties': {
+                    'protocol': {'enum': ['vnc', 'rdp', 'serial', 'spice']},
+                    'type': {'enum': ['novnc', 'xpvnc', 'rdp-html5',
+                                      'spice-html5', 'serial']},
+                    'url': {
+                        'type': 'string',
+                        'format': 'uri'
+                    }
+                },
+                'additionalProperties': False,
+                'required': ['protocol', 'type', 'url']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['remote_console']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_9/servers.py b/tempest/lib/api_schema/response/compute/v2_9/servers.py
index 470190c..e260e48 100644
--- a/tempest/lib/api_schema/response/compute/v2_9/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py
@@ -14,7 +14,7 @@
 
 import copy
 
-from tempest.lib.api_schema.response.compute.v2_3 import servers
+from tempest.lib.api_schema.response.compute.v2_6 import servers
 
 list_servers = copy.deepcopy(servers.list_servers)
 
diff --git a/tempest/api/image/admin/__init__.py b/tempest/lib/api_schema/response/volume/__init__.py
similarity index 100%
copy from tempest/api/image/admin/__init__.py
copy to tempest/lib/api_schema/response/volume/__init__.py
diff --git a/tempest/lib/api_schema/response/volume/versions.py b/tempest/lib/api_schema/response/volume/versions.py
new file mode 100644
index 0000000..2391a8c
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/versions.py
@@ -0,0 +1,60 @@
+# Copyright 2015 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+list_versions = {
+    'status_code': [300],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'versions': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'status': {'type': 'string'},
+                        'updated': {'type': 'string'},
+                        'id': {'type': 'string'},
+                        'links': {
+                            'type': 'array',
+                            'items': {
+                                'type': 'object',
+                                'properties': {
+                                    'href': {'type': 'string',
+                                             'format': 'uri'},
+                                    'rel': {'type': 'string'},
+                                    'type': {'type': 'string'},
+                                },
+                                'required': ['href', 'rel']
+                            }
+                        },
+                        'min_version': {'type': 'string'},
+                        'version': {'type': 'string'},
+                        'media-types': {
+                            'type': 'array',
+                            'properties': {
+                                'base': {'type': 'string'},
+                                'type': {'type': 'string'}
+                            },
+                            'required': ['base', 'type']
+                        }
+                    },
+                    'required': ['status', 'updated', 'id', 'links',
+                                 'min_version', 'version', 'media-types']
+                }
+            }
+        },
+        'required': ['versions'],
+    }
+}
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 83aa405..ab4308f 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -539,18 +539,18 @@
 
         # Select entries with matching service type
         service_catalog = [ep for ep in catalog if ep['type'] == service]
-        if len(service_catalog) > 0:
+        if service_catalog:
             if name is not None:
                 service_catalog = (
                     [ep for ep in service_catalog if ep['name'] == name])
-                if len(service_catalog) > 0:
+                if service_catalog:
                     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':
+            if not catalog 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.
@@ -571,13 +571,13 @@
         # Filter by endpoint type (interface)
         filtered_catalog = [ep for ep in service_catalog if
                             ep['interface'] == endpoint_type]
-        if len(filtered_catalog) == 0:
+        if not filtered_catalog:
             # No matching type, keep all and try matching by region at least
             filtered_catalog = service_catalog
         # Filter by region
         filtered_catalog = [ep for ep in filtered_catalog if
                             ep['region'] == region]
-        if len(filtered_catalog) == 0:
+        if not filtered_catalog:
             # 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.
diff --git a/tempest/lib/cli/output_parser.py b/tempest/lib/cli/output_parser.py
index 716f374..2edd5c1 100644
--- a/tempest/lib/cli/output_parser.py
+++ b/tempest/lib/cli/output_parser.py
@@ -114,7 +114,7 @@
                 label = line
             else:
                 LOG.warning('Invalid line between tables: %s', line)
-    if len(table_) > 0:
+    if table_:
         LOG.warning('Missing end of table')
 
     return tables_
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index 2fe957b..101d692 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -26,13 +26,10 @@
 from oslo_utils import uuidutils
 import six.moves.urllib.parse as urlparse
 
-# TODO(oomichi): Need to remove this after switching all modules to decorators
-OLD_DECORATOR_MODULE = 'test'
-
 DECORATOR_MODULE = 'decorators'
 DECORATOR_NAME = 'idempotent_id'
 DECORATOR_IMPORT = 'tempest.%s' % DECORATOR_MODULE
-IMPORT_LINE = 'from tempest import %s' % DECORATOR_MODULE
+IMPORT_LINE = 'from tempest.lib import %s' % DECORATOR_MODULE
 DECORATOR_TEMPLATE = "@%s.%s('%%s')" % (DECORATOR_MODULE,
                                         DECORATOR_NAME)
 UNIT_TESTS_EXCLUDE = 'tempest.tests'
@@ -120,15 +117,14 @@
 
     @staticmethod
     def _get_idempotent_id(test_node):
-        """Return key-value dict with all metadata from @test.idempotent_id"""
+        "Return key-value dict with metadata from @decorators.idempotent_id"
         idempotent_id = None
         for decorator in test_node.decorator_list:
             if (hasattr(decorator, 'func') and
                 hasattr(decorator.func, 'attr') and
                 decorator.func.attr == DECORATOR_NAME and
                 hasattr(decorator.func, 'value') and
-                (decorator.func.value.id == DECORATOR_MODULE or
-                 decorator.func.value.id == OLD_DECORATOR_MODULE)):
+                decorator.func.value.id == DECORATOR_MODULE):
                 for arg in decorator.args:
                     idempotent_id = ast.literal_eval(arg)
         return idempotent_id
@@ -308,7 +304,8 @@
         Returns true if untagged tests exist.
         """
         def report(module_name, test_name, tests):
-            error_str = "%s:%s\nmissing @test.idempotent_id('...')\n%s\n" % (
+            error_str = ("%s:%s\nmissing @decorators.idempotent_id"
+                         "('...')\n%s\n") % (
                 tests[module_name]['source_path'],
                 tests[module_name]['tests'][test_name].lineno,
                 test_name
@@ -356,9 +353,10 @@
     else:
         errors = checker.report_untagged(untagged) or errors
     if errors:
-        sys.exit("@test.idempotent_id existence and uniqueness checks failed\n"
-                 "Run 'tox -v -euuidgen' to automatically fix tests with\n"
-                 "missing @test.idempotent_id decorators.")
+        sys.exit("@decorators.idempotent_id existence and uniqueness checks "
+                 "failed\n"
+                 "Run 'tox -v -e uuidgen' to automatically fix tests with\n"
+                 "missing @decorators.idempotent_id decorators.")
 
 if __name__ == '__main__':
     run()
diff --git a/tempest/lib/cmd/skip_tracker.py b/tempest/lib/cmd/skip_tracker.py
index 07b811d..87806b7 100755
--- a/tempest/lib/cmd/skip_tracker.py
+++ b/tempest/lib/cmd/skip_tracker.py
@@ -34,10 +34,14 @@
 LPCACHEDIR = os.path.expanduser('~/.launchpadlib/cache')
 LOG = logging.getLogger(__name__)
 
+BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))
+TESTDIR = os.path.join(BASEDIR, 'tempest')
+
 
 def parse_args():
     parser = argparse.ArgumentParser()
-    parser.add_argument('test_path', help='Path of test dir')
+    parser.add_argument('test_path', nargs='?', default=TESTDIR,
+                        help='Path of test dir')
     return parser.parse_args()
 
 
diff --git a/tempest/lib/common/api_version_utils.py b/tempest/lib/common/api_version_utils.py
index 1371b3c..98f174d 100644
--- a/tempest/lib/common/api_version_utils.py
+++ b/tempest/lib/common/api_version_utils.py
@@ -12,6 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log as logging
 import testtools
 
 from tempest.lib.common import api_version_request
@@ -19,6 +20,7 @@
 
 
 LATEST_MICROVERSION = 'latest'
+LOG = logging.getLogger(__name__)
 
 
 class BaseMicroversionTest(object):
@@ -120,3 +122,60 @@
                                     api_microversion,
                                     response_header))
         raise exceptions.InvalidHTTPResponseHeader(msg)
+
+
+def compare_version_header_to_response(api_microversion_header_name,
+                                       api_microversion,
+                                       response_header,
+                                       operation='eq'):
+    """Compares API microversion in response header to ``api_microversion``.
+
+    Compare the ``api_microversion`` value in response header if microversion
+    header is present in response, otherwise return false.
+
+    To make this function work for APIs which do not return microversion
+    header in response (example compute v2.0), this function does *not* raise
+    InvalidHTTPResponseHeader.
+
+    :param api_microversion_header_name: Microversion header name. Example:
+        'Openstack-Api-Version'.
+    :param api_microversion: Microversion number. Example:
+
+        * '2.10' for the old-style header name, 'X-OpenStack-Nova-API-Version'
+        * 'Compute 2.10' for the new-style header name, 'Openstack-Api-Version'
+
+    :param response_header: Response header where microversion is
+        expected to be present.
+    :param operation: The boolean operation to use to compare the
+        ``api_microversion`` to the microversion in ``response_header``.
+        Can be 'lt', 'eq', 'gt', 'le', 'ne', 'ge'. Default is 'eq'. The
+        operation type should be based on the order of the arguments:
+        ``api_microversion`` <operation> ``response_header`` microversion.
+    :returns: True if the comparison is logically true, else False if the
+        comparison is logically false or if ``api_microversion_header_name`` is
+        missing in the ``response_header``.
+    :raises InvalidParam: If the operation is not lt, eq, gt, le, ne or ge.
+    """
+    api_microversion_header_name = api_microversion_header_name.lower()
+    if api_microversion_header_name not in response_header:
+        return False
+
+    op = getattr(api_version_request.APIVersionRequest,
+                 '__%s__' % operation, None)
+
+    if op is None:
+        msg = ("Operation %s is invalid. Valid options include: lt, eq, gt, "
+               "le, ne, ge." % operation)
+        LOG.debug(msg)
+        raise exceptions.InvalidParam(invalid_param=msg)
+
+    # Remove "volume" from "volume <microversion>", for example, so that the
+    # microversion can be converted to `APIVersionRequest`.
+    api_version = api_microversion.split(' ')[-1]
+    resp_version = response_header[api_microversion_header_name].split(' ')[-1]
+    if not op(
+        api_version_request.APIVersionRequest(api_version),
+        api_version_request.APIVersionRequest(resp_version)):
+        return False
+
+    return True
diff --git a/tempest/lib/common/cred_client.py b/tempest/lib/common/cred_client.py
index ea06011..a81f53c 100644
--- a/tempest/lib/common/cred_client.py
+++ b/tempest/lib/common/cred_client.py
@@ -59,7 +59,8 @@
     def _check_role_exists(self, role_name):
         try:
             roles = self._list_roles()
-            role = next(r for r in roles if r['name'] == role_name)
+            lc_role_name = role_name.lower()
+            role = next(r for r in roles if r['name'].lower() == lc_role_name)
         except StopIteration:
             return None
         return role
diff --git a/tempest/lib/common/cred_provider.py b/tempest/lib/common/cred_provider.py
index 1b450ab..42ed41b 100644
--- a/tempest/lib/common/cred_provider.py
+++ b/tempest/lib/common/cred_provider.py
@@ -22,8 +22,9 @@
 
 @six.add_metaclass(abc.ABCMeta)
 class CredentialProvider(object):
-    def __init__(self, identity_version, name=None, network_resources=None,
-                 credentials_domain=None, admin_role=None):
+    def __init__(self, identity_version, name=None,
+                 network_resources=None, credentials_domain=None,
+                 admin_role=None, identity_uri=None):
         """A CredentialProvider supplies credentials to test classes.
 
         :param identity_version: Identity version of the credentials provided
@@ -33,8 +34,11 @@
                                   credentials
         :param credentials_domain: Domain credentials belong to
         :param admin_role: Name of the role of the admin account
+        :param identity_uri: Identity URI of the target cloud. This *must* be
+                             specified for anything to work.
         """
         self.identity_version = identity_version
+        self.identity_uri = identity_uri
         self.name = name or "test_creds"
         self.network_resources = network_resources
         self.credentials_domain = credentials_domain or 'Default'
diff --git a/tempest/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
similarity index 87%
rename from tempest/common/dynamic_creds.py
rename to tempest/lib/common/dynamic_creds.py
index 632a876..9a6c8f5 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -12,15 +12,17 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import ipaddress
+
 import netaddr
 from oslo_log import log as logging
 import six
 
-from tempest import clients
 from tempest.lib.common import cred_client
 from tempest.lib.common import cred_provider
 from tempest.lib.common.utils import data_utils
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.services import clients
 
 LOG = logging.getLogger(__name__)
 
@@ -33,7 +35,8 @@
                  identity_admin_role='admin', extra_roles=None,
                  neutron_available=False, create_networks=True,
                  project_network_cidr=None, project_network_mask_bits=None,
-                 public_network_id=None, resource_prefix=None):
+                 public_network_id=None, resource_prefix=None,
+                 identity_admin_endpoint_type='public', identity_uri=None):
         """Creates credentials dynamically for tests
 
         A credential provider that, based on an initial set of
@@ -67,10 +70,14 @@
         :param project_network_mask_bits: The network mask bits to use for
                                           created project networks
         :param public_network_id: The id for the public network to use
+        :param identity_admin_endpoint_type: The endpoint type for identity
+                                             admin clients. Defaults to public.
+        :param identity_uri: Identity URI of the target cloud
         """
         super(DynamicCredentialProvider, self).__init__(
-            identity_version=identity_version, admin_role=admin_role,
-            name=name, credentials_domain=credentials_domain,
+            identity_version=identity_version, identity_uri=identity_uri,
+            admin_role=admin_role, name=name,
+            credentials_domain=credentials_domain,
             network_resources=network_resources)
         self.network_resources = network_resources
         self._creds = {}
@@ -84,6 +91,7 @@
         self.default_admin_creds = admin_creds
         self.identity_admin_domain_scope = identity_admin_domain_scope
         self.identity_admin_role = identity_admin_role or 'admin'
+        self.identity_admin_endpoint_type = identity_admin_endpoint_type
         self.extra_roles = extra_roles or []
         (self.identity_admin_client,
          self.tenants_admin_client,
@@ -94,7 +102,8 @@
          self.routers_admin_client,
          self.subnets_admin_client,
          self.ports_admin_client,
-         self.security_groups_admin_client) = self._get_admin_clients()
+         self.security_groups_admin_client) = self._get_admin_clients(
+            identity_admin_endpoint_type)
         # Domain where isolated credentials are provisioned (v3 only).
         # Use that of the admin account is None is configured.
         self.creds_domain_name = None
@@ -110,32 +119,43 @@
             self.domains_admin_client,
             self.creds_domain_name)
 
-    def _get_admin_clients(self):
+    def _get_admin_clients(self, endpoint_type):
         """Returns a tuple with instances of the following admin clients
 
         (in this order):
             identity
             network
         """
-        os = clients.Manager(self.default_admin_creds)
+        os = clients.ServiceClients(self.default_admin_creds,
+                                    self.identity_uri)
+        params = {'endpoint_type': endpoint_type}
         if self.identity_version == 'v2':
-            return (os.identity_client, os.tenants_client, os.users_client,
-                    os.roles_client, None,
-                    os.networks_client, os.routers_client, os.subnets_client,
-                    os.ports_client, os.security_groups_client)
+            return (os.identity_v2.IdentityClient(**params),
+                    os.identity_v2.TenantsClient(**params),
+                    os.identity_v2.UsersClient(**params),
+                    os.identity_v2.RolesClient(**params), None,
+                    os.network.NetworksClient(),
+                    os.network.RoutersClient(),
+                    os.network.SubnetsClient(),
+                    os.network.PortsClient(),
+                    os.network.SecurityGroupsClient())
         else:
             # We use a dedicated client manager for identity client in case we
             # need a different token scope for them.
             scope = 'domain' if self.identity_admin_domain_scope else 'project'
-            identity_os = clients.Manager(self.default_admin_creds,
-                                          scope=scope)
-            return (identity_os.identity_v3_client,
-                    identity_os.projects_client,
-                    identity_os.users_v3_client, identity_os.roles_v3_client,
-                    identity_os.domains_client,
-                    os.networks_client, os.routers_client,
-                    os.subnets_client, os.ports_client,
-                    os.security_groups_client)
+            identity_os = clients.ServiceClients(self.default_admin_creds,
+                                                 self.identity_uri,
+                                                 scope=scope)
+            return (identity_os.identity_v3.IdentityClient(**params),
+                    identity_os.identity_v3.ProjectsClient(**params),
+                    identity_os.identity_v3.UsersClient(**params),
+                    identity_os.identity_v3.RolesClient(**params),
+                    identity_os.identity_v3.DomainsClient(**params),
+                    os.network.NetworksClient(),
+                    os.network.RoutersClient(),
+                    os.network.SubnetsClient(),
+                    os.network.PortsClient(),
+                    os.network.SecurityGroupsClient())
 
     def _create_creds(self, admin=False, roles=None):
         """Create credentials with random name.
@@ -275,14 +295,16 @@
                             name=subnet_name,
                             tenant_id=tenant_id,
                             enable_dhcp=self.network_resources['dhcp'],
-                            ip_version=4)
+                            ip_version=(ipaddress.ip_network(
+                                six.text_type(subnet_cidr)).version))
                 else:
                     resp_body = self.subnets_admin_client.\
                         create_subnet(network_id=network_id,
                                       cidr=str(subnet_cidr),
                                       name=subnet_name,
                                       tenant_id=tenant_id,
-                                      ip_version=4)
+                                      ip_version=(ipaddress.ip_network(
+                                          six.text_type(subnet_cidr)).version))
                 break
             except lib_exc.BadRequest as e:
                 if 'overlaps with another subnet' not in str(e):
@@ -293,12 +315,12 @@
         return resp_body['subnet']
 
     def _create_router(self, router_name, tenant_id):
-        external_net_id = dict(
-            network_id=self.public_network_id)
-        resp_body = self.routers_admin_client.create_router(
-            name=router_name,
-            external_gateway_info=external_net_id,
-            tenant_id=tenant_id)
+        kwargs = {'name': router_name,
+                  'tenant_id': tenant_id}
+        if self.public_network_id:
+            kwargs['external_gateway_info'] = dict(
+                network_id=self.public_network_id)
+        resp_body = self.routers_admin_client.create_router(**kwargs)
         return resp_body['router']
 
     def _add_router_interface(self, router_id, subnet_id):
@@ -429,7 +451,7 @@
                             creds.username)
             # NOTE(zhufl): Only when neutron's security_group ext is
             # enabled, _cleanup_default_secgroup will not raise error. But
-            # here cannot use test.is_extension_enabled for it will cause
+            # here cannot use test_utils.is_extension_enabled for it will cause
             # "circular dependency". So here just use try...except to
             # ensure tenant deletion without big changes.
             try:
diff --git a/tempest/common/fixed_network.py b/tempest/lib/common/fixed_network.py
similarity index 99%
rename from tempest/common/fixed_network.py
rename to tempest/lib/common/fixed_network.py
index f50edbd..e2054a4 100644
--- a/tempest/common/fixed_network.py
+++ b/tempest/lib/common/fixed_network.py
@@ -11,10 +11,11 @@
 #    under the License.
 
 import copy
+
 from oslo_log import log as logging
 
-from tempest import exceptions
 from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
 
 LOG = logging.getLogger(__name__)
 
diff --git a/tempest/lib/common/http.py b/tempest/lib/common/http.py
index 8a47d44..b4b1fc9 100644
--- a/tempest/lib/common/http.py
+++ b/tempest/lib/common/http.py
@@ -25,8 +25,7 @@
         if disable_ssl_certificate_validation:
             urllib3.disable_warnings()
             kwargs['cert_reqs'] = 'CERT_NONE'
-
-        if ca_certs:
+        elif ca_certs:
             kwargs['cert_reqs'] = 'CERT_REQUIRED'
             kwargs['ca_certs'] = ca_certs
 
diff --git a/tempest/common/preprov_creds.py b/tempest/lib/common/preprov_creds.py
similarity index 96%
rename from tempest/common/preprov_creds.py
rename to tempest/lib/common/preprov_creds.py
index a92d16a..cd3a10e 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/lib/common/preprov_creds.py
@@ -20,12 +20,11 @@
 import six
 import yaml
 
-from tempest import clients
-from tempest.common import fixed_network
-from tempest import exceptions
 from tempest.lib import auth
 from tempest.lib.common import cred_provider
+from tempest.lib.common import fixed_network
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.services import clients
 
 LOG = logging.getLogger(__name__)
 
@@ -51,7 +50,7 @@
     def __init__(self, identity_version, test_accounts_file,
                  accounts_lock_dir, name=None, credentials_domain=None,
                  admin_role=None, object_storage_operator_role=None,
-                 object_storage_reseller_admin_role=None):
+                 object_storage_reseller_admin_role=None, identity_uri=None):
         """Credentials provider using pre-provisioned accounts
 
         This credentials provider loads the details of pre-provisioned
@@ -79,10 +78,12 @@
                                    (if no domain is configured)
         :param object_storage_operator_role: name of the role
         :param object_storage_reseller_admin_role: name of the role
+        :param identity_uri: Identity URI of the target cloud
         """
         super(PreProvisionedCredentialProvider, self).__init__(
             identity_version=identity_version, name=name,
-            admin_role=admin_role, credentials_domain=credentials_domain)
+            admin_role=admin_role, credentials_domain=credentials_domain,
+            identity_uri=identity_uri)
         self.test_accounts_file = test_accounts_file
         if test_accounts_file:
             accounts = read_accounts_yaml(self.test_accounts_file)
@@ -241,7 +242,7 @@
 
     def _get_creds(self, roles=None):
         useable_hashes = self._get_match_hash_list(roles)
-        if len(useable_hashes) == 0:
+        if not useable_hashes:
             msg = 'No users configured for type/roles %s' % roles
             raise lib_exc.InvalidCredentials(msg)
         free_hash = self._get_free_hash(useable_hashes)
@@ -341,13 +342,14 @@
             auth_url=None, fill_in=False,
             identity_version=self.identity_version, **creds_dict)
         net_creds = cred_provider.TestResources(credential)
-        net_clients = clients.Manager(credentials=credential)
-        compute_network_client = net_clients.compute_networks_client
+        net_clients = clients.ServiceClients(credentials=credential,
+                                             identity_uri=self.identity_uri)
+        compute_network_client = net_clients.compute.NetworksClient()
         net_name = self.hash_dict['networks'].get(hash, None)
         try:
             network = fixed_network.get_network_from_name(
                 net_name, compute_network_client)
-        except exceptions.InvalidTestResource:
+        except lib_exc.InvalidTestResource:
             network = {}
         net_creds.set_resources(network=network)
         return net_creds
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index d0e21ff..f58d737 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -23,6 +23,7 @@
 from oslo_log import log as logging
 from oslo_serialization import jsonutils as json
 import six
+from six.moves import urllib
 
 from tempest.lib.common import http
 from tempest.lib.common import jsonschema_validator
@@ -69,7 +70,6 @@
     :param str http_timeout: Timeout in seconds to wait for the http request to
                              return
     """
-    TYPE = "json"
 
     # The version of the API this client implements
     api_version = None
@@ -104,12 +104,6 @@
             disable_ssl_certificate_validation=dscv, ca_certs=ca_certs,
             timeout=http_timeout)
 
-    def _get_type(self):
-        if self.TYPE != "json":
-            self.LOG.warning("Tempest has dropped XML support and the TYPE "
-                             "became meaningless")
-        return self.TYPE
-
     def get_headers(self, accept_type=None, send_type=None):
         """Return the default headers which will be used with outgoing requests
 
@@ -124,9 +118,9 @@
                  dict for outgoing request
         """
         if accept_type is None:
-            accept_type = self._get_type()
+            accept_type = 'json'
         if send_type is None:
-            send_type = self._get_type()
+            send_type = 'json'
         return {'Content-Type': 'application/%s' % send_type,
                 'Accept': 'application/%s' % accept_type}
 
@@ -377,7 +371,7 @@
         on the endpoint in the catalog will return a list of supported API
         versions.
 
-        :return tuple with response headers and list of version numbers
+        :return: tuple with response headers and list of version numbers
         :rtype: tuple
         """
         resp, body = self.get('')
@@ -480,7 +474,7 @@
             # Ensure there are not more than one top-level keys
             # NOTE(freerunner): Ensure, that JSON is not nullable to
             # to prevent StopIteration Exception
-            if len(body.keys()) != 1:
+            if not hasattr(body, "keys") or len(body.keys()) != 1:
                 return body
             # Just return the "wrapped" element
             first_key, first_item = six.next(six.iteritems(body))
@@ -617,6 +611,7 @@
         :raises BadRequest: If a 400 response code is received
         :raises Gone: If a 410 response code is received
         :raises Conflict: If a 409 response code is received
+        :raises PreconditionFailed: If a 412 response code is received
         :raises OverLimit: If a 413 response code is received and over_limit is
                           not in the response body
         :raises RateLimitExceeded: If a 413 response code is received and
@@ -730,12 +725,21 @@
         if resp.status < 400:
             return
 
-        JSON_ENC = ['application/json', 'application/json; charset=utf-8']
+        # NOTE(zhipengh): There is a purposefully duplicate of content-type
+        # with the only difference is with or without spaces, as specified
+        # in RFC7231.
+        JSON_ENC = ['application/json', 'application/json; charset=utf-8',
+                    'application/json;charset=utf-8']
+
         # NOTE(mtreinish): This is for compatibility with Glance and swift
         # APIs. These are the return content types that Glance api v1
         # (and occasionally swift) are using.
+        # NOTE(zhipengh): There is a purposefully duplicate of content-type
+        # with the only difference is with or without spaces, as specified
+        # in RFC7231.
         TXT_ENC = ['text/plain', 'text/html', 'text/html; charset=utf-8',
-                   'text/plain; charset=utf-8']
+                   'text/plain; charset=utf-8', 'text/html;charset=utf-8',
+                   'text/plain;charset=utf-8']
 
         if ctype.lower() in JSON_ENC:
             parse_resp = True
@@ -775,6 +779,11 @@
                 resp_body = self._parse_resp(resp_body)
             raise exceptions.Conflict(resp_body, resp=resp)
 
+        if resp.status == 412:
+            if parse_resp:
+                resp_body = self._parse_resp(resp_body)
+            raise exceptions.PreconditionFailed(resp_body, resp=resp)
+
         if resp.status == 413:
             if parse_resp:
                 resp_body = self._parse_resp(resp_body)
@@ -909,6 +918,16 @@
                     msg = ("HTTP response header is invalid (%s)" % ex)
                     raise exceptions.InvalidHTTPResponseHeader(msg)
 
+    def _get_base_version_url(self):
+        # TODO(oomichi): This method can be used for auth's replace_version().
+        # So it is nice to have common logic for the maintenance.
+        endpoint = self.base_url
+        url = urllib.parse.urlsplit(endpoint)
+        new_path = re.split(r'(^|/)+v\d+(\.\d+)?', url.path)[0]
+        url = list(url)
+        url[2] = new_path + '/'
+        return urllib.parse.urlunsplit(url)
+
 
 class ResponseBody(dict):
     """Class that wraps an http response and dict body into a single value.
diff --git a/tempest/lib/common/ssh.py b/tempest/lib/common/ssh.py
index 5e65bee..d4ec6ad 100644
--- a/tempest/lib/common/ssh.py
+++ b/tempest/lib/common/ssh.py
@@ -84,10 +84,6 @@
         ssh.set_missing_host_key_policy(
             paramiko.AutoAddPolicy())
         _start_time = time.time()
-        if self.proxy_client is not None:
-            proxy_chan = self._get_proxy_channel()
-        else:
-            proxy_chan = None
         if self.pkey is not None:
             LOG.info("Creating ssh connection to '%s:%d' as '%s'"
                      " with public key authentication",
@@ -98,6 +94,10 @@
                      self.host, self.port, self.username, str(self.password))
         attempts = 0
         while True:
+            if self.proxy_client is not None:
+                proxy_chan = self._get_proxy_channel()
+            else:
+                proxy_chan = None
             try:
                 ssh.connect(self.host, port=self.port, username=self.username,
                             password=self.password,
@@ -111,6 +111,7 @@
             except (EOFError,
                     socket.error, socket.timeout,
                     paramiko.SSHException) as e:
+                ssh.close()
                 if self._is_timed_out(_start_time):
                     LOG.exception("Failed to establish authenticated ssh"
                                   " connection to %s@%s after %d attempts",
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 75c2e51..a0941ef 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -14,12 +14,12 @@
 #    under the License.
 
 import itertools
-import netaddr
 import random
 import string
 import uuid
 
 from debtcollector import removals
+import netaddr
 from oslo_utils import netutils
 from oslo_utils import uuidutils
 import six.moves
@@ -43,7 +43,7 @@
     return uuid.uuid4().hex
 
 
-def rand_name(name='', prefix=None):
+def rand_name(name='', prefix='tempest'):
     """Generate a random name that includes a random number
 
     :param str name: The name that you want to include
diff --git a/tempest/api/image/admin/__init__.py b/tempest/lib/common/utils/linux/__init__.py
similarity index 100%
rename from tempest/api/image/admin/__init__.py
rename to tempest/lib/common/utils/linux/__init__.py
diff --git a/tempest/lib/common/utils/linux/remote_client.py b/tempest/lib/common/utils/linux/remote_client.py
new file mode 100644
index 0000000..cd4092b
--- /dev/null
+++ b/tempest/lib/common/utils/linux/remote_client.py
@@ -0,0 +1,126 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    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 sys
+
+import netaddr
+from oslo_log import log as logging
+import six
+
+from tempest.lib.common import ssh
+from tempest.lib.common.utils import test_utils
+import tempest.lib.exceptions
+
+LOG = logging.getLogger(__name__)
+
+
+def debug_ssh(function):
+    """Decorator to generate extra debug info in case off SSH failure"""
+    def wrapper(self, *args, **kwargs):
+        try:
+            return function(self, *args, **kwargs)
+        except Exception as e:
+            caller = test_utils.find_test_caller() or "not found"
+            if not isinstance(e, tempest.lib.exceptions.SSHTimeout):
+                message = ('Initializing SSH connection to %(ip)s failed. '
+                           'Error: %(error)s' % {'ip': self.ip_address,
+                                                 'error': e})
+                message = '(%s) %s' % (caller, message)
+                LOG.error(message)
+                raise
+            else:
+                try:
+                    original_exception = sys.exc_info()
+                    if self.server:
+                        msg = 'Caller: %s. Timeout trying to ssh to server %s'
+                        LOG.debug(msg, caller, self.server)
+                        if self.console_output_enabled and self.servers_client:
+                            try:
+                                msg = 'Console log for server %s: %s'
+                                console_log = (
+                                    self.servers_client.get_console_output(
+                                        self.server['id'])['output'])
+                                LOG.debug(msg, self.server['id'], console_log)
+                            except Exception:
+                                msg = 'Could not get console_log for server %s'
+                                LOG.debug(msg, self.server['id'])
+                    # re-raise the original ssh timeout exception
+                    six.reraise(*original_exception)
+                finally:
+                    # Delete the traceback to avoid circular references
+                    _, _, trace = original_exception
+                    del trace
+    return wrapper
+
+
+class RemoteClient(object):
+
+    def __init__(self, ip_address, username, password=None, pkey=None,
+                 server=None, servers_client=None, ssh_timeout=300,
+                 connect_timeout=60, console_output_enabled=True,
+                 ssh_shell_prologue="set -eu -o pipefail; PATH=$PATH:/sbin;",
+                 ping_count=1, ping_size=56):
+        """Executes commands in a VM over ssh
+
+        :param ip_address: IP address to ssh to
+        :param username: Ssh username
+        :param password: Ssh password
+        :param pkey: Ssh public key
+        :param server: Server dict, used for debugging purposes
+        :param servers_client: Servers client, used for debugging purposes
+        :param ssh_timeout: Timeout in seconds to wait for the ssh banner
+        :param connect_timeout: Timeout in seconds to wait for TCP connection
+        :param console_output_enabled: Support serial console output?
+        :param ssh_shell_prologue: Shell fragments to use before command
+        :param ping_count: Number of ping packets
+        :param ping_size: Packet size for ping packets
+        """
+        self.server = server
+        self.servers_client = servers_client
+        self.ip_address = ip_address
+        self.console_output_enabled = console_output_enabled
+        self.ssh_shell_prologue = ssh_shell_prologue
+        self.ping_count = ping_count
+        self.ping_size = ping_size
+
+        self.ssh_client = ssh.Client(ip_address, username, password,
+                                     ssh_timeout, pkey=pkey,
+                                     channel_timeout=connect_timeout)
+
+    @debug_ssh
+    def exec_command(self, cmd):
+        # Shell options below add more clearness on failures,
+        # path is extended for some non-cirros guest oses (centos7)
+        cmd = self.ssh_shell_prologue + " " + cmd
+        LOG.debug("Remote command: %s", cmd)
+        return self.ssh_client.exec_command(cmd)
+
+    @debug_ssh
+    def validate_authentication(self):
+        """Validate ssh connection and authentication
+
+           This method raises an Exception when the validation fails.
+        """
+        self.ssh_client.test_connection_auth()
+
+    def ping_host(self, host, count=None, size=None, nic=None):
+        if count is None:
+            count = self.ping_count
+        if size is None:
+            size = self.ping_size
+
+        addr = netaddr.IPAddress(host)
+        cmd = 'ping6' if addr.version == 6 else 'ping'
+        if nic:
+            cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
+        cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
+        return self.exec_command(cmd)
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index 6ed99b4..f82f707 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -15,9 +15,13 @@
 import functools
 import uuid
 
+import debtcollector.removals
+from oslo_log import log as logging
 import six
 import testtools
 
+LOG = logging.getLogger(__name__)
+
 
 def skip_because(*args, **kwargs):
     """A decorator useful to skip tests hitting known bugs
@@ -44,6 +48,28 @@
     return decorator
 
 
+def related_bug(bug, status_code=None):
+    """A decorator useful to know solutions from launchpad bug reports
+
+    @param bug: The launchpad bug number causing the test
+    @param status_code: The status code related to the bug report
+    """
+    def decorator(f):
+        @functools.wraps(f)
+        def wrapper(self, *func_args, **func_kwargs):
+            try:
+                return f(self, *func_args, **func_kwargs)
+            except Exception as exc:
+                exc_status_code = getattr(exc, 'status_code', None)
+                if status_code is None or status_code == exc_status_code:
+                    LOG.error('Hints: This test was made for the bug %s. '
+                              'The failure could be related to '
+                              'https://launchpad.net/bugs/%s', bug, bug)
+                raise exc
+        return wrapper
+    return decorator
+
+
 def idempotent_id(id):
     """Stub for metadata decorator"""
     if not isinstance(id, six.string_types):
@@ -61,6 +87,7 @@
     return decorator
 
 
+@debtcollector.removals.remove(removal_version='Queen')
 class skip_unless_attr(object):
     """Decorator to skip tests if a specified attr does not exists or False"""
     def __init__(self, attr, msg=None):
@@ -77,3 +104,21 @@
                 raise testtools.TestCase.skipException(self.message)
             func(*args, **kw)
         return _skipper
+
+
+def attr(**kwargs):
+    """A decorator which applies the testtools attr decorator
+
+    This decorator applies the testtools.testcase.attr if it is in the list of
+    attributes to testtools we want to apply.
+    """
+
+    def decorator(f):
+        if 'type' in kwargs and isinstance(kwargs['type'], str):
+            f = testtools.testcase.attr(kwargs['type'])(f)
+        elif 'type' in kwargs and isinstance(kwargs['type'], list):
+            for attr in kwargs['type']:
+                f = testtools.testcase.attr(attr)(f)
+        return f
+
+    return decorator
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index 108ba70..9b2e87e 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -32,7 +32,7 @@
         except Exception:
             # at least get the core message out if something happened
             self._error_string = self.message
-        if len(args) > 0:
+        if args:
             # 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
@@ -44,6 +44,9 @@
     def __str__(self):
         return self._error_string
 
+    def __repr__(self):
+        return self._error_string
+
 
 class RestClientException(TempestException,
                           testtools.TestCase.failureException):
@@ -101,6 +104,11 @@
     message = "The requested resource is no longer available"
 
 
+class PreconditionFailed(ClientRestClientException):
+    status_code = 412
+    message = "Precondition Failed"
+
+
 class RateLimitExceeded(ClientRestClientException):
     status_code = 413
     message = "Rate limit exceeded"
@@ -259,3 +267,16 @@
 
 class VolumeBackupException(TempestException):
     message = "Volume backup %(backup_id)s failed and is in ERROR status"
+
+
+class DeleteErrorException(TempestException):
+    message = ("Resource %(resource_id)s failed to delete "
+               "and is in ERROR status")
+
+
+class InvalidTestResource(TempestException):
+    message = "%(name)s is not a valid %(type)s, or the name is ambiguous"
+
+
+class InvalidParam(TempestException):
+    message = ("Invalid Parameter passed: %(invalid_param)s")
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index 445e8bd..4fa7a7a 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -17,8 +17,12 @@
 import copy
 import importlib
 import inspect
+import sys
+import warnings
 
+from debtcollector import removals
 from oslo_log import log as logging
+import testtools
 
 from tempest.lib import auth
 from tempest.lib.common.utils import misc
@@ -29,7 +33,7 @@
 from tempest.lib.services import network
 from tempest.lib.services import volume
 
-
+warnings.simplefilter("once")
 LOG = logging.getLogger(__name__)
 
 
@@ -70,19 +74,20 @@
     :raise PluginRegistrationException: if a plugin exposes a service_version
         already defined by Tempest or another plugin.
 
-    Examples:
+    Examples::
 
-        >>> from tempest import config
-        >>> params = {}
-        >>> for service_version in available_modules():
-        >>>     service = service_version.split('.')[0]
-        >>>     params[service] = config.service_client_config(service)
-        >>> service_clients = ServiceClients(creds, identity_uri,
-        >>>                                  client_parameters=params)
+        from tempest import config
+        params = {}
+        for service_version in available_modules():
+            service = service_version.split('.')[0]
+            params[service] = config.service_client_config(service)
+        service_clients = ServiceClients(creds, identity_uri,
+                                         client_parameters=params)
     """
     extra_service_versions = set([])
     _tempest_modules = set(tempest_modules())
     plugin_services = ClientsRegistry().get_service_clients()
+    name_conflicts = []
     for plugin_name in plugin_services:
         plug_service_versions = set([x['service_version'] for x in
                                      plugin_services[plugin_name]])
@@ -94,8 +99,8 @@
                     'claimed by another one' % (plugin_name,
                                                 extra_service_versions &
                                                 plug_service_versions))
-                raise exceptions.PluginRegistrationException(
-                    name=plugin_name, detailed_error=detailed_error)
+                name_conflicts.append(exceptions.PluginRegistrationException(
+                    name=plugin_name, detailed_error=detailed_error))
             # NOTE(andreaf) Once all tempest clients are stable, the following
             # if will have to be removed.
             if not plug_service_versions.isdisjoint(
@@ -105,9 +110,14 @@
                     'claimed by a Tempest one' % (plugin_name,
                                                   _tempest_internal_modules() &
                                                   plug_service_versions))
-                raise exceptions.PluginRegistrationException(
-                    name=plugin_name, detailed_error=detailed_error)
+                name_conflicts.append(exceptions.PluginRegistrationException(
+                    name=plugin_name, detailed_error=detailed_error))
         extra_service_versions |= plug_service_versions
+    if name_conflicts:
+        LOG.error(
+            'Failed to list available modules due to name conflicts: %s',
+            name_conflicts)
+        raise testtools.MultipleExceptions(*name_conflicts)
     return _tempest_modules | extra_service_versions
 
 
@@ -125,6 +135,9 @@
                 name=plugin_name,
                 detailed_error=detailed_error % plugin_name)
         self._service_clients[plugin_name] = service_client_data
+        LOG.debug("Successfully registered plugin %s in the service client "
+                  "registry with configuration: %s", plugin_name,
+                  service_client_data)
 
     def get_service_clients(self):
         return self._service_clients
@@ -156,19 +169,19 @@
         :param kwargs: Parameters to be passed to all clients. Parameters
             values can be overwritten when clients are initialised, but
             parameters cannot be deleted.
-        :raise ImportError if the specified module_path cannot be imported
+        :raise ImportError: if the specified module_path cannot be imported
 
-        Example:
+        Example::
 
-            >>> # Get credentials and an auth_provider
-            >>> clients = ClientsFactory(
-            >>>     module_path='my_service.my_service_clients',
-            >>>     client_names=['ServiceClient1', 'ServiceClient2'],
-            >>>     auth_provider=auth_provider,
-            >>>     service='my_service',
-            >>>     region='region1')
-            >>> my_api_client = clients.MyApiClient()
-            >>> my_api_client_region2 = clients.MyApiClient(region='region2')
+            # Get credentials and an auth_provider
+            clients = ClientsFactory(
+                module_path='my_service.my_service_clients',
+                client_names=['ServiceClient1', 'ServiceClient2'],
+                auth_provider=auth_provider,
+                service='my_service',
+                region='region1')
+            my_api_client = clients.MyApiClient()
+            my_api_client_region2 = clients.MyApiClient(region='region2')
 
         """
         # Import the module. If it's not importable, the raised exception
@@ -239,13 +252,19 @@
     It hides some of the complexity from the authorization and configuration
     layers.
 
-    Examples:
+    Examples::
 
-        >>> from tempest.lib.services import clients
-        >>> johndoe = cred_provider.get_creds_by_role(['johndoe'])
-        >>> johndoe_clients = clients.ServiceClients(johndoe,
-        >>>                                                  identity_uri)
-        >>> johndoe_servers = johndoe_clients.servers_client.list_servers()
+        # johndoe is a tempest.lib.auth.Credentials type instance
+        johndoe_clients = clients.ServiceClients(johndoe, identity_uri)
+
+        # List servers in default region
+        johndoe_servers_client = johndoe_clients.compute.ServersClient()
+        johndoe_servers = johndoe_servers_client.list_servers()
+
+        # List servers in Region B
+        johndoe_servers_client_B = johndoe_clients.compute.ServersClient(
+            region='B')
+        johndoe_servers = johndoe_servers_client_B.list_servers()
 
     """
     # NOTE(andreaf) This class does not depend on tempest configuration
@@ -254,6 +273,7 @@
     # initialises this class using values from tempest CONF object. The wrapper
     # class should only be used by tests hosted in Tempest.
 
+    @removals.removed_kwarg('client_parameters')
     def __init__(self, credentials, identity_uri, region=None, scope='project',
                  disable_ssl_certificate_validation=True, ca_certs=None,
                  trace_requests='', client_parameters=None):
@@ -269,7 +289,12 @@
         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.
+        Any other client parameter should be set via ClientsRegistry.
+
+        Client parameter used to be set via client_parameters, but this is
+        deprecated, and it is actually already not honoured
+        anymore: https://launchpad.net/bugs/1680915.
+
         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
@@ -284,14 +309,18 @@
         - Volume client for 'volume' accepts 'default_volume_size'
         - Servers client from 'compute' accepts 'enable_instance_password'
 
-        Examples:
+        If Tempest configuration is used, parameters will be loaded in the
+        Registry automatically for all service client (Tempest stable ones
+        and plugins).
 
-            >>> 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)
+        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
@@ -307,15 +336,6 @@
             name, as declared in `service_clients.available_modules()` except
             for the version. Values are dictionaries of parameters that are
             going to be passed to all clients in the service client module.
-
-        Examples:
-
-            >>> params_service_x = {'param_name': 'param_value'}
-            >>> client_parameters = { 'service_x': params_service_x }
-
-            >>> params_service_y = config.service_client_config('service_y')
-            >>> client_parameters['service_y'] = params_service_y
-
         """
         self._registered_services = set([])
         self.credentials = credentials
@@ -363,6 +383,7 @@
         # Register service clients from the registry (__tempest__ and plugins)
         clients_registry = ClientsRegistry()
         plugin_service_clients = clients_registry.get_service_clients()
+        registration_errors = []
         for plugin in plugin_service_clients:
             service_clients = plugin_service_clients[plugin]
             # Each plugin returns a list of service client parameters
@@ -373,10 +394,12 @@
                 try:
                     self.register_service_client_module(**service_client)
                 except Exception:
+                    registration_errors.append(sys.exc_info())
                     LOG.exception(
                         'Failed to register service client from plugin %s '
                         'with parameters %s', plugin, service_client)
-                    raise
+        if registration_errors:
+            raise testtools.MultipleExceptions(*registration_errors)
 
     def register_service_client_module(self, name, service_version,
                                        module_path, client_names, **kwargs):
diff --git a/tempest/lib/services/compute/agents_client.py b/tempest/lib/services/compute/agents_client.py
index 169d978..408f75d 100644
--- a/tempest/lib/services/compute/agents_client.py
+++ b/tempest/lib/services/compute/agents_client.py
@@ -28,7 +28,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listbuilds
+        https://developer.openstack.org/api-ref/compute/#list-agent-builds
         """
         url = 'os-agents'
         if params:
@@ -43,7 +43,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#agentbuild
+        https://developer.openstack.org/api-ref/compute/#create-agent-build
         """
         post_body = json.dumps({'agent': kwargs})
         resp, body = self.post('os-agents', post_body)
@@ -56,7 +56,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteBuild
+        https://developer.openstack.org/api-ref/compute/#delete-agent-build
         """
         resp, body = self.delete("os-agents/%s" % agent_id)
         self.validate_response(schema.delete_agent, resp, body)
@@ -67,7 +67,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#updatebuild
+        https://developer.openstack.org/api-ref/compute/#update-agent-build
         """
         put_body = json.dumps({'para': kwargs})
         resp, body = self.put('os-agents/%s' % agent_id, put_body)
diff --git a/tempest/lib/services/compute/aggregates_client.py b/tempest/lib/services/compute/aggregates_client.py
index c1a6c8c..713d7a3 100644
--- a/tempest/lib/services/compute/aggregates_client.py
+++ b/tempest/lib/services/compute/aggregates_client.py
@@ -42,7 +42,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createAggregate
+        https://developer.openstack.org/api-ref/compute/#create-aggregate
         """
         post_body = json.dumps({'aggregate': kwargs})
         resp, body = self.post('os-aggregates', post_body)
@@ -56,7 +56,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#updateAggregate
+        https://developer.openstack.org/api-ref/compute/#update-aggregate
         """
         put_body = json.dumps({'aggregate': kwargs})
         resp, body = self.put('os-aggregates/%s' % aggregate_id, put_body)
@@ -88,7 +88,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#addHost
+        https://developer.openstack.org/api-ref/compute/#add-host
         """
         post_body = json.dumps({'add_host': kwargs})
         resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
@@ -102,7 +102,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#removeAggregateHost
+        https://developer.openstack.org/api-ref/compute/#remove-host
         """
         post_body = json.dumps({'remove_host': kwargs})
         resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
@@ -116,7 +116,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#addAggregateMetadata
+        https://developer.openstack.org/api-ref/compute/#create-or-update-aggregate-metadata
         """
         post_body = json.dumps({'set_metadata': kwargs})
         resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
diff --git a/tempest/lib/services/compute/baremetal_nodes_client.py b/tempest/lib/services/compute/baremetal_nodes_client.py
index 06dc369..e44c195 100644
--- a/tempest/lib/services/compute/baremetal_nodes_client.py
+++ b/tempest/lib/services/compute/baremetal_nodes_client.py
@@ -25,7 +25,12 @@
     """Tests Baremetal API"""
 
     def list_baremetal_nodes(self, **params):
-        """List all baremetal nodes."""
+        """List all baremetal nodes.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#list-bare-metal-nodes
+        """
         url = 'os-baremetal-nodes'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -35,7 +40,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def show_baremetal_node(self, baremetal_node_id):
-        """Return the details of a single baremetal node."""
+        """Show the details of a single baremetal node.
+
+        For more information, please refer to the official API reference:
+        https://developer.openstack.org/api-ref/compute/#show-bare-metal-node-details
+        """
         url = 'os-baremetal-nodes/%s' % baremetal_node_id
         resp, body = self.get(url)
         body = json.loads(body)
diff --git a/tempest/lib/services/compute/fixed_ips_client.py b/tempest/lib/services/compute/fixed_ips_client.py
index 682ee86..968646c 100644
--- a/tempest/lib/services/compute/fixed_ips_client.py
+++ b/tempest/lib/services/compute/fixed_ips_client.py
@@ -34,7 +34,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#reserveIP
+        https://developer.openstack.org/api-ref/compute/#reserve-or-release-a-fixed-ip
         """
         url = "os-fixed-ips/%s/action" % fixed_ip
         resp, body = self.post(url, json.dumps(kwargs))
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index a83c68b..0fb1991 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -32,7 +32,8 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listFlavors
+        https://developer.openstack.org/api-ref/compute/#list-flavors
+        https://developer.openstack.org/api-ref/compute/#list-flavors-with-details
         """
         url = 'flavors'
         _schema = schema.list_flavors
@@ -53,7 +54,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#showFlavor
+        https://developer.openstack.org/api-ref/compute/#show-flavor-details
         """
         resp, body = self.get("flavors/%s" % flavor_id)
         body = json.loads(body)
@@ -65,7 +66,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createFlavor
+        https://developer.openstack.org/api-ref/compute/#create-flavor
         """
         if 'ephemeral' in kwargs:
             kwargs['OS-FLV-EXT-DATA:ephemeral'] = kwargs.pop('ephemeral')
@@ -84,7 +85,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteFlavor
+        https://developer.openstack.org/api-ref/compute/#delete-flavor
         """
         resp, body = self.delete("flavors/{0}".format(flavor_id))
         self.validate_response(schema.delete_flavor, resp, body)
@@ -110,7 +111,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createFlavorExtraSpec
+        https://developer.openstack.org/api-ref/compute/#create-extra-specs-for-a-flavor
         """
         post_body = json.dumps({'extra_specs': kwargs})
         resp, body = self.post('flavors/%s/os-extra_specs' % flavor_id,
@@ -125,7 +126,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listFlavorExtraSpecs
+        https://developer.openstack.org/api-ref/compute/#list-extra-specs-for-a-flavor
         """
         resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id)
         body = json.loads(body)
@@ -138,7 +139,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#showFlavorExtraSpec
+        https://developer.openstack.org/api-ref/compute/#show-an-extra-spec-for-a-flavor
         """
         resp, body = self.get('flavors/%s/os-extra_specs/%s' % (flavor_id,
                               key))
@@ -153,7 +154,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#updateFlavorExtraSpec
+        https://developer.openstack.org/api-ref/compute/#update-an-extra-spec-for-a-flavor
         """
         resp, body = self.put('flavors/%s/os-extra_specs/%s' %
                               (flavor_id, key), json.dumps(kwargs))
@@ -170,7 +171,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteFlavorExtraSpec
+        https://developer.openstack.org/api-ref/compute/#delete-an-extra-spec-for-a-flavor
         """
         resp, body = self.delete('flavors/%s/os-extra_specs/%s' %
                                  (flavor_id, key))
@@ -182,7 +183,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listFlavorAccess
+        https://developer.openstack.org/api-ref/compute/#list-flavor-access-information-for-given-flavor
         """
         resp, body = self.get('flavors/%s/os-flavor-access' % flavor_id)
         body = json.loads(body)
@@ -195,7 +196,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#addFlavorAccess
+        https://developer.openstack.org/api-ref/compute/#add-flavor-access-to-tenant-addtenantaccess-action
         """
         post_body = {
             'addTenantAccess': {
@@ -214,7 +215,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#removeFlavorAccess
+        https://developer.openstack.org/api-ref/compute/#remove-flavor-access-from-tenant-removetenantaccess-action
         """
         post_body = {
             'removeTenantAccess': {
diff --git a/tempest/lib/services/compute/floating_ips_client.py b/tempest/lib/services/compute/floating_ips_client.py
index 744e14c..5364d97 100644
--- a/tempest/lib/services/compute/floating_ips_client.py
+++ b/tempest/lib/services/compute/floating_ips_client.py
@@ -29,7 +29,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listfloatingipsObject
+        https://developer.openstack.org/api-ref/compute/#list-floating-ip-addresses
         """
         url = 'os-floating-ips'
         if params:
@@ -45,7 +45,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#showFloatingIP
+        https://developer.openstack.org/api-ref/compute/#show-floating-ip-address-details
         """
         url = "os-floating-ips/%s" % floating_ip_id
         resp, body = self.get(url)
@@ -58,7 +58,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createFloatingIP
+        https://developer.openstack.org/api-ref/compute/#create-allocate-floating-ip-address
         """
         url = 'os-floating-ips'
         post_body = json.dumps(kwargs)
@@ -72,7 +72,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteFloatingIP
+        https://developer.openstack.org/api-ref/compute/#delete-deallocate-floating-ip-address
         """
         url = "os-floating-ips/%s" % floating_ip_id
         resp, body = self.delete(url)
diff --git a/tempest/lib/services/compute/hosts_client.py b/tempest/lib/services/compute/hosts_client.py
index 1b93b00..1fdd907 100644
--- a/tempest/lib/services/compute/hosts_client.py
+++ b/tempest/lib/services/compute/hosts_client.py
@@ -23,7 +23,12 @@
 class HostsClient(base_compute_client.BaseComputeClient):
 
     def list_hosts(self, **params):
-        """List all hosts."""
+        """List all hosts.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#list-hosts
+        """
 
         url = 'os-hosts'
         if params:
@@ -47,7 +52,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#enablehost
+        https://developer.openstack.org/api-ref/compute/#update-host-status
         """
 
         request_body = {
diff --git a/tempest/lib/services/compute/images_client.py b/tempest/lib/services/compute/images_client.py
index e937c13..86bea9e 100644
--- a/tempest/lib/services/compute/images_client.py
+++ b/tempest/lib/services/compute/images_client.py
@@ -29,7 +29,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createImage
+        https://developer.openstack.org/api-ref/compute/#create-image-createimage-action
         """
 
         post_body = {'createImage': kwargs}
@@ -44,7 +44,8 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listImages
+        https://developer.openstack.org/api-ref/compute/#list-images
+        https://developer.openstack.org/api-ref/compute/#list-images-with-details
         """
         url = 'images'
         _schema = schema.list_images
@@ -85,7 +86,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createImageMetadata
+        https://developer.openstack.org/api-ref/compute/#update-image-metadata
         """
         post_body = json.dumps({'metadata': meta})
         resp, body = self.put('images/%s/metadata' % image_id, post_body)
@@ -98,7 +99,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#updateImageMetadata
+        https://developer.openstack.org/api-ref/compute/#create-image-metadata
         """
         post_body = json.dumps({'metadata': meta})
         resp, body = self.post('images/%s/metadata' % image_id, post_body)
@@ -118,7 +119,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#setImageMetadataItem
+        https://developer.openstack.org/api-ref/compute/#create-or-update-image-metadata-item
         """
         post_body = json.dumps({'meta': meta})
         resp, body = self.put('images/%s/metadata/%s' % (image_id, key),
diff --git a/tempest/lib/services/compute/interfaces_client.py b/tempest/lib/services/compute/interfaces_client.py
index 37157a4..d7c3107 100644
--- a/tempest/lib/services/compute/interfaces_client.py
+++ b/tempest/lib/services/compute/interfaces_client.py
@@ -33,7 +33,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createAttachInterface
+        https://developer.openstack.org/api-ref/compute/#create-interface
         """
         post_body = {'interfaceAttachment': kwargs}
         post_body = json.dumps(post_body)
diff --git a/tempest/lib/services/compute/keypairs_client.py b/tempest/lib/services/compute/keypairs_client.py
index c3f1781..5215fca 100644
--- a/tempest/lib/services/compute/keypairs_client.py
+++ b/tempest/lib/services/compute/keypairs_client.py
@@ -32,7 +32,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listKeypairs
+        https://developer.openstack.org/api-ref/compute/#list-keypairs
         """
         url = 'os-keypairs'
         if params:
@@ -48,7 +48,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#showKeypair
+        https://developer.openstack.org/api-ref/compute/#show-keypair-details
         """
         url = "os-keypairs/%s" % keypair_name
         if params:
@@ -64,7 +64,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createKeypair
+        https://developer.openstack.org/api-ref/compute/#create-or-import-keypair
         """
         post_body = json.dumps({'keypair': kwargs})
         resp, body = self.post("os-keypairs", body=post_body)
@@ -78,7 +78,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteKeypair
+        https://developer.openstack.org/api-ref/compute/#delete-keypair
         """
         url = "os-keypairs/%s" % keypair_name
         if params:
diff --git a/tempest/lib/services/compute/migrations_client.py b/tempest/lib/services/compute/migrations_client.py
index 375cbda..68c8f3f 100644
--- a/tempest/lib/services/compute/migrations_client.py
+++ b/tempest/lib/services/compute/migrations_client.py
@@ -32,7 +32,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listMigrations
+        https://developer.openstack.org/api-ref/compute/#list-migrations
         """
 
         url = 'os-migrations'
diff --git a/tempest/lib/services/compute/quota_classes_client.py b/tempest/lib/services/compute/quota_classes_client.py
index 523a306..0fe9868 100644
--- a/tempest/lib/services/compute/quota_classes_client.py
+++ b/tempest/lib/services/compute/quota_classes_client.py
@@ -35,9 +35,8 @@
     def update_quota_class_set(self, quota_class_id, **kwargs):
         """Update the quota class's limits for one or more resources.
 
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#updatequota
+        # NOTE: Current api-site doesn't contain this API description.
+        # LP: https://bugs.launchpad.net/nova/+bug/1602400
         """
         post_body = json.dumps({'quota_class_set': kwargs})
 
diff --git a/tempest/lib/services/compute/quotas_client.py b/tempest/lib/services/compute/quotas_client.py
index a2b0397..daf4bc0 100644
--- a/tempest/lib/services/compute/quotas_client.py
+++ b/tempest/lib/services/compute/quotas_client.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import quotas as schema
 from tempest.lib.common import rest_client
@@ -22,15 +23,29 @@
 
 class QuotasClient(base_compute_client.BaseComputeClient):
 
-    def show_quota_set(self, tenant_id, user_id=None):
-        """List the quota set for a tenant."""
+    def show_quota_set(self, tenant_id, user_id=None, detail=False):
+        """List the quota set for a tenant.
 
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-compute-v2.1.html/#show-a-quota
+        http://developer.openstack.org/api-ref-compute-v2.1.html/#show-the-detail-of-quota
+        """
+
+        params = {}
         url = 'os-quota-sets/%s' % tenant_id
+        if detail:
+            url += '/detail'
         if user_id:
-            url += '?user_id=%s' % user_id
+            params.update({'user_id': user_id})
+        if params:
+            url += '?%s' % urllib.urlencode(params)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(schema.get_quota_set, resp, body)
+        if detail:
+            self.validate_response(schema.get_quota_set_details, resp, body)
+        else:
+            self.validate_response(schema.get_quota_set, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_default_quota_set(self, tenant_id):
@@ -47,7 +62,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#updateQuota
+        https://developer.openstack.org/api-ref/compute/#update-quotas
         """
 
         post_body = json.dumps({'quota_set': kwargs})
diff --git a/tempest/lib/services/compute/security_group_default_rules_client.py b/tempest/lib/services/compute/security_group_default_rules_client.py
index e2d3c98..70cab88 100644
--- a/tempest/lib/services/compute/security_group_default_rules_client.py
+++ b/tempest/lib/services/compute/security_group_default_rules_client.py
@@ -28,7 +28,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createSecGroupDefaultRule
+        https://developer.openstack.org/api-ref/compute/#create-default-security-group-rule
         """
         post_body = json.dumps({'security_group_default_rule': kwargs})
         url = 'os-security-group-default-rules'
diff --git a/tempest/lib/services/compute/security_group_rules_client.py b/tempest/lib/services/compute/security_group_rules_client.py
index 3121e24..710bfab 100644
--- a/tempest/lib/services/compute/security_group_rules_client.py
+++ b/tempest/lib/services/compute/security_group_rules_client.py
@@ -28,7 +28,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createSecGroupRule
+        https://developer.openstack.org/api-ref/compute/#create-security-group-rule
         """
         post_body = json.dumps({'security_group_rule': kwargs})
         url = 'os-security-group-rules'
diff --git a/tempest/lib/services/compute/security_groups_client.py b/tempest/lib/services/compute/security_groups_client.py
index a247346..b525f68 100644
--- a/tempest/lib/services/compute/security_groups_client.py
+++ b/tempest/lib/services/compute/security_groups_client.py
@@ -30,7 +30,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listSecGroups
+        https://developer.openstack.org/api-ref/compute/#list-security-groups
         """
 
         url = 'os-security-groups'
@@ -47,7 +47,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#showSecGroup
+        https://developer.openstack.org/api-ref/compute/#show-security-group-details
         """
         url = "os-security-groups/%s" % security_group_id
         resp, body = self.get(url)
@@ -60,7 +60,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createSecGroup
+        https://developer.openstack.org/api-ref/compute/#create-security-group
         """
         post_body = json.dumps({'security_group': kwargs})
         resp, body = self.post('os-security-groups', post_body)
@@ -73,7 +73,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#updateSecGroup
+        https://developer.openstack.org/api-ref/compute/#update-security-group
         """
         post_body = json.dumps({'security_group': kwargs})
         resp, body = self.put('os-security-groups/%s' % security_group_id,
@@ -87,7 +87,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteSecGroup
+        https://developer.openstack.org/api-ref/compute/#delete-security-group
         """
         resp, body = self.delete(
             'os-security-groups/%s' % security_group_id)
diff --git a/tempest/lib/services/compute/server_groups_client.py b/tempest/lib/services/compute/server_groups_client.py
index 9ba8d38..03cd645 100644
--- a/tempest/lib/services/compute/server_groups_client.py
+++ b/tempest/lib/services/compute/server_groups_client.py
@@ -17,29 +17,36 @@
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
+from tempest.lib.api_schema.response.compute.v2_13 import servers as schemav213
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
 
 
 class ServerGroupsClient(base_compute_client.BaseComputeClient):
 
+    schema_versions_info = [
+        {'min': None, 'max': '2.12', 'schema': schema},
+        {'min': '2.13', 'max': None, 'schema': schemav213}]
+
     def create_server_group(self, **kwargs):
         """Create the server group.
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createServerGroup
+        https://developer.openstack.org/api-ref/compute/#create-server-group
         """
         post_body = json.dumps({'server_group': kwargs})
         resp, body = self.post('os-server-groups', post_body)
 
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.create_show_server_group, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_server_group(self, server_group_id):
         """Delete the given server-group."""
         resp, body = self.delete("os-server-groups/%s" % server_group_id)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.delete_server_group, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -47,6 +54,7 @@
         """List the server-groups."""
         resp, body = self.get("os-server-groups")
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.list_server_groups, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -54,5 +62,6 @@
         """Get the details of given server_group."""
         resp, body = self.get("os-server-groups/%s" % server_group_id)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.create_show_server_group, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 50ce32e..598d5a6 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -1,5 +1,6 @@
 # Copyright 2012 OpenStack Foundation
 # Copyright 2013 Hewlett-Packard Development Company, L.P.
+# Copyright 2017 AT&T Corp.
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -19,24 +20,34 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
+from tempest.lib.api_schema.response.compute.v2_1 import \
+    security_groups as security_groups_schema
 from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
 from tempest.lib.api_schema.response.compute.v2_16 import servers as schemav216
 from tempest.lib.api_schema.response.compute.v2_19 import servers as schemav219
 from tempest.lib.api_schema.response.compute.v2_26 import servers as schemav226
 from tempest.lib.api_schema.response.compute.v2_3 import servers as schemav23
+from tempest.lib.api_schema.response.compute.v2_47 import servers as schemav247
+from tempest.lib.api_schema.response.compute.v2_48 import servers as schemav248
+from tempest.lib.api_schema.response.compute.v2_6 import servers as schemav26
 from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
 
 
 class ServersClient(base_compute_client.BaseComputeClient):
+    """Service client for the resource /servers"""
+
     schema_versions_info = [
         {'min': None, 'max': '2.2', 'schema': schema},
-        {'min': '2.3', 'max': '2.8', 'schema': schemav23},
+        {'min': '2.3', 'max': '2.5', 'schema': schemav23},
+        {'min': '2.6', 'max': '2.8', 'schema': schemav26},
         {'min': '2.9', 'max': '2.15', 'schema': schemav29},
         {'min': '2.16', 'max': '2.18', 'schema': schemav216},
         {'min': '2.19', 'max': '2.25', 'schema': schemav219},
-        {'min': '2.26', 'max': None, 'schema': schemav226}]
+        {'min': '2.26', 'max': '2.46', 'schema': schemav226},
+        {'min': '2.47', 'max': '2.47', 'schema': schemav247},
+        {'min': '2.48', 'max': None, 'schema': schemav248}]
 
     def __init__(self, auth_provider, service, region,
                  enable_instance_password=True, **kwargs):
@@ -94,7 +105,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#updateServer
+        https://developer.openstack.org/api-ref/compute/#update-server
 
         Most parameters except the following are passed to the API without
         any changes.
@@ -128,7 +139,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteServer
+        https://developer.openstack.org/api-ref/compute/#delete-server
         """
         resp, body = self.delete("servers/%s" % server_id)
         self.validate_response(schema.delete_server, resp, body)
@@ -139,8 +150,8 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listServers
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listDetailServers
+        https://developer.openstack.org/api-ref/compute/#list-servers
+        https://developer.openstack.org/api-ref/compute/#list-servers-detailed
         """
 
         url = 'servers'
@@ -163,7 +174,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#list-ips
+        https://developer.openstack.org/api-ref/compute/#list-ips
         """
         resp, body = self.get("servers/%s/ips" % server_id)
         body = json.loads(body)
@@ -194,7 +205,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createBackup
+        https://developer.openstack.org/api-ref/compute/#create-server-back-up-createbackup-action
         """
         return self.action(server_id, "createBackup", **kwargs)
 
@@ -203,7 +214,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#changePassword
+        https://developer.openstack.org/api-ref/compute/#change-administrative-password-changepassword-action
         """
         return self.action(server_id, 'changePassword', **kwargs)
 
@@ -231,7 +242,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#reboot
+        https://developer.openstack.org/api-ref/compute/#reboot-server-reboot-action
         """
         return self.action(server_id, 'reboot', **kwargs)
 
@@ -240,7 +251,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#rebuild
+        https://developer.openstack.org/api-ref/compute/#rebuild-server-rebuild-action
 
         Most parameters except the following are passed to the API without
         any changes.
@@ -262,7 +273,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#resize
+        https://developer.openstack.org/api-ref/compute/#resize-server-resize-action
 
         Most parameters except the following are passed to the API without
         any changes.
@@ -278,7 +289,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#confirmResize
+        https://developer.openstack.org/api-ref/compute/#confirm-resized-server-confirmresize-action
         """
         return self.action(server_id, 'confirmResize',
                            schema.server_actions_confirm_resize,
@@ -289,7 +300,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#revertResize
+        https://developer.openstack.org/api-ref/compute/#revert-resized-server-revertresize-action
         """
         return self.action(server_id, 'revertResize', **kwargs)
 
@@ -298,7 +309,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listServerMetadata
+        https://developer.openstack.org/api-ref/compute/#list-all-metadata
         """
         resp, body = self.get("servers/%s/metadata" % server_id)
         body = json.loads(body)
@@ -310,7 +321,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createServerMetadata
+        https://developer.openstack.org/api-ref/compute/#create-or-replace-metadata-items
         """
         if no_metadata_field:
             post_body = ""
@@ -327,7 +338,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#updateServerMetadata
+        https://developer.openstack.org/api-ref/compute/#update-metadata-items
         """
         post_body = json.dumps({'metadata': meta})
         resp, body = self.post('servers/%s/metadata' % server_id,
@@ -342,7 +353,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#showServerMetadataItem
+        https://developer.openstack.org/api-ref/compute/#show-metadata-item-details
         """
         resp, body = self.get("servers/%s/metadata/%s" % (server_id, key))
         body = json.loads(body)
@@ -355,7 +366,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#setServerMetadataItem
+        https://developer.openstack.org/api-ref/compute/#create-or-update-metadata-item
         """
         post_body = json.dumps({'meta': meta})
         resp, body = self.put('servers/%s/metadata/%s' % (server_id, key),
@@ -370,7 +381,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteServerMetadataItem
+        https://developer.openstack.org/api-ref/compute/#delete-metadata-item
         """
         resp, body = self.delete("servers/%s/metadata/%s" %
                                  (server_id, key))
@@ -383,7 +394,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#stop
+        https://developer.openstack.org/api-ref/compute/#stop-server-os-stop-action
         """
         return self.action(server_id, 'os-stop', **kwargs)
 
@@ -392,7 +403,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#start
+        https://developer.openstack.org/api-ref/compute/#start-server-os-start-action
         """
         return self.action(server_id, 'os-start', **kwargs)
 
@@ -401,7 +412,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#attachVolume
+        https://developer.openstack.org/api-ref/compute/#attach-a-volume-to-an-instance
         """
         post_body = json.dumps({'volumeAttachment': kwargs})
         resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
@@ -424,7 +435,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteVolumeAttachment
+        https://developer.openstack.org/api-ref/compute/#detach-a-volume-from-an-instance
         """
         resp, body = self.delete('servers/%s/os-volume_attachments/%s' %
                                  (server_id, volume_id))
@@ -436,7 +447,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#getVolumeAttachmentDetails
+        https://developer.openstack.org/api-ref/compute/#show-a-detail-of-a-volume-attachment
         """
         resp, body = self.get('servers/%s/os-volume_attachments/%s' % (
             server_id, volume_id))
@@ -449,7 +460,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listVolumeAttachments
+        https://developer.openstack.org/api-ref/compute/#list-volume-attachments-for-an-instance
         """
         resp, body = self.get('servers/%s/os-volume_attachments' % (
             server_id))
@@ -462,12 +473,8 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#addSecurityGroup
+        https://developer.openstack.org/api-ref/compute/#add-security-group-to-a-server-addsecuritygroup-action
         """
-        # TODO(oomichi): The api-site doesn't contain this API description.
-        # So the above should be changed to the api-site link after
-        # adding the description on the api-site.
-        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524199
         return self.action(server_id, 'addSecurityGroup', **kwargs)
 
     def remove_security_group(self, server_id, **kwargs):
@@ -475,12 +482,8 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#removeSecurityGroup
+        https://developer.openstack.org/api-ref/compute/#remove-security-group-from-a-server-removesecuritygroup-action
         """
-        # TODO(oomichi): The api-site doesn't contain this API description.
-        # So the above should be changed to the api-site link after
-        # adding the description on the api-site.
-        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524199
         return self.action(server_id, 'removeSecurityGroup', **kwargs)
 
     def live_migrate_server(self, server_id, **kwargs):
@@ -488,7 +491,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#migrateLive
+        https://developer.openstack.org/api-ref/compute/#live-migrate-server-os-migratelive-action
         """
         return self.action(server_id, 'os-migrateLive', **kwargs)
 
@@ -497,7 +500,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#migrate
+        https://developer.openstack.org/api-ref/compute/#migrate-server-migrate-action
         """
         return self.action(server_id, 'migrate', **kwargs)
 
@@ -506,7 +509,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#lock
+        https://developer.openstack.org/api-ref/compute/#lock-server-lock-action
         """
         return self.action(server_id, 'lock', **kwargs)
 
@@ -515,7 +518,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#unlock
+        https://developer.openstack.org/api-ref/compute/#unlock-server-unlock-action
         """
         return self.action(server_id, 'unlock', **kwargs)
 
@@ -524,7 +527,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#suspend
+        https://developer.openstack.org/api-ref/compute/#suspend-server-suspend-action
         """
         return self.action(server_id, 'suspend', **kwargs)
 
@@ -533,7 +536,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#resume
+        https://developer.openstack.org/api-ref/compute/#resume-suspended-server-resume-action
         """
         return self.action(server_id, 'resume', **kwargs)
 
@@ -542,7 +545,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#pause
+        https://developer.openstack.org/api-ref/compute/#pause-server-pause-action
         """
         return self.action(server_id, 'pause', **kwargs)
 
@@ -551,7 +554,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#unpause
+        https://developer.openstack.org/api-ref/compute/#unpause-server-unpause-action
         """
         return self.action(server_id, 'unpause', **kwargs)
 
@@ -560,7 +563,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#resetState
+        https://developer.openstack.org/api-ref/compute/#reset-server-state-os-resetstate-action
         """
         return self.action(server_id, 'os-resetState', **kwargs)
 
@@ -569,7 +572,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#shelve
+        https://developer.openstack.org/api-ref/compute/#shelve-server-shelve-action
         """
         return self.action(server_id, 'shelve', **kwargs)
 
@@ -578,7 +581,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#unshelve
+        https://developer.openstack.org/api-ref/compute/#unshelve-restore-shelved-server-unshelve-action
         """
         return self.action(server_id, 'unshelve', **kwargs)
 
@@ -587,7 +590,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#shelveOffload
+        https://developer.openstack.org/api-ref/compute/#shelf-offload-remove-server-shelveoffload-action
         """
         return self.action(server_id, 'shelveOffload', **kwargs)
 
@@ -596,11 +599,34 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#getConsoleOutput
+        https://developer.openstack.org/api-ref/compute/#show-console-output-os-getconsoleoutput-action
         """
         return self.action(server_id, 'os-getConsoleOutput',
                            schema.get_console_output, **kwargs)
 
+    def get_remote_console(self, server_id, console_type, protocol, **kwargs):
+        """Get a remote console.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        TODO (markus_z) The api-ref for that isn't yet available, update this
+        here when the docs in Nova are updated. The old API is at
+        http://developer.openstack.org/api-ref/compute/#get-serial-console-os-getserialconsole-action
+        """
+        param = {
+            'remote_console': {
+                'type': console_type,
+                'protocol': protocol,
+            }
+        }
+        post_body = json.dumps(param)
+        resp, body = self.post("servers/%s/remote-consoles" % server_id,
+                               post_body)
+        body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.get_remote_consoles, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
     def list_virtual_interfaces(self, server_id):
         """List the virtual interfaces used in an instance."""
         resp, body = self.get('/'.join(['servers', server_id,
@@ -614,7 +640,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#rescue
+        https://developer.openstack.org/api-ref/compute/#rescue-server-rescue-action
         """
         if self.enable_instance_password:
             rescue_schema = schema.rescue_server_with_admin_pass
@@ -627,14 +653,17 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#unrescue
+        https://developer.openstack.org/api-ref/compute/#unrescue-server-unrescue-action
         """
         return self.action(server_id, 'unrescue')
 
     def show_server_diagnostics(self, server_id):
         """Get the usage data for a server."""
         resp, body = self.get("servers/%s/diagnostics" % server_id)
-        return rest_client.ResponseBody(resp, json.loads(body))
+        body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.show_server_diagnostics, resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_instance_actions(self, server_id):
         """List the provided server action."""
@@ -657,7 +686,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#forceDelete
+        https://developer.openstack.org/api-ref/compute/#force-delete-server-forcedelete-action
         """
         return self.action(server_id, 'forceDelete', **kwargs)
 
@@ -666,7 +695,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#restore
+        https://developer.openstack.org/api-ref/compute/#restore-soft-deleted-instance-restore-action
         """
         return self.action(server_id, 'restore', **kwargs)
 
@@ -675,7 +704,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#resetNetwork
+        https://developer.openstack.org/api-ref/compute/#reset-networking-on-a-server-resetnetwork-action
         """
         return self.action(server_id, 'resetNetwork', **kwargs)
 
@@ -684,7 +713,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#injectNetworkInfo
+        https://developer.openstack.org/api-ref/compute/#inject-network-information-injectnetworkinfo-action
         """
         return self.action(server_id, 'injectNetworkInfo', **kwargs)
 
@@ -693,7 +722,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#getVNCConsole
+        https://developer.openstack.org/api-ref/compute/#get-vnc-console-os-getvncconsole-action
         """
         return self.action(server_id, "os-getVNCConsole",
                            schema.get_vnc_console, **kwargs)
@@ -703,7 +732,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#addFixedIp
+        https://developer.openstack.org/api-ref/compute/#add-associate-fixed-ip-addfixedip-action
         """
         return self.action(server_id, 'addFixedIp', **kwargs)
 
@@ -712,6 +741,124 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#removeFixedIp
+        https://developer.openstack.org/api-ref/compute/#remove-disassociate-fixed-ip-removefixedip-action
         """
         return self.action(server_id, 'removeFixedIp', **kwargs)
+
+    def list_security_groups_by_server(self, server_id):
+        """Lists security groups for a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#list-security-groups-by-server
+        """
+        resp, body = self.get("servers/%s/os-security-groups" % server_id)
+        body = json.loads(body)
+        self.validate_response(security_groups_schema.list_security_groups,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_tags(self, server_id):
+        """Lists all tags for a server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#list-tags
+        """
+        url = 'servers/%s/tags' % server_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.list_tags, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_all_tags(self, server_id, tags):
+        """Replaces all tags on specified server with the new set of tags.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#replace-tags
+
+        :param tags: List of tags to replace current server tags with.
+        """
+        url = 'servers/%s/tags' % server_id
+        put_body = {'tags': tags}
+        resp, body = self.put(url, json.dumps(put_body))
+        body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.update_all_tags, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_all_tags(self, server_id):
+        """Deletes all tags from the specified server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#delete-all-tags
+        """
+        url = 'servers/%s/tags' % server_id
+        resp, body = self.delete(url)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.delete_all_tags, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def check_tag_existence(self, server_id, tag):
+        """Checks tag existence on the server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#check-tag-existence
+
+        :param tag: Check for existence of tag on specified server.
+        """
+        url = 'servers/%s/tags/%s' % (server_id, tag)
+        resp, body = self.get(url)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.check_tag_existence, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_tag(self, server_id, tag):
+        """Adds a single tag to the server if server has no specified tag.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#add-a-single-tag
+
+        :param tag: Tag to be added to the specified server.
+        """
+        url = 'servers/%s/tags/%s' % (server_id, tag)
+        resp, body = self.put(url, None)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.update_tag, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_tag(self, server_id, tag):
+        """Deletes a single tag from the specified server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#delete-a-single-tag
+
+        :param tag: Tag to be removed from the specified server.
+        """
+        url = 'servers/%s/tags/%s' % (server_id, tag)
+        resp, body = self.delete(url)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.delete_tag, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def evacuate_server(self, server_id, **kwargs):
+        """Evacuate the given server.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#evacuate-server-evacuate-action
+        """
+        if self.enable_instance_password:
+            evacuate_schema = schema.evacuate_server_with_admin_pass
+        else:
+            evacuate_schema = schema.evacuate_server
+
+        return self.action(server_id, 'evacuate',
+                           evacuate_schema,
+                           **kwargs)
diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py
index 0dbd1b2..b046c35 100644
--- a/tempest/lib/services/compute/services_client.py
+++ b/tempest/lib/services/compute/services_client.py
@@ -18,18 +18,24 @@
 from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import services as schema
+from tempest.lib.api_schema.response.compute.v2_11 import services \
+    as schemav211
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
 
 
 class ServicesClient(base_compute_client.BaseComputeClient):
 
+    schema_versions_info = [
+        {'min': None, 'max': '2.10', 'schema': schema},
+        {'min': '2.11', 'max': None, 'schema': schemav211}]
+
     def list_services(self, **params):
         """Lists all running Compute services for a tenant.
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listServices
+        https://developer.openstack.org/api-ref/compute/#list-compute-services
         """
         url = 'os-services'
         if params:
@@ -37,7 +43,8 @@
 
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(schema.list_services, resp, body)
+        _schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(_schema.list_services, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def enable_service(self, **kwargs):
@@ -45,7 +52,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#enableScheduling
+        https://developer.openstack.org/api-ref/compute/#enable-scheduling-for-a-compute-service
         """
         post_body = json.dumps(kwargs)
         resp, body = self.put('os-services/enable', post_body)
@@ -58,10 +65,38 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#disableScheduling
+        https://developer.openstack.org/api-ref/compute/#disable-scheduling-for-a-compute-service
         """
         post_body = json.dumps(kwargs)
         resp, body = self.put('os-services/disable', post_body)
         body = json.loads(body)
         self.validate_response(schema.enable_disable_service, resp, body)
         return rest_client.ResponseBody(resp, body)
+
+    def disable_log_reason(self, **kwargs):
+        """Disables scheduling for a Compute service and logs reason.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#disable-scheduling-for-a-compute-service-and-log-disabled-reason
+        """
+        post_body = json.dumps(kwargs)
+        resp, body = self.put('os-services/disable-log-reason', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.disable_log_reason, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_forced_down(self, **kwargs):
+        """Set or unset ``forced_down`` flag for the service.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#update-forced-down
+        """
+        post_body = json.dumps(kwargs)
+        resp, body = self.put('os-services/force-down', post_body)
+        body = json.loads(body)
+        # NOTE: Use schemav211.update_forced_down directly because there is no
+        # update_forced_down schema for <2.11.
+        self.validate_response(schemav211.update_forced_down, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/snapshots_client.py b/tempest/lib/services/compute/snapshots_client.py
index fde5288..df8d6fb 100644
--- a/tempest/lib/services/compute/snapshots_client.py
+++ b/tempest/lib/services/compute/snapshots_client.py
@@ -29,7 +29,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createSnapshot
+        https://developer.openstack.org/api-ref/compute/#create-snapshot
         """
         post_body = {
             'volume_id': volume_id
@@ -49,6 +49,12 @@
         return rest_client.ResponseBody(resp, body)
 
     def list_snapshots(self, detail=False, params=None):
+        """List snapshots.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#list-snapshots
+        """
         url = 'os-snapshots'
 
         if detail:
diff --git a/tempest/lib/services/compute/tenant_usages_client.py b/tempest/lib/services/compute/tenant_usages_client.py
index d0eb1c9..ade60e5 100644
--- a/tempest/lib/services/compute/tenant_usages_client.py
+++ b/tempest/lib/services/compute/tenant_usages_client.py
@@ -28,7 +28,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/compute/#list-tenant-usage-for-all-tenants
+        https://developer.openstack.org/api-ref/compute/#list-tenant-usage-statistics-for-all-tenants
         """
         url = 'os-simple-tenant-usage'
         if params:
@@ -44,7 +44,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/compute/#show-usage-details-for-tenant
+        https://developer.openstack.org/api-ref/compute/#show-usage-statistics-for-tenant
         """
         url = 'os-simple-tenant-usage/%s' % tenant_id
         if params:
diff --git a/tempest/lib/services/compute/versions_client.py b/tempest/lib/services/compute/versions_client.py
index b2052c3..8fbb136 100644
--- a/tempest/lib/services/compute/versions_client.py
+++ b/tempest/lib/services/compute/versions_client.py
@@ -12,10 +12,9 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-import re
+import time
 
 from oslo_serialization import jsonutils as json
-from six.moves import urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import versions as schema
 from tempest.lib.common import rest_client
@@ -24,22 +23,15 @@
 
 class VersionsClient(base_compute_client.BaseComputeClient):
 
-    def _get_base_version_url(self):
-        # NOTE: The URL which is got from keystone's catalog contains
-        # API version and project-id like "/app-name/v2/{project-id}" or
-        # "/v2/{project-id}", but we need to access the URL which doesn't
-        # contain API version for getting API versions. For that, here
-        # should use raw_request() instead of get().
-        endpoint = self.base_url
-        url = urllib.parse.urlsplit(endpoint)
-        new_path = re.split(r'(^|/)+v\d+(\.\d+)?', url.path)[0]
-        url = list(url)
-        url[2] = new_path + '/'
-        return urllib.parse.urlunsplit(url)
-
     def list_versions(self):
         version_url = self._get_base_version_url()
+
+        start = time.time()
         resp, body = self.raw_request(version_url, 'GET')
+        end = time.time()
+        self._log_request('GET', version_url, resp, secs=(end - start),
+                          resp_body=body)
+
         self._error_checker(resp, body)
         body = json.loads(body)
         self.validate_response(schema.list_versions, resp, body)
diff --git a/tempest/lib/services/compute/volumes_client.py b/tempest/lib/services/compute/volumes_client.py
index b75f22e..95cdd53 100644
--- a/tempest/lib/services/compute/volumes_client.py
+++ b/tempest/lib/services/compute/volumes_client.py
@@ -29,7 +29,8 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#listVolumes
+        https://developer.openstack.org/api-ref/compute/#list-volumes
+        https://developer.openstack.org/api-ref/compute/#list-volumes-with-details
         """
         url = 'os-volumes'
 
@@ -48,7 +49,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#showVolume
+        https://developer.openstack.org/api-ref/compute/#show-volume-details
         """
         url = "os-volumes/%s" % volume_id
         resp, body = self.get(url)
@@ -61,7 +62,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#createVolume
+        https://developer.openstack.org/api-ref/compute/#create-volume
         """
         post_body = json.dumps({'volume': kwargs})
         resp, body = self.post('os-volumes', post_body)
@@ -74,7 +75,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-compute-v2.1.html#deleteVolume
+        https://developer.openstack.org/api-ref/compute/#delete-volume
         """
         resp, body = self.delete("os-volumes/%s" % volume_id)
         self.validate_response(schema.delete_volume, resp, body)
diff --git a/tempest/lib/services/identity/v2/endpoints_client.py b/tempest/lib/services/identity/v2/endpoints_client.py
index 770e8ae..db8d7cc 100644
--- a/tempest/lib/services/identity/v2/endpoints_client.py
+++ b/tempest/lib/services/identity/v2/endpoints_client.py
@@ -25,7 +25,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-identity-v2-ext.html#createEndpoint
+        https://developer.openstack.org/api-ref/identity/v2-admin/index.html#create-endpoint-template
         """
 
         post_body = json.dumps({'endpoint': kwargs})
diff --git a/tempest/lib/services/identity/v2/identity_client.py b/tempest/lib/services/identity/v2/identity_client.py
index 6caff0e..c610d65 100644
--- a/tempest/lib/services/identity/v2/identity_client.py
+++ b/tempest/lib/services/identity/v2/identity_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
 
@@ -45,3 +46,24 @@
         self.expected_success(200, resp.status)
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
+
+    def list_endpoints_for_token(self, token_id):
+        """List endpoints for a token """
+        resp, body = self.get("tokens/%s/endpoints" % token_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def check_token_existence(self, token_id, **params):
+        """Validates a token and confirms that it belongs to a tenant.
+
+        For a full list of available parameters, please refer to the
+        official API reference:
+        https://developer.openstack.org/api-ref/identity/v2-admin/#validate-token
+        """
+        url = "tokens/%s" % token_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.head(url)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v2/roles_client.py b/tempest/lib/services/identity/v2/roles_client.py
index 635d013..9e841dd 100644
--- a/tempest/lib/services/identity/v2/roles_client.py
+++ b/tempest/lib/services/identity/v2/roles_client.py
@@ -24,7 +24,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-identity-v2-ext.html#createRole
+        https://developer.openstack.org/api-ref/identity/v2-ext/index.html#create-a-role
         """
         post_body = json.dumps({'role': kwargs})
         resp, body = self.post('OS-KSADM/roles', post_body)
@@ -37,9 +37,9 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-identity-v2-ext.html#showRoleByID
+        https://developer.openstack.org/api-ref/identity/v2-ext/index.html#show-a-role
         OR
-        http://developer.openstack.org/api-ref-identity-v2-ext.html#showRoleByName
+        https://developer.openstack.org/api-ref/identity/v2-ext/index.html#show-role-information-by-name
         """
         resp, body = self.get('OS-KSADM/roles/%s' % role_id_or_name)
         self.expected_success(200, resp.status)
@@ -51,7 +51,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-identity-v2-ext.html#listRoles
+        https://developer.openstack.org/api-ref/identity/v2-ext/index.html#list-all-roles
         """
         url = 'OS-KSADM/roles'
         if params:
@@ -66,7 +66,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-identity-v2-ext.html#deleteRole
+        https://developer.openstack.org/api-ref/identity/v2-ext/index.html#delete-a-role
         """
         resp, body = self.delete('OS-KSADM/roles/%s' % role_id)
         self.expected_success(204, resp.status)
@@ -77,7 +77,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-identity-v2-ext.html#grantRoleToUserOnTenant
+        https://developer.openstack.org/api-ref/identity/v2-ext/index.html#grant-roles-to-user-on-tenant
         """
         resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
                               (tenant_id, user_id, role_id), "")
@@ -101,7 +101,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-identity-v2-ext.html#revokeRoleFromUserOnTenant
+        https://developer.openstack.org/api-ref/identity/v2-ext/index.html#revoke-role-from-user-on-tenant
         """
         resp, body = self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
                                  (tenant_id, user_id, role_id))
diff --git a/tempest/lib/services/identity/v2/services_client.py b/tempest/lib/services/identity/v2/services_client.py
index b3f94aa..47398db 100644
--- a/tempest/lib/services/identity/v2/services_client.py
+++ b/tempest/lib/services/identity/v2/services_client.py
@@ -26,7 +26,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/identity/v2-ext/?expanded=#create-service-admin-extension
+        http://developer.openstack.org/api-ref/identity/v2-ext/#create-service-admin-extension
         """
         post_body = json.dumps({'OS-KSADM:service': kwargs})
         resp, body = self.post('/OS-KSADM/services', post_body)
@@ -47,7 +47,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/identity/v2-ext/?expanded=#list-services-admin-extension
+        http://developer.openstack.org/api-ref/identity/v2-ext/#list-services-admin-extension
         """
         url = '/OS-KSADM/services'
         if params:
diff --git a/tempest/lib/services/identity/v2/tenants_client.py b/tempest/lib/services/identity/v2/tenants_client.py
index b687332..026db64 100644
--- a/tempest/lib/services/identity/v2/tenants_client.py
+++ b/tempest/lib/services/identity/v2/tenants_client.py
@@ -39,7 +39,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-identity-v2-ext.html#deleteTenant
+        https://developer.openstack.org/api-ref/identity/v2-admin/index.html#delete-tenant
         """
         resp, body = self.delete('tenants/%s' % str(tenant_id))
         self.expected_success(204, resp.status)
@@ -50,7 +50,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-identity-v2-ext.html#admin-showTenantById
+        https://developer.openstack.org/api-ref/identity/v2-admin/index.html#show-tenant-details-by-id
         """
         resp, body = self.get('tenants/%s' % str(tenant_id))
         self.expected_success(200, resp.status)
diff --git a/tempest/lib/services/identity/v2/users_client.py b/tempest/lib/services/identity/v2/users_client.py
index f20fdc4..44bb5fd 100644
--- a/tempest/lib/services/identity/v2/users_client.py
+++ b/tempest/lib/services/identity/v2/users_client.py
@@ -50,7 +50,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-identity-admin-v2.html#admin-showUser
+        https://developer.openstack.org/api-ref/identity/v2-admin/index.html#show-user-details-admin-endpoint
         """
         resp, body = self.get("users/%s" % user_id)
         self.expected_success(200, resp.status)
@@ -62,7 +62,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-identity-admin-v2.html#admin-deleteUser
+        https://developer.openstack.org/api-ref/identity/v2-admin/index.html#delete-user-admin-endpoint
         """
         resp, body = self.delete("users/%s" % user_id)
         self.expected_success(204, resp.status)
@@ -88,7 +88,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-identity-v2-ext.html#enableUser
+        https://developer.openstack.org/api-ref/identity/v2-ext/index.html#enable-disable-user
         """
         # NOTE: The URL (users/<id>/enabled) is different from the api-site
         # one (users/<id>/OS-KSADM/enabled) , but they are the same API
diff --git a/tempest/lib/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py
index 8058d51..a539d08 100644
--- a/tempest/lib/services/identity/v3/__init__.py
+++ b/tempest/lib/services/identity/v3/__init__.py
@@ -12,14 +12,26 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
+from tempest.lib.services.identity.v3.catalog_client import \
+    CatalogClient
 from tempest.lib.services.identity.v3.credentials_client import \
     CredentialsClient
+from tempest.lib.services.identity.v3.domain_configuration_client \
+    import DomainConfigurationClient
 from tempest.lib.services.identity.v3.domains_client import DomainsClient
+from tempest.lib.services.identity.v3.endpoint_filter_client import \
+    EndPointsFilterClient
+from tempest.lib.services.identity.v3.endpoint_groups_client import \
+    EndPointGroupsClient
 from tempest.lib.services.identity.v3.endpoints_client import EndPointsClient
 from tempest.lib.services.identity.v3.groups_client import GroupsClient
 from tempest.lib.services.identity.v3.identity_client import IdentityClient
 from tempest.lib.services.identity.v3.inherited_roles_client import \
     InheritedRolesClient
+from tempest.lib.services.identity.v3.oauth_consumers_client import \
+    OAUTHConsumerClient
+from tempest.lib.services.identity.v3.oauth_token_client import \
+    OAUTHTokenClient
 from tempest.lib.services.identity.v3.policies_client import PoliciesClient
 from tempest.lib.services.identity.v3.projects_client import ProjectsClient
 from tempest.lib.services.identity.v3.regions_client import RegionsClient
@@ -30,9 +42,13 @@
 from tempest.lib.services.identity.v3.token_client import V3TokenClient
 from tempest.lib.services.identity.v3.trusts_client import TrustsClient
 from tempest.lib.services.identity.v3.users_client import UsersClient
+from tempest.lib.services.identity.v3.versions_client import VersionsClient
 
-__all__ = ['CredentialsClient', 'DomainsClient', 'EndPointsClient',
-           'GroupsClient', 'IdentityClient', 'InheritedRolesClient',
-           'PoliciesClient', 'ProjectsClient', 'RegionsClient',
-           'RoleAssignmentsClient', 'RolesClient', 'ServicesClient',
-           'V3TokenClient', 'TrustsClient', 'UsersClient', ]
+__all__ = ['CatalogClient', 'CredentialsClient', 'DomainsClient',
+           'DomainConfigurationClient', 'EndPointGroupsClient',
+           'EndPointsClient', 'EndPointsFilterClient', 'GroupsClient',
+           'IdentityClient', 'InheritedRolesClient', 'OAUTHConsumerClient',
+           'OAUTHTokenClient', 'PoliciesClient', 'ProjectsClient',
+           'RegionsClient', 'RoleAssignmentsClient', 'RolesClient',
+           'ServicesClient', 'V3TokenClient', 'TrustsClient', 'UsersClient',
+           'VersionsClient']
diff --git a/tempest/services/object_storage/capabilities_client.py b/tempest/lib/services/identity/v3/catalog_client.py
similarity index 71%
copy from tempest/services/object_storage/capabilities_client.py
copy to tempest/lib/services/identity/v3/catalog_client.py
index 0fe437f..232b85a 100644
--- a/tempest/services/object_storage/capabilities_client.py
+++ b/tempest/lib/services/identity/v3/catalog_client.py
@@ -1,6 +1,3 @@
-# Copyright 2012 OpenStack Foundation
-# All Rights Reserved.
-#
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
 #    not use this file except in compliance with the License. You may obtain
 #    a copy of the License at
@@ -13,19 +10,20 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+"""
+https://developer.openstack.org/api-ref/identity/v3/index.html#get-service-catalog
+"""
+
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.common import rest_client
 
 
-class CapabilitiesClient(rest_client.RestClient):
+class CatalogClient(rest_client.RestClient):
+    api_version = "v3"
 
-    def list_capabilities(self):
-        self.skip_path()
-        try:
-            resp, body = self.get('info')
-        finally:
-            self.reset_path()
-        body = json.loads(body)
+    def show_catalog(self):
+        resp, body = self.get('auth/catalog')
         self.expected_success(200, resp.status)
-        return resp, body
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/domain_configuration_client.py b/tempest/lib/services/identity/v3/domain_configuration_client.py
new file mode 100644
index 0000000..d57f2d4
--- /dev/null
+++ b/tempest/lib/services/identity/v3/domain_configuration_client.py
@@ -0,0 +1,188 @@
+# Copyright 2017 AT&T Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class DomainConfigurationClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def show_default_config_settings(self):
+        """Show default configuration settings.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#show-default-configuration-settings
+        """
+        url = 'domains/config/default'
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_default_group_config(self, group):
+        """Show default configuration for a group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#show-default-configuration-for-a-group
+        """
+        url = 'domains/config/%s/default' % group
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_default_group_option(self, group, option):
+        """Show default option for a group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#show-default-option-for-a-group
+        """
+        url = 'domains/config/%s/%s/default' % (group, option)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_domain_group_option_config(self, domain_id, group, option):
+        """Show domain group option configuration.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#show-domain-group-option-configuration
+        """
+        url = 'domains/%s/config/%s/%s' % (domain_id, group, option)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_domain_group_option_config(self, domain_id, group, option,
+                                          **kwargs):
+        """Update domain group option configuration.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-domain-group-option-configuration
+        """
+        url = 'domains/%s/config/%s/%s' % (domain_id, group, option)
+        resp, body = self.patch(url, json.dumps({'config': kwargs}))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_domain_group_option_config(self, domain_id, group, option):
+        """Delete domain group option configuration.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#delete-domain-group-option-configuration
+        """
+        url = 'domains/%s/config/%s/%s' % (domain_id, group, option)
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_domain_group_config(self, domain_id, group):
+        """Shows details for a domain group configuration.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#show-domain-group-configuration
+        """
+        url = 'domains/%s/config/%s' % (domain_id, group)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_domain_group_config(self, domain_id, group, **kwargs):
+        """Update domain group configuration.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-domain-group-configuration
+        """
+        url = 'domains/%s/config/%s' % (domain_id, group)
+        resp, body = self.patch(url, json.dumps({'config': kwargs}))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_domain_group_config(self, domain_id, group):
+        """Delete domain group configuration.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#delete-domain-group-configuration
+        """
+        url = 'domains/%s/config/%s' % (domain_id, group)
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_domain_config(self, domain_id, **kwargs):
+        """Create domain configuration.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#create-domain-configuration
+        """
+        url = 'domains/%s/config' % domain_id
+        resp, body = self.put(url, json.dumps({'config': kwargs}))
+        self.expected_success([200, 201], resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_domain_config(self, domain_id):
+        """Show domain configuration.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#show-domain-configuration
+        """
+        url = 'domains/%s/config' % domain_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_domain_config(self, domain_id, **kwargs):
+        """Update domain configuration.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-domain-configuration
+        """
+        url = 'domains/%s/config' % domain_id
+        resp, body = self.patch(url, json.dumps({'config': kwargs}))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_domain_config(self, domain_id):
+        """Delete domain configuration.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#delete-domain-configuration
+        """
+        url = 'domains/%s/config' % domain_id
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/endpoint_filter_client.py b/tempest/lib/services/identity/v3/endpoint_filter_client.py
new file mode 100644
index 0000000..a8cd722
--- /dev/null
+++ b/tempest/lib/services/identity/v3/endpoint_filter_client.py
@@ -0,0 +1,68 @@
+# Copyright 2017 AT&T 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.
+
+"""
+https://developer.openstack.org/api-ref/identity/v3-ext/#os-ep-filter-api
+"""
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class EndPointsFilterClient(rest_client.RestClient):
+    api_version = "v3"
+    ep_filter = "OS-EP-FILTER"
+
+    def list_projects_for_endpoint(self, endpoint_id):
+        """List all projects that are associated with the endpoint."""
+        resp, body = self.get(self.ep_filter + '/endpoints/%s/projects' %
+                              endpoint_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def add_endpoint_to_project(self, project_id, endpoint_id):
+        """Add association between project and endpoint. """
+        body = None
+        resp, body = self.put(
+            self.ep_filter + '/projects/%s/endpoints/%s' %
+            (project_id, endpoint_id), body)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def check_endpoint_in_project(self, project_id, endpoint_id):
+        """Check association of Project with Endpoint."""
+        resp, body = self.head(
+            self.ep_filter + '/projects/%s/endpoints/%s' %
+            (project_id, endpoint_id), None)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_endpoints_in_project(self, project_id):
+        """List Endpoints associated with Project."""
+        resp, body = self.get(self.ep_filter + '/projects/%s/endpoints'
+                              % project_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_endpoint_from_project(self, project_id, endpoint_id):
+        """Delete association between project and endpoint."""
+        resp, body = self.delete(
+            self.ep_filter + '/projects/%s/endpoints/%s'
+            % (project_id, endpoint_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/endpoint_groups_client.py b/tempest/lib/services/identity/v3/endpoint_groups_client.py
new file mode 100644
index 0000000..ce99389
--- /dev/null
+++ b/tempest/lib/services/identity/v3/endpoint_groups_client.py
@@ -0,0 +1,78 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class EndPointGroupsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_endpoint_group(self, **kwargs):
+        """Create endpoint group.
+
+        For a full list of available parameters, please refer to the
+        official API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/#create-endpoint-group
+        """
+        post_body = json.dumps({'endpoint_group': kwargs})
+        resp, body = self.post('OS-EP-FILTER/endpoint_groups', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_endpoint_group(self, endpoint_group_id, **kwargs):
+        """Update endpoint group.
+
+        For a full list of available parameters, please refer to the
+        official API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/#update-endpoint-group
+        """
+        post_body = json.dumps({'endpoint_group': kwargs})
+        resp, body = self.patch(
+            'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_endpoint_group(self, endpoint_group_id):
+        """Delete endpoint group."""
+        resp_header, resp_body = self.delete(
+            'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id)
+        self.expected_success(204, resp_header.status)
+        return rest_client.ResponseBody(resp_header, resp_body)
+
+    def show_endpoint_group(self, endpoint_group_id):
+        """Get endpoint group."""
+        resp_header, resp_body = self.get(
+            'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id)
+        self.expected_success(200, resp_header.status)
+        resp_body = json.loads(resp_body)
+        return rest_client.ResponseBody(resp_header, resp_body)
+
+    def check_endpoint_group(self, endpoint_group_id):
+        """Check endpoint group."""
+        resp_header, resp_body = self.head(
+            'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id)
+        self.expected_success(200, resp_header.status)
+        return rest_client.ResponseBody(resp_header, resp_body)
+
+    def list_endpoint_groups(self):
+        """Get endpoint groups."""
+        resp_header, resp_body = self.get('OS-EP-FILTER/endpoint_groups')
+        self.expected_success(200, resp_header.status)
+        resp_body = json.loads(resp_body)
+        return rest_client.ResponseBody(resp_header, resp_body)
diff --git a/tempest/lib/services/identity/v3/endpoints_client.py b/tempest/lib/services/identity/v3/endpoints_client.py
index c4c0d8d..e24dca7 100644
--- a/tempest/lib/services/identity/v3/endpoints_client.py
+++ b/tempest/lib/services/identity/v3/endpoints_client.py
@@ -14,10 +14,11 @@
 #    under the License.
 
 """
-http://developer.openstack.org/api-ref-identity-v3.html#endpoints-v3
+https://developer.openstack.org/api-ref/identity/v3/index.html#service-catalog-and-endpoints
 """
 
 from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
@@ -25,9 +26,17 @@
 class EndPointsClient(rest_client.RestClient):
     api_version = "v3"
 
-    def list_endpoints(self):
-        """GET endpoints."""
-        resp, body = self.get('endpoints')
+    def list_endpoints(self, **params):
+        """List endpoints.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/#list-endpoints
+        """
+        url = 'endpoints'
+        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/v3/groups_client.py b/tempest/lib/services/identity/v3/groups_client.py
index 5e68939..bc6ead0 100644
--- a/tempest/lib/services/identity/v3/groups_client.py
+++ b/tempest/lib/services/identity/v3/groups_client.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 """
-http://developer.openstack.org/api-ref-identity-v3.html#groups-v3
+https://developer.openstack.org/api-ref/identity/v3/index.html#groups
 """
 
 from oslo_serialization import jsonutils as json
diff --git a/tempest/lib/services/identity/v3/identity_client.py b/tempest/lib/services/identity/v3/identity_client.py
index 8177e35..2512a3e 100644
--- a/tempest/lib/services/identity/v3/identity_client.py
+++ b/tempest/lib/services/identity/v3/identity_client.py
@@ -43,3 +43,17 @@
         resp, body = self.delete("auth/tokens", headers=headers)
         self.expected_success(204, resp.status)
         return rest_client.ResponseBody(resp, body)
+
+    def check_token_existence(self, resp_token):
+        """Validates a token."""
+        headers = {'X-Subject-Token': resp_token}
+        resp, body = self.head("auth/tokens", headers=headers)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_auth_projects(self):
+        """Get available project scopes."""
+        resp, body = self.get("auth/projects")
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/oauth_consumers_client.py b/tempest/lib/services/identity/v3/oauth_consumers_client.py
new file mode 100644
index 0000000..97fb141
--- /dev/null
+++ b/tempest/lib/services/identity/v3/oauth_consumers_client.py
@@ -0,0 +1,95 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class OAUTHConsumerClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_consumer(self, description=None):
+        """Creates a consumer.
+
+        :param str description: Optional field to add notes about the consumer
+
+        For more information, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/#create-consumer
+        """
+        post_body = {"description": description}
+        post_body = json.dumps({'consumer': post_body})
+        resp, body = self.post('OS-OAUTH1/consumers', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_consumer(self, consumer_id):
+        """Deletes a consumer.
+
+        :param str consumer_id: The ID of the consumer that will be deleted
+
+        For more information, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/#delete-consumer
+        """
+        resp, body = self.delete('OS-OAUTH1/consumers/%s' % consumer_id)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_consumer(self, consumer_id, description=None):
+        """Updates a consumer.
+
+        :param str consumer_id: The ID of the consumer that will be updated
+        :param str description: Optional field to add notes about the consumer
+
+        For more information, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/#update-consumer
+        """
+        post_body = {"description": description}
+        post_body = json.dumps({'consumer': post_body})
+        resp, body = self.patch('OS-OAUTH1/consumers/%s' % consumer_id,
+                                post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_consumer(self, consumer_id):
+        """Show consumer details.
+
+        :param str consumer_id: The ID of the consumer that will be shown
+
+        For more information, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/#show-consumer-details
+        """
+        resp, body = self.get('OS-OAUTH1/consumers/%s' % consumer_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_consumers(self):
+        """List all consumers.
+
+        For more information, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/#list-consumers
+        """
+        resp, body = self.get('OS-OAUTH1/consumers')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/oauth_token_client.py b/tempest/lib/services/identity/v3/oauth_token_client.py
new file mode 100644
index 0000000..b1d298b
--- /dev/null
+++ b/tempest/lib/services/identity/v3/oauth_token_client.py
@@ -0,0 +1,236 @@
+# Copyright 2017 AT&T 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 binascii
+import hashlib
+import hmac
+import random
+import time
+
+import six
+from six.moves.urllib import parse as urlparse
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class OAUTHTokenClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def _escape(self, s):
+        """Escape a unicode string in an OAuth-compatible fashion."""
+        safe = b'~'
+        s = s.encode('utf-8') if isinstance(s, six.text_type) else s
+        s = urlparse.quote(s, safe)
+        if isinstance(s, six.binary_type):
+            s = s.decode('utf-8')
+        return s
+
+    def _generate_params_with_signature(self, client_key, uri,
+                                        client_secret=None,
+                                        resource_owner_key=None,
+                                        resource_owner_secret=None,
+                                        callback_uri=None,
+                                        verifier=None,
+                                        http_method='GET'):
+        """Generate OAUTH params along with signature."""
+        timestamp = six.text_type(int(time.time()))
+        nonce = six.text_type(random.getrandbits(64)) + timestamp
+        oauth_params = [
+            ('oauth_nonce', nonce),
+            ('oauth_timestamp', timestamp),
+            ('oauth_version', '1.0'),
+            ('oauth_signature_method', 'HMAC-SHA1'),
+            ('oauth_consumer_key', client_key),
+        ]
+        if resource_owner_key:
+            oauth_params.append(('oauth_token', resource_owner_key))
+        if callback_uri:
+            oauth_params.append(('oauth_callback', callback_uri))
+        if verifier:
+            oauth_params.append(('oauth_verifier', verifier))
+
+        # normalize_params
+        key_values = [(self._escape(k), self._escape(v))
+                      for k, v in oauth_params]
+        key_values.sort()
+        parameter_parts = ['{0}={1}'.format(k, v) for k, v in key_values]
+        normalized_params = '&'.join(parameter_parts)
+
+        # normalize_uri
+        scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri)
+        scheme = scheme.lower()
+        netloc = netloc.lower()
+        normalized_uri = urlparse.urlunparse((scheme, netloc, path,
+                                              params, '', ''))
+
+        # construct base string
+        base_string = self._escape(http_method.upper())
+        base_string += '&'
+        base_string += self._escape(normalized_uri)
+        base_string += '&'
+        base_string += self._escape(normalized_params)
+
+        # sign using hmac-sha1
+        key = self._escape(client_secret or '')
+        key += '&'
+        key += self._escape(resource_owner_secret or '')
+        key_utf8 = key.encode('utf-8')
+        text_utf8 = base_string.encode('utf-8')
+        signature = hmac.new(key_utf8, text_utf8, hashlib.sha1)
+        sig = binascii.b2a_base64(signature.digest())[:-1].decode('utf-8')
+
+        oauth_params.append(('oauth_signature', sig))
+        return oauth_params
+
+    def _generate_oauth_header(self, oauth_params):
+        authorization_header = {}
+        authorization_header_parameters_parts = []
+        for oauth_parameter_name, value in oauth_params:
+            escaped_name = self._escape(oauth_parameter_name)
+            escaped_value = self._escape(value)
+            part = '{0}="{1}"'.format(escaped_name, escaped_value)
+            authorization_header_parameters_parts.append(part)
+
+        authorization_header_parameters = ', '.join(
+            authorization_header_parameters_parts)
+        oauth_string = 'OAuth %s' % authorization_header_parameters
+        authorization_header['Authorization'] = oauth_string
+
+        return authorization_header
+
+    def create_request_token(self, consumer_key, consumer_secret, project_id):
+        """Create request token.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/#create-request-token
+        """
+        endpoint = 'OS-OAUTH1/request_token'
+        headers = {'Requested-Project-Id': project_id}
+        oauth_params = self._generate_params_with_signature(
+            consumer_key,
+            self.base_url + '/' + endpoint,
+            client_secret=consumer_secret,
+            callback_uri='oob',
+            http_method='POST')
+        oauth_header = self._generate_oauth_header(oauth_params)
+        headers.update(oauth_header)
+        resp, body = self.post(endpoint,
+                               body=None,
+                               headers=headers)
+        self.expected_success(201, resp.status)
+        if not isinstance(body, str):
+            body = body.decode('utf-8')
+        body = dict(item.split("=") for item in body.split("&"))
+        return rest_client.ResponseBody(resp, body)
+
+    def authorize_request_token(self, request_token_id, role_ids):
+        """Authorize request token.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/#authorize-request-token
+        """
+        roles = [{'id': role_id} for role_id in role_ids]
+        body = {'roles': roles}
+        post_body = json.dumps(body)
+        resp, body = self.put("OS-OAUTH1/authorize/%s" % request_token_id,
+                              post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_access_token(self, consumer_key, consumer_secret, request_key,
+                            request_secret, oauth_verifier):
+        """Create access token.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/#create-access-token
+        """
+        endpoint = 'OS-OAUTH1/access_token'
+        oauth_params = self._generate_params_with_signature(
+            consumer_key,
+            self.base_url + '/' + endpoint,
+            client_secret=consumer_secret,
+            resource_owner_key=request_key,
+            resource_owner_secret=request_secret,
+            verifier=oauth_verifier,
+            http_method='POST')
+        headers = self._generate_oauth_header(oauth_params)
+        resp, body = self.post(endpoint, body=None, headers=headers)
+        self.expected_success(201, resp.status)
+        if not isinstance(body, str):
+            body = body.decode('utf-8')
+        body = dict(item.split("=") for item in body.split("&"))
+        return rest_client.ResponseBody(resp, body)
+
+    def get_access_token(self, user_id, access_token_id):
+        """Get access token.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/#get-access-token
+        """
+        resp, body = self.get("users/%s/OS-OAUTH1/access_tokens/%s"
+                              % (user_id, access_token_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def revoke_access_token(self, user_id, access_token_id):
+        """Revoke access token.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/#revoke-access-token
+        """
+        resp, body = self.delete("users/%s/OS-OAUTH1/access_tokens/%s"
+                                 % (user_id, access_token_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_access_tokens(self, user_id):
+        """List access tokens.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/#list-access-tokens
+        """
+        resp, body = self.get("users/%s/OS-OAUTH1/access_tokens"
+                              % (user_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_access_token_roles(self, user_id, access_token_id):
+        """List roles for an access token.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/#list-roles-for-an-access-token
+        """
+        resp, body = self.get("users/%s/OS-OAUTH1/access_tokens/%s/roles"
+                              % (user_id, access_token_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def get_access_token_role(self, user_id, access_token_id, role_id):
+        """Show role details for an access token.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3-ext/#show-role-details-for-an-access-token
+        """
+        resp, body = self.get("users/%s/OS-OAUTH1/access_tokens/%s/roles/%s"
+                              % (user_id, access_token_id, role_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/policies_client.py b/tempest/lib/services/identity/v3/policies_client.py
index 0282745..d4560e2 100644
--- a/tempest/lib/services/identity/v3/policies_client.py
+++ b/tempest/lib/services/identity/v3/policies_client.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 """
-http://developer.openstack.org/api-ref-identity-v3.html#policies-v3
+https://developer.openstack.org/api-ref/identity/v3/index.html#policies
 """
 
 from oslo_serialization import jsonutils as json
diff --git a/tempest/lib/services/identity/v3/regions_client.py b/tempest/lib/services/identity/v3/regions_client.py
index 33c754a..d7507cf 100644
--- a/tempest/lib/services/identity/v3/regions_client.py
+++ b/tempest/lib/services/identity/v3/regions_client.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 """
-http://developer.openstack.org/api-ref-identity-v3.html#regions-v3
+https://developer.openstack.org/api-ref/identity/v3/index.html#regions
 """
 
 from oslo_serialization import jsonutils as json
diff --git a/tempest/lib/services/identity/v3/role_assignments_client.py b/tempest/lib/services/identity/v3/role_assignments_client.py
index 10de03f..a426e69 100644
--- a/tempest/lib/services/identity/v3/role_assignments_client.py
+++ b/tempest/lib/services/identity/v3/role_assignments_client.py
@@ -26,7 +26,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/identity/v3/?expanded=list-effective-role-assignments-detail
+        http://developer.openstack.org/api-ref/identity/v3/#list-role-assignments
 
         :param effective: If True, returns the effective assignments, including
                           any assignments gained by virtue of group membership
diff --git a/tempest/lib/services/identity/v3/roles_client.py b/tempest/lib/services/identity/v3/roles_client.py
index 0df23ce..43e3c01 100644
--- a/tempest/lib/services/identity/v3/roles_client.py
+++ b/tempest/lib/services/identity/v3/roles_client.py
@@ -214,6 +214,18 @@
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
+    def list_all_role_inference_rules(self):
+        """Lists all role inference rules.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#list-all-role-inference-rules
+        """
+        resp, body = self.get('role_inferences')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
     def check_role_inference_rule(self, prior_role, implies_role):
         """Check a role inference rule."""
         resp, body = self.head('roles/%s/implies/%s' %
diff --git a/tempest/lib/services/identity/v3/services_client.py b/tempest/lib/services/identity/v3/services_client.py
index 14c81cc..7bbe850 100644
--- a/tempest/lib/services/identity/v3/services_client.py
+++ b/tempest/lib/services/identity/v3/services_client.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 """
-http://developer.openstack.org/api-ref-identity-v3.html#service-catalog-v3
+https://developer.openstack.org/api-ref/identity/v3/index.html#service-catalog-and-endpoints
 """
 
 from oslo_serialization import jsonutils as json
@@ -76,7 +76,7 @@
         url = 'services'
         if params:
             url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get('services')
+        resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/versions_client.py b/tempest/lib/services/identity/v3/versions_client.py
new file mode 100644
index 0000000..441ee0d
--- /dev/null
+++ b/tempest/lib/services/identity/v3/versions_client.py
@@ -0,0 +1,38 @@
+# Copyright 2017 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 time
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class VersionsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def list_versions(self):
+        """List API versions"""
+        version_url = self._get_base_version_url()
+
+        start = time.time()
+        resp, body = self.raw_request(version_url, 'GET')
+        end = time.time()
+        self._log_request('GET', version_url, resp, secs=(end - start),
+                          resp_body=body)
+        self._error_checker(resp, body)
+
+        self.expected_success(300, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/image/v1/images_client.py b/tempest/lib/services/image/v1/images_client.py
index 03f4c4b..42f8cf2 100644
--- a/tempest/lib/services/image/v1/images_client.py
+++ b/tempest/lib/services/image/v1/images_client.py
@@ -61,7 +61,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-image-v1.html#createImage-v1
+        https://developer.openstack.org/api-ref/image/v1/index.html#create-image
         """
         if headers is None:
             headers = {}
@@ -79,7 +79,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-image-v1.html#updateImage-v1
+        https://developer.openstack.org/api-ref/image/v1/index.html#update-image
         """
         if headers is None:
             headers = {}
@@ -118,7 +118,7 @@
         if 'changes_since' in kwargs:
             kwargs['changes-since'] = kwargs.pop('changes_since')
 
-        if len(kwargs) > 0:
+        if kwargs:
             url += '?%s' % urllib.urlencode(kwargs)
 
         resp, body = self.get(url)
diff --git a/tempest/lib/services/image/v2/__init__.py b/tempest/lib/services/image/v2/__init__.py
index 7d973e5..99a5321 100644
--- a/tempest/lib/services/image/v2/__init__.py
+++ b/tempest/lib/services/image/v2/__init__.py
@@ -25,7 +25,9 @@
 from tempest.lib.services.image.v2.resource_types_client import \
     ResourceTypesClient
 from tempest.lib.services.image.v2.schemas_client import SchemasClient
+from tempest.lib.services.image.v2.versions_client import VersionsClient
 
 __all__ = ['ImageMembersClient', 'ImagesClient', 'NamespaceObjectsClient',
            'NamespacePropertiesClient', 'NamespaceTagsClient',
-           'NamespacesClient', 'ResourceTypesClient', 'SchemasClient']
+           'NamespacesClient', 'ResourceTypesClient', 'SchemasClient',
+           'VersionsClient']
diff --git a/tempest/lib/services/image/v2/namespace_tags_client.py b/tempest/lib/services/image/v2/namespace_tags_client.py
index ac8b569..61cc33d 100644
--- a/tempest/lib/services/image/v2/namespace_tags_client.py
+++ b/tempest/lib/services/image/v2/namespace_tags_client.py
@@ -41,7 +41,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#get_tag_definition
+        https://developer.openstack.org/api-ref/image/v2/metadefs-index.html#get-tag-definition
         """
         url = 'metadefs/namespaces/%s/tags/%s' % (namespace,
                                                   tag_name)
@@ -115,5 +115,11 @@
         """
         url = 'metadefs/namespaces/%s/tags' % namespace
         resp, _ = self.delete(url)
-        self.expected_success(200, resp.status)
+
+        # NOTE(rosmaita): Bug 1656183 fixed the success response code for
+        # this call to make it consistent with the other metadefs delete
+        # calls.  Accept both codes in case tempest is being run against
+        # an old Glance.
+        self.expected_success([200, 204], resp.status)
+
         return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/image/v2/resource_types_client.py b/tempest/lib/services/image/v2/resource_types_client.py
index 1b6889f..13259d1 100644
--- a/tempest/lib/services/image/v2/resource_types_client.py
+++ b/tempest/lib/services/image/v2/resource_types_client.py
@@ -26,7 +26,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html?expanded=#list-resource-types
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#list-resource-types
         """
         url = 'metadefs/resource_types'
         resp, body = self.get(url)
@@ -39,7 +39,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html?expanded=#create-resource-type-association
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#create-resource-type-association
         """
         url = 'metadefs/namespaces/%s/resource_types' % namespace_id
         data = json.dumps(kwargs)
@@ -53,7 +53,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html?expanded=#list-resource-type-associations
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#list-resource-type-associations
         """
         url = 'metadefs/namespaces/%s/resource_types' % namespace_id
         resp, body = self.get(url)
@@ -66,7 +66,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html?expanded=#remove-resource-type-association
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#remove-resource-type-association
         """
         url = 'metadefs/namespaces/%s/resource_types/%s' % (namespace_id,
                                                             resource_name)
diff --git a/tempest/lib/services/image/v2/versions_client.py b/tempest/lib/services/image/v2/versions_client.py
new file mode 100644
index 0000000..1adc466
--- /dev/null
+++ b/tempest/lib/services/image/v2/versions_client.py
@@ -0,0 +1,38 @@
+# Copyright 2017 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 time
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class VersionsClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def list_versions(self):
+        """List API versions"""
+        version_url = self._get_base_version_url()
+
+        start = time.time()
+        resp, body = self.raw_request(version_url, 'GET')
+        end = time.time()
+        self._log_request('GET', version_url, resp, secs=(end - start),
+                          resp_body=body)
+        self._error_checker(resp, body)
+
+        self.expected_success(300, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/network/__init__.py b/tempest/lib/services/network/__init__.py
index 19e5463..419e593 100644
--- a/tempest/lib/services/network/__init__.py
+++ b/tempest/lib/services/network/__init__.py
@@ -31,11 +31,12 @@
     ServiceProvidersClient
 from tempest.lib.services.network.subnetpools_client import SubnetpoolsClient
 from tempest.lib.services.network.subnets_client import SubnetsClient
+from tempest.lib.services.network.tags_client import TagsClient
 from tempest.lib.services.network.versions_client import NetworkVersionsClient
 
 __all__ = ['AgentsClient', 'ExtensionsClient', 'FloatingIPsClient',
            'MeteringLabelRulesClient', 'MeteringLabelsClient',
-           'NetworksClient', 'PortsClient', 'QuotasClient', 'RoutersClient',
-           'SecurityGroupRulesClient', 'SecurityGroupsClient',
-           'ServiceProvidersClient', 'SubnetpoolsClient', 'SubnetsClient',
-           'NetworkVersionsClient']
+           'NetworksClient', 'NetworkVersionsClient', 'PortsClient',
+           'QuotasClient', 'RoutersClient', 'SecurityGroupRulesClient',
+           'SecurityGroupsClient', 'ServiceProvidersClient',
+           'SubnetpoolsClient', 'SubnetsClient', 'TagsClient']
diff --git a/tempest/lib/services/network/base.py b/tempest/lib/services/network/base.py
index b6f9c91..fe8b244 100644
--- a/tempest/lib/services/network/base.py
+++ b/tempest/lib/services/network/base.py
@@ -54,7 +54,8 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def create_resource(self, uri, post_data, expect_empty_body=False):
+    def create_resource(self, uri, post_data, expect_empty_body=False,
+                        expect_response_code=201):
         req_uri = self.uri_prefix + uri
         req_post_data = json.dumps(post_data)
         resp, body = self.post(req_uri, req_post_data)
@@ -65,10 +66,11 @@
             body = json.loads(body)
         else:
             body = None
-        self.expected_success(201, resp.status)
+        self.expected_success(expect_response_code, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def update_resource(self, uri, post_data, expect_empty_body=False):
+    def update_resource(self, uri, post_data, expect_empty_body=False,
+                        expect_response_code=200):
         req_uri = self.uri_prefix + uri
         req_post_data = json.dumps(post_data)
         resp, body = self.put(req_uri, req_post_data)
@@ -79,5 +81,5 @@
             body = json.loads(body)
         else:
             body = None
-        self.expected_success(200, resp.status)
+        self.expected_success(expect_response_code, resp.status)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/network/ports_client.py b/tempest/lib/services/network/ports_client.py
index 93138b9..daa15d7 100644
--- a/tempest/lib/services/network/ports_client.py
+++ b/tempest/lib/services/network/ports_client.py
@@ -73,7 +73,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/networking/v2/index.html?expanded=#bulk-create-ports
+        http://developer.openstack.org/api-ref/networking/v2/index.html#bulk-create-ports
         """
         uri = '/ports'
         return self.create_resource(uri, kwargs)
diff --git a/tempest/lib/services/network/security_groups_client.py b/tempest/lib/services/network/security_groups_client.py
index 1f30216..d3ebf20 100644
--- a/tempest/lib/services/network/security_groups_client.py
+++ b/tempest/lib/services/network/security_groups_client.py
@@ -10,6 +10,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.network import base
 
 
@@ -66,3 +67,10 @@
         """
         uri = '/security-groups'
         return self.list_resources(uri, **filters)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_security_group(id)
+        except lib_exc.NotFound:
+            return True
+        return False
diff --git a/tempest/lib/services/network/tags_client.py b/tempest/lib/services/network/tags_client.py
new file mode 100644
index 0000000..5d49a79
--- /dev/null
+++ b/tempest/lib/services/network/tags_client.py
@@ -0,0 +1,84 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib.services.network import base
+
+
+class TagsClient(base.BaseNetworkClient):
+
+    def create_tag(self, resource_type, resource_id, tag):
+        """Adds a tag on the resource.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#add-a-tag
+        """
+        uri = '/%s/%s/tags/%s' % (resource_type, resource_id, tag)
+        return self.update_resource(
+            uri, json.dumps({}), expect_response_code=201,
+            expect_empty_body=True)
+
+    def check_tag_existence(self, resource_type, resource_id, tag):
+        """Confirm that a given tag is set on the resource.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#confirm-a-tag
+        """
+        # TODO(felipemonteiro): Use the "check_resource" method in
+        # ``BaseNetworkClient`` once it has been implemented.
+        uri = '%s/%s/%s/tags/%s' % (
+            self.uri_prefix, resource_type, resource_id, tag)
+        resp, _ = self.get(uri)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def update_all_tags(self, resource_type, resource_id, tags):
+        """Replace all tags on the resource.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#replace-all-tags
+        """
+        uri = '/%s/%s/tags' % (resource_type, resource_id)
+        put_body = {"tags": tags}
+        return self.update_resource(uri, put_body)
+
+    def delete_tag(self, resource_type, resource_id, tag):
+        """Removes a tag on the resource.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#remove-a-tag
+        """
+        uri = '/%s/%s/tags/%s' % (resource_type, resource_id, tag)
+        return self.delete_resource(uri)
+
+    def delete_all_tags(self, resource_type, resource_id):
+        """Removes all tags on the resource.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#remove-all-tags
+        """
+        uri = '/%s/%s/tags' % (resource_type, resource_id)
+        return self.delete_resource(uri)
+
+    def list_tags(self, resource_type, resource_id):
+        """Retrieves the tags for a resource.
+
+        For more information, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/networking/v2/index.html#obtain-tag-list
+        """
+        uri = '/%s/%s/tags' % (resource_type, resource_id)
+        return self.list_resources(uri)
diff --git a/tempest/lib/services/network/versions_client.py b/tempest/lib/services/network/versions_client.py
index a9c3bbf..f87fe87 100644
--- a/tempest/lib/services/network/versions_client.py
+++ b/tempest/lib/services/network/versions_client.py
@@ -15,7 +15,6 @@
 import time
 
 from oslo_serialization import jsonutils as json
-from six.moves import urllib
 
 from tempest.lib.services.network import base
 
@@ -25,9 +24,7 @@
     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)
+        version_url = self._get_base_version_url()
 
         # Note: we do a raw_request here because we want to use
         # an unversioned URL, not "v2/$project_id/".
diff --git a/tempest/api/image/admin/__init__.py b/tempest/lib/services/object_storage/__init__.py
similarity index 100%
copy from tempest/api/image/admin/__init__.py
copy to tempest/lib/services/object_storage/__init__.py
diff --git a/tempest/lib/services/object_storage/bulk_middleware_client.py b/tempest/lib/services/object_storage/bulk_middleware_client.py
new file mode 100644
index 0000000..c11a105
--- /dev/null
+++ b/tempest/lib/services/object_storage/bulk_middleware_client.py
@@ -0,0 +1,62 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.common import rest_client
+
+
+class BulkMiddlewareClient(rest_client.RestClient):
+
+    def upload_archive(self, upload_path, data,
+                       archive_file_format='tar', headers=None):
+        """Expand tar files into a Swift cluster.
+
+        To extract containers and objects on Swift cluster from
+        uploaded archived file. For More information please check:
+        https://docs.openstack.org/swift/latest/middleware.html#module-swift.common.middleware.bulk
+        """
+        url = '%s?extract-archive=%s' % (upload_path, archive_file_format)
+        if headers is None:
+            headers = {}
+        resp, body = self.put(url, data, headers)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBodyData(resp, body)
+
+    def delete_bulk_data(self, data=None, headers=None):
+        """Delete multiple objects or containers from their account.
+
+        For More information please check:
+        https://docs.openstack.org/swift/latest/middleware.html#module-swift.common.middleware.bulk
+        """
+        url = '?bulk-delete'
+
+        if headers is None:
+            headers = {}
+        resp, body = self.delete(url, headers, data)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBodyData(resp, body)
+
+    def delete_bulk_data_with_post(self, data=None, headers=None):
+        """Delete multiple objects or containers with POST request.
+
+        For More information please check:
+        https://docs.openstack.org/swift/latest/middleware.html#module-swift.common.middleware.bulk
+        """
+        url = '?bulk-delete'
+
+        if headers is None:
+            headers = {}
+        resp, body = self.post(url, data, headers)
+        self.expected_success([200, 204], resp.status)
+        return rest_client.ResponseBodyData(resp, body)
diff --git a/tempest/services/object_storage/capabilities_client.py b/tempest/lib/services/object_storage/capabilities_client.py
similarity index 94%
rename from tempest/services/object_storage/capabilities_client.py
rename to tempest/lib/services/object_storage/capabilities_client.py
index 0fe437f..d31bbc2 100644
--- a/tempest/services/object_storage/capabilities_client.py
+++ b/tempest/lib/services/object_storage/capabilities_client.py
@@ -28,4 +28,4 @@
             self.reset_path()
         body = json.loads(body)
         self.expected_success(200, resp.status)
-        return resp, body
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/base_client.py b/tempest/lib/services/volume/base_client.py
new file mode 100644
index 0000000..c7fb21a
--- /dev/null
+++ b/tempest/lib/services/volume/base_client.py
@@ -0,0 +1,45 @@
+# Copyright 2016 Andrew Kerr
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.common import api_version_utils
+from tempest.lib.common import rest_client
+
+VOLUME_MICROVERSION = None
+
+
+class BaseClient(rest_client.RestClient):
+    """Base volume service clients class to support microversion."""
+    api_microversion_header_name = 'Openstack-Api-Version'
+
+    def get_headers(self, accept_type=None, send_type=None):
+        headers = super(BaseClient, self).get_headers(
+            accept_type=accept_type, send_type=send_type)
+        if VOLUME_MICROVERSION:
+            headers[self.api_microversion_header_name] = ('volume %s' %
+                                                          VOLUME_MICROVERSION)
+        return headers
+
+    def request(self, method, url, extra_headers=False, headers=None,
+                body=None, chunked=False):
+
+        resp, resp_body = super(BaseClient, self).request(
+            method, url, extra_headers, headers, body, chunked)
+        if (VOLUME_MICROVERSION and
+            VOLUME_MICROVERSION != api_version_utils.LATEST_MICROVERSION):
+            api_version_utils.assert_version_header_matches_request(
+                self.api_microversion_header_name,
+                'volume %s' % VOLUME_MICROVERSION,
+                resp)
+        return resp, resp_body
diff --git a/tempest/lib/services/volume/v1/backups_client.py b/tempest/lib/services/volume/v1/backups_client.py
index 8677913..77c40b3 100644
--- a/tempest/lib/services/volume/v1/backups_client.py
+++ b/tempest/lib/services/volume/v1/backups_client.py
@@ -102,3 +102,8 @@
         except lib_exc.NotFound:
             return True
         return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'backup'
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
index 8acad0f..68982d9 100644
--- a/tempest/lib/services/volume/v2/__init__.py
+++ b/tempest/lib/services/volume/v2/__init__.py
@@ -23,6 +23,8 @@
 from tempest.lib.services.volume.v2.hosts_client import HostsClient
 from tempest.lib.services.volume.v2.limits_client import LimitsClient
 from tempest.lib.services.volume.v2.qos_client import QosSpecsClient
+from tempest.lib.services.volume.v2.quota_classes_client import \
+    QuotaClassesClient
 from tempest.lib.services.volume.v2.quotas_client import QuotasClient
 from tempest.lib.services.volume.v2.scheduler_stats_client import \
     SchedulerStatsClient
@@ -30,11 +32,15 @@
 from tempest.lib.services.volume.v2.snapshot_manage_client import \
     SnapshotManageClient
 from tempest.lib.services.volume.v2.snapshots_client import SnapshotsClient
+from tempest.lib.services.volume.v2.transfers_client import TransfersClient
 from tempest.lib.services.volume.v2.types_client import TypesClient
+from tempest.lib.services.volume.v2.volume_manage_client import \
+    VolumeManageClient
 from tempest.lib.services.volume.v2.volumes_client import VolumesClient
 
 __all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
            'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
            'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
            'LimitsClient', 'CapabilitiesClient', 'SchedulerStatsClient',
-           'SnapshotManageClient']
+           'SnapshotManageClient', 'VolumeManageClient', 'TransfersClient',
+           'QuotaClassesClient']
diff --git a/tempest/lib/services/volume/v2/backups_client.py b/tempest/lib/services/volume/v2/backups_client.py
index ab5eefd..adfa6a6 100644
--- a/tempest/lib/services/volume/v2/backups_client.py
+++ b/tempest/lib/services/volume/v2/backups_client.py
@@ -14,12 +14,14 @@
 #    under the License.
 
 from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume import base_client
 
 
-class BackupsClient(rest_client.RestClient):
+class BackupsClient(base_client.BaseClient):
     """Volume V2 Backups client"""
     api_version = "v2"
 
@@ -28,7 +30,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-blockstorage-v2.html#createBackup
+        https://developer.openstack.org/api-ref/block-storage/v2/index.html#create-backup
         """
         post_body = json.dumps({'backup': kwargs})
         resp, body = self.post('backups', post_body)
@@ -41,7 +43,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-blockstorage-v2.html#restoreBackup
+        https://developer.openstack.org/api-ref/block-storage/v2/index.html#restore-backup
         """
         post_body = json.dumps({'restore': kwargs})
         resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
@@ -63,11 +65,19 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def list_backups(self, detail=False):
-        """Information for all the tenant's backups."""
+    def list_backups(self, detail=False, **params):
+        """List all the tenant's backups.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-backups
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-backups-with-details
+        """
         url = "backups"
         if detail:
             url += "/detail"
+        if params:
+            url += '?%s' % urllib.urlencode(params)
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
@@ -102,3 +112,8 @@
         except lib_exc.NotFound:
             return True
         return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'backup'
diff --git a/tempest/lib/services/volume/v2/capabilities_client.py b/tempest/lib/services/volume/v2/capabilities_client.py
index b6de5b9..240be13 100644
--- a/tempest/lib/services/volume/v2/capabilities_client.py
+++ b/tempest/lib/services/volume/v2/capabilities_client.py
@@ -19,13 +19,14 @@
 
 
 class CapabilitiesClient(rest_client.RestClient):
+    api_version = "v2"
 
     def show_backend_capabilities(self, host):
         """Shows capabilities for a storage back end.
 
-         Output params: see http://developer.openstack.org/
-                            api-ref-blockstorage-v2.html
-                            #showBackendCapabilities
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/index.html#show-back-end-capabilities
         """
         url = 'capabilities/%s' % host
         resp, body = self.get(url)
diff --git a/tempest/lib/services/volume/v2/encryption_types_client.py b/tempest/lib/services/volume/v2/encryption_types_client.py
index 8b01f11..20f3356 100755
--- a/tempest/lib/services/volume/v2/encryption_types_client.py
+++ b/tempest/lib/services/volume/v2/encryption_types_client.py
@@ -50,9 +50,9 @@
     def create_encryption_type(self, volume_type_id, **kwargs):
         """Create encryption type.
 
-        TODO: Current api-site doesn't contain this API description.
-        After fixing the api-site, we need to fix here also for putting
-        the link to api-site.
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/#create-an-encryption-type-for-v2
         """
         url = "/types/%s/encryption" % volume_type_id
         post_body = json.dumps({'encryption': kwargs})
@@ -67,3 +67,17 @@
             "/types/%s/encryption/provider" % volume_type_id)
         self.expected_success(202, resp.status)
         return rest_client.ResponseBody(resp, body)
+
+    def update_encryption_type(self, volume_type_id, **kwargs):
+        """Update an encryption type for an existing volume type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/#update-an-encryption-type-for-v2
+        """
+        url = "/types/%s/encryption/provider" % volume_type_id
+        put_body = json.dumps({'encryption': kwargs})
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/hosts_client.py b/tempest/lib/services/volume/v2/hosts_client.py
index dd7c482..f44bda3 100644
--- a/tempest/lib/services/volume/v2/hosts_client.py
+++ b/tempest/lib/services/volume/v2/hosts_client.py
@@ -24,8 +24,12 @@
     api_version = "v2"
 
     def list_hosts(self, **params):
-        """Lists all hosts."""
+        """Lists all hosts.
 
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/#list-all-hosts
+        """
         url = 'os-hosts'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -34,3 +38,11 @@
         body = json.loads(body)
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
+
+    def show_host(self, host_name):
+        """Show host details."""
+        url = 'os-hosts/%s' % host_name
+        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/volume/v2/qos_client.py b/tempest/lib/services/volume/v2/qos_client.py
index 40d4a3f..47d3914 100644
--- a/tempest/lib/services/volume/v2/qos_client.py
+++ b/tempest/lib/services/volume/v2/qos_client.py
@@ -43,9 +43,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/index.html
-                                ?expanded=create-qos-specification-detail
-                                #quality-of-service-qos-specifications-qos-specs
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-qos-specification
         """
         post_body = json.dumps({'qos_specs': kwargs})
         resp, body = self.post('qos-specs', post_body)
@@ -81,9 +79,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/index.html
-                            ?expanded=set-keys-in-qos-specification-detail
-                            #quality-of-service-qos-specifications-qos-specs
+        http://developer.openstack.org/api-ref/block-storage/v2/#set-keys-in-qos-specification
         """
         put_body = json.dumps({"qos_specs": kwargs})
         resp, body = self.put('qos-specs/%s' % qos_id, put_body)
@@ -98,9 +94,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/index.html
-                            ?expanded=unset-keys-in-qos-specification-detail
-                            #quality-of-service-qos-specifications-qos-specs
+        http://developer.openstack.org/api-ref/block-storage/v2/#unset-keys-in-qos-specification
         """
         put_body = json.dumps({'keys': keys})
         resp, body = self.put('qos-specs/%s/delete_keys' % qos_id, put_body)
diff --git a/tempest/lib/services/volume/v2/quota_classes_client.py b/tempest/lib/services/volume/v2/quota_classes_client.py
new file mode 100644
index 0000000..d40d2d9
--- /dev/null
+++ b/tempest/lib/services/volume/v2/quota_classes_client.py
@@ -0,0 +1,49 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES 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 QuotaClassesClient(rest_client.RestClient):
+    """Volume quota class V2 client."""
+
+    api_version = "v2"
+
+    def show_quota_class_set(self, quota_class_id):
+        """List quotas for a quota class.
+
+        TODO: Current api-site doesn't contain this API description.
+        LP: https://bugs.launchpad.net/nova/+bug/1602400
+        """
+        url = 'os-quota-class-sets/%s' % quota_class_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_quota_class_set(self, quota_class_id, **kwargs):
+        """Update quotas for a quota class.
+
+        TODO: Current api-site doesn't contain this API description.
+        LP: https://bugs.launchpad.net/nova/+bug/1602400
+        """
+        url = 'os-quota-class-sets/%s' % quota_class_id
+        put_body = json.dumps({'quota_class_set': kwargs})
+        resp, body = self.put(url, put_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/quotas_client.py b/tempest/lib/services/volume/v2/quotas_client.py
index 430957d..e4b2895 100644
--- a/tempest/lib/services/volume/v2/quotas_client.py
+++ b/tempest/lib/services/volume/v2/quotas_client.py
@@ -49,7 +49,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref-blockstorage-v2.html#updateQuota
+        https://developer.openstack.org/api-ref/block-storage/v2/index.html#update-quotas
         """
         put_body = jsonutils.dumps({'quota_set': kwargs})
         resp, body = self.put('os-quota-sets/%s' % tenant_id, put_body)
diff --git a/tempest/lib/services/volume/v2/scheduler_stats_client.py b/tempest/lib/services/volume/v2/scheduler_stats_client.py
index 637254b..0d04f85 100644
--- a/tempest/lib/services/volume/v2/scheduler_stats_client.py
+++ b/tempest/lib/services/volume/v2/scheduler_stats_client.py
@@ -19,12 +19,14 @@
 
 
 class SchedulerStatsClient(rest_client.RestClient):
+    api_version = "v2"
 
     def list_pools(self, detail=False):
         """List all the volumes pools (hosts).
 
-        Output params: see http://developer.openstack.org/
-                           api-ref-blockstorage-v2.html#listPools
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/index.html#list-back-end-storage-pools
         """
         url = 'scheduler-stats/get_pools'
         if detail:
diff --git a/tempest/lib/services/volume/v2/snapshots_client.py b/tempest/lib/services/volume/v2/snapshots_client.py
index 2bdf1b1..4bc2842 100644
--- a/tempest/lib/services/volume/v2/snapshots_client.py
+++ b/tempest/lib/services/volume/v2/snapshots_client.py
@@ -27,7 +27,8 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots-with-details
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots
         """
         url = 'snapshots'
         if detail:
@@ -45,7 +46,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-details
         """
         url = "snapshots/%s" % snapshot_id
         resp, body = self.get(url)
@@ -71,7 +72,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot
         """
         put_body = json.dumps({'snapshot': kwargs})
         resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
@@ -84,7 +85,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#delete-snapshot-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#delete-snapshot
         """
         resp, body = self.delete("snapshots/%s" % snapshot_id)
         self.expected_success(202, resp.status)
@@ -123,7 +124,12 @@
         return rest_client.ResponseBody(resp, body)
 
     def create_snapshot_metadata(self, snapshot_id, metadata):
-        """Create metadata for the snapshot."""
+        """Create metadata for the snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-snapshot-metadata
+        """
         put_body = json.dumps({'metadata': metadata})
         url = "snapshots/%s/metadata" % snapshot_id
         resp, body = self.post(url, put_body)
@@ -136,7 +142,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-metadata-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-metadata
         """
         url = "snapshots/%s/metadata" % snapshot_id
         resp, body = self.get(url)
@@ -149,7 +155,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-metadata-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-metadata
         """
         put_body = json.dumps(kwargs)
         url = "snapshots/%s/metadata" % snapshot_id
@@ -158,6 +164,14 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def show_snapshot_metadata_item(self, snapshot_id, id):
+        """Show metadata item for the snapshot."""
+        url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
     def update_snapshot_metadata_item(self, snapshot_id, id, **kwargs):
         """Update metadata item for the snapshot."""
         # TODO(piyush): Current api-site doesn't contain this API description.
diff --git a/tempest/lib/services/volume/v2/transfers_client.py b/tempest/lib/services/volume/v2/transfers_client.py
new file mode 100644
index 0000000..2dfbe7b
--- /dev/null
+++ b/tempest/lib/services/volume/v2/transfers_client.py
@@ -0,0 +1,83 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class TransfersClient(rest_client.RestClient):
+    """Client class to send CRUD Volume Transfer V2 API requests"""
+    api_version = "v2"
+
+    def create_volume_transfer(self, **kwargs):
+        """Create a volume transfer.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/#create-volume-transfer
+        """
+        post_body = json.dumps({'transfer': kwargs})
+        resp, body = self.post('os-volume-transfer', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_transfer(self, transfer_id):
+        """Returns the details of a volume transfer."""
+        url = "os-volume-transfer/%s" % transfer_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_volume_transfers(self, detail=False, **params):
+        """List all the volume transfers created.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers
+        https://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers-with-details
+        """
+        url = 'os-volume-transfer'
+        if detail:
+            url += '/detail'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume_transfer(self, transfer_id):
+        """Delete a volume transfer."""
+        resp, body = self.delete("os-volume-transfer/%s" % transfer_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def accept_volume_transfer(self, transfer_id, **kwargs):
+        """Accept a volume transfer.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/#accept-volume-transfer
+        """
+        url = 'os-volume-transfer/%s/accept' % transfer_id
+        post_body = json.dumps({'accept': kwargs})
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/types_client.py b/tempest/lib/services/volume/v2/types_client.py
index 31597d7..af4fd8c 100644
--- a/tempest/lib/services/volume/v2/types_client.py
+++ b/tempest/lib/services/volume/v2/types_client.py
@@ -41,7 +41,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-types-v2
+        https://developer.openstack.org/api-ref/block-storage/v2/#list-all-volume-types-for-v2
         """
         url = 'types'
         if params:
@@ -57,7 +57,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-v2
+        https://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-details-for-v2
         """
         url = "types/%s" % volume_type_id
         resp, body = self.get(url)
@@ -70,7 +70,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type-v2
+        https://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type-for-v2
         """
         post_body = json.dumps({'volume_type': kwargs})
         resp, body = self.post('types', post_body)
@@ -83,7 +83,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#delete-volume-type-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#delete-volume-type
         """
         resp, body = self.delete("types/%s" % volume_type_id)
         self.expected_success(202, resp.status)
@@ -138,7 +138,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type
         """
         put_body = json.dumps({'volume_type': kwargs})
         resp, body = self.put('types/%s' % volume_type_id, put_body)
@@ -156,7 +156,7 @@
                      updated value.
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-extra-specs-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-extra-specs-for-a-volume-type
         """
         url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
         put_body = json.dumps(extra_specs)
@@ -170,7 +170,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#add-type-access-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#add-private-volume-type-access
         """
         post_body = json.dumps({'addProjectAccess': kwargs})
         url = 'types/%s/action' % volume_type_id
@@ -183,7 +183,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#remove-type-access-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#remove-private-volume-type-access
         """
         post_body = json.dumps({'removeProjectAccess': kwargs})
         url = 'types/%s/action' % volume_type_id
@@ -196,7 +196,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#list-type-access-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-private-volume-type-access-details
         """
         url = 'types/%s/os-volume-type-access' % volume_type_id
         resp, body = self.get(url)
diff --git a/tempest/lib/services/volume/v2/volume_manage_client.py b/tempest/lib/services/volume/v2/volume_manage_client.py
new file mode 100644
index 0000000..12f4240
--- /dev/null
+++ b/tempest/lib/services/volume/v2/volume_manage_client.py
@@ -0,0 +1,37 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES 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 VolumeManageClient(rest_client.RestClient):
+    """Volume manage V2 client."""
+
+    api_version = "v2"
+
+    def manage_volume(self, **kwargs):
+        """Manage existing volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/#manage-existing-volume
+        """
+        post_body = json.dumps({'volume': kwargs})
+        resp, body = self.post('os-volume-manage', post_body)
+        self.expected_success(202, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index 8b8e249..d13e449 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from debtcollector import moves
 from debtcollector import removals
 from oslo_serialization import jsonutils as json
 import six
@@ -20,12 +21,44 @@
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume import base_client
+from tempest.lib.services.volume.v2 import transfers_client
 
 
-class VolumesClient(rest_client.RestClient):
+class VolumesClient(base_client.BaseClient):
     """Client class to send CRUD Volume V2 API requests"""
     api_version = "v2"
 
+    create_volume_transfer = moves.moved_function(
+        transfers_client.TransfersClient.create_volume_transfer,
+        'VolumesClient.create_volume_transfer', __name__,
+        message='Use create_volume_transfer from new location.',
+        version='Pike', removal_version='Queens')
+
+    show_volume_transfer = moves.moved_function(
+        transfers_client.TransfersClient.show_volume_transfer,
+        'VolumesClient.show_volume_transfer', __name__,
+        message='Use show_volume_transfer from new location.',
+        version='Pike', removal_version='Queens')
+
+    list_volume_transfers = moves.moved_function(
+        transfers_client.TransfersClient.list_volume_transfers,
+        'VolumesClient.list_volume_transfers', __name__,
+        message='Use list_volume_transfer from new location.',
+        version='Pike', removal_version='Queens')
+
+    delete_volume_transfer = moves.moved_function(
+        transfers_client.TransfersClient.delete_volume_transfer,
+        'VolumesClient.delete_volume_transfer', __name__,
+        message='Use delete_volume_transfer from new location.',
+        version='Pike', removal_version='Queens')
+
+    accept_volume_transfer = moves.moved_function(
+        transfers_client.TransfersClient.accept_volume_transfer,
+        'VolumesClient.accept_volume_transfer', __name__,
+        message='Use accept_volume_transfer from new location.',
+        version='Pike', removal_version='Queens')
+
     def _prepare_params(self, params):
         """Prepares params for use in get or _ext_get methods.
 
@@ -40,6 +73,10 @@
         """List all the volumes created.
 
         Params can be a string (must be urlencoded) or a dictionary.
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-volumes-with-details
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-volumes
         """
         url = 'volumes'
         if detail:
@@ -65,7 +102,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume
         """
         post_body = json.dumps({'volume': kwargs})
         resp, body = self.post('volumes', post_body)
@@ -78,7 +115,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume
         """
         put_body = json.dumps({'volume': kwargs})
         resp, body = self.put('volumes/%s' % volume_id, put_body)
@@ -86,9 +123,17 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def delete_volume(self, volume_id):
-        """Deletes the Specified Volume."""
-        resp, body = self.delete("volumes/%s" % volume_id)
+    def delete_volume(self, volume_id, **params):
+        """Deletes the Specified Volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/#delete-volume
+        """
+        url = 'volumes/%s' % volume_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.delete(url)
         self.expected_success(202, resp.status)
         return rest_client.ResponseBody(resp, body)
 
@@ -106,7 +151,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#attach-volume-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#attach-volume-to-server
         """
         post_body = json.dumps({'os-attach': kwargs})
         url = 'volumes/%s/action' % (volume_id)
@@ -115,7 +160,12 @@
         return rest_client.ResponseBody(resp, body)
 
     def set_bootable_volume(self, volume_id, **kwargs):
-        """set a bootable flag for a volume - true or false."""
+        """Set a bootable flag for a volume - true or false.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-bootable-status
+        """
         post_body = json.dumps({'os-set_bootable': kwargs})
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
@@ -147,10 +197,18 @@
         return rest_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
+        """Check the specified resource is deleted or not.
+
+        :param id: A checked resource id
+        :raises lib_exc.DeleteErrorException: If the specified resource is on
+        the status the delete was failed.
+        """
         try:
-            self.show_volume(id)
+            volume = self.show_volume(id)
         except lib_exc.NotFound:
             return True
+        if volume["volume"]["status"] == "error_deleting":
+            raise lib_exc.DeleteErrorException(resource_id=id)
         return False
 
     @property
@@ -163,7 +221,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#extend-volume-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#extend-volume-size
         """
         post_body = json.dumps({'os-extend': kwargs})
         url = 'volumes/%s/action' % (volume_id)
@@ -176,69 +234,13 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#reset-volume-status-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#reset-volume-statuses
         """
         post_body = json.dumps({'os-reset_status': kwargs})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
         self.expected_success(202, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def create_volume_transfer(self, **kwargs):
-        """Create a volume transfer.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-transfer-v2
-        """
-        post_body = json.dumps({'transfer': kwargs})
-        resp, body = self.post('os-volume-transfer', post_body)
-        body = json.loads(body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_volume_transfer(self, transfer_id):
-        """Returns the details of a volume transfer."""
-        url = "os-volume-transfer/%s" % transfer_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def list_volume_transfers(self, **params):
-        """List all the volume transfers created.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers-v2
-        """
-        url = 'os-volume-transfer'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_volume_transfer(self, transfer_id):
-        """Delete a volume transfer."""
-        resp, body = self.delete("os-volume-transfer/%s" % transfer_id)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def accept_volume_transfer(self, transfer_id, **kwargs):
-        """Accept a volume transfer.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#accept-volume-transfer-v2
-        """
-        url = 'os-volume-transfer/%s/accept' % transfer_id
-        post_body = json.dumps({'accept': kwargs})
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
     def update_volume_readonly(self, volume_id, **kwargs):
         """Update the Specified Volume readonly."""
         post_body = json.dumps({'os-update_readonly_flag': kwargs})
@@ -255,7 +257,12 @@
         return rest_client.ResponseBody(resp, body)
 
     def create_volume_metadata(self, volume_id, metadata):
-        """Create metadata for the volume."""
+        """Create metadata for the volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-metadata
+        """
         put_body = json.dumps({'metadata': metadata})
         url = "volumes/%s/metadata" % volume_id
         resp, body = self.post(url, put_body)
@@ -272,7 +279,12 @@
         return rest_client.ResponseBody(resp, body)
 
     def update_volume_metadata(self, volume_id, metadata):
-        """Update metadata for the volume."""
+        """Update metadata for the volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-metadata
+        """
         put_body = json.dumps({'metadata': metadata})
         url = "volumes/%s/metadata" % volume_id
         resp, body = self.put(url, put_body)
@@ -280,6 +292,14 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def show_volume_metadata_item(self, volume_id, id):
+        """Show metadata item for the volume."""
+        url = "volumes/%s/metadata/%s" % (volume_id, id)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
     def update_volume_metadata_item(self, volume_id, id, meta_item):
         """Update metadata item for the volume."""
         put_body = json.dumps({'meta': meta_item})
@@ -297,17 +317,36 @@
         return rest_client.ResponseBody(resp, body)
 
     def retype_volume(self, volume_id, **kwargs):
-        """Updates volume with new volume type."""
+        """Updates volume with new volume type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/#retype-volume
+        """
         post_body = json.dumps({'os-retype': kwargs})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
         self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def force_detach_volume(self, volume_id, **kwargs):
+        """Force detach a volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/#force-detach-volume
+        """
+        post_body = json.dumps({'os-force_detach': kwargs})
+        url = 'volumes/%s/action' % volume_id
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
 
     def update_volume_image_metadata(self, volume_id, **kwargs):
         """Update image metadata for the volume.
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-image-metadata-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#set-image-metadata-for-volume
         """
         post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
         url = "volumes/%s/action" % (volume_id)
@@ -324,7 +363,16 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    @removals.remove(message="use show_pools from tempest.lib.services."
+    def show_volume_image_metadata(self, volume_id):
+        """Show image metadata for the volume."""
+        post_body = json.dumps({'os-show_image_metadata': {}})
+        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)
+
+    @removals.remove(message="use list_pools from tempest.lib.services."
                              "volume.v2.scheduler_stats_client")
     def show_pools(self, detail=False):
         # List all the volumes pools (hosts)
@@ -344,10 +392,22 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#show_backend_capabilities-v2
+        http://developer.openstack.org/api-ref/block-storage/v2/#show-back-end-capabilities
         """
         url = 'capabilities/%s' % host
         resp, body = self.get(url)
         body = json.loads(body)
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
+
+    def unmanage_volume(self, volume_id):
+        """Unmanage volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/#unmanage-volume
+        """
+        post_body = json.dumps({'os-unmanage': {}})
+        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/__init__.py b/tempest/lib/services/volume/v3/__init__.py
index a4600a8..2d85553 100644
--- a/tempest/lib/services/volume/v3/__init__.py
+++ b/tempest/lib/services/volume/v3/__init__.py
@@ -12,7 +12,18 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
+from tempest.lib.services.volume.v3.backups_client import BackupsClient
 from tempest.lib.services.volume.v3.base_client import BaseClient
+from tempest.lib.services.volume.v3.group_snapshots_client import \
+    GroupSnapshotsClient
+from tempest.lib.services.volume.v3.group_types_client import GroupTypesClient
+from tempest.lib.services.volume.v3.groups_client import GroupsClient
 from tempest.lib.services.volume.v3.messages_client import MessagesClient
+from tempest.lib.services.volume.v3.snapshots_client import SnapshotsClient
+from tempest.lib.services.volume.v3.versions_client import VersionsClient
+from tempest.lib.services.volume.v3.volumes_client import VolumesClient
 
-__all__ = ['MessagesClient', 'BaseClient']
+__all__ = ['BackupsClient', 'BaseClient', 'GroupsClient',
+           'GroupSnapshotsClient', 'GroupTypesClient',
+           'MessagesClient', 'SnapshotsClient', 'VersionsClient',
+           'VolumesClient']
diff --git a/tempest/lib/services/volume/v3/backups_client.py b/tempest/lib/services/volume/v3/backups_client.py
new file mode 100644
index 0000000..e742e39
--- /dev/null
+++ b/tempest/lib/services/volume/v3/backups_client.py
@@ -0,0 +1,37 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v2 import backups_client
+
+
+class BackupsClient(backups_client.BackupsClient):
+    """Volume V3 Backups client"""
+    api_version = "v3"
+
+    def update_backup(self, backup_id, **kwargs):
+        """Updates the specified volume backup.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#update-a-backup
+        """
+        put_body = json.dumps({'backup': kwargs})
+        resp, body = self.put('backups/%s' % backup_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/base_client.py b/tempest/lib/services/volume/v3/base_client.py
index 958212a..e78380b 100644
--- a/tempest/lib/services/volume/v3/base_client.py
+++ b/tempest/lib/services/volume/v3/base_client.py
@@ -13,34 +13,11 @@
 #    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
+from debtcollector import moves
 
-VOLUME_MICROVERSION = None
+from tempest.lib.services.volume import base_client
 
 
-class BaseClient(rest_client.RestClient):
-    """Base class to handle Cinder v3 client microversion support."""
-    api_version = 'v3'
-    api_microversion_header_name = 'Openstack-Api-Version'
-
-    def get_headers(self, accept_type=None, send_type=None):
-        headers = super(BaseClient, self).get_headers(
-            accept_type=accept_type, send_type=send_type)
-        if VOLUME_MICROVERSION:
-            headers[self.api_microversion_header_name] = ('volume %s' %
-                                                          VOLUME_MICROVERSION)
-        return headers
-
-    def request(self, method, url, extra_headers=False, headers=None,
-                body=None, chunked=False):
-
-        resp, resp_body = super(BaseClient, self).request(
-            method, url, extra_headers, headers, body, chunked)
-        if (VOLUME_MICROVERSION and
-            VOLUME_MICROVERSION != api_version_utils.LATEST_MICROVERSION):
-            api_version_utils.assert_version_header_matches_request(
-                self.api_microversion_header_name,
-                'volume %s' % VOLUME_MICROVERSION,
-                resp)
-        return resp, resp_body
+BaseClient = moves.moved_class(base_client.BaseClient, 'BaseClient', __name__,
+                               version="Pike", removal_version='?')
+BaseClient.api_version = 'v3'
diff --git a/tempest/lib/services/volume/v3/group_snapshots_client.py b/tempest/lib/services/volume/v3/group_snapshots_client.py
new file mode 100644
index 0000000..e644f02
--- /dev/null
+++ b/tempest/lib/services/volume/v3/group_snapshots_client.py
@@ -0,0 +1,88 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume import base_client
+
+
+class GroupSnapshotsClient(base_client.BaseClient):
+    """Client class to send CRUD Volume Group Snapshot API requests"""
+    api_version = 'v3'
+
+    def create_group_snapshot(self, **kwargs):
+        """Creates a group snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#create-group-snapshot
+        """
+        post_body = json.dumps({'group_snapshot': kwargs})
+        resp, body = self.post('group_snapshots', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_group_snapshot(self, group_snapshot_id):
+        """Deletes a group snapshot.
+
+        For more information, please refer to the official API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#delete-group-snapshot
+        """
+        resp, body = self.delete('group_snapshots/%s' % group_snapshot_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_group_snapshot(self, group_snapshot_id):
+        """Returns the details of a single group snapshot.
+
+        For more information, please refer to the official API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#show-group-snapshot-details
+        """
+        url = "group_snapshots/%s" % str(group_snapshot_id)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_group_snapshots(self, **params):
+        """Information for all the tenant's group snapshots.
+
+        For more information, please refer to the official API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#list-group-snapshots
+        https://developer.openstack.org/api-ref/block-storage/v3/#list-group-snapshots-with-details
+        """
+        url = "group_snapshots"
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_group_snapshot(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'group-snapshot'
diff --git a/tempest/lib/services/volume/v3/group_types_client.py b/tempest/lib/services/volume/v3/group_types_client.py
new file mode 100644
index 0000000..97bac48
--- /dev/null
+++ b/tempest/lib/services/volume/v3/group_types_client.py
@@ -0,0 +1,77 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib.services.volume import base_client
+
+
+class GroupTypesClient(base_client.BaseClient):
+    """Client class to send CRUD Volume V3 Group Types API requests"""
+    api_version = 'v3'
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'group-type'
+
+    def create_group_type(self, **kwargs):
+        """Create group_type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#create-group-type
+        """
+        post_body = json.dumps({'group_type': kwargs})
+        resp, body = self.post('group_types', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_group_type(self, group_type_id):
+        """Deletes the specified group_type."""
+        resp, body = self.delete("group_types/%s" % group_type_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_group_types(self, **params):
+        """List all the group_types created.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#list-group-types
+        """
+        url = 'group_types'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_group_type(self, group_type_id):
+        """Returns the details of a single group_type.
+
+        For more information, please refer to the official API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#show-group-type-details
+        """
+        url = "group_types/%s" % group_type_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/groups_client.py b/tempest/lib/services/volume/v3/groups_client.py
new file mode 100644
index 0000000..b463fdf
--- /dev/null
+++ b/tempest/lib/services/volume/v3/groups_client.py
@@ -0,0 +1,122 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume import base_client
+
+
+class GroupsClient(base_client.BaseClient):
+    """Client class to send CRUD Volume Group API requests"""
+    api_version = 'v3'
+
+    def create_group(self, **kwargs):
+        """Creates a group.
+
+        group_type and volume_types are required parameters in kwargs.
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#create-group
+        """
+        post_body = json.dumps({'group': kwargs})
+        resp, body = self.post('groups', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_group(self, group_id, delete_volumes=True):
+        """Deletes a group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#delete-group
+        """
+        post_body = {'delete-volumes': delete_volumes}
+        post_body = json.dumps({'delete': post_body})
+        resp, body = self.post('groups/%s/action' % group_id,
+                               post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_group(self, group_id):
+        """Returns the details of a single group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#show-group-details
+        """
+        url = "groups/%s" % str(group_id)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_groups(self, detail=False, **params):
+        """Lists information for all the tenant's groups.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#list-groups
+        https://developer.openstack.org/api-ref/block-storage/v3/#list-groups-with-details
+        """
+        url = "groups"
+        if detail:
+            url += "/detail"
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_group_from_source(self, **kwargs):
+        """Creates a group from source.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#create-group-from-source
+        """
+        post_body = json.dumps({'create-from-src': kwargs})
+        resp, body = self.post('groups/action', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_group(self, group_id, **kwargs):
+        """Updates the specified group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#update-group
+        """
+        put_body = json.dumps({'group': kwargs})
+        resp, body = self.put('groups/%s' % group_id, put_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_group(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 'group'
diff --git a/tempest/lib/services/volume/v3/messages_client.py b/tempest/lib/services/volume/v3/messages_client.py
index 8a01864..0127271 100644
--- a/tempest/lib/services/volume/v3/messages_client.py
+++ b/tempest/lib/services/volume/v3/messages_client.py
@@ -17,11 +17,12 @@
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
-from tempest.lib.services.volume.v3 import base_client
+from tempest.lib.services.volume import base_client
 
 
 class MessagesClient(base_client.BaseClient):
     """Client class to send user messages API requests."""
+    api_version = 'v3'
 
     def show_message(self, message_id):
         """Show details for a single message."""
diff --git a/tempest/lib/services/volume/v3/snapshots_client.py b/tempest/lib/services/volume/v3/snapshots_client.py
new file mode 100644
index 0000000..88c094f
--- /dev/null
+++ b/tempest/lib/services/volume/v3/snapshots_client.py
@@ -0,0 +1,21 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v2 import snapshots_client
+
+
+class SnapshotsClient(snapshots_client.SnapshotsClient):
+    """Client class to send CRUD Volume Snapshot V3 API requests."""
+    api_version = "v3"
diff --git a/tempest/lib/services/volume/v3/versions_client.py b/tempest/lib/services/volume/v3/versions_client.py
new file mode 100644
index 0000000..5702f95
--- /dev/null
+++ b/tempest/lib/services/volume/v3/versions_client.py
@@ -0,0 +1,48 @@
+# Copyright 2017 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 time
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.volume import versions as schema
+from tempest.lib.common import rest_client
+from tempest.lib.services.volume import base_client
+
+
+class VersionsClient(base_client.BaseClient):
+    api_version = 'v3'
+
+    def list_versions(self):
+        """List API versions
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#list-all-api-versions
+        """
+        version_url = self._get_base_version_url()
+
+        start = time.time()
+        resp, body = self.raw_request(version_url, 'GET')
+        end = time.time()
+        # NOTE: We need a raw_request() here instead of request() call because
+        # "list API versions" API doesn't require an authentication and we can
+        # skip it with raw_request() call.
+        self._log_request('GET', version_url, resp, secs=(end - start),
+                          resp_body=body)
+        self._error_checker(resp, body)
+
+        body = json.loads(body)
+        self.validate_response(schema.list_versions, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/volumes_client.py b/tempest/lib/services/volume/v3/volumes_client.py
new file mode 100644
index 0000000..5f4b278
--- /dev/null
+++ b/tempest/lib/services/volume/v3/volumes_client.py
@@ -0,0 +1,40 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v2 import volumes_client
+
+
+class VolumesClient(volumes_client.VolumesClient):
+    """Client class to send CRUD Volume V3 API requests"""
+    api_version = "v3"
+
+    def show_volume_summary(self, **params):
+        """Get volumes summary.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#get-volumes-summary
+        """
+        url = 'volumes/summary'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/scenario/README.rst b/tempest/scenario/README.rst
index 38e0de9..c1dcccc 100644
--- a/tempest/scenario/README.rst
+++ b/tempest/scenario/README.rst
@@ -21,9 +21,9 @@
     4. create a snapshot of the vm
 
 
-Why are these tests in tempest?
+Why are these tests in Tempest?
 -------------------------------
-This is one of tempests core purposes, testing the integration between
+This is one of Tempest's core purposes, testing the integration between
 projects.
 
 
@@ -43,7 +43,7 @@
 specific in your interactions. A giant "this is my data center" smoke
 test is hard to debug when it goes wrong.
 
-A flow of interactions between glance and nova, like in the
+A flow of interactions between Glance and Nova, like in the
 introduction, is a good example. Especially if it involves a repeated
 interaction when a resource is setup, modified, detached, and then
 reused later again.
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 5422e16..2843222 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -23,12 +23,12 @@
 
 from tempest.common import compute
 from tempest.common import image as common_image
-from tempest.common.utils import data_utils
 from tempest.common.utils.linux import remote_client
 from tempest.common.utils import net_utils
 from tempest.common import waiters
 from tempest import config
 from tempest import exceptions
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
 import tempest.test
@@ -47,45 +47,42 @@
     def setup_clients(cls):
         super(ScenarioTest, cls).setup_clients()
         # Clients (in alphabetical order)
-        cls.flavors_client = cls.manager.flavors_client
+        cls.flavors_client = cls.os_primary.flavors_client
         cls.compute_floating_ips_client = (
-            cls.manager.compute_floating_ips_client)
+            cls.os_primary.compute_floating_ips_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.manager.image_client
+                cls.image_client = cls.os_primary.image_client
             elif CONF.image_feature_enabled.api_v2:
-                cls.image_client = cls.manager.image_client_v2
+                cls.image_client = cls.os_primary.image_client_v2
             else:
                 raise lib_exc.InvalidConfiguration(
                     'Either api_v1 or api_v2 must be True in '
                     '[image-feature-enabled].')
         # Compute image client
-        cls.compute_images_client = cls.manager.compute_images_client
-        cls.keypairs_client = cls.manager.keypairs_client
+        cls.compute_images_client = cls.os_primary.compute_images_client
+        cls.keypairs_client = cls.os_primary.keypairs_client
         # Nova security groups client
         cls.compute_security_groups_client = (
-            cls.manager.compute_security_groups_client)
+            cls.os_primary.compute_security_groups_client)
         cls.compute_security_group_rules_client = (
-            cls.manager.compute_security_group_rules_client)
-        cls.servers_client = cls.manager.servers_client
-        cls.interface_client = cls.manager.interfaces_client
+            cls.os_primary.compute_security_group_rules_client)
+        cls.servers_client = cls.os_primary.servers_client
+        cls.interface_client = cls.os_primary.interfaces_client
         # Neutron network client
-        cls.networks_client = cls.manager.networks_client
-        cls.ports_client = cls.manager.ports_client
-        cls.routers_client = cls.manager.routers_client
-        cls.subnets_client = cls.manager.subnets_client
-        cls.floating_ips_client = cls.manager.floating_ips_client
-        cls.security_groups_client = cls.manager.security_groups_client
+        cls.networks_client = cls.os_primary.networks_client
+        cls.ports_client = cls.os_primary.ports_client
+        cls.routers_client = cls.os_primary.routers_client
+        cls.subnets_client = cls.os_primary.subnets_client
+        cls.floating_ips_client = cls.os_primary.floating_ips_client
+        cls.security_groups_client = cls.os_primary.security_groups_client
         cls.security_group_rules_client = (
-            cls.manager.security_group_rules_client)
-
-        if CONF.volume_feature_enabled.api_v2:
-            cls.volumes_client = cls.manager.volumes_v2_client
-            cls.snapshots_client = cls.manager.snapshots_v2_client
-        else:
-            cls.volumes_client = cls.manager.volumes_client
-            cls.snapshots_client = cls.manager.snapshots_client
+            cls.os_primary.security_group_rules_client)
+        # Use the latest available volume clients
+        if CONF.service_available.cinder:
+            cls.volumes_client = cls.os_primary.volumes_client_latest
+            cls.snapshots_client = cls.os_primary.snapshots_client_latest
 
     # ## Test functions library
     #
@@ -117,7 +114,7 @@
         return body['keypair']
 
     def create_server(self, name=None, image_id=None, flavor=None,
-                      validatable=False, wait_until=None,
+                      validatable=False, wait_until='ACTIVE',
                       clients=None, **kwargs):
         """Wrapper utility that returns a test server.
 
@@ -138,7 +135,7 @@
 
         # Needed for the cross_tenant_traffic test:
         if clients is None:
-            clients = self.manager
+            clients = self.os_primary
 
         if name is None:
             name = data_utils.rand_name(self.__class__.__name__ + "-server")
@@ -200,7 +197,7 @@
 
         tenant_network = self.get_tenant_network()
 
-        body, servers = compute.create_test_server(
+        body, _ = compute.create_test_server(
             clients,
             tenant_network=tenant_network,
             wait_until=wait_until,
@@ -235,22 +232,34 @@
                         volume['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.volumes_client.delete_volume, volume['id'])
-
-        # NOTE(e0ne): Cinder API v2 uses name instead of display_name
-        if 'display_name' in volume:
-            self.assertEqual(name, volume['display_name'])
-        else:
-            self.assertEqual(name, volume['name'])
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume['id'], 'available')
+        self.assertEqual(name, volume['name'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
         # The volume retrieved on creation has a non-up-to-date status.
         # Retrieval after it becomes active ensures correct details.
         volume = self.volumes_client.show_volume(volume['id'])['volume']
         return volume
 
+    def create_volume_snapshot(self, volume_id, name=None, description=None,
+                               metadata=None, force=False):
+        name = name or data_utils.rand_name(
+            self.__class__.__name__ + '-snapshot')
+        snapshot = self.snapshots_client.create_snapshot(
+            volume_id=volume_id,
+            force=force,
+            display_name=name,
+            description=description,
+            metadata=metadata)['snapshot']
+        self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
+                        snapshot['id'])
+        self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
+        waiters.wait_for_volume_resource_status(self.snapshots_client,
+                                                snapshot['id'], 'available')
+        return snapshot
+
     def create_volume_type(self, client=None, name=None, backend_name=None):
         if not client:
-            client = self.admin_volume_types_client
+            client = self.os_admin.volume_types_v2_client
         if not name:
             class_name = self.__class__.__name__
             name = data_utils.rand_name(class_name + '-volume-type')
@@ -323,13 +332,15 @@
 
         return secgroup
 
-    def get_remote_client(self, ip_address, username=None, private_key=None):
+    def get_remote_client(self, ip_address, username=None, private_key=None,
+                          server=None):
         """Get a SSH client to a remote server
 
         @param ip_address the server floating or fixed IP address to use
                           for ssh validation
         @param username name of the Linux account on the remote server
         @param private_key the SSH private key to use
+        @param server: server dict, used for debugging purposes
         @return a RemoteClient object
         """
 
@@ -344,22 +355,10 @@
         else:
             password = CONF.validation.image_ssh_password
             private_key = None
-        linux_client = remote_client.RemoteClient(ip_address, username,
-                                                  pkey=private_key,
-                                                  password=password)
-        try:
-            linux_client.validate_authentication()
-        except Exception as e:
-            message = ('Initializing SSH connection to %(ip)s failed. '
-                       'Error: %(error)s' % {'ip': ip_address,
-                                             'error': e})
-            caller = test_utils.find_test_caller()
-            if caller:
-                message = '(%s) %s' % (caller, message)
-            LOG.exception(message)
-            self._log_console_output()
-            raise
-
+        linux_client = remote_client.RemoteClient(
+            ip_address, username, pkey=private_key, password=password,
+            server=server, servers_client=self.servers_client)
+        linux_client.validate_authentication()
         return linux_client
 
     def _image_create(self, name, fmt, path,
@@ -421,16 +420,17 @@
 
         return image
 
-    def _log_console_output(self, servers=None):
+    def _log_console_output(self, servers=None, client=None):
         if not CONF.compute_feature_enabled.console_output:
             LOG.debug('Console output not supported, cannot log')
             return
+        client = client or self.servers_client
         if not servers:
-            servers = self.servers_client.list_servers()
+            servers = client.list_servers()
             servers = servers['servers']
         for server in servers:
             try:
-                console_output = self.servers_client.get_console_output(
+                console_output = client.get_console_output(
                     server['id'])['output']
                 LOG.debug('Console output for %s\nbody=\n%s',
                           server['id'], console_output)
@@ -481,8 +481,9 @@
                 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                                 self.snapshots_client.delete_snapshot,
                                 snapshot_id)
-                waiters.wait_for_snapshot_status(self.snapshots_client,
-                                                 snapshot_id, 'available')
+                waiters.wait_for_volume_resource_status(self.snapshots_client,
+                                                        snapshot_id,
+                                                        'available')
         image_name = snapshot_image['name']
         self.assertEqual(name, image_name)
         LOG.debug("Created snapshot image %s for server %s",
@@ -494,16 +495,16 @@
             server['id'], volumeId=volume_to_attach['id'], device='/dev/%s'
             % CONF.compute.volume_device_name)['volumeAttachment']
         self.assertEqual(volume_to_attach['id'], volume['id'])
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume['id'], 'in-use')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'in-use')
 
         # Return the updated volume after the attachment
         return self.volumes_client.show_volume(volume['id'])['volume']
 
     def nova_volume_detach(self, server, volume):
         self.servers_client.detach_volume(server['id'], volume['id'])
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume['id'], 'available')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
 
         volume = self.volumes_client.show_volume(volume['id'])['volume']
         self.assertEqual('available', volume['status'])
@@ -634,13 +635,14 @@
                                             private_key=private_key)
         if dev_name is not None:
             ssh_client.make_fs(dev_name)
-            ssh_client.mount(dev_name, mount_path)
+            ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
+                                                               mount_path))
         cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
         ssh_client.exec_command(cmd_timestamp)
         timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
                                             % mount_path)
         if dev_name is not None:
-            ssh_client.umount(mount_path)
+            ssh_client.exec_command('sudo umount %s' % mount_path)
         return timestamp
 
     def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
@@ -652,7 +654,7 @@
         timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
                                             % mount_path)
         if dev_name is not None:
-            ssh_client.umount(mount_path)
+            ssh_client.exec_command('sudo umount %s' % mount_path)
         return timestamp
 
     def get_server_ip(self, server):
@@ -730,36 +732,6 @@
                         network['id'])
         return network
 
-    def _list_networks(self, *args, **kwargs):
-        """List networks using admin creds """
-        networks_list = self.admin_manager.networks_client.list_networks(
-            *args, **kwargs)
-        return networks_list['networks']
-
-    def _list_subnets(self, *args, **kwargs):
-        """List subnets using admin creds """
-        subnets_list = self.admin_manager.subnets_client.list_subnets(
-            *args, **kwargs)
-        return subnets_list['subnets']
-
-    def _list_routers(self, *args, **kwargs):
-        """List routers using admin creds """
-        routers_list = self.admin_manager.routers_client.list_routers(
-            *args, **kwargs)
-        return routers_list['routers']
-
-    def _list_ports(self, *args, **kwargs):
-        """List ports using admin creds """
-        ports_list = self.admin_manager.ports_client.list_ports(
-            *args, **kwargs)
-        return ports_list['ports']
-
-    def _list_agents(self, *args, **kwargs):
-        """List agents using admin creds """
-        agents_list = self.admin_manager.network_agents_client.list_agents(
-            *args, **kwargs)
-        return agents_list['agents']
-
     def _create_subnet(self, network, subnets_client=None,
                        routers_client=None, namestart='subnet-smoke',
                        **kwargs):
@@ -778,7 +750,8 @@
             :returns: True if subnet with cidr already exist in tenant
                   False else
             """
-            cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
+            cidr_in_use = self.os_admin.subnets_client.list_subnets(
+                tenant_id=tenant_id, cidr=cidr)['subnets']
             return len(cidr_in_use) != 0
 
         ip_version = kwargs.pop('ip_version', 4)
@@ -826,7 +799,8 @@
         return subnet
 
     def _get_server_port_id_and_ip4(self, server, ip_addr=None):
-        ports = self._list_ports(device_id=server['id'], fixed_ip=ip_addr)
+        ports = self.os_admin.ports_client.list_ports(
+            device_id=server['id'], fixed_ip=ip_addr)['ports']
         # A port can have more than one IP address in some cases.
         # If the network is dual-stack (IPv4 + IPv6), this port is associated
         # with 2 subnets
@@ -846,7 +820,7 @@
         if inactive:
             LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
 
-        self.assertNotEqual(0, len(port_map),
+        self.assertNotEmpty(port_map,
                             "No IPv4 addresses found in: %s" % ports)
         self.assertEqual(len(port_map), 1,
                          "Found multiple IPv4 addresses: %s. "
@@ -855,8 +829,9 @@
         return port_map[0]
 
     def _get_network_by_name(self, network_name):
-        net = self._list_networks(name=network_name)
-        self.assertNotEqual(len(net), 0,
+        net = self.os_admin.networks_client.list_networks(
+            name=network_name)['networks']
+        self.assertNotEmpty(net,
                             "Unable to get network by name: %s" % network_name)
         return net[0]
 
@@ -913,16 +888,16 @@
                       show_floatingip(floatingip_id)['floatingip'])
             return status == result['status']
 
-        test_utils.call_until_true(refresh,
-                                   CONF.network.build_timeout,
-                                   CONF.network.build_interval)
-        floating_ip = self.floating_ips_client.show_floatingip(
-            floatingip_id)['floatingip']
-        self.assertEqual(status, floating_ip['status'],
-                         message="FloatingIP: {fp} is at status: {cst}. "
-                                 "failed  to reach status: {st}"
-                         .format(fp=floating_ip, cst=floating_ip['status'],
-                                 st=status))
+        if not test_utils.call_until_true(refresh,
+                                          CONF.network.build_timeout,
+                                          CONF.network.build_interval):
+            floating_ip = self.floating_ips_client.show_floatingip(
+                floatingip_id)['floatingip']
+            self.assertEqual(status, floating_ip['status'],
+                             message="FloatingIP: {fp} is at status: {cst}. "
+                                     "failed  to reach status: {st}"
+                             .format(fp=floating_ip, cst=floating_ip['status'],
+                                     st=status))
         LOG.info("FloatingIP: {fp} is at status: {st}"
                  .format(fp=floating_ip, st=status))
 
@@ -938,7 +913,7 @@
         # The target login is assumed to have been configured for
         # key-based authentication by cloud-init.
         try:
-            for net_name, ip_addresses in server['addresses'].items():
+            for ip_addresses in server['addresses'].values():
                 for ip_address in ip_addresses:
                     self.check_vm_connectivity(ip_address['addr'],
                                                username,
@@ -952,14 +927,15 @@
 
     def _check_remote_connectivity(self, source, dest, should_succeed=True,
                                    nic=None):
-        """check ping server via source ssh connection
+        """assert ping server via source ssh connection
+
+        Note: This is an internal method.  Use check_remote_connectivity
+        instead.
 
         :param source: RemoteClient: an ssh connection from which to ping
         :param dest: and IP to ping against
         :param should_succeed: boolean should ping succeed or not
         :param nic: specific network interface to ping from
-        :returns: boolean -- should_succeed == ping
-        :returns: ping is false if ping failed
         """
         def ping_remote():
             try:
@@ -974,6 +950,25 @@
                                           CONF.validation.ping_timeout,
                                           1)
 
+    def check_remote_connectivity(self, source, dest, should_succeed=True,
+                                  nic=None):
+        """assert ping server via source ssh connection
+
+        :param source: RemoteClient: an ssh connection from which to ping
+        :param dest: and IP to ping against
+        :param should_succeed: boolean should ping succeed or not
+        :param nic: specific network interface to ping from
+        """
+        result = self._check_remote_connectivity(source, dest, should_succeed,
+                                                 nic)
+        source_host = source.ssh_client.host
+        if should_succeed:
+            msg = "Timed out waiting for %s to become reachable from %s" \
+                % (dest, source_host)
+        else:
+            msg = "%s is reachable from %s" % (dest, source_host)
+        self.assertTrue(result, msg)
+
     def _create_security_group(self, security_group_rules_client=None,
                                tenant_id=None,
                                namestart='secgroup-smoke',
@@ -1043,7 +1038,7 @@
             if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
         ]
         msg = "No default security group for tenant %s." % (tenant_id)
-        self.assertGreater(len(sgs), 0, msg)
+        self.assertNotEmpty(sgs, msg)
         return sgs[0]
 
     def _create_security_group_rule(self, secgroup=None,
@@ -1256,14 +1251,9 @@
     @classmethod
     def setup_clients(cls):
         super(EncryptionScenarioTest, cls).setup_clients()
-        if CONF.volume_feature_enabled.api_v2:
-            cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
-            cls.admin_encryption_types_client =\
-                cls.os_adm.encryption_types_v2_client
-        else:
-            cls.admin_volume_types_client = cls.os_adm.volume_types_client
-            cls.admin_encryption_types_client =\
-                cls.os_adm.encryption_types_client
+        cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
+        cls.admin_encryption_types_client =\
+            cls.os_admin.encryption_types_v2_client
 
     def create_encryption_type(self, client=None, type_id=None, provider=None,
                                key_size=None, cipher=None,
@@ -1278,6 +1268,17 @@
             type_id, provider=provider, key_size=key_size, cipher=cipher,
             control_location=control_location)['encryption']
 
+    def create_encrypted_volume(self, encryption_provider, volume_type,
+                                key_size=256, cipher='aes-xts-plain64',
+                                control_location='front-end'):
+        volume_type = self.create_volume_type(name=volume_type)
+        self.create_encryption_type(type_id=volume_type['id'],
+                                    provider=encryption_provider,
+                                    key_size=key_size,
+                                    cipher=cipher,
+                                    control_location=control_location)
+        return self.create_volume(volume_type=volume_type['name'])
+
 
 class ObjectStorageScenarioTest(ScenarioTest):
     """Provide harness to do Object Storage scenario tests.
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index 50fe9c8..9ff6227 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -14,10 +14,10 @@
 #    under the License.
 
 from tempest.common import tempest_fixtures as fixtures
-from tempest.common.utils import data_utils
+from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.scenario import manager
-from tempest import test
 
 
 class TestAggregatesBasicOps(manager.ScenarioTest):
@@ -36,9 +36,8 @@
     def setup_clients(cls):
         super(TestAggregatesBasicOps, cls).setup_clients()
         # Use admin client by default
-        cls.manager = cls.admin_manager
-        cls.aggregates_client = cls.manager.aggregates_client
-        cls.hosts_client = cls.manager.hosts_client
+        cls.aggregates_client = cls.os_admin.aggregates_client
+        cls.hosts_client = cls.os_admin.hosts_client
 
     def _create_aggregate(self, **kwargs):
         aggregate = (self.aggregates_client.create_aggregate(**kwargs)
@@ -53,7 +52,7 @@
 
     def _get_host_name(self):
         hosts = self.hosts_client.list_hosts()['hosts']
-        self.assertGreaterEqual(len(hosts), 1)
+        self.assertNotEmpty(hosts)
         computes = [x for x in hosts if x['service'] == 'compute']
         return computes[0]['host_name']
 
@@ -83,7 +82,7 @@
         aggregate = self.aggregates_client.set_metadata(aggregate['id'],
                                                         metadata=meta)
 
-        for key, value in meta.items():
+        for key in meta.keys():
             self.assertEqual(meta[key],
                              aggregate['aggregate']['metadata'][key])
 
@@ -97,7 +96,8 @@
         return aggregate
 
     @decorators.idempotent_id('cb2b4c4f-0c7c-4164-bdde-6285b302a081')
-    @test.services('compute')
+    @decorators.attr(type='slow')
+    @utils.services('compute')
     def test_aggregate_basic_ops(self):
         self.useFixture(fixtures.LockFixture('availability_zone'))
         az = 'foo_zone'
diff --git a/tempest/scenario/test_encrypted_cinder_volumes.py b/tempest/scenario/test_encrypted_cinder_volumes.py
index 77bd10f..b5220e9 100644
--- a/tempest/scenario/test_encrypted_cinder_volumes.py
+++ b/tempest/scenario/test_encrypted_cinder_volumes.py
@@ -13,10 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 
@@ -46,25 +46,15 @@
         image = self.glance_image_create()
         keypair = self.create_keypair()
 
-        return self.create_server(image_id=image,
-                                  key_name=keypair['name'],
-                                  wait_until='ACTIVE')
-
-    def create_encrypted_volume(self, encryption_provider, volume_type):
-        volume_type = self.create_volume_type(name=volume_type)
-        self.create_encryption_type(type_id=volume_type['id'],
-                                    provider=encryption_provider,
-                                    key_size=256,
-                                    cipher='aes-xts-plain64',
-                                    control_location='front-end')
-        return self.create_volume(volume_type=volume_type['name'])
+        return self.create_server(image_id=image, key_name=keypair['name'])
 
     def attach_detach_volume(self, server, volume):
         attached_volume = self.nova_volume_attach(server, volume)
         self.nova_volume_detach(server, attached_volume)
 
     @decorators.idempotent_id('79165fb4-5534-4b9d-8429-97ccffb8f86e')
-    @test.services('compute', 'volume', 'image')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'volume', 'image')
     def test_encrypted_cinder_volumes_luks(self):
         server = self.launch_instance()
         volume = self.create_encrypted_volume('nova.volume.encryptors.'
@@ -73,7 +63,8 @@
         self.attach_detach_volume(server, volume)
 
     @decorators.idempotent_id('cbc752ed-b716-4717-910f-956cce965722')
-    @test.services('compute', 'volume', 'image')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'volume', 'image')
     def test_encrypted_cinder_volumes_cryptsetup(self):
         server = self.launch_instance()
         volume = self.create_encrypted_volume('nova.volume.encryptors.'
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 4b1c362..29f1743 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -13,14 +13,16 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.common import custom_matchers
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 
@@ -92,21 +94,23 @@
             raise exceptions.TimeoutException(msg)
 
     def _get_floating_ip_in_server_addresses(self, floating_ip, server):
-        for network_name, addresses in server['addresses'].items():
+        for addresses in server['addresses'].values():
             for address in addresses:
                 if (address['OS-EXT-IPS:type'] == 'floating' and
                         address['addr'] == floating_ip['ip']):
                     return address
 
     @decorators.idempotent_id('bdbb5441-9204-419d-a225-b4fdbfb1a1a8')
-    @test.services('compute', 'volume', 'image', 'network')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    @testtools.skipUnless(CONF.network_feature_enabled.floating_ips,
+                          'Floating ips are not available')
+    @utils.services('compute', 'volume', 'image', 'network')
     def test_minimum_basic_scenario(self):
         image = self.glance_image_create()
         keypair = self.create_keypair()
 
-        server = self.create_server(image_id=image,
-                                    key_name=keypair['name'],
-                                    wait_until='ACTIVE')
+        server = self.create_server(image_id=image, key_name=keypair['name'])
         servers = self.servers_client.list_servers()['servers']
         self.assertIn(server['id'], [x['id'] for x in servers])
 
@@ -137,14 +141,16 @@
 
         # check that we can SSH to the server before reboot
         self.linux_client = self.get_remote_client(
-            floating_ip['ip'], private_key=keypair['private_key'])
+            floating_ip['ip'], private_key=keypair['private_key'],
+            server=server)
 
         self.nova_reboot(server)
 
         # check that we can SSH to the server after reboot
         # (both connections are part of the scenario)
         self.linux_client = self.get_remote_client(
-            floating_ip['ip'], private_key=keypair['private_key'])
+            floating_ip['ip'], private_key=keypair['private_key'],
+            server=server)
 
         self.check_disks()
 
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 7843531..340c3c9 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -15,11 +15,11 @@
 
 import testtools
 
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 
@@ -38,8 +38,7 @@
     @classmethod
     def setup_clients(cls):
         super(TestNetworkAdvancedServerOps, cls).setup_clients()
-        cls.admin_servers_client = cls.os_adm.servers_client
-        cls.admin_hosts_client = cls.os_adm.hosts_client
+        cls.admin_servers_client = cls.os_admin.servers_client
 
     @classmethod
     def skip_checks(cls):
@@ -49,6 +48,8 @@
             msg = ('Either project_networks_reachable must be "true", or '
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
+        if not CONF.network_feature_enabled.floating_ips:
+            raise cls.skipException("Floating ips are not available")
 
     @classmethod
     def setup_credentials(cls):
@@ -58,15 +59,14 @@
 
     def _setup_server(self, keypair):
         security_groups = []
-        if test.is_extension_enabled('security-group', 'network'):
+        if utils.is_extension_enabled('security-group', 'network'):
             security_group = self._create_security_group()
             security_groups = [{'name': security_group['name']}]
-        network, subnet, router = self.create_networks()
+        network, _, _ = self.create_networks()
         server = self.create_server(
             networks=[{'uuid': network['id']}],
             key_name=keypair['name'],
-            security_groups=security_groups,
-            wait_until='ACTIVE')
+            security_groups=security_groups)
         return server
 
     def _setup_network(self, server, keypair):
@@ -106,7 +106,8 @@
         return body['OS-EXT-SRV-ATTR:host']
 
     @decorators.idempotent_id('61f1aa9a-1573-410e-9054-afa557cab021')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_server_connectivity_stop_start(self):
         keypair = self.create_keypair()
         server = self._setup_server(keypair)
@@ -121,7 +122,7 @@
             server, keypair, floating_ip)
 
     @decorators.idempotent_id('7b6860c2-afa3-4846-9522-adeb38dfbe08')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_server_connectivity_reboot(self):
         keypair = self.create_keypair()
         server = self._setup_server(keypair)
@@ -131,7 +132,8 @@
             server, keypair, floating_ip)
 
     @decorators.idempotent_id('88a529c2-1daa-4c85-9aec-d541ba3eb699')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_server_connectivity_rebuild(self):
         keypair = self.create_keypair()
         server = self._setup_server(keypair)
@@ -145,7 +147,8 @@
     @decorators.idempotent_id('2b2642db-6568-4b35-b812-eceed3fa20ce')
     @testtools.skipUnless(CONF.compute_feature_enabled.pause,
                           'Pause is not available.')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_server_connectivity_pause_unpause(self):
         keypair = self.create_keypair()
         server = self._setup_server(keypair)
@@ -162,7 +165,8 @@
     @decorators.idempotent_id('5cdf9499-541d-4923-804e-b9a60620a7f0')
     @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
                           'Suspend is not available.')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_server_connectivity_suspend_resume(self):
         keypair = self.create_keypair()
         server = self._setup_server(keypair)
@@ -179,12 +183,10 @@
     @decorators.idempotent_id('719eb59d-2f42-4b66-b8b1-bb1254473967')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize is not available.')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_server_connectivity_resize(self):
         resize_flavor = CONF.compute.flavor_ref_alt
-        if resize_flavor == CONF.compute.flavor_ref:
-            msg = "Skipping test - flavor_ref and flavor_ref_alt are identical"
-            raise self.skipException(msg)
         keypair = self.create_keypair()
         server = self._setup_server(keypair)
         floating_ip = self._setup_network(server, keypair)
@@ -202,7 +204,8 @@
     @testtools.skipUnless(CONF.compute.min_compute_nodes > 1,
                           'Less than 2 compute nodes, skipping multinode '
                           'tests.')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_server_connectivity_cold_migration(self):
         keypair = self.create_keypair()
         server = self._setup_server(keypair)
@@ -227,7 +230,8 @@
     @testtools.skipUnless(CONF.compute.min_compute_nodes > 1,
                           'Less than 2 compute nodes, skipping multinode '
                           'tests.')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_server_connectivity_cold_migration_revert(self):
         keypair = self.create_keypair()
         server = self._setup_server(keypair)
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 69b5250..2d38b06 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -19,13 +19,13 @@
 from oslo_log import log as logging
 import testtools
 
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
@@ -87,9 +87,11 @@
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
         for ext in ['router', 'security-group']:
-            if not test.is_extension_enabled(ext, 'network'):
+            if not utils.is_extension_enabled(ext, 'network'):
                 msg = "%s extension not enabled." % ext
                 raise cls.skipException(msg)
+        if not CONF.network_feature_enabled.floating_ips:
+            raise cls.skipException("Floating ips are not available")
 
     @classmethod
     def setup_credentials(cls):
@@ -104,18 +106,17 @@
 
     def _setup_network_and_servers(self, **kwargs):
         boot_with_port = kwargs.pop('boot_with_port', False)
-        self.security_group = self._create_security_group()
         self.network, self.subnet, self.router = self.create_networks(**kwargs)
         self.check_networks()
 
         self.ports = []
-        self.port_id = None
+        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.ports.append({'port': self.port_id})
+            port_id = self._create_port(self.network['id'])['id']
+            self.ports.append({'port': port_id})
 
-        server = self._create_server(self.network, self.port_id)
+        server = self._create_server(self.network, port_id)
         self._check_tenant_network_connectivity()
 
         floating_ip = self.create_floating_ip(server)
@@ -127,23 +128,23 @@
         via checking the result of list_[networks,routers,subnets]
         """
 
-        seen_nets = self._list_networks()
-        seen_names = [n['name'] for n in seen_nets]
-        seen_ids = [n['id'] for n in seen_nets]
+        seen_nets = self.os_admin.networks_client.list_networks()
+        seen_names = [n['name'] for n in seen_nets['networks']]
+        seen_ids = [n['id'] for n in seen_nets['networks']]
         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]
+            seen_subnets = self.os_admin.subnets_client.list_subnets()
+            seen_net_ids = [n['network_id'] for n in seen_subnets['subnets']]
+            seen_subnet_ids = [n['id'] for n in seen_subnets['subnets']]
             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]
+            seen_routers = self.os_admin.routers_client.list_routers()
+            seen_router_ids = [n['id'] for n in seen_routers['routers']]
+            seen_router_names = [n['name'] for n in seen_routers['routers']]
             self.assertIn(self.router['name'],
                           seen_router_names)
             self.assertIn(self.router['id'],
@@ -152,7 +153,9 @@
     def _create_server(self, network, port_id=None):
         keypair = self.create_keypair()
         self.keypairs[keypair['name']] = keypair
-        security_groups = [{'name': self.security_group['name']}]
+        security_groups = [
+            {'name': self._create_security_group()['name']}
+        ]
         network = {'uuid': network['id']}
         if port_id is not None:
             network['port'] = port_id
@@ -160,8 +163,7 @@
         server = self.create_server(
             networks=[network],
             key_name=keypair['name'],
-            security_groups=security_groups,
-            wait_until='ACTIVE')
+            security_groups=security_groups)
         self.servers.append(server)
         return server
 
@@ -210,7 +212,7 @@
             self.servers, mtu=mtu)
 
     def _disassociate_floating_ips(self):
-        floating_ip, server = self.floating_ip_tuple
+        floating_ip, _ = self.floating_ip_tuple
         self._disassociate_floating_ip(floating_ip)
         self.floating_ip_tuple = Floating_IP_tuple(
             floating_ip, None)
@@ -238,10 +240,11 @@
         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)
+            ip_address, private_key=private_key, server=server)
         old_nic_list = self._get_server_nics(ssh_client)
         # get a port from a list of one item
-        port_list = self._list_ports(device_id=server['id'])
+        port_list = self.os_admin.ports_client.list_ports(
+            device_id=server['id'])['ports']
         self.assertEqual(1, len(port_list))
         old_port = port_list[0]
         interface = self.interface_client.create_interface(
@@ -254,9 +257,12 @@
                         server['id'], interface['port_id'])
 
         def check_ports():
-            self.new_port_list = [port for port in
-                                  self._list_ports(device_id=server['id'])
-                                  if port['id'] != old_port['id']]
+            self.new_port_list = [
+                port for port in
+                self.os_admin.ports_client.list_ports(
+                    device_id=server['id'])['ports']
+                if port['id'] != old_port['id']
+            ]
             return len(self.new_port_list) == 1
 
         if not test_utils.call_until_true(
@@ -281,15 +287,16 @@
                                               "guest after %s sec"
                                               % CONF.network.build_timeout)
 
-        num, new_nic = self.diff_list[0]
-        ssh_client.assign_static_ip(nic=new_nic,
-                                    addr=new_port['fixed_ips'][0][
-                                        'ip_address'])
-        ssh_client.set_nic_state(nic=new_nic)
+        _, new_nic = self.diff_list[0]
+        ssh_client.exec_command("sudo ip addr add %s/%s dev %s" % (
+                                new_port['fixed_ips'][0]['ip_address'],
+                                CONF.network.project_network_mask_bits,
+                                new_nic))
+        ssh_client.exec_command("sudo ip link set %s up" % new_nic)
 
     def _get_server_nics(self, ssh_client):
-        reg = re.compile(r'(?P<num>\d+): (?P<nic_name>\w+):')
-        ipatxt = ssh_client.get_ip_list()
+        reg = re.compile(r'(?P<num>\d+): (?P<nic_name>\w+)[@]?.*:')
+        ipatxt = ssh_client.exec_command("ip address")
         return reg.findall(ipatxt)
 
     def _check_network_internal_connectivity(self, network,
@@ -302,10 +309,13 @@
         floating_ip, server = self.floating_ip_tuple
         # get internal ports' ips:
         # 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'])
-                        if p['device_owner'].startswith('network'))
+        internal_ips = (
+            p['fixed_ips'][0]['ip_address'] for p in
+            self.os_admin.ports_client.list_ports(
+                tenant_id=server['tenant_id'],
+                network_id=network['id'])['ports']
+            if p['device_owner'].startswith('network')
+        )
 
         self._check_server_connectivity(floating_ip,
                                         internal_ips,
@@ -321,8 +331,11 @@
         # We ping the external IP from the instance using its floating IP
         # which is always IPv4, so we must only test connectivity to
         # external IPv4 IPs if the external network is dualstack.
-        v4_subnets = [s for s in self._list_subnets(
-            network_id=CONF.network.public_network_id) if s['ip_version'] == 4]
+        v4_subnets = [
+            s for s in self.os_admin.subnets_client.list_subnets(
+                network_id=CONF.network.public_network_id)['subnets']
+            if s['ip_version'] == 4
+        ]
         self.assertEqual(1, len(v4_subnets),
                          "Found %d IPv4 subnets" % len(v4_subnets))
 
@@ -335,27 +348,16 @@
         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)
+            ip_address, private_key=private_key,
+            server=self.floating_ip_tuple.server)
 
         for remote_ip in address_list:
-            if should_connect:
-                msg = ("Timed out waiting for %s to become "
-                       "reachable") % remote_ip
-            else:
-                msg = "ip address %s is reachable" % remote_ip
-            try:
-                self.assertTrue(self._check_remote_connectivity
-                                (ssh_source, remote_ip, should_connect),
-                                msg)
-            except Exception:
-                LOG.exception("Unable to access {dest} via ssh to "
-                              "floating-ip {src}".format(dest=remote_ip,
-                                                         src=floating_ip))
-                raise
+            self.check_remote_connectivity(ssh_source, remote_ip,
+                                           should_connect)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('f323b3ba-82f8-4db7-8ea6-6a895869ec49')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_network_basic_ops(self):
         """Basic network operation test
 
@@ -407,9 +409,10 @@
                                                    "floating ip")
 
     @decorators.idempotent_id('b158ea55-472e-4086-8fa9-c64ac0c6c1d0')
-    @testtools.skipUnless(test.is_extension_enabled('net-mtu', 'network'),
+    @testtools.skipUnless(utils.is_extension_enabled('net-mtu', 'network'),
                           'No way to calculate MTU for networks')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_mtu_sized_frames(self):
         """Validate that network MTU sized frames fit through."""
         self._setup_network_and_servers()
@@ -421,7 +424,8 @@
                       'Connectivity can only be tested when in a '
                       'multitenant network environment')
     @decorators.skip_because(bug="1610994")
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_connectivity_between_vms_on_different_networks(self):
         """Test connectivity between VMs on different networks
 
@@ -475,7 +479,7 @@
     @testtools.skipIf(CONF.network.port_vnic_type in ['direct', 'macvtap'],
                       'NIC hotplug not supported for '
                       'vnic_type direct or macvtap')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_hotplug_nic(self):
         """Test hotplug network interface
 
@@ -496,7 +500,8 @@
     @testtools.skipIf(CONF.network.shared_physical_network,
                       'Router state can be altered only with multitenant '
                       'networks capabilities')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_update_router_admin_state(self):
         """Test to update admin state up of router
 
@@ -529,7 +534,8 @@
                       'network isolation not available')
     @testtools.skipUnless(CONF.scenario.dhcp_client,
                           "DHCP client is not available.")
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_subnet_details(self):
         """Tests that subnet's extra configuration details are affecting VMs.
 
@@ -570,7 +576,7 @@
         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)
+            ip_address, private_key=private_key, server=server)
 
         dns_servers = [initial_dns_server]
         servers = ssh_client.get_dns_servers()
@@ -595,7 +601,8 @@
             # NOTE(amuller): we are renewing the lease as part of the retry
             # because Neutron updates dnsmasq asynchronously after the
             # subnet-update API call returns.
-            ssh_client.renew_lease(fixed_ip=floating_ip['fixed_ip_address'])
+            ssh_client.renew_lease(fixed_ip=floating_ip['fixed_ip_address'],
+                                   dhcp_client=CONF.scenario.dhcp_client)
             if ssh_client.get_dns_servers() != [alt_dns_server]:
                 LOG.debug("Failed to update DNS nameservers")
                 return False
@@ -611,7 +618,8 @@
     @testtools.skipUnless(CONF.network_feature_enabled.port_admin_state_change,
                           "Changing a port's admin state is not supported "
                           "by the test environment")
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_update_instance_port_admin_state(self):
         """Test to update admin_state_up attribute of instance port
 
@@ -623,9 +631,10 @@
                 admin_state_up attribute of instance port to True
         """
         self._setup_network_and_servers()
-        floating_ip, server = self.floating_ip_tuple
+        _, server = self.floating_ip_tuple
         server_id = server['id']
-        port_id = self._list_ports(device_id=server_id)[0]['id']
+        port_id = self.os_admin.ports_client.list_ports(
+            device_id=server_id)['ports'][0]['id']
         server_pip = server['addresses'][self.network['name']][0]['addr']
 
         server2 = self._create_server(self.network)
@@ -633,29 +642,31 @@
 
         private_key = self._get_server_key(server2)
         ssh_client = self.get_remote_client(server2_fip['floating_ip_address'],
-                                            private_key=private_key)
+                                            private_key=private_key,
+                                            server=server2)
 
         self.check_public_network_connectivity(
             should_connect=True, msg="before updating "
             "admin_state_up of instance port to False")
-        self._check_remote_connectivity(ssh_client, dest=server_pip,
-                                        should_succeed=True)
+        self.check_remote_connectivity(ssh_client, dest=server_pip,
+                                       should_succeed=True)
         self.ports_client.update_port(port_id, admin_state_up=False)
         self.check_public_network_connectivity(
             should_connect=False, msg="after updating "
             "admin_state_up of instance port to False",
             should_check_floating_ip_status=False)
-        self._check_remote_connectivity(ssh_client, dest=server_pip,
-                                        should_succeed=False)
+        self.check_remote_connectivity(ssh_client, dest=server_pip,
+                                       should_succeed=False)
         self.ports_client.update_port(port_id, admin_state_up=True)
         self.check_public_network_connectivity(
             should_connect=True, msg="after updating "
             "admin_state_up of instance port to True")
-        self._check_remote_connectivity(ssh_client, dest=server_pip,
-                                        should_succeed=True)
+        self.check_remote_connectivity(ssh_client, dest=server_pip,
+                                       should_succeed=True)
 
     @decorators.idempotent_id('759462e1-8535-46b0-ab3a-33aa45c55aaa')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_preserve_preexisting_port(self):
         """Test preserve pre-existing port
 
@@ -678,8 +689,8 @@
                              'Server should have been created from a '
                              'pre-existing port.')
         # Assert the port is bound to the server.
-        port_list = self._list_ports(device_id=server['id'],
-                                     network_id=self.network['id'])
+        port_list = self.os_admin.ports_client.list_ports(
+            device_id=server['id'], network_id=self.network['id'])['ports']
         self.assertEqual(1, len(port_list),
                          'There should only be one port created for '
                          'server %s.' % server['id'])
@@ -697,16 +708,17 @@
         # Boot another server with the same port to make sure nothing was
         # left around that could cause issues.
         server = self._create_server(self.network, port['id'])
-        port_list = self._list_ports(device_id=server['id'],
-                                     network_id=self.network['id'])
+        port_list = self.os_admin.ports_client.list_ports(
+            device_id=server['id'], network_id=self.network['id'])['ports']
         self.assertEqual(1, len(port_list),
                          'There should only be one port created for '
                          'server %s.' % server['id'])
         self.assertEqual(port['id'], port_list[0]['id'])
 
-    @test.requires_ext(service='network', extension='l3_agent_scheduler')
+    @utils.requires_ext(service='network', extension='l3_agent_scheduler')
     @decorators.idempotent_id('2e788c46-fb3f-4ac9-8f82-0561555bea73')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_router_rescheduling(self):
         """Tests that router can be removed from agent and add to a new agent.
 
@@ -721,21 +733,23 @@
         # TODO(yfried): refactor this test to be used for other agents (dhcp)
         # as well
 
-        list_hosts = (self.admin_manager.routers_client.
+        list_hosts = (self.os_admin.routers_client.
                       list_l3_agents_hosting_router)
-        schedule_router = (self.admin_manager.network_agents_client.
+        schedule_router = (self.os_admin.network_agents_client.
                            create_router_on_l3_agent)
-        unschedule_router = (self.admin_manager.network_agents_client.
+        unschedule_router = (self.os_admin.network_agents_client.
                              delete_router_from_l3_agent)
 
-        agent_list_alive = set(a["id"] for a in
-                               self._list_agents(agent_type="L3 agent") if
-                               a["alive"] is True)
+        agent_list_alive = set(
+            a["id"] for a in
+            self.os_admin.network_agents_client.list_agents(
+                agent_type="L3 agent")['agents'] if a["alive"] is True
+        )
         self._setup_network_and_servers()
 
         # NOTE(kevinbenton): we have to use the admin credentials to check
         # for the distributed flag because self.router only has a project view.
-        admin = self.admin_manager.routers_client.show_router(
+        admin = self.os_admin.routers_client.show_router(
             self.router['id'])
         if admin['router'].get('distributed', False):
             msg = "Rescheduling test does not apply to distributed routers."
@@ -779,11 +793,12 @@
             should_connect=True,
             msg='After router rescheduling')
 
-    @test.requires_ext(service='network', extension='port-security')
+    @utils.requires_ext(service='network', extension='port-security')
     @testtools.skipUnless(CONF.compute_feature_enabled.interface_attach,
                           'NIC hotplug not available')
     @decorators.idempotent_id('7c0bb1a2-d053-49a4-98f9-ca1a1d849f63')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_port_security_macspoofing_port(self):
         """Tests port_security extension enforces mac spoofing
 
@@ -812,24 +827,31 @@
         self._create_new_network()
         self._hotplug_server()
         fip, server = self.floating_ip_tuple
-        new_ports = self._list_ports(device_id=server["id"],
-                                     network_id=self.new_net["id"])
+        new_ports = self.os_admin.ports_client.list_ports(
+            device_id=server["id"], network_id=self.new_net["id"])['ports']
         spoof_port = new_ports[0]
         private_key = self._get_server_key(server)
         ssh_client = self.get_remote_client(fip['floating_ip_address'],
-                                            private_key=private_key)
+                                            private_key=private_key,
+                                            server=server)
         spoof_nic = ssh_client.get_nic_name_by_mac(spoof_port["mac_address"])
         peer = self._create_server(self.new_net)
         peer_address = peer['addresses'][self.new_net['name']][0]['addr']
-        self._check_remote_connectivity(ssh_client, dest=peer_address,
-                                        nic=spoof_nic, should_succeed=True)
-        ssh_client.set_mac_address(spoof_nic, spoof_mac)
+        self.check_remote_connectivity(ssh_client, dest=peer_address,
+                                       nic=spoof_nic, should_succeed=True)
+        # Set a mac address by making nic down temporary
+        cmd = ("sudo ip link set {nic} down;"
+               "sudo ip link set dev {nic} address {mac};"
+               "sudo ip link set {nic} up").format(nic=spoof_nic,
+                                                   mac=spoof_mac)
+        ssh_client.exec_command(cmd)
+
         new_mac = ssh_client.get_mac_address(nic=spoof_nic)
         self.assertEqual(spoof_mac, new_mac)
-        self._check_remote_connectivity(ssh_client, dest=peer_address,
-                                        nic=spoof_nic, should_succeed=False)
+        self.check_remote_connectivity(ssh_client, dest=peer_address,
+                                       nic=spoof_nic, should_succeed=False)
         self.ports_client.update_port(spoof_port["id"],
                                       port_security_enabled=False,
                                       security_groups=[])
-        self._check_remote_connectivity(ssh_client, dest=peer_address,
-                                        nic=spoof_nic, should_succeed=True)
+        self.check_remote_connectivity(ssh_client, dest=peer_address,
+                                       nic=spoof_nic, should_succeed=True)
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index f235934..b687aa0 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -14,11 +14,11 @@
 #    under the License.
 import functools
 
+from tempest.common import utils
 from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 
@@ -51,6 +51,8 @@
         if CONF.network.shared_physical_network:
             msg = 'Deployment uses a shared physical network'
             raise cls.skipException(msg)
+        if not CONF.network_feature_enabled.floating_ips:
+            raise cls.skipException("Floating ips are not available")
 
     @classmethod
     def setup_credentials(cls):
@@ -72,11 +74,11 @@
         if dualnet - create IPv6 subnets on a different network
         :return: list of created networks
         """
-        self.network = self._create_network()
+        network = self._create_network()
         if dualnet:
-            self.network_v6 = self._create_network()
+            network_v6 = self._create_network()
 
-        sub4 = self._create_subnet(network=self.network,
+        sub4 = self._create_subnet(network=network,
                                    namestart='sub4',
                                    ip_version=4)
 
@@ -90,7 +92,7 @@
 
         self.subnets_v6 = []
         for _ in range(n_subnets6):
-            net6 = self.network_v6 if dualnet else self.network
+            net6 = network_v6 if dualnet else network
             sub6 = self._create_subnet(network=net6,
                                        namestart='sub6',
                                        ip_version=6,
@@ -105,12 +107,12 @@
                             router['id'], subnet_id=sub6['id'])
 
             self.subnets_v6.append(sub6)
-        return [self.network, self.network_v6] if dualnet else [self.network]
+        return [network, network_v6] if dualnet else [network]
 
     @staticmethod
     def define_server_ips(srv):
         ips = {'4': None, '6': []}
-        for net_name, nics in srv['addresses'].items():
+        for nics in srv['addresses'].values():
             for nic in nics:
                 if nic['version'] == 6:
                     ips['6'].append(nic['addr'])
@@ -121,21 +123,18 @@
     def prepare_server(self, networks=None):
         username = CONF.validation.image_ssh_user
 
-        networks = networks or [self.network]
-
         srv = self.create_server(
             key_name=self.keypair['name'],
             security_groups=[{'name': self.sec_grp['name']}],
-            networks=[{'uuid': n['id']} for n in networks],
-            wait_until='ACTIVE')
+            networks=[{'uuid': n['id']} for n in networks])
         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'],
-            username=username)
+            username=username, server=srv)
         return ssh, ips, srv["id"]
 
-    def turn_nic6_on(self, ssh, sid):
+    def turn_nic6_on(self, ssh, sid, network_id):
         """Turns the IPv6 vNIC on
 
         Required because guest images usually set only the first vNIC on boot.
@@ -143,16 +142,21 @@
 
         @param ssh: RemoteClient ssh instance to server
         @param sid: server uuid
+        @param network_id: the network id the NIC is connected to
         """
-        ports = [p["mac_address"] for p in
-                 self._list_ports(device_id=sid,
-                                  network_id=self.network_v6['id'])]
+        ports = [
+            p["mac_address"] for p in
+            self.os_admin.ports_client.list_ports(
+                device_id=sid, network_id=network_id)['ports']
+        ]
+
         self.assertEqual(1, len(ports),
                          message=("Multiple IPv6 ports found on network %s. "
                                   "ports: %s")
-                         % (self.network_v6, ports))
+                         % (network_id, ports))
         mac6 = ports[0]
-        ssh.set_nic_state(ssh.get_nic_name_by_mac(mac6))
+        nic = ssh.get_nic_name_by_mac(mac6)
+        ssh.exec_command("sudo ip link set %s up" % nic)
 
     def _prepare_and_test(self, address6_mode, n_subnets6=1, dualnet=False):
         net_list = self.prepare_network(address6_mode=address6_mode,
@@ -163,16 +167,17 @@
         sshv4_2, ips_from_api_2, sid2 = self.prepare_server(networks=net_list)
 
         def guest_has_address(ssh, addr):
-            return addr in ssh.get_ip_list()
+            return addr in ssh.exec_command("ip address")
 
         # Turn on 2nd NIC for Cirros when dualnet
         if dualnet:
-            self.turn_nic6_on(sshv4_1, sid1)
-            self.turn_nic6_on(sshv4_2, sid2)
+            _, network_v6 = net_list
+            self.turn_nic6_on(sshv4_1, sid1, network_v6['id'])
+            self.turn_nic6_on(sshv4_2, sid2, network_v6['id'])
 
         # get addresses assigned to vNIC as reported by 'ip address' utility
-        ips_from_ip_1 = sshv4_1.get_ip_list()
-        ips_from_ip_2 = sshv4_2.get_ip_list()
+        ips_from_ip_1 = sshv4_1.exec_command("ip address")
+        ips_from_ip_2 = sshv4_2.exec_command("ip address")
         self.assertIn(ips_from_api_1['4'], ips_from_ip_1)
         self.assertIn(ips_from_api_2['4'], ips_from_ip_2)
         for i in range(n_subnets6):
@@ -190,70 +195,64 @@
             self.assertTrue(test_utils.call_until_true(srv2_v6_addr_assigned,
                             CONF.validation.ping_timeout, 1))
 
-        self._check_connectivity(sshv4_1, ips_from_api_2['4'])
-        self._check_connectivity(sshv4_2, ips_from_api_1['4'])
+        self.check_remote_connectivity(sshv4_1, ips_from_api_2['4'])
+        self.check_remote_connectivity(sshv4_2, ips_from_api_1['4'])
 
         for i in range(n_subnets6):
-            self._check_connectivity(sshv4_1,
-                                     ips_from_api_2['6'][i])
-            self._check_connectivity(sshv4_1,
-                                     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.check_remote_connectivity(sshv4_1,
+                                           ips_from_api_2['6'][i])
+            self.check_remote_connectivity(sshv4_1,
+                                           self.subnets_v6[i]['gateway_ip'])
+            self.check_remote_connectivity(sshv4_2,
+                                           ips_from_api_1['6'][i])
+            self.check_remote_connectivity(sshv4_2,
+                                           self.subnets_v6[i]['gateway_ip'])
 
-    def _check_connectivity(self, source, dest):
-        self.assertTrue(
-            self._check_remote_connectivity(source, dest),
-            "Timed out waiting for %s to become reachable from %s" %
-            (dest, source.ssh_client.host)
-        )
-
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('2c92df61-29f0-4eaa-bee3-7c65bef62a43')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_slaac_from_os(self):
         self._prepare_and_test(address6_mode='slaac')
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('d7e1f858-187c-45a6-89c9-bdafde619a9f')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_dhcp6_stateless_from_os(self):
         self._prepare_and_test(address6_mode='dhcpv6-stateless')
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('7ab23f41-833b-4a16-a7c9-5b42fe6d4123')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_multi_prefix_dhcpv6_stateless(self):
         self._prepare_and_test(address6_mode='dhcpv6-stateless', n_subnets6=2)
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('dec222b1-180c-4098-b8c5-cc1b8342d611')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_multi_prefix_slaac(self):
         self._prepare_and_test(address6_mode='slaac', n_subnets6=2)
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('b6399d76-4438-4658-bcf5-0d6c8584fde2')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_dualnet_slaac_from_os(self):
         self._prepare_and_test(address6_mode='slaac', dualnet=True)
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('76f26acd-9688-42b4-bc3e-cd134c4cb09e')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_dualnet_dhcp6_stateless_from_os(self):
         self._prepare_and_test(address6_mode='dhcpv6-stateless', dualnet=True)
 
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('cf1c4425-766b-45b8-be35-e2959728eb00')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_dualnet_multi_prefix_dhcpv6_stateless(self):
         self._prepare_and_test(address6_mode='dhcpv6-stateless', n_subnets6=2,
                                dualnet=True)
 
     @decorators.idempotent_id('9178ad42-10e4-47e9-8987-e02b170cc5cd')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_dualnet_multi_prefix_slaac(self):
         self._prepare_and_test(address6_mode='slaac', n_subnets6=2,
                                dualnet=True)
diff --git a/tempest/scenario/test_object_storage_basic_ops.py b/tempest/scenario/test_object_storage_basic_ops.py
index c989e01..da0b1e8 100644
--- a/tempest/scenario/test_object_storage_basic_ops.py
+++ b/tempest/scenario/test_object_storage_basic_ops.py
@@ -13,14 +13,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.common import utils
 from tempest.lib import decorators
 from tempest.scenario import manager
-from tempest import test
 
 
 class TestObjectStorageBasicOps(manager.ObjectStorageScenarioTest):
     @decorators.idempotent_id('b920faf1-7b8a-4657-b9fe-9c4512bfb381')
-    @test.services('object_storage')
+    @utils.services('object_storage')
     def test_swift_basic_ops(self):
         """Test swift basic ops.
 
@@ -46,7 +46,8 @@
         self.delete_container(container_name)
 
     @decorators.idempotent_id('916c7111-cb1f-44b2-816d-8f760e4ea910')
-    @test.services('object_storage')
+    @decorators.attr(type='slow')
+    @utils.services('object_storage')
     def test_swift_acl_anonymous_download(self):
         """This test will cover below steps:
 
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 07f9d0f..e39afe0 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -15,12 +15,13 @@
 from oslo_log import log
 import testtools
 
-from tempest.common.utils import data_utils
+from tempest.common import compute
+from tempest.common import utils
 from tempest.common.utils import net_info
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 LOG = log.getLogger(__name__)
@@ -122,6 +123,7 @@
             self.router = None
             self.security_groups = {}
             self.servers = list()
+            self.access_point = None
 
         def set_network(self, network, subnet, router):
             self.network = network
@@ -140,13 +142,15 @@
             msg = ('Either project_networks_reachable must be "true", or '
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
-        if not test.is_extension_enabled('security-group', 'network'):
+        if not utils.is_extension_enabled('security-group', 'network'):
             msg = "security-group extension not enabled."
             raise cls.skipException(msg)
         if CONF.network.shared_physical_network:
             msg = ('Deployment uses a shared physical network, security '
                    'groups not supported')
             raise cls.skipException(msg)
+        if not CONF.network_feature_enabled.floating_ips:
+            raise cls.skipException("Floating ips are not available")
 
     @classmethod
     def setup_credentials(cls):
@@ -159,7 +163,7 @@
         super(TestSecurityGroupsBasicOps, cls).resource_setup()
 
         cls.multi_node = CONF.compute.min_compute_nodes > 1 and \
-            test.is_scheduler_filter_enabled("DifferentHostFilter")
+            compute.is_scheduler_filter_enabled("DifferentHostFilter")
         if cls.multi_node:
             LOG.info("Working in Multi Node mode")
         else:
@@ -167,7 +171,7 @@
 
         cls.floating_ips = {}
         cls.tenants = {}
-        cls.primary_tenant = cls.TenantProperties(cls.os)
+        cls.primary_tenant = cls.TenantProperties(cls.os_primary)
         cls.alt_tenant = cls.TenantProperties(cls.os_alt)
         for tenant in [cls.primary_tenant, cls.alt_tenant]:
             cls.tenants[tenant.creds.tenant_id] = tenant
@@ -220,30 +224,36 @@
         # Checks that we see the newly created network/subnet/router via
         # checking the result of list_[networks,routers,subnets]
         # Check that (router, subnet) couple exist in port_list
-        seen_nets = self._list_networks()
-        seen_names = [n['name'] for n in seen_nets]
-        seen_ids = [n['id'] for n in seen_nets]
+        seen_nets = self.os_admin.networks_client.list_networks()
+        seen_names = [n['name'] for n in seen_nets['networks']]
+        seen_ids = [n['id'] for n in seen_nets['networks']]
 
         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()]
+        seen_subnets = [
+            (n['id'], n['cidr'], n['network_id']) for n in
+            self.os_admin.subnets_client.list_subnets()['subnets']
+        ]
         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]
+        seen_routers = self.os_admin.routers_client.list_routers()
+        seen_router_ids = [n['id'] for n in seen_routers['routers']]
+        seen_router_names = [n['name'] for n in seen_routers['routers']]
 
         self.assertIn(tenant.router['name'], seen_router_names)
         self.assertIn(tenant.router['id'], seen_router_ids)
 
         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 net_info.is_router_interface_port(i)]
+        router_ports = [
+            (i['device_id'], f['subnet_id'])
+            for i in self.os_admin.ports_client.list_ports(
+                device_id=tenant.router['id'])['ports']
+            if net_info.is_router_interface_port(i)
+            for f in i['fixed_ips']
+        ]
 
         self.assertIn(myport, router_ports)
 
@@ -263,7 +273,6 @@
             networks=[{'uuid': tenant.network["id"]}],
             key_name=tenant.keypair['name'],
             security_groups=security_groups_names,
-            wait_until='ACTIVE',
             clients=tenant.manager,
             **kwargs)
         if 'security_groups' in server:
@@ -273,7 +282,7 @@
 
         # Verify servers are on different compute nodes
         if self.multi_node:
-            adm_get_server = self.admin_manager.servers_client.show_server
+            adm_get_server = self.os_admin.servers_client.show_server
             new_host = adm_get_server(server["id"])["server"][
                 "OS-EXT-SRV-ATTR:host"]
             host_list = [adm_get_server(s)["server"]["OS-EXT-SRV-ATTR:host"]
@@ -363,20 +372,12 @@
             access_point_ssh, private_key=private_key)
         return access_point_ssh
 
-    def _check_connectivity(self, access_point, ip, should_succeed=True):
-        if should_succeed:
-            msg = "Timed out waiting for %s to become reachable" % ip
-        else:
-            msg = "%s is reachable" % ip
-        self.assertTrue(self._check_remote_connectivity(access_point, ip,
-                                                        should_succeed), msg)
-
     def _test_in_tenant_block(self, tenant):
         access_point_ssh = self._connect_to_access_point(tenant)
         for server in tenant.servers:
-            self._check_connectivity(access_point=access_point_ssh,
-                                     ip=self._get_server_ip(server),
-                                     should_succeed=False)
+            self.check_remote_connectivity(source=access_point_ssh,
+                                           dest=self._get_server_ip(server),
+                                           should_succeed=False)
 
     def _test_in_tenant_allow(self, tenant):
         ruleset = dict(
@@ -391,8 +392,8 @@
         )
         access_point_ssh = self._connect_to_access_point(tenant)
         for server in tenant.servers:
-            self._check_connectivity(access_point=access_point_ssh,
-                                     ip=self._get_server_ip(server))
+            self.check_remote_connectivity(source=access_point_ssh,
+                                           dest=self._get_server_ip(server))
 
     def _test_cross_tenant_block(self, source_tenant, dest_tenant):
         # if public router isn't defined, then dest_tenant access is via
@@ -400,8 +401,8 @@
         access_point_ssh = self._connect_to_access_point(source_tenant)
         ip = self._get_server_ip(dest_tenant.access_point,
                                  floating=self.floating_ip_access)
-        self._check_connectivity(access_point=access_point_ssh, ip=ip,
-                                 should_succeed=False)
+        self.check_remote_connectivity(source=access_point_ssh, dest=ip,
+                                       should_succeed=False)
 
     def _test_cross_tenant_allow(self, source_tenant, dest_tenant):
         """check for each direction:
@@ -422,7 +423,7 @@
         access_point_ssh = self._connect_to_access_point(source_tenant)
         ip = self._get_server_ip(dest_tenant.access_point,
                                  floating=self.floating_ip_access)
-        self._check_connectivity(access_point_ssh, ip)
+        self.check_remote_connectivity(access_point_ssh, ip)
 
         # test that reverse traffic is still blocked
         self._test_cross_tenant_block(dest_tenant, source_tenant)
@@ -439,7 +440,7 @@
         access_point_ssh_2 = self._connect_to_access_point(dest_tenant)
         ip = self._get_server_ip(source_tenant.access_point,
                                  floating=self.floating_ip_access)
-        self._check_connectivity(access_point_ssh_2, ip)
+        self.check_remote_connectivity(access_point_ssh_2, ip)
 
     def _verify_mac_addr(self, tenant):
         """Verify that VM has the same ip, mac as listed in port"""
@@ -449,7 +450,8 @@
         mac_addr = mac_addr.strip().lower()
         # Get the fixed_ips and mac_address fields of all ports. Select
         # only those two columns to reduce the size of the response.
-        port_list = self._list_ports(fields=['fixed_ips', 'mac_address'])
+        port_list = self.os_admin.ports_client.list_ports(
+            fields=['fixed_ips', 'mac_address'])['ports']
         port_detail_list = [
             (port['fixed_ips'][0]['subnet_id'],
              port['fixed_ips'][0]['ip_address'],
@@ -460,8 +462,16 @@
         subnet_id = tenant.subnet['id']
         self.assertIn((subnet_id, server_ip, mac_addr), port_detail_list)
 
+    def _log_console_output_for_all_tenants(self):
+        for tenant in self.tenants.values():
+            client = tenant.manager.servers_client
+            self._log_console_output(servers=tenant.servers, client=client)
+            if tenant.access_point is not None:
+                self._log_console_output(
+                    servers=[tenant.access_point], client=client)
+
     @decorators.idempotent_id('e79f879e-debb-440c-a7e4-efeda05b6848')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_cross_tenant_traffic(self):
         if not self.credentials_provider.is_multi_tenant():
             raise self.skipException("No secondary tenant defined")
@@ -477,12 +487,11 @@
             self._test_cross_tenant_block(source_tenant, dest_tenant)
             self._test_cross_tenant_allow(source_tenant, dest_tenant)
         except Exception:
-            for tenant in self.tenants.values():
-                self._log_console_output(servers=tenant.servers)
+            self._log_console_output_for_all_tenants()
             raise
 
     @decorators.idempotent_id('63163892-bbf6-4249-aa12-d5ea1f8f421b')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_in_tenant_traffic(self):
         try:
             self._create_tenant_servers(self.primary_tenant, num=1)
@@ -491,12 +500,12 @@
             self._test_in_tenant_block(self.primary_tenant)
             self._test_in_tenant_allow(self.primary_tenant)
         except Exception:
-            for tenant in self.tenants.values():
-                self._log_console_output(servers=tenant.servers)
+            self._log_console_output_for_all_tenants()
             raise
 
     @decorators.idempotent_id('f4d556d7-1526-42ad-bafb-6bebf48568f6')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_port_update_new_security_group(self):
         """Verifies the traffic after updating the vm port
 
@@ -531,25 +540,26 @@
         # Check connectivity failure with default security group
         try:
             access_point_ssh = self._connect_to_access_point(new_tenant)
-            self._check_connectivity(access_point=access_point_ssh,
-                                     ip=self._get_server_ip(server),
-                                     should_succeed=False)
+            self.check_remote_connectivity(source=access_point_ssh,
+                                           dest=self._get_server_ip(server),
+                                           should_succeed=False)
             server_id = server['id']
-            port_id = self._list_ports(device_id=server_id)[0]['id']
+            port_id = self.os_admin.ports_client.list_ports(
+                device_id=server_id)['ports'][0]['id']
 
             # 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']])
-            self._check_connectivity(
-                access_point=access_point_ssh,
-                ip=self._get_server_ip(server))
+            self.check_remote_connectivity(
+                source=access_point_ssh,
+                dest=self._get_server_ip(server))
         except Exception:
-            for tenant in self.tenants.values():
-                self._log_console_output(servers=tenant.servers)
+            self._log_console_output_for_all_tenants()
             raise
 
     @decorators.idempotent_id('d2f77418-fcc4-439d-b935-72eca704e293')
-    @test.services('compute', 'network')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
     def test_multiple_security_groups(self):
         """Verify multiple security groups and checks that rules
 
@@ -580,9 +590,10 @@
                                    private_key=private_key,
                                    should_connect=True)
 
-    @test.requires_ext(service='network', extension='port-security')
+    @decorators.attr(type='slow')
+    @utils.requires_ext(service='network', extension='port-security')
     @decorators.idempotent_id('7c811dcc-263b-49a3-92d2-1b4d8405f50c')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_port_security_disable_security_group(self):
         """Verify the default security group rules is disabled."""
         new_tenant = self.primary_tenant
@@ -597,33 +608,31 @@
 
         access_point_ssh = self._connect_to_access_point(new_tenant)
         server_id = server['id']
-        port_id = self._list_ports(device_id=server_id)[0]['id']
+        port_id = self.os_admin.ports_client.list_ports(
+            device_id=server_id)['ports'][0]['id']
 
         # Flip the port's port security and check connectivity
         try:
             self.ports_client.update_port(port_id,
                                           port_security_enabled=True,
                                           security_groups=[])
-            self._check_connectivity(access_point=access_point_ssh,
-                                     ip=self._get_server_ip(server),
-                                     should_succeed=False)
+            self.check_remote_connectivity(source=access_point_ssh,
+                                           dest=self._get_server_ip(server),
+                                           should_succeed=False)
 
             self.ports_client.update_port(port_id,
                                           port_security_enabled=False,
                                           security_groups=[])
-            self._check_connectivity(
-                access_point=access_point_ssh,
-                ip=self._get_server_ip(server))
+            self.check_remote_connectivity(
+                source=access_point_ssh,
+                dest=self._get_server_ip(server))
         except Exception:
-            for tenant in self.tenants.values():
-                self._log_console_output(servers=tenant.servers)
+            self._log_console_output_for_all_tenants()
             raise
 
-    @test.requires_ext(service='network', extension='port-security')
+    @decorators.attr(type='slow')
+    @utils.requires_ext(service='network', extension='port-security')
     @decorators.idempotent_id('13ccf253-e5ad-424b-9c4a-97b88a026699')
-    @testtools.skipUnless(
-        CONF.compute_feature_enabled.allow_port_security_disabled,
-        'Port security must be enabled.')
     # TODO(mriedem): We shouldn't actually need to check this since neutron
     # disables the port_security extension by default, but the problem is nova
     # assumes port_security_enabled=True if it's not set on the network
@@ -632,7 +641,7 @@
     @testtools.skipUnless(
         CONF.network_feature_enabled.port_security,
         'Port security must be enabled.')
-    @test.services('compute', 'network')
+    @utils.services('compute', 'network')
     def test_boot_into_disabled_port_security_network_without_secgroup(self):
         tenant = self.primary_tenant
         self._create_tenant_network(tenant, port_security_enabled=False)
@@ -641,7 +650,8 @@
         sec_groups = []
         server = self._create_server(name, tenant, sec_groups)
         server_id = server['id']
-        ports = self._list_ports(device_id=server_id)
+        ports = self.os_admin.ports_client.list_ports(
+            device_id=server_id)['ports']
         self.assertEqual(1, len(ports))
         for port in ports:
             self.assertEmpty(port['security_groups'],
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 6cc5cd3..d4f29ad 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -16,11 +16,11 @@
 from oslo_log import log as logging
 import testtools
 
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 
@@ -37,24 +37,18 @@
     """
 
     @classmethod
-    def skip_checks(cls):
-        super(TestServerAdvancedOps, cls).skip_checks()
-        if CONF.compute.flavor_ref_alt == CONF.compute.flavor_ref:
-            msg = "Skipping test - flavor_ref and flavor_ref_alt are identical"
-            raise cls.skipException(msg)
-
-    @classmethod
     def setup_credentials(cls):
         cls.set_network_resources()
         super(TestServerAdvancedOps, cls).setup_credentials()
 
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('e6c28180-7454-4b59-b188-0257af08a63b')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize is not available.')
-    @test.services('compute', 'volume')
+    @utils.services('compute', 'volume')
     def test_resize_volume_backed_server_confirm(self):
         # We create an instance for use in this test
-        instance = self.create_server(wait_until='ACTIVE', volume_backed=True)
+        instance = self.create_server(volume_backed=True)
         instance_id = instance['id']
         resize_flavor = CONF.compute.flavor_ref_alt
         LOG.debug("Resizing instance %s from flavor %s to flavor %s",
@@ -69,37 +63,22 @@
         waiters.wait_for_server_status(self.servers_client, instance_id,
                                        'ACTIVE')
 
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('949da7d5-72c8-4808-8802-e3d70df98e2c')
     @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
                           'Suspend is not available.')
-    @test.services('compute')
+    @utils.services('compute')
     def test_server_sequence_suspend_resume(self):
         # We create an instance for use in this test
-        instance = self.create_server(wait_until='ACTIVE')
-        instance_id = instance['id']
-        LOG.debug("Suspending instance %s. Current status: %s",
-                  instance_id, instance['status'])
-        self.servers_client.suspend_server(instance_id)
-        waiters.wait_for_server_status(self.servers_client, instance_id,
-                                       'SUSPENDED')
-        fetched_instance = (self.servers_client.show_server(instance_id)
-                            ['server'])
-        LOG.debug("Resuming instance %s. Current status: %s",
-                  instance_id, fetched_instance['status'])
-        self.servers_client.resume_server(instance_id)
-        waiters.wait_for_server_status(self.servers_client, instance_id,
-                                       'ACTIVE')
-        fetched_instance = (self.servers_client.show_server(instance_id)
-                            ['server'])
-        LOG.debug("Suspending instance %s. Current status: %s",
-                  instance_id, fetched_instance['status'])
-        self.servers_client.suspend_server(instance_id)
-        waiters.wait_for_server_status(self.servers_client, instance_id,
-                                       'SUSPENDED')
-        fetched_instance = (self.servers_client.show_server(instance_id)
-                            ['server'])
-        LOG.debug("Resuming instance %s. Current status: %s",
-                  instance_id, fetched_instance['status'])
-        self.servers_client.resume_server(instance_id)
-        waiters.wait_for_server_status(self.servers_client, instance_id,
-                                       'ACTIVE')
+        instance_id = self.create_server()['id']
+
+        for _ in range(2):
+            LOG.debug("Suspending instance %s", instance_id)
+            self.servers_client.suspend_server(instance_id)
+            waiters.wait_for_server_status(self.servers_client, instance_id,
+                                           'SUSPENDED')
+
+            LOG.debug("Resuming instance %s", instance_id)
+            self.servers_client.resume_server(instance_id)
+            waiters.wait_for_server_status(self.servers_client, instance_id,
+                                           'ACTIVE')
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 1af68ff..d5c378e 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -16,6 +16,7 @@
 import json
 import re
 
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import data_utils
@@ -23,7 +24,6 @@
 from tempest.lib import decorators
 from tempest.lib import exceptions
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 
@@ -43,10 +43,14 @@
      * Terminate the instance
     """
 
+    @classmethod
+    def skip_checks(cls):
+        super(TestServerBasicOps, cls).skip_checks()
+        if not CONF.network_feature_enabled.floating_ips:
+            raise cls.skipException("Floating ips are not available")
+
     def setUp(self):
         super(TestServerBasicOps, self).setUp()
-        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
 
@@ -58,7 +62,8 @@
             self.ssh_client = self.get_remote_client(
                 ip_address=self.fip,
                 username=self.ssh_user,
-                private_key=keypair['private_key'])
+                private_key=keypair['private_key'],
+                server=self.instance)
 
     def verify_metadata(self):
         if self.run_ssh and CONF.compute_feature_enabled.metadata_service:
@@ -126,20 +131,17 @@
             # instance info and do direct comparison.
 
     @decorators.idempotent_id('7fff3fb3-91d8-4fd0-bd7d-0204f1f180ba')
-    @test.attr(type='smoke')
-    @test.services('compute', 'network')
+    @decorators.attr(type='smoke')
+    @utils.services('compute', 'network')
     def test_server_basic_ops(self):
         keypair = self.create_keypair()
         security_group = self._create_security_group()
         self.md = {'meta1': 'data1', 'meta2': 'data2', 'metaN': 'dataN'}
         self.instance = self.create_server(
-            image_id=self.image_ref,
-            flavor=self.flavor_ref,
             key_name=keypair['name'],
             security_groups=[{'name': security_group['name']}],
             config_drive=CONF.compute_feature_enabled.config_drive,
-            metadata=self.md,
-            wait_until='ACTIVE')
+            metadata=self.md)
         self.verify_ssh(keypair)
         self.verify_metadata()
         self.verify_metadata_on_config_drive()
diff --git a/tempest/scenario/test_server_multinode.py b/tempest/scenario/test_server_multinode.py
index df83063..fdf875c 100644
--- a/tempest/scenario/test_server_multinode.py
+++ b/tempest/scenario/test_server_multinode.py
@@ -13,12 +13,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 
@@ -35,21 +34,12 @@
             raise cls.skipException(
                 "Less than 2 compute nodes, skipping multinode tests.")
 
-    @classmethod
-    def setup_clients(cls):
-        super(TestServerMultinode, cls).setup_clients()
-        # Use admin client by default
-        cls.manager = cls.admin_manager
-        # this is needed so that we can use the availability_zone:host
-        # scheduler hint, which is admin_only by default
-        cls.servers_client = cls.admin_manager.servers_client
-
     @decorators.idempotent_id('9cecbe35-b9d4-48da-a37e-7ce70aa43d30')
-    @test.attr(type='smoke')
-    @test.services('compute', 'network')
+    @decorators.attr(type='smoke')
+    @utils.services('compute', 'network')
     def test_schedule_to_all_nodes(self):
         available_zone = \
-            self.os_adm.availability_zone_client.list_availability_zones(
+            self.os_admin.availability_zone_client.list_availability_zones(
                 detail=True)['availabilityZoneInfo']
         hosts = []
         for zone in available_zone:
@@ -74,10 +64,15 @@
         for host in hosts[:CONF.compute.min_compute_nodes]:
             # by getting to active state here, this means this has
             # landed on the host in question.
+            # in order to use the availability_zone:host scheduler hint,
+            # admin client is need here.
             inst = self.create_server(
-                availability_zone='%(zone)s:%(host_name)s' % host,
-                wait_until='ACTIVE')
-            server = self.servers_client.show_server(inst['id'])['server']
+                clients=self.os_admin,
+                availability_zone='%(zone)s:%(host_name)s' % host)
+            server = self.os_admin.servers_client.show_server(
+                inst['id'])['server']
+            # ensure server is located on the requested host
+            self.assertEqual(host['host_name'], server['OS-EXT-SRV-ATTR:host'])
             servers.append(server)
 
         # make sure we really have the number of servers we think we should
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 2264a98..68f18d1 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -13,12 +13,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.common import compute
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 
@@ -55,10 +57,8 @@
         security_groups = [{'name': security_group['name']}]
 
         server = self.create_server(
-            image_id=CONF.compute.image_ref,
             key_name=keypair['name'],
             security_groups=security_groups,
-            wait_until='ACTIVE',
             volume_backed=boot_from_volume)
 
         instance_ip = self.get_server_ip(server)
@@ -74,12 +74,18 @@
                                         private_key=keypair['private_key'])
         self.assertEqual(timestamp, timestamp2)
 
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('1164e700-0af0-4a4c-8792-35909a88743c')
-    @test.services('compute', 'network', 'image')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    @utils.services('compute', 'network', 'image')
     def test_shelve_instance(self):
         self._create_server_then_shelve_and_unshelve()
 
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('c1b6318c-b9da-490b-9c67-9339b627271f')
-    @test.services('compute', 'volume', 'network', 'image')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    @utils.services('compute', 'volume', 'network', 'image')
     def test_shelve_volume_backed_instance(self):
         self._create_server_then_shelve_and_unshelve(boot_from_volume=True)
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 374cdba..b51a781 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -13,10 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 
@@ -39,7 +41,10 @@
             raise cls.skipException("Snapshotting is not available.")
 
     @decorators.idempotent_id('608e604b-1d63-4a82-8e3e-91bc665c90b4')
-    @test.services('compute', 'network', 'image')
+    @decorators.attr(type='slow')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    @utils.services('compute', 'network', 'image')
     def test_snapshot_pattern(self):
         # prepare for booting an instance
         keypair = self.create_keypair()
@@ -47,10 +52,8 @@
 
         # boot an instance and create a timestamp file in it
         server = self.create_server(
-            image_id=CONF.compute.image_ref,
             key_name=keypair['name'],
-            security_groups=[{'name': security_group['name']}],
-            wait_until='ACTIVE')
+            security_groups=[{'name': security_group['name']}])
 
         instance_ip = self.get_server_ip(server)
         timestamp = self.create_timestamp(instance_ip,
@@ -63,8 +66,7 @@
         server_from_snapshot = self.create_server(
             image_id=snapshot_image['id'],
             key_name=keypair['name'],
-            security_groups=[{'name': security_group['name']}],
-            wait_until='ACTIVE')
+            security_groups=[{'name': security_group['name']}])
 
         # check the existence of the timestamp file in the second instance
         server_from_snapshot_ip = self.get_server_ip(server_from_snapshot)
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index a302b1c..ef369d6 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -13,19 +13,15 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import time
-
 from oslo_log import log as logging
 import testtools
 
-from tempest.common.utils import data_utils
-from tempest.common import waiters
+from tempest.common import utils
 from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
@@ -59,27 +55,6 @@
         if not CONF.volume_feature_enabled.snapshot:
             raise cls.skipException("Cinder volume snapshots are disabled")
 
-    def _create_volume_snapshot(self, volume):
-        snapshot_name = data_utils.rand_name('scenario-snapshot')
-        snapshot = self.snapshots_client.create_snapshot(
-            volume_id=volume['id'], display_name=snapshot_name)['snapshot']
-
-        def cleaner():
-            self.snapshots_client.delete_snapshot(snapshot['id'])
-            try:
-                while self.snapshots_client.show_snapshot(
-                        snapshot['id'])['snapshot']:
-                    time.sleep(1)
-            except lib_exc.NotFound:
-                pass
-        self.addCleanup(cleaner)
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume['id'], 'available')
-        waiters.wait_for_snapshot_status(self.snapshots_client,
-                                         snapshot['id'], 'available')
-        self.assertEqual(snapshot_name, snapshot['display_name'])
-        return snapshot
-
     def _wait_for_volume_available_on_the_system(self, ip_address,
                                                  private_key):
         ssh = self.get_remote_client(ip_address, private_key=private_key)
@@ -94,11 +69,14 @@
                                           CONF.compute.build_interval):
             raise lib_exc.TimeoutException
 
-    @decorators.skip_because(bug="1205344")
+    @decorators.attr(type='slow')
+    @decorators.skip_because(bug="1664793")
     @decorators.idempotent_id('10fd234a-515c-41e5-b092-8323060598c5')
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
                           'Snapshotting is not available.')
-    @test.services('compute', 'network', 'volume', 'image')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    @utils.services('compute', 'network', 'volume', 'image')
     def test_stamp_pattern(self):
         # prepare for booting an instance
         keypair = self.create_keypair()
@@ -107,10 +85,8 @@
         # boot an instance and create a timestamp file in it
         volume = self.create_volume()
         server = self.create_server(
-            image_id=CONF.compute.image_ref,
             key_name=keypair['name'],
-            security_groups=security_group,
-            wait_until='ACTIVE')
+            security_groups=[{'name': security_group['name']}])
 
         # create and add floating IP to server1
         ip_for_server = self.get_server_ip(server)
@@ -124,7 +100,7 @@
         self.nova_volume_detach(server, volume)
 
         # snapshot the volume
-        volume_snapshot = self._create_volume_snapshot(volume)
+        volume_snapshot = self.create_volume_snapshot(volume['id'])
 
         # snapshot the instance
         snapshot_image = self.create_server_snapshot(server=server)
@@ -137,7 +113,7 @@
         server_from_snapshot = self.create_server(
             image_id=snapshot_image['id'],
             key_name=keypair['name'],
-            security_groups=security_group)
+            security_groups=[{'name': security_group['name']}])
 
         # create and add floating IP to server_from_snapshot
         ip_for_snapshot = self.get_server_ip(server_from_snapshot)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index e34efca..64ea8f6 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -11,19 +11,20 @@
 #    under the License.
 
 from oslo_log import log as logging
+import testtools
 
-from tempest.common.utils import data_utils
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
-class TestVolumeBootPattern(manager.ScenarioTest):
+class TestVolumeBootPattern(manager.EncryptionScenarioTest):
 
     # Boot from volume scenario is quite slow, and needs extra
     # breathing room to get through deletes in the time allotted.
@@ -42,16 +43,13 @@
         return self.create_volume(name=vol_name, imageRef=img_uuid)
 
     def _get_bdm(self, source_id, source_type, delete_on_termination=False):
-        # NOTE(gfidente): the syntax for block_device_mapping is
-        # dev_name=id:type:size:delete_on_terminate
-        # where type needs to be "snap" if the server is booted
-        # from a snapshot, size instead can be safely left empty
-
-        bd_map = [{
-            'device_name': 'vda',
-            '{}_id'.format(source_type): source_id,
-            'delete_on_termination': str(int(delete_on_termination))}]
-        return {'block_device_mapping': bd_map}
+        bd_map_v2 = [{
+            'uuid': source_id,
+            'source_type': source_type,
+            'destination_type': 'volume',
+            'boot_index': 0,
+            'delete_on_termination': delete_on_termination}]
+        return {'block_device_mapping_v2': bd_map_v2}
 
     def _boot_instance_from_resource(self, source_id,
                                      source_type,
@@ -69,39 +67,16 @@
             source_type,
             delete_on_termination=delete_on_termination))
 
-        return self.create_server(
-            image_id='',
-            wait_until='ACTIVE',
-            **create_kwargs)
-
-    def _create_snapshot_from_volume(self, vol_id):
-        snap_name = data_utils.rand_name(
-            self.__class__.__name__ + '-snapshot')
-        snap = self.snapshots_client.create_snapshot(
-            volume_id=vol_id,
-            force=True,
-            display_name=snap_name)['snapshot']
-        self.addCleanup(
-            self.snapshots_client.wait_for_resource_deletion, snap['id'])
-        self.addCleanup(self.snapshots_client.delete_snapshot, snap['id'])
-        waiters.wait_for_snapshot_status(self.snapshots_client,
-                                         snap['id'], 'available')
-
-        # NOTE(e0ne): Cinder API v2 uses name instead of display_name
-        if 'display_name' in snap:
-            self.assertEqual(snap_name, snap['display_name'])
-        else:
-            self.assertEqual(snap_name, snap['name'])
-
-        return snap
+        return self.create_server(image_id='', **create_kwargs)
 
     def _delete_server(self, server):
         self.servers_client.delete_server(server['id'])
         waiters.wait_for_server_termination(self.servers_client, server['id'])
 
     @decorators.idempotent_id('557cd2c2-4eb8-4dce-98be-f86765ff311b')
-    @test.attr(type='smoke')
-    @test.services('compute', 'volume', 'image')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    @utils.services('compute', 'volume', 'image')
     def test_volume_boot_pattern(self):
 
         """This test case attempts to reproduce the following steps:
@@ -157,7 +132,7 @@
 
         # snapshot a volume
         LOG.info("Creating snapshot from volume: %s", volume_origin['id'])
-        snapshot = self._create_snapshot_from_volume(volume_origin['id'])
+        snapshot = self.create_volume_snapshot(volume_origin['id'], force=True)
 
         # create a 3rd instance from snapshot
         LOG.info("Creating third instance from snapshot: %s", snapshot['id'])
@@ -180,13 +155,14 @@
         self.assertEqual(timestamp, timestamp3)
 
     @decorators.idempotent_id('05795fb2-b2a7-4c9f-8fac-ff25aedb1489')
-    @test.services('compute', 'image', 'volume')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'image', 'volume')
     def test_create_server_from_volume_snapshot(self):
         # Create a volume from an image
         boot_volume = self._create_volume_from_image()
 
         # Create a snapshot
-        boot_snapshot = self._create_snapshot_from_volume(boot_volume['id'])
+        boot_snapshot = self.create_volume_snapshot(boot_volume['id'])
 
         # Create a server from a volume snapshot
         server = self._boot_instance_from_resource(
@@ -199,6 +175,8 @@
         # The created volume when creating a server from a snapshot
         created_volume = server_info['os-extended-volumes:volumes_attached']
 
+        self.assertNotEmpty(created_volume, "No volume attachment found.")
+
         created_volume_info = self.volumes_client.show_volume(
             created_volume[0]['id'])['volume']
 
@@ -214,7 +192,7 @@
                          created_volume_info['attachments'][0]['volume_id'])
 
     @decorators.idempotent_id('36c34c67-7b54-4b59-b188-02a2f458a63b')
-    @test.services('compute', 'volume', 'image')
+    @utils.services('compute', 'volume', 'image')
     def test_create_ebs_image_and_check_boot(self):
         # create an instance from volume
         volume_origin = self._create_volume_from_image()
@@ -229,20 +207,30 @@
         self._delete_server(instance)
 
         # boot instance from EBS image
-        instance = self.create_server(
-            image_id=image['id'])
+        instance = self.create_server(image_id=image['id'])
         # just ensure that instance booted
 
         # delete instance
         self._delete_server(instance)
 
+    @decorators.idempotent_id('cb78919a-e553-4bab-b73b-10cf4d2eb125')
+    @testtools.skipUnless(CONF.compute_feature_enabled.attach_encrypted_volume,
+                          'Encrypted volume attach is not supported')
+    @utils.services('compute', 'volume')
+    def test_boot_server_from_encrypted_volume_luks(self):
+        # Create an encrypted volume
+        volume = self.create_encrypted_volume('nova.volume.encryptors.'
+                                              'luks.LuksEncryptor',
+                                              volume_type='luks')
 
-class TestVolumeBootPatternV2(TestVolumeBootPattern):
-    def _get_bdm(self, source_id, source_type, delete_on_termination=False):
-        bd_map_v2 = [{
-            'uuid': source_id,
-            'source_type': source_type,
-            'destination_type': 'volume',
-            'boot_index': 0,
-            'delete_on_termination': delete_on_termination}]
-        return {'block_device_mapping_v2': bd_map_v2}
+        self.volumes_client.set_bootable_volume(volume['id'], bootable=True)
+
+        # Boot a server from the encrypted volume
+        server = self._boot_instance_from_resource(
+            source_id=volume['id'],
+            source_type='volume',
+            delete_on_termination=False)
+
+        server_info = self.servers_client.show_server(server['id'])['server']
+        created_volume = server_info['os-extended-volumes:volumes_attached']
+        self.assertEqual(volume['id'], created_volume[0]['id'])
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index 161e6f2..cd10bbd 100644
--- a/tempest/scenario/test_volume_migrate_attached.py
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -12,11 +12,11 @@
 
 from oslo_log import log as logging
 
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
 from tempest.scenario import manager
-from tempest import test
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
@@ -38,14 +38,6 @@
     credentials = ['primary', 'admin']
 
     @classmethod
-    def setup_clients(cls):
-        super(TestVolumeMigrateRetypeAttached, cls).setup_clients()
-        if CONF.volume_feature_enabled.api_v1:
-            cls.admin_volume_types_client = cls.os_adm.volume_types_client
-        else:
-            cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
-
-    @classmethod
     def skip_checks(cls):
         super(TestVolumeMigrateRetypeAttached, cls).skip_checks()
         if not CONF.volume_feature_enabled.multi_backend:
@@ -62,7 +54,7 @@
         block_device_mapping = [{'device_name': 'vda', 'volume_id': vol_id,
                                  'delete_on_termination': False}]
 
-        return self.create_server(image_id='', wait_until='ACTIVE',
+        return self.create_server(image_id='',
                                   key_name=key_name,
                                   security_groups=security_groups,
                                   block_device_mapping=block_device_mapping)
@@ -91,8 +83,9 @@
         waiters.wait_for_volume_retype(self.volumes_client,
                                        volume_id, new_volume_type)
 
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('deadd2c2-beef-4dce-98be-f86765ff311b')
-    @test.services('compute', 'volume')
+    @utils.services('compute', 'volume')
     def test_volume_migrate_attached(self):
         LOG.info("Creating keypair and security group")
         keypair = self.create_keypair()
diff --git a/tempest/services/object_storage/__init__.py b/tempest/services/object_storage/__init__.py
index d1a61d6..a2f0992 100644
--- a/tempest/services/object_storage/__init__.py
+++ b/tempest/services/object_storage/__init__.py
@@ -12,11 +12,13 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-from tempest.services.object_storage.account_client import AccountClient
-from tempest.services.object_storage.capabilities_client import \
+from tempest.lib.services.object_storage.bulk_middleware_client import \
+    BulkMiddlewareClient
+from tempest.lib.services.object_storage.capabilities_client import \
     CapabilitiesClient
+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', 'CapabilitiesClient', 'ContainerClient',
-           'ObjectClient']
+__all__ = ['AccountClient', 'BulkMiddlewareClient', 'CapabilitiesClient',
+           'ContainerClient', 'ObjectClient']
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 9932b4a..5a1737e 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -23,39 +23,30 @@
 
 class AccountClient(rest_client.RestClient):
 
-    def create_account(self, data=None,
-                       params=None,
-                       metadata=None,
-                       remove_metadata=None,
-                       metadata_prefix='X-Account-Meta-',
-                       remove_metadata_prefix='X-Remove-Account-Meta-'):
-        """Create an account."""
-        if metadata is None:
-            metadata = {}
-        if remove_metadata is None:
-            remove_metadata = {}
-        url = ''
-        if params:
-            url += '?%s' % urllib.urlencode(params)
+    def create_update_or_delete_account_metadata(
+            self,
+            create_update_metadata=None,
+            delete_metadata=None,
+            create_update_metadata_prefix='X-Account-Meta-',
+            delete_metadata_prefix='X-Remove-Account-Meta-'):
+        """Creates, Updates or deletes an account metadata entry.
 
+        Account Metadata can be created, updated or deleted based on
+        metadata header or value. For detailed info, please refer to the
+        official API reference:
+        http://developer.openstack.org/api-ref/object-storage/?expanded=create-update-or-delete-account-metadata-detail
+        """
         headers = {}
-        for key in metadata:
-            headers[metadata_prefix + key] = metadata[key]
-        for key in remove_metadata:
-            headers[remove_metadata_prefix + key] = remove_metadata[key]
+        if create_update_metadata:
+            for key in create_update_metadata:
+                metadata_header_name = create_update_metadata_prefix + key
+                headers[metadata_header_name] = create_update_metadata[key]
+        if delete_metadata:
+            for key in delete_metadata:
+                headers[delete_metadata_prefix + key] = delete_metadata[key]
 
-        resp, body = self.put(url, data, headers)
-        self.expected_success(200, resp.status)
-        return resp, body
-
-    def delete_account(self, data=None, params=None):
-        """Delete an account."""
-        url = ''
-        if params:
-            url = '?%s%s' % (url, urllib.urlencode(params))
-
-        resp, body = self.delete(url, headers={}, body=data)
-        self.expected_success(200, resp.status)
+        resp, body = self.post('', headers=headers, body=None)
+        self.expected_success([200, 204], resp.status)
         return resp, body
 
     def list_account_metadata(self):
@@ -67,51 +58,6 @@
         self.expected_success(204, resp.status)
         return resp, body
 
-    def create_account_metadata(self, metadata,
-                                metadata_prefix='X-Account-Meta-',
-                                data=None, params=None):
-        """Creates an account metadata entry."""
-        headers = {}
-        if metadata:
-            for key in metadata:
-                headers[metadata_prefix + key] = metadata[key]
-
-        url = ''
-        if params:
-            url = '?%s%s' % (url, urllib.urlencode(params))
-
-        resp, body = self.post(url, headers=headers, body=data)
-        self.expected_success([200, 204], resp.status)
-        return resp, body
-
-    def delete_account_metadata(self, metadata,
-                                metadata_prefix='X-Remove-Account-Meta-'):
-        """Deletes an account metadata entry."""
-
-        headers = {}
-        for item in metadata:
-            headers[metadata_prefix + item] = metadata[item]
-        resp, body = self.post('', headers=headers, body=None)
-        self.expected_success(204, resp.status)
-        return resp, body
-
-    def create_and_delete_account_metadata(
-            self,
-            create_metadata=None,
-            delete_metadata=None,
-            create_metadata_prefix='X-Account-Meta-',
-            delete_metadata_prefix='X-Remove-Account-Meta-'):
-        """Creates and deletes an account metadata entry."""
-        headers = {}
-        for key in create_metadata:
-            headers[create_metadata_prefix + key] = create_metadata[key]
-        for key in delete_metadata:
-            headers[delete_metadata_prefix + key] = delete_metadata[key]
-
-        resp, body = self.post('', headers=headers, body=None)
-        self.expected_success(204, resp.status)
-        return resp, body
-
     def list_account_containers(self, params=None):
         """GET on the (base) storage URL
 
@@ -128,6 +74,13 @@
             than the specified marker.
             DEFAULT: No Marker
 
+        prefix=[string value Y]
+            Given string value Y, return object names starting with that prefix
+
+        reverse=[boolean value Z]
+            Reverse the result order based on the boolean value Z
+            DEFAULT: False
+
         format=[string value, either 'json' or 'xml']
             Specify either json or xml to return the respective serialized
             response.
diff --git a/tempest/test.py b/tempest/test.py
index 039afa1..a4cc2cc 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 import atexit
-import functools
 import os
 import sys
 
@@ -26,12 +25,11 @@
 
 from tempest import clients
 from tempest.common import credentials_factory as credentials
-from tempest.common import fixed_network
+from tempest.common import utils
 import tempest.common.validation_resources as vresources
 from tempest import config
-from tempest import exceptions
 from tempest.lib.common import cred_client
-from tempest.lib.common.utils import test_utils
+from tempest.lib.common import fixed_network
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
@@ -39,149 +37,31 @@
 
 CONF = config.CONF
 
-idempotent_id = decorators.idempotent_id
+# TODO(oomichi): This test.idempotent_id should be removed after all projects
+# switch to use decorators.idempotent_id.
+idempotent_id = debtcollector.moves.moved_function(
+    decorators.idempotent_id, 'idempotent_id', __name__,
+    version='Mitaka', removal_version='?')
 
 
-def attr(**kwargs):
-    """A decorator which applies the testtools attr decorator
-
-    This decorator applies the testtools.testcase.attr if it is in the list of
-    attributes to testtools we want to apply.
-    """
-
-    def decorator(f):
-        if 'type' in kwargs and isinstance(kwargs['type'], str):
-            f = testtools.testcase.attr(kwargs['type'])(f)
-        elif 'type' in kwargs and isinstance(kwargs['type'], list):
-            for attr in kwargs['type']:
-                f = testtools.testcase.attr(attr)(f)
-        return f
-
-    return decorator
+attr = debtcollector.moves.moved_function(
+    decorators.attr, 'attr', __name__,
+    version='Pike', removal_version='?')
 
 
-def get_service_list():
-    service_list = {
-        'compute': CONF.service_available.nova,
-        'image': CONF.service_available.glance,
-        'volume': CONF.service_available.cinder,
-        'network': True,
-        'identity': True,
-        'object_storage': CONF.service_available.swift,
-    }
-    return service_list
+services = debtcollector.moves.moved_function(
+    utils.services, 'services', __name__,
+    version='Pike', removal_version='?')
 
 
-def services(*args):
-    """A decorator used to set an attr for each service used in a test case
-
-    This decorator applies a testtools attr for each service that gets
-    exercised by a test case.
-    """
-    def decorator(f):
-        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 '
-                                                   'service' % service)
-        attr(type=list(args))(f)
-
-        @functools.wraps(f)
-        def wrapper(self, *func_args, **func_kwargs):
-            service_list = get_service_list()
-
-            for service in args:
-                if not service_list[service]:
-                    msg = 'Skipped because the %s service is not available' % (
-                        service)
-                    raise testtools.TestCase.skipException(msg)
-            return f(self, *func_args, **func_kwargs)
-        return wrapper
-    return decorator
+requires_ext = debtcollector.moves.moved_function(
+    utils.requires_ext, 'requires_ext', __name__,
+    version='Pike', removal_version='?')
 
 
-def requires_ext(**kwargs):
-    """A decorator to skip tests if an extension is not enabled
-
-    @param extension
-    @param service
-    """
-    def decorator(func):
-        @functools.wraps(func)
-        def wrapper(*func_args, **func_kwargs):
-            if not is_extension_enabled(kwargs['extension'],
-                                        kwargs['service']):
-                msg = "Skipped because %s extension: %s is not enabled" % (
-                    kwargs['service'], kwargs['extension'])
-                raise testtools.TestCase.skipException(msg)
-            return func(*func_args, **func_kwargs)
-        return wrapper
-    return decorator
-
-
-def is_extension_enabled(extension_name, service):
-    """A function that will check the list of enabled extensions from config
-
-    """
-    config_dict = {
-        'compute': CONF.compute_feature_enabled.api_extensions,
-        'volume': CONF.volume_feature_enabled.api_extensions,
-        'network': CONF.network_feature_enabled.api_extensions,
-        'object': CONF.object_storage_feature_enabled.discoverable_apis,
-        'identity': CONF.identity_feature_enabled.api_extensions
-    }
-    if len(config_dict[service]) == 0:
-        return False
-    if config_dict[service][0] == 'all':
-        return True
-    if extension_name in config_dict[service]:
-        return True
-    return False
-
-
-def related_bug(bug, status_code=None):
-    """A decorator useful to know solutions from launchpad bug reports
-
-    @param bug: The launchpad bug number causing the test
-    @param status_code: The status code related to the bug report
-    """
-    def decorator(f):
-        @functools.wraps(f)
-        def wrapper(self, *func_args, **func_kwargs):
-            try:
-                return f(self, *func_args, **func_kwargs)
-            except Exception as exc:
-                exc_status_code = getattr(exc, 'status_code', None)
-                if status_code is None or status_code == exc_status_code:
-                    LOG.error('Hints: This test was made for the bug %s. '
-                              'The failure could be related to '
-                              'https://launchpad.net/bugs/%s', bug, bug)
-                raise exc
-        return wrapper
-    return decorator
-
-
-def is_scheduler_filter_enabled(filter_name):
-    """Check the list of enabled compute scheduler filters from config.
-
-    This function checks whether the given compute scheduler filter is
-    available and configured in the config file. If the
-    scheduler_available_filters option is set to 'all' (Default value. which
-    means default filters are configured in nova) in tempest.conf then, this
-    function returns True with assumption that requested filter 'filter_name'
-    is one of available filter in nova ("nova.scheduler.filters.all_filters").
-    """
-
-    filters = CONF.compute_feature_enabled.scheduler_available_filters
-    if len(filters) == 0:
-        return False
-    if 'all' in filters:
-        return True
-    if filter_name in filters:
-        return True
-    return False
-
+is_extension_enabled = debtcollector.moves.moved_function(
+    utils.is_extension_enabled, 'is_extension_enabled', __name__,
+    version='Pike', removal_version='?')
 
 at_exit_set = set()
 
@@ -272,6 +152,9 @@
 
     @classmethod
     def tearDownClass(cls):
+        # insert pdb breakpoint when pause_teardown is enabled
+        if CONF.pause_teardown:
+            cls.insert_pdb_breakpoint()
         at_exit_set.discard(cls)
         # It should never be overridden by descendants
         if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
@@ -308,6 +191,22 @@
             finally:
                 del trace  # to avoid circular refs
 
+    def tearDown(self):
+        super(BaseTestCase, self).tearDown()
+        # insert pdb breakpoint when pause_teardown is enabled
+        if CONF.pause_teardown:
+            BaseTestCase.insert_pdb_breakpoint()
+
+    @classmethod
+    def insert_pdb_breakpoint(cls):
+        """Add pdb breakpoint.
+
+        This can help in debugging process, cleaning of resources is
+        paused, so they can be examined.
+        """
+        import pdb
+        pdb.set_trace()
+
     @classmethod
     def skip_checks(cls):
         """Class level skip checks.
@@ -321,10 +220,13 @@
         resource_setup or at test level.
         """
         identity_version = cls.get_identity_version()
-        if 'admin' in cls.credentials and not credentials.is_admin_available(
-                identity_version=identity_version):
-            msg = "Missing Identity Admin API credentials in configuration."
-            raise cls.skipException(msg)
+        # setting force_tenant_isolation to True also needs admin credentials.
+        if ('admin' in cls.credentials or
+                getattr(cls, 'force_tenant_isolation', False)):
+            if not credentials.is_admin_available(
+                    identity_version=identity_version):
+                raise cls.skipException(
+                    "Missing Identity Admin API credentials in configuration.")
         if 'alt' in cls.credentials and not credentials.is_alt_available(
                 identity_version=identity_version):
             msg = "Missing a 2nd set of API credentials in configuration."
@@ -356,16 +258,32 @@
                 manager = cls.get_client_manager(
                     credential_type=credentials_type)
                 setattr(cls, 'os_%s' % credentials_type, manager)
+                # NOTE(jordanP): Tempest should use os_primary, os_admin
+                # and os_alt throughout its code base but we keep the aliases
+                # around for a while for Tempest plugins. Aliases should be
+                # removed eventually.
                 # Setup some common aliases
-                # TODO(andreaf) The aliases below are a temporary hack
-                # to avoid changing too much code in one patch. They should
-                # be removed eventually
                 if credentials_type == 'primary':
-                    cls.os = cls.manager = cls.os_primary
+                    cls.os = debtcollector.moves.moved_read_only_property(
+                        'os', 'os_primary', version='Pike',
+                        removal_version='Queens')
+                    cls.manager =\
+                        debtcollector.moves.moved_read_only_property(
+                            'manager', 'os_primary', version='Pike',
+                            removal_version='Queens')
                 if credentials_type == 'admin':
-                    cls.os_adm = cls.admin_manager = cls.os_admin
+                    cls.os_adm = debtcollector.moves.moved_read_only_property(
+                        'os_adm', 'os_admin', version='Pike',
+                        removal_version='Queens')
+                    cls.admin_manager =\
+                        debtcollector.moves.moved_read_only_property(
+                            'admin_manager', 'os_admin', version='Pike',
+                            removal_version='Queens')
                 if credentials_type == 'alt':
-                    cls.alt_manager = cls.os_alt
+                    cls.alt_manager =\
+                        debtcollector.moves.moved_read_only_property(
+                            'alt_manager', 'os_alt', version='Pike',
+                            removal_version='Queens')
             elif isinstance(credentials_type, list):
                 manager = cls.get_client_manager(roles=credentials_type[1:],
                                                  force_new=True)
@@ -382,9 +300,20 @@
     @classmethod
     def resource_setup(cls):
         """Class level resource setup for test cases."""
-        if hasattr(cls, "os"):
+        if (CONF.validation.ip_version_for_ssh not in (4, 6) and
+            CONF.service_available.neutron):
+            msg = "Invalid IP version %s in ip_version_for_ssh. Use 4 or 6"
+            raise lib_exc.InvalidConfiguration(
+                msg % CONF.validation.ip_version_for_ssh)
+        if hasattr(cls, "os_primary"):
+            vr = cls.validation_resources
             cls.validation_resources = vresources.create_validation_resources(
-                cls.os, cls.validation_resources)
+                cls.os_primary,
+                use_neutron=CONF.service_available.neutron,
+                ethertype='IPv' + str(CONF.validation.ip_version_for_ssh),
+                floating_network_id=CONF.network.public_network_id,
+                floating_network_name=CONF.network.floating_network_name,
+                **vr)
         else:
             LOG.warning("Client manager not found, validation resources not"
                         " created")
@@ -397,9 +326,11 @@
         resources, in case a failure during `resource_setup` should happen.
         """
         if cls.validation_resources:
-            if hasattr(cls, "os"):
-                vresources.clear_validation_resources(cls.os,
-                                                      cls.validation_resources)
+            if hasattr(cls, "os_primary"):
+                vr = cls.validation_resources
+                vresources.clear_validation_resources(
+                    cls.os_primary,
+                    use_neutron=CONF.service_available.neutron, **vr)
                 cls.validation_resources = {}
             else:
                 LOG.warning("Client manager not found, validation resources "
@@ -480,7 +411,9 @@
         """Returns a credentials provider
 
         If no credential provider exists yet creates one.
-        It uses self.identity_version if defined, or the configuration value
+        It always use the configuration value from identity.auth_version,
+        since we always want to provision accounts with the current version
+        of the identity API.
         """
         if (not hasattr(cls, '_creds_provider') or not cls._creds_provider or
                 not cls._creds_provider.name == cls.__name__):
@@ -489,8 +422,7 @@
 
             cls._creds_provider = credentials.get_credentials_provider(
                 name=cls.__name__, network_resources=cls.network_resources,
-                force_tenant_isolation=force_tenant_isolation,
-                identity_version=cls.get_identity_version())
+                force_tenant_isolation=force_tenant_isolation)
         return cls._creds_provider
 
     @classmethod
@@ -639,13 +571,24 @@
         return fixed_network.get_tenant_network(
             cred_provider, networks_client, CONF.compute.fixed_network_name)
 
-    def assertEmpty(self, list, msg=None):
-        self.assertEqual(0, len(list), msg)
+    def assertEmpty(self, items, msg=None):
+        """Asserts whether a sequence or collection is empty
 
-    def assertNotEmpty(self, list, msg=None):
-        self.assertGreater(len(list), 0, msg)
+        :param items: sequence or collection to be tested
+        :param msg: message to be passed to the AssertionError
+        :raises AssertionError: when items is not empty
+        """
+        if msg is None:
+            msg = "sequence or collection is not empty: %s" % items
+        self.assertFalse(items, msg)
 
+    def assertNotEmpty(self, items, msg=None):
+        """Asserts whether a sequence or collection is not empty
 
-call_until_true = debtcollector.moves.moved_function(
-    test_utils.call_until_true, 'call_until_true', __name__,
-    version='Newton', removal_version='Ocata')
+        :param items: sequence or collection to be tested
+        :param msg: message to be passed to the AssertionError
+        :raises AssertionError: when items is empty
+        """
+        if msg is None:
+            msg = "sequence or collection is empty."
+        self.assertTrue(items, msg)
diff --git a/tempest/test_discover/plugins.py b/tempest/test_discover/plugins.py
index abe2b73..1206e3f 100644
--- a/tempest/test_discover/plugins.py
+++ b/tempest/test_discover/plugins.py
@@ -46,10 +46,42 @@
         """Add additional configuration options to tempest.
 
         This method will be run for the plugin during the register_opts()
-        function in tempest.config
+        function in tempest.config.
 
         :param ConfigOpts conf: The conf object that can be used to register
             additional options on.
+
+        Example::
+
+            # Config options are defined in a config.py module
+            service_option = cfg.BoolOpt(
+                "my_service",
+                default=True,
+                help="Whether or not my service is available")
+
+            # Note: as long as the group is listed in get_opt_lists,
+            # it will be possible to access its optins in the plugin code
+            # via ("-" in the group name are replaces with "_"):
+            #     CONF.my_service.<option_name>
+            my_service_group = cfg.OptGroup(name="my-service",
+                                            title="My service options")
+
+            MyServiceGroup = [<list of options>]
+            # (...) More groups and options...
+
+            # Plugin is implemented in a plugin.py module
+            from my_plugin import config as my_config
+
+            def register_opts(self, conf):
+                conf.register_opt(my_config.service_option,
+                                  group='service_available')
+                conf.register_group(my_config.my_service_group)
+                conf.register_opts(my_config.MyService +
+                                   my_config.my_service_group)
+
+                conf.register_group(my_config.my_service_feature_group)
+                conf.register_opts(my_config.MyServiceFeaturesGroup,
+                                   my_config.my_service_feature_group)
         """
         return
 
@@ -60,6 +92,31 @@
         :return option_list: A list of tuples with the group name and options
                              in that group.
         :rtype: list
+
+        Example::
+
+            # Config options are defined in a config.py module
+            service_option = cfg.BoolOpt(
+                "my_service", default=True,
+                help="Whether or not my service is available")
+
+            my_service_group = cfg.OptGroup(name="my-service",
+                                            title="My service options")
+            my_service_features_group = cfg.OptGroup(
+                name="my-service-features",
+                title="My service available features")
+
+            MyServiceGroup = [<list of options>]
+            MyServiceFeaturesGroup = [<list of options>]
+
+            # Plugin is implemented in a plugin.py module
+            from my_plugin import config as my_config
+
+            def get_opt_lists(self, conf):
+                return [
+                    (my_service_group.name, MyServiceGroup),
+                    (my_service_features_group.name, MyServiceFeaturesGroup)
+                ]
         """
         return []
 
@@ -71,43 +128,46 @@
         in any ServiceClients object instantiated by tests.
         The default implementation returns an empty list.
 
-        :return list of dictionaries. Each element of the list represents
-            the service client for an API. Each dict must define all
-            parameters required for the invocation of
-            `service_clients.ServiceClients.register_service_client_module`.
-        :rtype: list
+        :returns: Each element of the list represents the service client for an
+          API. Each dict must define all parameters required for the invocation
+          of `service_clients.ServiceClients.register_service_client_module`.
+        :rtype: list of dictionaries
 
-        Example:
+        Example implementation with one service client::
 
-            >>>  # Example implementation with one service client
-            >>>  myservice_config = config.service_client_config('myservice')
-            >>>  params = {
-            >>>     'name': 'myservice',
-            >>>     'service_version': 'myservice',
-            >>>     'module_path': 'myservice_tempest_tests.services',
-            >>>     'client_names': ['API1Client', 'API2Client'],
-            >>>  }
-            >>>  params.update(myservice_config)
-            >>>  return [params]
+            def get_service_clients(self):
+                # Example implementation with one service client
+                myservice_config = config.service_client_config('myservice')
+                params = {
+                    'name': 'myservice',
+                    'service_version': 'myservice',
+                    'module_path': 'myservice_tempest_tests.services',
+                    'client_names': ['API1Client', 'API2Client'],
+                }
+                params.update(myservice_config)
+                return [params]
 
-            >>>  # Example implementation with two service clients
-            >>>  foo1_config = config.service_client_config('foo')
-            >>>  params_foo1 = {
-            >>>     'name': 'foo_v1',
-            >>>     'service_version': 'foo.v1',
-            >>>     'module_path': 'bar_tempest_tests.services.foo.v1',
-            >>>     'client_names': ['API1Client', 'API2Client'],
-            >>>  }
-            >>>  params_foo1.update(foo_config)
-            >>>  foo2_config = config.service_client_config('foo')
-            >>>  params_foo2 = {
-            >>>     'name': 'foo_v2',
-            >>>     'service_version': 'foo.v2',
-            >>>     'module_path': 'bar_tempest_tests.services.foo.v2',
-            >>>     'client_names': ['API1Client', 'API2Client'],
-            >>>  }
-            >>>  params_foo2.update(foo2_config)
-            >>>  return [params_foo1, params_foo2]
+        Example implementation with two service clients::
+
+            def get_service_clients(self):
+                # Example implementation with two service clients
+                foo1_config = config.service_client_config('foo')
+                params_foo1 = {
+                    'name': 'foo_v1',
+                    'service_version': 'foo.v1',
+                    'module_path': 'bar_tempest_tests.services.foo.v1',
+                    'client_names': ['API1Client', 'API2Client'],
+                }
+                params_foo1.update(foo_config)
+                foo2_config = config.service_client_config('foo')
+                params_foo2 = {
+                    'name': 'foo_v2',
+                    'service_version': 'foo.v2',
+                    'module_path': 'bar_tempest_tests.services.foo.v2',
+                    'client_names': ['API1Client', 'API2Client'],
+                }
+                params_foo2.update(foo2_config)
+                return [params_foo1, params_foo2]
         """
         return []
 
@@ -124,7 +184,6 @@
             'tempest.test_plugins', invoke_on_load=True,
             propagate_map_exceptions=True,
             on_load_failure_callback=self.failure_hook)
-        self._register_service_clients()
 
     @staticmethod
     def failure_hook(_, ep, err):
diff --git a/tempest/tests/README.rst b/tempest/tests/README.rst
index e54d4c0..0587e7b 100644
--- a/tempest/tests/README.rst
+++ b/tempest/tests/README.rst
@@ -7,16 +7,16 @@
 ---------------------
 
 Unit tests are the self checks for Tempest. They provide functional
-verification and regression checking for the internal components of tempest.
-They should be used to just verify that the individual pieces of tempest are
+verification and regression checking for the internal components of Tempest.
+They should be used to just verify that the individual pieces of Tempest are
 working as expected. They should not require an external service to be running
-and should be able to run solely from the tempest tree.
+and should be able to run solely from the Tempest tree.
 
-Why are these tests in tempest?
+Why are these tests in Tempest?
 -------------------------------
 These tests exist to make sure that the mechanisms that we use inside of
-tempest to are valid and remain functional. They are only here for self
-validation of tempest.
+Tempest are valid and remain functional. They are only here for self
+validation of Tempest.
 
 
 Scope of these tests
diff --git a/tempest/tests/api/compute/test_base.py b/tempest/tests/api/compute/test_base.py
index a1da343..6345728 100644
--- a/tempest/tests/api/compute/test_base.py
+++ b/tempest/tests/api/compute/test_base.py
@@ -48,10 +48,14 @@
 
     @mock.patch.multiple(compute_base.BaseV2ComputeTest,
                          compute_images_client=mock.DEFAULT,
+                         servers_client=mock.DEFAULT,
                          images=[], create=True)
     @mock.patch.object(waiters, 'wait_for_image_status')
+    @mock.patch.object(waiters, 'wait_for_server_status')
     def test_create_image_from_server_wait_until_active(self,
+                                                        wait_for_server_status,
                                                         wait_for_image_status,
+                                                        servers_client,
                                                         compute_images_client):
         """Tests create_image_from_server with wait_until='ACTIVE' kwarg."""
         # setup mocks
@@ -67,6 +71,35 @@
         # make our assertions
         wait_for_image_status.assert_called_once_with(
             compute_images_client, image_id, 'ACTIVE')
+        wait_for_server_status.assert_called_once_with(
+            servers_client, mock.sentinel.server_id, 'ACTIVE')
+        compute_images_client.show_image.assert_called_once_with(image_id)
+
+    @mock.patch.multiple(compute_base.BaseV2ComputeTest,
+                         compute_images_client=mock.DEFAULT,
+                         servers_client=mock.DEFAULT,
+                         images=[], create=True)
+    @mock.patch.object(waiters, 'wait_for_image_status')
+    @mock.patch.object(waiters, 'wait_for_server_status')
+    def test_create_image_from_server_wait_until_active_no_server_wait(
+            self, wait_for_server_status, wait_for_image_status,
+            servers_client, compute_images_client):
+        """Tests create_image_from_server with wait_until='ACTIVE' kwarg."""
+        # setup mocks
+        image_id = uuidutils.generate_uuid()
+        fake_image = mock.Mock(response={'location': image_id})
+        compute_images_client.create_image.return_value = fake_image
+        compute_images_client.show_image.return_value = (
+            {'image': fake_image})
+        # call the utility method
+        image = compute_base.BaseV2ComputeTest.create_image_from_server(
+            mock.sentinel.server_id, wait_until='ACTIVE',
+            wait_for_server=False)
+        self.assertEqual(fake_image, image)
+        # make our assertions
+        wait_for_image_status.assert_called_once_with(
+            compute_images_client, image_id, 'ACTIVE')
+        self.assertEqual(0, wait_for_server_status.call_count)
         compute_images_client.show_image.assert_called_once_with(image_id)
 
     @mock.patch.multiple(compute_base.BaseV2ComputeTest,
diff --git a/tempest/tests/cmd/test_account_generator.py b/tempest/tests/cmd/test_account_generator.py
index 6773b2f..f907bd0 100644
--- a/tempest/tests/cmd/test_account_generator.py
+++ b/tempest/tests/cmd/test_account_generator.py
@@ -20,7 +20,6 @@
 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):
@@ -85,14 +84,10 @@
 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)
@@ -115,7 +110,6 @@
 class TestAccountGeneratorV3(TestAccountGeneratorV2):
 
     identity_version = 3
-    identity_response = fake_identity._fake_v3_response
 
     def setUp(self):
         super(TestAccountGeneratorV3, self).setUp()
@@ -145,16 +139,13 @@
 class TestGenerateResourcesV2(base.TestCase, MockHelpersMixin):
 
     identity_version = 2
-    identity_response = fake_identity._fake_v2_response
     cred_client = 'tempest.lib.common.cred_client.V2CredsClient'
-    dynamic_creds = 'tempest.common.dynamic_creds.DynamicCredentialProvider'
+    dynamic_creds = ('tempest.lib.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()
@@ -244,7 +235,6 @@
 class TestGenerateResourcesV3(TestGenerateResourcesV2):
 
     identity_version = 3
-    identity_response = fake_identity._fake_v3_response
     cred_client = 'tempest.lib.common.cred_client.V3CredsClient'
 
     def setUp(self):
@@ -255,17 +245,14 @@
 class TestDumpAccountsV2(base.TestCase, MockHelpersMixin):
 
     identity_version = 2
-    identity_response = fake_identity._fake_v2_response
     cred_client = 'tempest.lib.common.cred_client.V2CredsClient'
-    dynamic_creds = 'tempest.common.dynamic_creds.DynamicCredentialProvider'
+    dynamic_creds = ('tempest.lib.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()
@@ -337,7 +324,6 @@
 class TestDumpAccountsV3(TestDumpAccountsV2):
 
     identity_version = 3
-    identity_response = fake_identity._fake_v3_response
     cred_client = 'tempest.lib.common.cred_client.V3CredsClient'
     domain_is_in = True
 
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
index 7ac347d..6e1250f 100644
--- a/tempest/tests/cmd/test_run.py
+++ b/tempest/tests/cmd/test_run.py
@@ -13,6 +13,7 @@
 # under the License.
 
 import argparse
+import atexit
 import os
 import shutil
 import subprocess
@@ -25,6 +26,7 @@
 from tempest.tests import base
 
 DEVNULL = open(os.devnull, 'wb')
+atexit.register(DEVNULL.close)
 
 
 class TestTempestRun(base.TestCase):
@@ -68,6 +70,34 @@
         self.assertEqual('i_am_a_fun_little_regex',
                          self.run_cmd._build_regex(args))
 
+    def test__build_whitelist_file(self):
+        args = mock.Mock(spec=argparse.Namespace)
+        setattr(args, 'smoke', False)
+        setattr(args, 'regex', None)
+        self.tests = tempfile.NamedTemporaryFile(
+            prefix='whitelist', delete=False)
+        self.tests.write(b"volume \n compute")
+        self.tests.close()
+        setattr(args, 'whitelist_file', self.tests.name)
+        setattr(args, 'blacklist_file', None)
+        self.assertEqual("volume|compute",
+                         self.run_cmd._build_regex(args))
+        os.unlink(self.tests.name)
+
+    def test__build_blacklist_file(self):
+        args = mock.Mock(spec=argparse.Namespace)
+        setattr(args, 'smoke', False)
+        setattr(args, 'regex', None)
+        self.tests = tempfile.NamedTemporaryFile(
+            prefix='blacklist', delete=False)
+        self.tests.write(b"volume \n compute")
+        self.tests.close()
+        setattr(args, 'whitelist_file', None)
+        setattr(args, 'blacklist_file', self.tests.name)
+        self.assertEqual("^((?!compute|volume).)*$",
+                         self.run_cmd._build_regex(args))
+        os.unlink(self.tests.name)
+
 
 class TestRunReturnCode(base.TestCase):
     def setUp(self):
diff --git a/tempest/tests/cmd/test_subunit_describe_calls.py b/tempest/tests/cmd/test_subunit_describe_calls.py
index 1c24c37..5f3d770 100644
--- a/tempest/tests/cmd/test_subunit_describe_calls.py
+++ b/tempest/tests/cmd/test_subunit_describe_calls.py
@@ -33,6 +33,16 @@
         p.communicate()
         self.assertEqual(0, p.returncode)
 
+    def test_return_code_no_output(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],
+            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__)),
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 1af0d95..810f9e5 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -12,13 +12,17 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import fixtures
 import mock
 from oslo_serialization import jsonutils as json
-from oslotest import mockpatch
 
+from tempest import clients
 from tempest.cmd import verify_tempest_config
+from tempest.common import credentials_factory
 from tempest import config
+from tempest.lib.common import rest_client
 from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest.tests import base
 from tempest.tests import fake_config
 
@@ -73,12 +77,12 @@
                          fake_config.FakePrivate)
 
     def test_get_keystone_api_versions(self):
-        self.useFixture(mockpatch.PatchObject(
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': {'values': [{'id': 'v2.0'}, {'id': 'v3.0'}]}}
         fake_resp = json.dumps(fake_resp)
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             'tempest.lib.common.http.ClosingHttp.request',
             return_value=(None, fake_resp)))
         fake_os = mock.MagicMock()
@@ -87,12 +91,12 @@
         self.assertIn('v3.0', versions)
 
     def test_get_cinder_api_versions(self):
-        self.useFixture(mockpatch.PatchObject(
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': [{'id': 'v1.0'}, {'id': 'v2.0'}]}
         fake_resp = json.dumps(fake_resp)
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             'tempest.lib.common.http.ClosingHttp.request',
             return_value=(None, fake_resp)))
         fake_os = mock.MagicMock()
@@ -101,12 +105,12 @@
         self.assertIn('v2.0', versions)
 
     def test_get_nova_versions(self):
-        self.useFixture(mockpatch.PatchObject(
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': [{'id': 'v2.0'}, {'id': 'v3.0'}]}
         fake_resp = json.dumps(fake_resp)
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             'tempest.lib.common.http.ClosingHttp.request',
             return_value=(None, fake_resp)))
         fake_os = mock.MagicMock()
@@ -117,17 +121,17 @@
     def test_get_versions_invalid_response(self):
         # When the response doesn't contain a JSON response, an error is
         # logged.
-        mock_log_error = self.useFixture(mockpatch.PatchObject(
+        mock_log_error = self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config.LOG, 'error')).mock
 
-        self.useFixture(mockpatch.PatchObject(
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, '_get_unversioned_endpoint'))
 
         # Simulated response is not JSON.
         sample_body = (
             '<html><head>Sample Response</head><body>This is the sample page '
             'for the web server. Why are you requesting it?</body></html>')
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             'tempest.lib.common.http.ClosingHttp.request',
             return_value=(None, sample_body)))
 
@@ -157,7 +161,7 @@
 
     @mock.patch('tempest.lib.common.http.ClosingHttp.request')
     def test_verify_keystone_api_versions_no_v3(self, mock_request):
-        self.useFixture(mockpatch.PatchObject(
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': {'values': [{'id': 'v2.0'}]}}
@@ -173,7 +177,7 @@
 
     @mock.patch('tempest.lib.common.http.ClosingHttp.request')
     def test_verify_keystone_api_versions_no_v2(self, mock_request):
-        self.useFixture(mockpatch.PatchObject(
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': {'values': [{'id': 'v3.0'}]}}
@@ -189,24 +193,26 @@
 
     @mock.patch('tempest.lib.common.http.ClosingHttp.request')
     def test_verify_cinder_api_versions_no_v3(self, mock_request):
-        self.useFixture(mockpatch.PatchObject(
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
-        fake_resp = {'versions': [{'id': 'v1.0'}, {'id': 'v2.0'}]}
+        fake_resp = {'versions': [{'id': 'v2.0'}]}
         fake_resp = json.dumps(fake_resp)
         mock_request.return_value = (None, fake_resp)
         fake_os = mock.MagicMock()
         with mock.patch.object(verify_tempest_config,
                                'print_and_or_update') as print_mock:
             verify_tempest_config.verify_cinder_api_versions(fake_os, True)
-        print_mock.assert_not_called()
+        print_mock.assert_any_call('api_v3', 'volume-feature-enabled',
+                                   False, True)
+        self.assertEqual(1, print_mock.call_count)
 
     @mock.patch('tempest.lib.common.http.ClosingHttp.request')
     def test_verify_cinder_api_versions_no_v2(self, mock_request):
-        self.useFixture(mockpatch.PatchObject(
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
-        fake_resp = {'versions': [{'id': 'v1.0'}, {'id': 'v3.0'}]}
+        fake_resp = {'versions': [{'id': 'v3.0'}]}
         fake_resp = json.dumps(fake_resp)
         mock_request.return_value = (None, fake_resp)
         fake_os = mock.MagicMock()
@@ -215,13 +221,11 @@
             verify_tempest_config.verify_cinder_api_versions(fake_os, True)
         print_mock.assert_any_call('api_v2', 'volume-feature-enabled',
                                    False, True)
-        print_mock.assert_any_call('api_v3', 'volume-feature-enabled',
-                                   True, True)
-        self.assertEqual(2, print_mock.call_count)
+        self.assertEqual(1, print_mock.call_count)
 
     @mock.patch('tempest.lib.common.http.ClosingHttp.request')
     def test_verify_cinder_api_versions_no_v1(self, mock_request):
-        self.useFixture(mockpatch.PatchObject(
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
         fake_resp = {'versions': [{'id': 'v2.0'}, {'id': 'v3.0'}]}
@@ -231,17 +235,18 @@
         with mock.patch.object(verify_tempest_config,
                                'print_and_or_update') as print_mock:
             verify_tempest_config.verify_cinder_api_versions(fake_os, True)
-        print_mock.assert_any_call('api_v1', 'volume-feature-enabled',
-                                   False, True)
-        print_mock.assert_any_call('api_v3', 'volume-feature-enabled',
-                                   True, True)
-        self.assertEqual(2, print_mock.call_count)
+        print_mock.assert_not_called()
 
     def test_verify_glance_version_no_v2_with_v1_1(self):
-        def fake_get_versions():
-            return (None, ['v1.1'])
+        # This test verifies that wrong config api_v2 = True is detected
+        class FakeClient(object):
+            def get_versions(self):
+                return (None, ['v1.0'])
+
         fake_os = mock.MagicMock()
-        fake_os.image_client.get_versions = fake_get_versions
+        fake_module = mock.MagicMock()
+        fake_module.ImagesClient = FakeClient
+        fake_os.image_v1 = fake_module
         with mock.patch.object(verify_tempest_config,
                                'print_and_or_update') as print_mock:
             verify_tempest_config.verify_glance_api_versions(fake_os, True)
@@ -249,10 +254,15 @@
                                            False, True)
 
     def test_verify_glance_version_no_v2_with_v1_0(self):
-        def fake_get_versions():
-            return (None, ['v1.0'])
+        # This test verifies that wrong config api_v2 = True is detected
+        class FakeClient(object):
+            def get_versions(self):
+                return (None, ['v1.0'])
+
         fake_os = mock.MagicMock()
-        fake_os.image_client.get_versions = fake_get_versions
+        fake_module = mock.MagicMock()
+        fake_module.ImagesClient = FakeClient
+        fake_os.image_v1 = fake_module
         with mock.patch.object(verify_tempest_config,
                                'print_and_or_update') as print_mock:
             verify_tempest_config.verify_glance_api_versions(fake_os, True)
@@ -260,25 +270,60 @@
                                            False, True)
 
     def test_verify_glance_version_no_v1(self):
-        def fake_get_versions():
-            return (None, ['v2.0'])
+        # This test verifies that wrong config api_v1 = True is detected
+        class FakeClient(object):
+            def get_versions(self):
+                raise lib_exc.NotFound()
+
+            def list_versions(self):
+                return {'versions': [{'id': 'v2.0'}]}
+
         fake_os = mock.MagicMock()
-        fake_os.image_client.get_versions = fake_get_versions
+        fake_module = mock.MagicMock()
+        fake_module.ImagesClient = FakeClient
+        fake_module.VersionsClient = FakeClient
+        fake_os.image_v1 = fake_module
+        fake_os.image_v2 = fake_module
         with mock.patch.object(verify_tempest_config,
                                'print_and_or_update') as print_mock:
             verify_tempest_config.verify_glance_api_versions(fake_os, True)
         print_mock.assert_called_once_with('api_v1', 'image-feature-enabled',
                                            False, True)
 
+    def test_verify_glance_version_no_version(self):
+        # This test verifies that wrong config api_v1 = True is detected
+        class FakeClient(object):
+            def get_versions(self):
+                raise lib_exc.NotFound()
+
+            def list_versions(self):
+                raise lib_exc.NotFound()
+
+        fake_os = mock.MagicMock()
+        fake_module = mock.MagicMock()
+        fake_module.ImagesClient = FakeClient
+        fake_module.VersionsClient = FakeClient
+        fake_os.image_v1 = fake_module
+        fake_os.image_v2 = fake_module
+        with mock.patch.object(verify_tempest_config,
+                               'print_and_or_update') as print_mock:
+            verify_tempest_config.verify_glance_api_versions(fake_os, True)
+        print_mock.assert_called_once_with('glance',
+                                           'service-available',
+                                           False, True)
+
     def test_verify_extensions_neutron(self):
         def fake_list_extensions():
             return {'extensions': [{'alias': 'fake1'},
                                    {'alias': 'fake2'},
                                    {'alias': 'not_fake'}]}
         fake_os = mock.MagicMock()
-        fake_os.network_extensions_client.list_extensions = (
-            fake_list_extensions)
-        self.useFixture(mockpatch.PatchObject(
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['fake1', 'fake2', 'fake3'])))
         results = verify_tempest_config.verify_extensions(fake_os,
@@ -299,9 +344,12 @@
                                    {'alias': 'fake2'},
                                    {'alias': 'not_fake'}]}
         fake_os = mock.MagicMock()
-        fake_os.network_extensions_client.list_extensions = (
-            fake_list_extensions)
-        self.useFixture(mockpatch.PatchObject(
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['all'])))
         results = verify_tempest_config.verify_extensions(fake_os,
@@ -317,15 +365,17 @@
                                    {'alias': 'fake2'},
                                    {'alias': 'not_fake'}]}
         fake_os = mock.MagicMock()
-        # NOTE (e0ne): mock both v1 and v2 APIs
-        fake_os.volumes_extension_client.list_extensions = fake_list_extensions
-        fake_os.volumes_v2_extension_client.list_extensions = (
-            fake_list_extensions)
-        self.useFixture(mockpatch.PatchObject(
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['fake1', 'fake2', 'fake3'])))
         results = verify_tempest_config.verify_extensions(fake_os,
                                                           'cinder', {})
+
         self.assertIn('cinder', results)
         self.assertIn('fake1', results['cinder'])
         self.assertTrue(results['cinder']['fake1'])
@@ -342,11 +392,12 @@
                                    {'alias': 'fake2'},
                                    {'alias': 'not_fake'}]}
         fake_os = mock.MagicMock()
-        # NOTE (e0ne): mock both v1 and v2 APIs
-        fake_os.volumes_extension_client.list_extensions = fake_list_extensions
-        fake_os.volumes_v2_extension_client.list_extensions = (
-            fake_list_extensions)
-        self.useFixture(mockpatch.PatchObject(
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['all'])))
         results = verify_tempest_config.verify_extensions(fake_os,
@@ -361,8 +412,12 @@
             return ([{'alias': 'fake1'}, {'alias': 'fake2'},
                      {'alias': 'not_fake'}])
         fake_os = mock.MagicMock()
-        fake_os.extensions_client.list_extensions = fake_list_extensions
-        self.useFixture(mockpatch.PatchObject(
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['fake1', 'fake2', 'fake3'])))
         results = verify_tempest_config.verify_extensions(fake_os,
@@ -383,8 +438,12 @@
                                     {'alias': 'fake2'},
                                     {'alias': 'not_fake'}]})
         fake_os = mock.MagicMock()
-        fake_os.extensions_client.list_extensions = fake_list_extensions
-        self.useFixture(mockpatch.PatchObject(
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['all'])))
         results = verify_tempest_config.verify_extensions(fake_os,
@@ -396,13 +455,17 @@
 
     def test_verify_extensions_swift(self):
         def fake_list_extensions():
-            return (None, {'fake1': 'metadata',
-                           'fake2': 'metadata',
-                           'not_fake': 'metadata',
-                           'swift': 'metadata'})
+            return {'fake1': 'metadata',
+                    'fake2': 'metadata',
+                    'not_fake': 'metadata',
+                    'swift': 'metadata'}
         fake_os = mock.MagicMock()
-        fake_os.capabilities_client.list_capabilities = fake_list_extensions
-        self.useFixture(mockpatch.PatchObject(
+        fake_client = mock.MagicMock()
+        fake_client.list_capabilities = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['fake1', 'fake2', 'fake3'])))
         results = verify_tempest_config.verify_extensions(fake_os, 'swift', {})
@@ -418,13 +481,17 @@
 
     def test_verify_extensions_swift_all(self):
         def fake_list_extensions():
-            return (None, {'fake1': 'metadata',
-                           'fake2': 'metadata',
-                           'not_fake': 'metadata',
-                           'swift': 'metadata'})
+            return {'fake1': 'metadata',
+                    'fake2': 'metadata',
+                    'not_fake': 'metadata',
+                    'swift': 'metadata'}
         fake_os = mock.MagicMock()
-        fake_os.capabilities_client.list_capabilities = fake_list_extensions
-        self.useFixture(mockpatch.PatchObject(
+        fake_client = mock.MagicMock()
+        fake_client.list_capabilities = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
+        self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['all'])))
         results = verify_tempest_config.verify_extensions(fake_os,
@@ -433,3 +500,13 @@
         self.assertIn('extensions', results['swift'])
         self.assertEqual(sorted(['not_fake', 'fake1', 'fake2']),
                          sorted(results['swift']['extensions']))
+
+    def test_get_extension_client(self):
+        creds = credentials_factory.get_credentials(
+            fill_in=False, username='fake_user', project_name='fake_project',
+            password='fake_password')
+        os = clients.Manager(creds)
+        for service in ['nova', 'neutron', 'swift', 'cinder']:
+            extensions_client = verify_tempest_config.get_extension_client(
+                os, service)
+            self.assertIsInstance(extensions_client, rest_client.RestClient)
diff --git a/tempest/tests/cmd/test_workspace.py b/tempest/tests/cmd/test_workspace.py
index 6ca4d42..a1c8c53 100644
--- a/tempest/tests/cmd/test_workspace.py
+++ b/tempest/tests/cmd/test_workspace.py
@@ -47,23 +47,25 @@
         self.assertEqual(return_code, expected, msg)
 
     def test_run_workspace_list(self):
-        cmd = ['tempest', 'workspace', '--workspace-path',
-               self.store_file, 'list']
+        cmd = ['tempest', 'workspace', 'list',
+               '--workspace-path', self.store_file]
         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]
+        cmd = ['tempest', 'workspace', 'register',
+               '--workspace-path', self.store_file,
+               '--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]
+        cmd = ['tempest', 'workspace', 'rename',
+               '--workspace-path', self.store_file,
+               '--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))
@@ -71,15 +73,24 @@
     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]
+        cmd = ['tempest', 'workspace', 'move',
+               '--workspace-path', self.store_file,
+               '--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]
+    def test_run_workspace_remove_entry(self):
+        cmd = ['tempest', 'workspace', 'remove',
+               '--workspace-path', self.store_file,
+               '--name', self.name]
+        self._run_cmd_gets_return_code(cmd, 0)
+        self.assertIsNone(self.workspace_manager.get_workspace(self.name))
+
+    def test_run_workspace_remove_directory(self):
+        cmd = ['tempest', 'workspace', 'remove',
+               '--workspace-path', self.store_file,
+               '--name', self.name, '--rmdir']
         self._run_cmd_gets_return_code(cmd, 0)
         self.assertIsNone(self.workspace_manager.get_workspace(self.name))
 
@@ -113,8 +124,13 @@
         self.assertEqual(
             self.workspace_manager.get_workspace(self.name), new_path)
 
-    def test_workspace_manager_remove(self):
-        self.workspace_manager.remove_workspace(self.name)
+    def test_workspace_manager_remove_entry(self):
+        self.workspace_manager.remove_workspace_entry(self.name)
+        self.assertIsNone(self.workspace_manager.get_workspace(self.name))
+
+    def test_workspace_manager_remove_directory(self):
+        path = self.workspace_manager.remove_workspace_entry(self.name)
+        self.workspace_manager.remove_workspace_directory(path)
         self.assertIsNone(self.workspace_manager.get_workspace(self.name))
 
     def test_path_expansion(self):
diff --git a/tempest/tests/common/test_admin_available.py b/tempest/tests/common/test_admin_available.py
index 01a9cd0..7b3b1b0 100644
--- a/tempest/tests/common/test_admin_available.py
+++ b/tempest/tests/common/test_admin_available.py
@@ -12,8 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import fixtures
 from oslo_config import cfg
-from oslotest import mockpatch
 
 from tempest.common import credentials_factory as credentials
 from tempest import config
@@ -52,16 +52,16 @@
                                  'project_name': 'admin',
                                  'password': 'p',
                                  'types': ['admin']})
-            self.useFixture(mockpatch.Patch(
-                'tempest.common.preprov_creds.read_accounts_yaml',
+            self.useFixture(fixtures.MockPatch(
+                'tempest.lib.common.preprov_creds.read_accounts_yaml',
                 return_value=accounts))
             cfg.CONF.set_default('test_accounts_file',
                                  use_accounts_file, group='auth')
-            self.useFixture(mockpatch.Patch('os.path.isfile',
-                                            return_value=True))
+            self.useFixture(fixtures.MockPatch('os.path.isfile',
+                                               return_value=True))
         else:
-            self.useFixture(mockpatch.Patch('os.path.isfile',
-                                            return_value=False))
+            self.useFixture(fixtures.MockPatch('os.path.isfile',
+                                               return_value=False))
             if admin_creds:
                 username = 'u'
                 project = 't'
diff --git a/tempest/tests/common/test_alt_available.py b/tempest/tests/common/test_alt_available.py
index 27db95c..a425bb8 100644
--- a/tempest/tests/common/test_alt_available.py
+++ b/tempest/tests/common/test_alt_available.py
@@ -12,8 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import fixtures
 from oslo_config import cfg
-from oslotest import mockpatch
 
 from tempest.common import credentials_factory as credentials
 from tempest import config
@@ -39,16 +39,16 @@
             accounts = [dict(username="u%s" % ii,
                              project_name="t%s" % ii,
                              password="p") for ii in creds]
-            self.useFixture(mockpatch.Patch(
-                'tempest.common.preprov_creds.read_accounts_yaml',
+            self.useFixture(fixtures.MockPatch(
+                'tempest.lib.common.preprov_creds.read_accounts_yaml',
                 return_value=accounts))
             cfg.CONF.set_default('test_accounts_file',
                                  use_accounts_file, group='auth')
-            self.useFixture(mockpatch.Patch('os.path.isfile',
-                                            return_value=True))
+            self.useFixture(fixtures.MockPatch('os.path.isfile',
+                                               return_value=True))
         else:
-            self.useFixture(mockpatch.Patch('os.path.isfile',
-                                            return_value=False))
+            self.useFixture(fixtures.MockPatch('os.path.isfile',
+                                               return_value=False))
         expected = len(set(creds)) > 1 or dynamic_creds
         observed = credentials.is_alt_available(
             identity_version=self.identity_version)
diff --git a/tempest/tests/common/test_compute.py b/tempest/tests/common/test_compute.py
new file mode 100644
index 0000000..c108be9
--- /dev/null
+++ b/tempest/tests/common/test_compute.py
@@ -0,0 +1,106 @@
+# Copyright 2017 Citrix Systems
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from six.moves.urllib import parse as urlparse
+
+import mock
+
+from tempest.common import compute
+from tempest.tests import base
+
+
+class TestCompute(base.TestCase):
+    def setUp(self):
+        super(TestCompute, self).setUp()
+        self.client_sock = mock.Mock()
+        self.url = urlparse.urlparse("http://www.fake.com:80")
+
+    def test_rfp_frame_not_cached(self):
+        # rfp negotiation frame arrived separately after upgrade
+        # response, so it's not cached.
+        RFP_VERSION = b'RFB.003.003\x0a'
+        rfp_frame_header = b'\x82\x0c'
+
+        self.client_sock.recv.side_effect = [
+            b'fake response start\r\n',
+            b'fake response end\r\n\r\n',
+            rfp_frame_header,
+            RFP_VERSION]
+        expect_response = b'fake response start\r\nfake response end\r\n\r\n'
+
+        webSocket = compute._WebSocket(self.client_sock, self.url)
+
+        self.assertEqual(webSocket.response, expect_response)
+        # no cache
+        self.assertEqual(webSocket.cached_stream, b'')
+        self.client_sock.recv.assert_has_calls([mock.call(4096),
+                                                mock.call(4096)])
+
+        self.client_sock.recv.reset_mock()
+        recv_version = webSocket.receive_frame()
+
+        self.assertEqual(recv_version, RFP_VERSION)
+        self.client_sock.recv.assert_has_calls([mock.call(2),
+                                                mock.call(12)])
+
+    def test_rfp_frame_fully_cached(self):
+        RFP_VERSION = b'RFB.003.003\x0a'
+        rfp_version_frame = b'\x82\x0c%s' % RFP_VERSION
+
+        self.client_sock.recv.side_effect = [
+            b'fake response start\r\n',
+            b'fake response end\r\n\r\n%s' % rfp_version_frame]
+        expect_response = b'fake response start\r\nfake response end\r\n\r\n'
+        webSocket = compute._WebSocket(self.client_sock, self.url)
+
+        self.client_sock.recv.assert_has_calls([mock.call(4096),
+                                                mock.call(4096)])
+        self.assertEqual(webSocket.response, expect_response)
+        self.assertEqual(webSocket.cached_stream, rfp_version_frame)
+
+        self.client_sock.recv.reset_mock()
+        recv_version = webSocket.receive_frame()
+
+        self.client_sock.recv.assert_not_called()
+        self.assertEqual(recv_version, RFP_VERSION)
+        # cached_stream should be empty in the end.
+        self.assertEqual(webSocket.cached_stream, b'')
+
+    def test_rfp_frame_partially_cached(self):
+        RFP_VERSION = b'RFB.003.003\x0a'
+        rfp_version_frame = b'\x82\x0c%s' % RFP_VERSION
+        frame_part1 = rfp_version_frame[:6]
+        frame_part2 = rfp_version_frame[6:]
+
+        self.client_sock.recv.side_effect = [
+            b'fake response start\r\n',
+            b'fake response end\r\n\r\n%s' % frame_part1,
+            frame_part2]
+        expect_response = b'fake response start\r\nfake response end\r\n\r\n'
+        webSocket = compute._WebSocket(self.client_sock, self.url)
+
+        self.client_sock.recv.assert_has_calls([mock.call(4096),
+                                                mock.call(4096)])
+        self.assertEqual(webSocket.response, expect_response)
+        self.assertEqual(webSocket.cached_stream, frame_part1)
+
+        self.client_sock.recv.reset_mock()
+
+        recv_version = webSocket.receive_frame()
+
+        self.client_sock.recv.assert_called_once_with(len(frame_part2))
+        self.assertEqual(recv_version, RFP_VERSION)
+        # cached_stream should be empty in the end.
+        self.assertEqual(webSocket.cached_stream, b'')
diff --git a/tempest/tests/common/test_credentials_factory.py b/tempest/tests/common/test_credentials_factory.py
new file mode 100644
index 0000000..020818e
--- /dev/null
+++ b/tempest/tests/common/test_credentials_factory.py
@@ -0,0 +1,279 @@
+# Copyright 2017 IBM Corp.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import mock
+from oslo_config import cfg
+import testtools
+
+from tempest.common import credentials_factory as cf
+from tempest import config
+from tempest.lib.common import dynamic_creds
+from tempest.lib.common import preprov_creds
+from tempest.lib import exceptions
+from tempest.tests import base
+from tempest.tests import fake_config
+from tempest.tests.lib import fake_credentials
+
+
+class TestCredentialsFactory(base.TestCase):
+
+    def setUp(self):
+        super(TestCredentialsFactory, self).setUp()
+        self.useFixture(fake_config.ConfigFixture())
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
+
+    def test_get_dynamic_provider_params_creds_v2(self):
+        expected_uri = 'EXPECTED_V2_URI'
+        cfg.CONF.set_default('uri', expected_uri, group='identity')
+        admin_creds = fake_credentials.FakeCredentials()
+        params = cf.get_dynamic_provider_params('v2', admin_creds=admin_creds)
+        expected_params = dict(identity_uri=expected_uri,
+                               admin_creds=admin_creds)
+        for key in expected_params:
+            self.assertIn(key, params)
+            self.assertEqual(expected_params[key], params[key])
+
+    def test_get_dynamic_provider_params_creds_v3(self):
+        expected_uri = 'EXPECTED_V3_URI'
+        cfg.CONF.set_default('uri_v3', expected_uri, group='identity')
+        admin_creds = fake_credentials.FakeCredentials()
+        params = cf.get_dynamic_provider_params('v3', admin_creds=admin_creds)
+        expected_params = dict(identity_uri=expected_uri,
+                               admin_creds=admin_creds)
+        for key in expected_params:
+            self.assertIn(key, params)
+            self.assertEqual(expected_params[key], params[key])
+
+    def test_get_dynamic_provider_params_creds_vx(self):
+        admin_creds = fake_credentials.FakeCredentials()
+        invalid_version = 'invalid_version_x'
+        with testtools.ExpectedException(
+                exc_type=exceptions.InvalidIdentityVersion,
+                value_re='Invalid version ' + invalid_version):
+            cf.get_dynamic_provider_params(invalid_version,
+                                           admin_creds=admin_creds)
+
+    def test_get_dynamic_provider_params_no_creds(self):
+        expected_identity_version = 'v3'
+        with mock.patch.object(
+                cf, 'get_configured_admin_credentials') as admin_creds_mock:
+            cf.get_dynamic_provider_params(expected_identity_version)
+            admin_creds_mock.assert_called_once_with(
+                fill_in=True, identity_version=expected_identity_version)
+
+    def test_get_preprov_provider_params_creds_v2(self):
+        expected_uri = 'EXPECTED_V2_URI'
+        cfg.CONF.set_default('uri', expected_uri, group='identity')
+        params = cf.get_preprov_provider_params('v2')
+        self.assertIn('identity_uri', params)
+        self.assertEqual(expected_uri, params['identity_uri'])
+
+    def test_get_preprov_provider_params_creds_v3(self):
+        expected_uri = 'EXPECTED_V3_URI'
+        cfg.CONF.set_default('uri_v3', expected_uri, group='identity')
+        params = cf.get_preprov_provider_params('v3')
+        self.assertIn('identity_uri', params)
+        self.assertEqual(expected_uri, params['identity_uri'])
+
+    def test_get_preprov_provider_params_creds_vx(self):
+        invalid_version = 'invalid_version_x'
+        with testtools.ExpectedException(
+                exc_type=exceptions.InvalidIdentityVersion,
+                value_re='Invalid version ' + invalid_version):
+            cf.get_dynamic_provider_params(invalid_version)
+
+    @mock.patch.object(dynamic_creds, 'DynamicCredentialProvider')
+    @mock.patch.object(cf, 'get_dynamic_provider_params')
+    def test_get_credentials_provider_dynamic(
+            self, mock_dynamic_provider_params,
+            mock_dynamic_credentials_provider_class):
+        cfg.CONF.set_default('use_dynamic_credentials', True, group='auth')
+        expected_params = {'foo': 'bar'}
+        mock_dynamic_provider_params.return_value = expected_params
+        expected_name = 'my_name'
+        expected_network_resources = {'network': 'resources'}
+        expected_identity_version = 'identity_version'
+        cf.get_credentials_provider(
+            expected_name,
+            network_resources=expected_network_resources,
+            force_tenant_isolation=False,
+            identity_version=expected_identity_version)
+        mock_dynamic_provider_params.assert_called_once_with(
+            expected_identity_version)
+        mock_dynamic_credentials_provider_class.assert_called_once_with(
+            name=expected_name, network_resources=expected_network_resources,
+            **expected_params)
+
+    @mock.patch.object(preprov_creds, 'PreProvisionedCredentialProvider')
+    @mock.patch.object(cf, 'get_preprov_provider_params')
+    def test_get_credentials_provider_preprov(
+            self, mock_preprov_provider_params,
+            mock_preprov_credentials_provider_class):
+        cfg.CONF.set_default('use_dynamic_credentials', False, group='auth')
+        cfg.CONF.set_default('test_accounts_file', '/some/file', group='auth')
+        expected_params = {'foo': 'bar'}
+        mock_preprov_provider_params.return_value = expected_params
+        expected_name = 'my_name'
+        expected_identity_version = 'identity_version'
+        cf.get_credentials_provider(
+            expected_name,
+            force_tenant_isolation=False,
+            identity_version=expected_identity_version)
+        mock_preprov_provider_params.assert_called_once_with(
+            expected_identity_version)
+        mock_preprov_credentials_provider_class.assert_called_once_with(
+            name=expected_name, **expected_params)
+
+    def test_get_credentials_provider_preprov_no_file(self):
+        cfg.CONF.set_default('use_dynamic_credentials', False, group='auth')
+        cfg.CONF.set_default('test_accounts_file', None, group='auth')
+        with testtools.ExpectedException(
+                exc_type=exceptions.InvalidConfiguration):
+            cf.get_credentials_provider(
+                'some_name',
+                force_tenant_isolation=False,
+                identity_version='some_version')
+
+    @mock.patch.object(dynamic_creds, 'DynamicCredentialProvider')
+    @mock.patch.object(cf, 'get_dynamic_provider_params')
+    def test_get_credentials_provider_force_dynamic(
+            self, mock_dynamic_provider_params,
+            mock_dynamic_credentials_provider_class):
+        cfg.CONF.set_default('use_dynamic_credentials', False, group='auth')
+        expected_params = {'foo': 'bar'}
+        mock_dynamic_provider_params.return_value = expected_params
+        expected_name = 'my_name'
+        expected_network_resources = {'network': 'resources'}
+        expected_identity_version = 'identity_version'
+        cf.get_credentials_provider(
+            expected_name,
+            network_resources=expected_network_resources,
+            force_tenant_isolation=True,
+            identity_version=expected_identity_version)
+        mock_dynamic_provider_params.assert_called_once_with(
+            expected_identity_version)
+        mock_dynamic_credentials_provider_class.assert_called_once_with(
+            name=expected_name, network_resources=expected_network_resources,
+            **expected_params)
+
+    @mock.patch.object(cf, 'get_credentials')
+    def test_get_configured_admin_credentials(self, mock_get_credentials):
+        cfg.CONF.set_default('auth_version', 'v3', 'identity')
+        all_params = [('admin_username', 'username', 'my_name'),
+                      ('admin_password', 'password', 'secret'),
+                      ('admin_project_name', 'project_name', 'my_pname'),
+                      ('admin_domain_name', 'domain_name', 'my_dname')]
+        expected_result = 'my_admin_credentials'
+        mock_get_credentials.return_value = expected_result
+        for config_item, _, value in all_params:
+            cfg.CONF.set_default(config_item, value, 'auth')
+        # Build the expected params
+        expected_params = dict(
+            [(field, value) for _, field, value in all_params])
+        expected_params.update(cf.DEFAULT_PARAMS)
+        admin_creds = cf.get_configured_admin_credentials()
+        mock_get_credentials.assert_called_once_with(
+            fill_in=True, identity_version='v3', **expected_params)
+        self.assertEqual(expected_result, admin_creds)
+
+    @mock.patch.object(cf, 'get_credentials')
+    def test_get_configured_admin_credentials_not_fill_valid(
+            self, mock_get_credentials):
+        cfg.CONF.set_default('auth_version', 'v2', 'identity')
+        all_params = [('admin_username', 'username', 'my_name'),
+                      ('admin_password', 'password', 'secret'),
+                      ('admin_project_name', 'project_name', 'my_pname'),
+                      ('admin_domain_name', 'domain_name', 'my_dname')]
+        expected_result = mock.Mock()
+        expected_result.is_valid.return_value = True
+        mock_get_credentials.return_value = expected_result
+        for config_item, _, value in all_params:
+            cfg.CONF.set_default(config_item, value, 'auth')
+        # Build the expected params
+        expected_params = dict(
+            [(field, value) for _, field, value in all_params])
+        expected_params.update(cf.DEFAULT_PARAMS)
+        admin_creds = cf.get_configured_admin_credentials(
+            fill_in=False, identity_version='v3')
+        mock_get_credentials.assert_called_once_with(
+            fill_in=False, identity_version='v3', **expected_params)
+        self.assertEqual(expected_result, admin_creds)
+        expected_result.is_valid.assert_called_once()
+
+    @mock.patch.object(cf, 'get_credentials')
+    def test_get_configured_admin_credentials_not_fill_not_valid(
+            self, mock_get_credentials):
+        cfg.CONF.set_default('auth_version', 'v2', 'identity')
+        expected_result = mock.Mock()
+        expected_result.is_valid.return_value = False
+        mock_get_credentials.return_value = expected_result
+        with testtools.ExpectedException(exceptions.InvalidConfiguration,
+                                         value_re='.*\n.*identity version v2'):
+            cf.get_configured_admin_credentials(fill_in=False)
+
+    @mock.patch('tempest.lib.auth.get_credentials')
+    def test_get_credentials_v2(self, mock_auth_get_credentials):
+        expected_uri = 'V2_URI'
+        expected_result = 'my_creds'
+        mock_auth_get_credentials.return_value = expected_result
+        cfg.CONF.set_default('uri', expected_uri, 'identity')
+        params = {'foo': 'bar'}
+        expected_params = params.copy()
+        expected_params.update(cf.DEFAULT_PARAMS)
+        result = cf.get_credentials(identity_version='v2', **params)
+        self.assertEqual(expected_result, result)
+        mock_auth_get_credentials.assert_called_once_with(
+            expected_uri, fill_in=True, identity_version='v2',
+            **expected_params)
+
+    @mock.patch('tempest.lib.auth.get_credentials')
+    def test_get_credentials_v3_no_domain(self, mock_auth_get_credentials):
+        expected_uri = 'V3_URI'
+        expected_result = 'my_creds'
+        expected_domain = 'my_domain'
+        mock_auth_get_credentials.return_value = expected_result
+        cfg.CONF.set_default('uri_v3', expected_uri, 'identity')
+        cfg.CONF.set_default('default_credentials_domain_name',
+                             expected_domain, 'auth')
+        params = {'foo': 'bar'}
+        expected_params = params.copy()
+        expected_params['domain_name'] = expected_domain
+        expected_params.update(cf.DEFAULT_PARAMS)
+        result = cf.get_credentials(fill_in=False, identity_version='v3',
+                                    **params)
+        self.assertEqual(expected_result, result)
+        mock_auth_get_credentials.assert_called_once_with(
+            expected_uri, fill_in=False, identity_version='v3',
+            **expected_params)
+
+    @mock.patch('tempest.lib.auth.get_credentials')
+    def test_get_credentials_v3_domain(self, mock_auth_get_credentials):
+        expected_uri = 'V3_URI'
+        expected_result = 'my_creds'
+        expected_domain = 'my_domain'
+        mock_auth_get_credentials.return_value = expected_result
+        cfg.CONF.set_default('uri_v3', expected_uri, 'identity')
+        cfg.CONF.set_default('default_credentials_domain_name',
+                             expected_domain, 'auth')
+        params = {'foo': 'bar', 'user_domain_name': expected_domain}
+        expected_params = params.copy()
+        expected_params.update(cf.DEFAULT_PARAMS)
+        result = cf.get_credentials(fill_in=False, identity_version='v3',
+                                    **params)
+        self.assertEqual(expected_result, result)
+        mock_auth_get_credentials.assert_called_once_with(
+            expected_uri, fill_in=False, identity_version='v3',
+            **expected_params)
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index 46f9526..bc197b5 100644
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -59,6 +59,7 @@
         # Tests that the wait method raises VolumeRestoreErrorException if
         # the volume status is 'error_restoring'.
         client = mock.Mock(spec=volumes_client.VolumesClient,
+                           resource_type="volume",
                            build_interval=1)
         volume1 = {'volume': {'status': 'restoring-backup'}}
         volume2 = {'volume': {'status': 'error_restoring'}}
@@ -66,7 +67,7 @@
         client.show_volume = mock_show
         volume_id = '7532b91e-aa0a-4e06-b3e5-20c0c5ee1caa'
         self.assertRaises(exceptions.VolumeRestoreErrorException,
-                          waiters.wait_for_volume_status,
+                          waiters.wait_for_volume_resource_status,
                           client, volume_id, 'available')
         mock_show.assert_has_calls([mock.call(volume_id),
                                     mock.call(volume_id)])
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index e4f4c04..739357b 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -12,11 +12,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import fixtures
 import time
 
+import fixtures
 from oslo_config import cfg
-from oslotest import mockpatch
 
 from tempest.common.utils.linux import remote_client
 from tempest import config
@@ -64,18 +63,8 @@
         cfg.CONF.set_default('connect_timeout', 1, group='validation')
 
         self.conn = remote_client.RemoteClient('127.0.0.1', 'user', 'pass')
-        self.ssh_mock = self.useFixture(mockpatch.PatchObject(self.conn,
-                                                              'ssh_client'))
-
-    def test_get_hostname(self):
-        self.ssh_mock.mock.exec_command.return_value = 'fake_hostname'
-        self.assertEqual(self.conn.get_hostname(), 'fake_hostname')
-
-    def test_get_ram_size(self):
-        free_output = "Mem:         48294      45738       2555          0" \
-                      "402      40346"
-        self.ssh_mock.mock.exec_command.return_value = free_output
-        self.assertEqual(self.conn.get_ram_size_in_mb(), '48294')
+        self.ssh_mock = self.useFixture(fixtures.MockPatchObject(self.conn,
+                                                                 'ssh_client'))
 
     def test_write_to_console_regular_str(self):
         self.conn.write_to_console('test')
@@ -102,11 +91,6 @@
         cmd = "set -eu -o pipefail; PATH=$PATH:/sbin; " + cmd
         self.ssh_mock.mock.exec_command.assert_called_with(cmd)
 
-    def test_get_number_of_vcpus(self):
-        self.ssh_mock.mock.exec_command.return_value = '16'
-        self.assertEqual(self.conn.get_number_of_vcpus(), 16)
-        self._assert_exec_called_with('grep -c ^processor /proc/cpuinfo')
-
     def test_get_disks(self):
         output_lsblk = """\
 NAME       MAJ:MIN    RM          SIZE RO TYPE MOUNTPOINT
@@ -126,7 +110,7 @@
         booted_at = 10000
         uptime_sec = 5000.02
         self.ssh_mock.mock.exec_command.return_value = uptime_sec
-        self.useFixture(mockpatch.PatchObject(
+        self.useFixture(fixtures.MockPatchObject(
             time, 'time', return_value=booted_at + uptime_sec))
         self.assertEqual(self.conn.get_boot_time(),
                          time.localtime(booted_at))
@@ -154,40 +138,6 @@
         self._assert_exec_called_with(
             "ip addr | awk '/ether/ {print $2}'")
 
-    def test_get_ip_list(self):
-        ips = """1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue
-    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
-    inet 127.0.0.1/8 scope host lo
-    inet6 ::1/128 scope host
-       valid_lft forever preferred_lft forever
-2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
-    link/ether fa:16:3e:6e:26:3b brd ff:ff:ff:ff:ff:ff
-    inet 10.0.0.4/24 brd 10.0.0.255 scope global eth0
-    inet6 fd55:faaf:e1ab:3d9:f816:3eff:fe6e:263b/64 scope global dynamic
-       valid_lft 2591936sec preferred_lft 604736sec
-    inet6 fe80::f816:3eff:fe6e:263b/64 scope link
-       valid_lft forever preferred_lft forever"""
-        self.ssh_mock.mock.exec_command.return_value = ips
-        self.assertEqual(self.conn.get_ip_list(), ips)
-        self._assert_exec_called_with('ip address')
-
-    def test_assign_static_ip(self):
-        self.ssh_mock.mock.exec_command.return_value = ''
-        ip = '10.0.0.2'
-        nic = 'eth0'
-        self.assertEqual(self.conn.assign_static_ip(nic, ip), '')
-        self._assert_exec_called_with(
-            "sudo ip addr add %s/%s dev %s" % (ip, '28', nic))
-
-    def test_set_nic_state(self):
-        nic = 'eth0'
-        self.conn.set_nic_state(nic)
-        self._assert_exec_called_with(
-            'sudo ip link set %s up' % nic)
-        self.conn.set_nic_state(nic, "down")
-        self._assert_exec_called_with(
-            'sudo ip link set %s down' % nic)
-
 
 class TestRemoteClientWithServer(base.TestCase):
 
@@ -214,7 +164,7 @@
                                            user='user',
                                            password='pass')))
         self.log = self.useFixture(fixtures.FakeLogger(
-            name='tempest.common.utils.linux.remote_client',
+            name='tempest.lib.common.utils.linux.remote_client',
             level='DEBUG'))
 
     def test_validate_debug_ssh_console(self):
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index 71a4c81..ee63684 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -93,12 +93,6 @@
         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')
@@ -109,5 +103,4 @@
         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/cli/test_execute.py b/tempest/tests/lib/cli/test_execute.py
index aaeb6f4..0130454 100644
--- a/tempest/tests/lib/cli/test_execute.py
+++ b/tempest/tests/lib/cli/test_execute.py
@@ -11,9 +11,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
 import subprocess
 
+import mock
+
 from tempest.lib.cli import base as cli_base
 from tempest.lib import exceptions
 from tempest.tests import base
diff --git a/tempest/tests/lib/common/test_api_version_utils.py b/tempest/tests/lib/common/test_api_version_utils.py
index 6206379..b99e8d4 100644
--- a/tempest/tests/lib/common/test_api_version_utils.py
+++ b/tempest/tests/lib/common/test_api_version_utils.py
@@ -12,6 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import six
 import testtools
 
 from tempest.lib.common import api_version_utils
@@ -30,7 +31,7 @@
                                                            cfg_max_version)
         except testtools.TestCase.skipException as e:
             if not expected_skip:
-                raise testtools.TestCase.failureException(e.message)
+                raise testtools.TestCase.failureException(six.text_type(e))
 
     def test_version_min_in_range(self):
         self._test_version('2.2', '2.10', '2.1', '2.7')
@@ -91,24 +92,106 @@
     def test_header_matches(self):
         microversion_header_name = 'x-openstack-xyz-api-version'
         request_microversion = '2.1'
-        test_respose = {microversion_header_name: request_microversion}
+        test_response = {microversion_header_name: request_microversion}
         api_version_utils.assert_version_header_matches_request(
-            microversion_header_name, request_microversion, test_respose)
+            microversion_header_name, request_microversion, test_response)
 
     def test_header_does_not_match(self):
         microversion_header_name = 'x-openstack-xyz-api-version'
         request_microversion = '2.1'
-        test_respose = {microversion_header_name: '2.2'}
+        test_response = {microversion_header_name: '2.2'}
         self.assertRaises(
             exceptions.InvalidHTTPResponseHeader,
             api_version_utils.assert_version_header_matches_request,
-            microversion_header_name, request_microversion, test_respose)
+            microversion_header_name, request_microversion, test_response)
 
     def test_header_not_present(self):
         microversion_header_name = 'x-openstack-xyz-api-version'
         request_microversion = '2.1'
-        test_respose = {}
+        test_response = {}
         self.assertRaises(
             exceptions.InvalidHTTPResponseHeader,
             api_version_utils.assert_version_header_matches_request,
-            microversion_header_name, request_microversion, test_respose)
+            microversion_header_name, request_microversion, test_response)
+
+    def test_compare_versions_less_than(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.2'
+        test_response = {microversion_header_name: '2.1'}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "lt"))
+
+    def test_compare_versions_less_than_equal(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.2'
+        test_response = {microversion_header_name: '2.1'}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "le"))
+
+    def test_compare_versions_greater_than_equal(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_response = {microversion_header_name: '2.2'}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "ge"))
+
+    def test_compare_versions_greater_than(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_response = {microversion_header_name: '2.2'}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "gt"))
+
+    def test_compare_versions_equal(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.11'
+        test_response = {microversion_header_name: '2.1'}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "eq"))
+
+    def test_compare_versions_not_equal(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_response = {microversion_header_name: '2.1'}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "ne"))
+
+    def test_compare_versions_with_name_in_microversion(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = 'volume 3.1'
+        test_response = {microversion_header_name: 'volume 3.1'}
+        self.assertTrue(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "eq"))
+
+    def test_compare_versions_invalid_operation(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_response = {microversion_header_name: '2.1'}
+        self.assertRaises(
+            exceptions.InvalidParam,
+            api_version_utils.compare_version_header_to_response,
+            microversion_header_name, request_microversion, test_response,
+            "foo")
+
+    def test_compare_versions_header_not_present(self):
+        microversion_header_name = 'x-openstack-xyz-api-version'
+        request_microversion = '2.1'
+        test_response = {}
+        self.assertFalse(
+            api_version_utils.compare_version_header_to_response(
+                microversion_header_name, request_microversion, test_response,
+                "eq"))
diff --git a/tempest/tests/lib/common/test_cred_client.py b/tempest/tests/lib/common/test_cred_client.py
index 1cb3103..3dff16f 100644
--- a/tempest/tests/lib/common/test_cred_client.py
+++ b/tempest/tests/lib/common/test_cred_client.py
@@ -73,6 +73,5 @@
 
     def test_delete_project(self):
         self.creds_client.delete_project('fake_id')
-        print(self.projects_client.calls)
         self.projects_client.delete_project.assert_called_once_with(
             'fake_id')
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/lib/common/test_dynamic_creds.py
similarity index 96%
rename from tempest/tests/common/test_dynamic_creds.py
rename to tempest/tests/lib/common/test_dynamic_creds.py
index b4fbd50..6aa7a42 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/lib/common/test_dynamic_creds.py
@@ -12,13 +12,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import fixtures
 import mock
 from oslo_config import cfg
-from oslotest import mockpatch
 
 from tempest.common import credentials_factory as credentials
-from tempest.common import dynamic_creds
 from tempest import config
+from tempest.lib.common import dynamic_creds
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.identity.v2 import identity_client as v2_iden_client
@@ -46,7 +46,8 @@
 
     fixed_params = {'name': 'test class',
                     'identity_version': 'v2',
-                    'admin_role': 'admin'}
+                    'admin_role': 'admin',
+                    'identity_uri': 'fake_uri'}
 
     token_client = v2_token_client
     iden_client = v2_iden_client
@@ -84,7 +85,7 @@
             tenant_name='fake_tenant')
 
     def _mock_user_create(self, id, name):
-        user_fix = self.useFixture(mockpatch.PatchObject(
+        user_fix = self.useFixture(fixtures.MockPatchObject(
             self.users_client.UsersClient,
             'create_user',
             return_value=(rest_client.ResponseBody
@@ -92,7 +93,7 @@
         return user_fix
 
     def _mock_tenant_create(self, id, name):
-        tenant_fix = self.useFixture(mockpatch.PatchObject(
+        tenant_fix = self.useFixture(fixtures.MockPatchObject(
             self.tenants_client.TenantsClient,
             'create_tenant',
             return_value=(rest_client.ResponseBody
@@ -100,7 +101,7 @@
         return tenant_fix
 
     def _mock_list_roles(self, id, name):
-        roles_fix = self.useFixture(mockpatch.PatchObject(
+        roles_fix = self.useFixture(fixtures.MockPatchObject(
             self.roles_client.RolesClient,
             'list_roles',
             return_value=(rest_client.ResponseBody
@@ -111,7 +112,7 @@
         return roles_fix
 
     def _mock_list_2_roles(self):
-        roles_fix = self.useFixture(mockpatch.PatchObject(
+        roles_fix = self.useFixture(fixtures.MockPatchObject(
             self.roles_client.RolesClient,
             'list_roles',
             return_value=(rest_client.ResponseBody
@@ -122,7 +123,7 @@
         return roles_fix
 
     def _mock_assign_user_role(self):
-        tenant_fix = self.useFixture(mockpatch.PatchObject(
+        tenant_fix = self.useFixture(fixtures.MockPatchObject(
             self.roles_client.RolesClient,
             'create_user_role_on_project',
             return_value=(rest_client.ResponseBody
@@ -130,7 +131,7 @@
         return tenant_fix
 
     def _mock_list_role(self):
-        roles_fix = self.useFixture(mockpatch.PatchObject(
+        roles_fix = self.useFixture(fixtures.MockPatchObject(
             self.roles_client.RolesClient,
             'list_roles',
             return_value=(rest_client.ResponseBody
@@ -140,7 +141,7 @@
         return roles_fix
 
     def _mock_list_ec2_credentials(self, user_id, tenant_id):
-        ec2_creds_fix = self.useFixture(mockpatch.PatchObject(
+        ec2_creds_fix = self.useFixture(fixtures.MockPatchObject(
             self.users_client.UsersClient,
             'list_user_ec2_credentials',
             return_value=(rest_client.ResponseBody
@@ -153,21 +154,21 @@
         return ec2_creds_fix
 
     def _mock_network_create(self, iso_creds, id, name):
-        net_fix = self.useFixture(mockpatch.PatchObject(
+        net_fix = self.useFixture(fixtures.MockPatchObject(
             iso_creds.networks_admin_client,
             'create_network',
             return_value={'network': {'id': id, 'name': name}}))
         return net_fix
 
     def _mock_subnet_create(self, iso_creds, id, name):
-        subnet_fix = self.useFixture(mockpatch.PatchObject(
+        subnet_fix = self.useFixture(fixtures.MockPatchObject(
             iso_creds.subnets_admin_client,
             'create_subnet',
             return_value={'subnet': {'id': id, 'name': name}}))
         return subnet_fix
 
     def _mock_router_create(self, id, name):
-        router_fix = self.useFixture(mockpatch.PatchObject(
+        router_fix = self.useFixture(fixtures.MockPatchObject(
             routers_client.RoutersClient,
             'create_router',
             return_value={'router': {'id': id, 'name': name}}))
@@ -619,7 +620,8 @@
 
     fixed_params = {'name': 'test class',
                     'identity_version': 'v3',
-                    'admin_role': 'admin'}
+                    'admin_role': 'admin',
+                    'identity_uri': 'fake_uri'}
 
     token_client = v3_token_client
     iden_client = v3_iden_client
@@ -634,7 +636,7 @@
     def setUp(self):
         super(TestDynamicCredentialProviderV3, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
-        self.useFixture(mockpatch.PatchObject(
+        self.useFixture(fixtures.MockPatchObject(
             domains_client.DomainsClient, 'list_domains',
             return_value=dict(domains=[dict(id='default',
                                             name='Default')])))
@@ -645,7 +647,7 @@
         pass
 
     def _mock_tenant_create(self, id, name):
-        project_fix = self.useFixture(mockpatch.PatchObject(
+        project_fix = self.useFixture(fixtures.MockPatchObject(
             self.tenants_client.ProjectsClient,
             'create_project',
             return_value=(rest_client.ResponseBody
@@ -657,7 +659,7 @@
         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:
+        with mock.patch('tempest.lib.common.dynamic_creds.LOG') as log_mock:
             creds._create_creds()
             log_mock.warning.assert_called_once_with(
                 "Member role already exists, ignoring conflict.")
diff --git a/tempest/tests/lib/common/test_http.py b/tempest/tests/lib/common/test_http.py
new file mode 100644
index 0000000..a292209
--- /dev/null
+++ b/tempest/tests/lib/common/test_http.py
@@ -0,0 +1,68 @@
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES 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 http
+from tempest.tests import base
+
+
+class TestClosingHttp(base.TestCase):
+    def setUp(self):
+        super(TestClosingHttp, self).setUp()
+        self.cert_none = "CERT_NONE"
+        self.cert_location = "/etc/ssl/certs/ca-certificates.crt"
+
+    def test_constructor_invalid_ca_certs_and_timeout(self):
+        connection = http.ClosingHttp(
+            disable_ssl_certificate_validation=False,
+            ca_certs=None,
+            timeout=None)
+        for attr in ('cert_reqs', 'ca_certs', 'timeout'):
+            self.assertNotIn(attr, connection.connection_pool_kw)
+
+    def test_constructor_valid_ca_certs(self):
+        cert_required = 'CERT_REQUIRED'
+        connection = http.ClosingHttp(
+            disable_ssl_certificate_validation=False,
+            ca_certs=self.cert_location,
+            timeout=None)
+        self.assertEqual(cert_required,
+                         connection.connection_pool_kw['cert_reqs'])
+        self.assertEqual(self.cert_location,
+                         connection.connection_pool_kw['ca_certs'])
+        self.assertNotIn('timeout',
+                         connection.connection_pool_kw)
+
+    def test_constructor_ssl_cert_validation_disabled(self):
+        connection = http.ClosingHttp(
+            disable_ssl_certificate_validation=True,
+            ca_certs=None,
+            timeout=30)
+        self.assertEqual(self.cert_none,
+                         connection.connection_pool_kw['cert_reqs'])
+        self.assertEqual(30,
+                         connection.connection_pool_kw['timeout'])
+        self.assertNotIn('ca_certs',
+                         connection.connection_pool_kw)
+
+    def test_constructor_ssl_cert_validation_disabled_and_ca_certs(self):
+        connection = http.ClosingHttp(
+            disable_ssl_certificate_validation=True,
+            ca_certs=self.cert_location,
+            timeout=None)
+        self.assertNotIn('timeout',
+                         connection.connection_pool_kw)
+        self.assertEqual(self.cert_none,
+                         connection.connection_pool_kw['cert_reqs'])
+        self.assertNotIn('ca_certs',
+                         connection.connection_pool_kw)
diff --git a/tempest/tests/common/test_preprov_creds.py b/tempest/tests/lib/common/test_preprov_creds.py
similarity index 87%
rename from tempest/tests/common/test_preprov_creds.py
rename to tempest/tests/lib/common/test_preprov_creds.py
index 1c9982c..5402e47 100644
--- a/tempest/tests/common/test_preprov_creds.py
+++ b/tempest/tests/lib/common/test_preprov_creds.py
@@ -14,19 +14,20 @@
 
 import hashlib
 import os
-import testtools
+import shutil
 
 import mock
+import six
+import testtools
+
+import fixtures
 from oslo_concurrency.fixture import lockutils as lockutils_fixtures
 from oslo_config import cfg
-from oslotest import mockpatch
-import shutil
-import six
 
-from tempest.common import preprov_creds
 from tempest import config
 from tempest.lib import auth
 from tempest.lib.common import cred_provider
+from tempest.lib.common import preprov_creds
 from tempest.lib import exceptions as lib_exc
 from tempest.tests import base
 from tempest.tests import fake_config
@@ -37,6 +38,7 @@
 
     fixed_params = {'name': 'test class',
                     'identity_version': 'v2',
+                    'identity_uri': 'fake_uri',
                     'test_accounts_file': 'fake_accounts_file',
                     'accounts_lock_dir': 'fake_locks_dir',
                     'admin_role': 'admin',
@@ -85,10 +87,14 @@
         self.patch(self.token_client, side_effect=self.identity_response)
         self.useFixture(lockutils_fixtures.ExternalLockFixture())
         self.test_accounts = self._fake_accounts(cfg.CONF.identity.admin_role)
-        self.accounts_mock = self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
+        self.accounts_mock = self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.preprov_creds.read_accounts_yaml',
             return_value=self.test_accounts))
-        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
+        self.useFixture(fixtures.MockPatch(
+            'os.path.isfile', return_value=True))
+        # NOTE(andreaf) Ensure config is loaded so service clients are
+        # registered in the registry before tests
+        config.service_client_config()
 
     def tearDown(self):
         super(TestPreProvisionedCredentials, self).tearDown()
@@ -137,7 +143,8 @@
 
     def test_create_hash_file_previous_file(self):
         # Emulate the lock existing on the filesystem
-        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
+        self.useFixture(fixtures.MockPatch(
+            'os.path.isfile', return_value=True))
         with mock.patch('six.moves.builtins.open', mock.mock_open(),
                         create=True):
             test_account_class = (
@@ -149,7 +156,8 @@
 
     def test_create_hash_file_no_previous_file(self):
         # Emulate the lock not existing on the filesystem
-        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=False))
+        self.useFixture(fixtures.MockPatch(
+            'os.path.isfile', return_value=False))
         with mock.patch('six.moves.builtins.open', mock.mock_open(),
                         create=True):
             test_account_class = (
@@ -162,10 +170,12 @@
     @mock.patch('oslo_concurrency.lockutils.lock')
     def test_get_free_hash_no_previous_accounts(self, lock_mock):
         # Emulate no pre-existing lock
-        self.useFixture(mockpatch.Patch('os.path.isdir', return_value=False))
+        self.useFixture(fixtures.MockPatch(
+            'os.path.isdir', return_value=False))
         hash_list = self._get_hash_list(self.test_accounts)
-        mkdir_mock = self.useFixture(mockpatch.Patch('os.mkdir'))
-        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=False))
+        mkdir_mock = self.useFixture(fixtures.MockPatch('os.mkdir'))
+        self.useFixture(fixtures.MockPatch(
+            'os.path.isfile', return_value=False))
         test_account_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
         with mock.patch('six.moves.builtins.open', mock.mock_open(),
@@ -181,9 +191,10 @@
     def test_get_free_hash_no_free_accounts(self, lock_mock):
         hash_list = self._get_hash_list(self.test_accounts)
         # Emulate pre-existing lock dir
-        self.useFixture(mockpatch.Patch('os.path.isdir', return_value=True))
-        # Emulate all lcoks in list are in use
-        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
+        self.useFixture(fixtures.MockPatch('os.path.isdir', return_value=True))
+        # Emulate all locks in list are in use
+        self.useFixture(fixtures.MockPatch(
+            'os.path.isfile', return_value=True))
         test_account_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
         with mock.patch('six.moves.builtins.open', mock.mock_open(),
@@ -194,7 +205,7 @@
     @mock.patch('oslo_concurrency.lockutils.lock')
     def test_get_free_hash_some_in_use_accounts(self, lock_mock):
         # Emulate no pre-existing lock
-        self.useFixture(mockpatch.Patch('os.path.isdir', return_value=True))
+        self.useFixture(fixtures.MockPatch('os.path.isdir', return_value=True))
         hash_list = self._get_hash_list(self.test_accounts)
         test_account_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
@@ -218,13 +229,14 @@
     def test_remove_hash_last_account(self, lock_mock):
         hash_list = self._get_hash_list(self.test_accounts)
         # Pretend the pseudo-lock is there
-        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
+        self.useFixture(
+            fixtures.MockPatch('os.path.isfile', return_value=True))
         # Pretend the lock dir is empty
-        self.useFixture(mockpatch.Patch('os.listdir', return_value=[]))
+        self.useFixture(fixtures.MockPatch('os.listdir', return_value=[]))
         test_account_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
-        remove_mock = self.useFixture(mockpatch.Patch('os.remove'))
-        rmdir_mock = self.useFixture(mockpatch.Patch('os.rmdir'))
+        remove_mock = self.useFixture(fixtures.MockPatch('os.remove'))
+        rmdir_mock = self.useFixture(fixtures.MockPatch('os.rmdir'))
         test_account_class.remove_hash(hash_list[2])
         hash_path = os.path.join(self.fixed_params['accounts_lock_dir'],
                                  hash_list[2])
@@ -236,14 +248,15 @@
     def test_remove_hash_not_last_account(self, lock_mock):
         hash_list = self._get_hash_list(self.test_accounts)
         # Pretend the pseudo-lock is there
-        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
+        self.useFixture(fixtures.MockPatch(
+            'os.path.isfile', return_value=True))
         # Pretend the lock dir is empty
-        self.useFixture(mockpatch.Patch('os.listdir', return_value=[
+        self.useFixture(fixtures.MockPatch('os.listdir', return_value=[
             hash_list[1], hash_list[4]]))
         test_account_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
-        remove_mock = self.useFixture(mockpatch.Patch('os.remove'))
-        rmdir_mock = self.useFixture(mockpatch.Patch('os.rmdir'))
+        remove_mock = self.useFixture(fixtures.MockPatch('os.remove'))
+        rmdir_mock = self.useFixture(fixtures.MockPatch('os.rmdir'))
         test_account_class.remove_hash(hash_list[2])
         hash_path = os.path.join(self.fixed_params['accounts_lock_dir'],
                                  hash_list[2])
@@ -257,8 +270,8 @@
 
     def test_is_not_multi_user(self):
         self.test_accounts = [self.test_accounts[0]]
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.preprov_creds.read_accounts_yaml',
             return_value=self.test_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
@@ -269,7 +282,7 @@
             **self.fixed_params)
         hashes = test_accounts_class.hash_dict['roles']['role4']
         temp_hash = hashes[0]
-        get_free_hash_mock = self.useFixture(mockpatch.PatchObject(
+        get_free_hash_mock = self.useFixture(fixtures.MockPatchObject(
             test_accounts_class, '_get_free_hash', return_value=temp_hash))
         # Test a single role returns all matching roles
         test_accounts_class._get_creds(roles=['role4'])
@@ -286,7 +299,7 @@
         hashes2 = test_accounts_class.hash_dict['roles']['role2']
         hashes = list(set(hashes) & set(hashes2))
         temp_hash = hashes[0]
-        get_free_hash_mock = self.useFixture(mockpatch.PatchObject(
+        get_free_hash_mock = self.useFixture(fixtures.MockPatchObject(
             test_accounts_class, '_get_free_hash', return_value=temp_hash))
         # Test an intersection of multiple roles
         test_accounts_class._get_creds(roles=['role2', 'role4'])
@@ -303,7 +316,7 @@
         admin_hashes = test_accounts_class.hash_dict['roles'][
             cfg.CONF.identity.admin_role]
         temp_hash = hashes[0]
-        get_free_hash_mock = self.useFixture(mockpatch.PatchObject(
+        get_free_hash_mock = self.useFixture(fixtures.MockPatchObject(
             test_accounts_class, '_get_free_hash', return_value=temp_hash))
         # Test an intersection of multiple roles
         test_accounts_class._get_creds()
@@ -321,8 +334,8 @@
             {'username': 'test_user14', 'tenant_name': 'test_tenant14',
              'password': 'p', 'roles': ['role-7', 'role-11'],
              'resources': {'network': 'network-2'}}]
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.preprov_creds.read_accounts_yaml',
             return_value=test_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
@@ -349,8 +362,8 @@
     def test_get_primary_creds_none_available(self):
         admin_accounts = [x for x in self.test_accounts if 'test_admin'
                           in x['username']]
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.preprov_creds.read_accounts_yaml',
             return_value=admin_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
@@ -367,8 +380,8 @@
     def test_get_alt_creds_none_available(self):
         admin_accounts = [x for x in self.test_accounts if 'test_admin'
                           in x['username']]
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.preprov_creds.read_accounts_yaml',
             return_value=admin_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
@@ -388,8 +401,8 @@
              'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
             {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
              'password': 'p', 'types': ['admin']}]
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.preprov_creds.read_accounts_yaml',
             return_value=test_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
@@ -402,8 +415,8 @@
              'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
             {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
              'password': 'p', 'roles': [cfg.CONF.identity.admin_role]}]
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.preprov_creds.read_accounts_yaml',
             return_value=test_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
@@ -413,8 +426,8 @@
     def test_get_admin_creds_none_available(self):
         non_admin_accounts = [x for x in self.test_accounts if 'test_admin'
                               not in x['username']]
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.preprov_creds.read_accounts_yaml',
             return_value=non_admin_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
@@ -427,6 +440,7 @@
 
     fixed_params = {'name': 'test class',
                     'identity_version': 'v3',
+                    'identity_uri': 'fake_uri',
                     'test_accounts_file': 'fake_accounts_file',
                     'accounts_lock_dir': 'fake_locks_dir_v3',
                     'admin_role': 'admin',
diff --git a/tempest/tests/lib/test_rest_client.py b/tempest/tests/lib/common/test_rest_client.py
similarity index 96%
rename from tempest/tests/lib/test_rest_client.py
rename to tempest/tests/lib/common/test_rest_client.py
index e6cf047..4c0bb57 100644
--- a/tempest/tests/lib/test_rest_client.py
+++ b/tempest/tests/lib/common/test_rest_client.py
@@ -15,8 +15,8 @@
 import copy
 import json
 
+import fixtures
 import jsonschema
-from oslotest import mockpatch
 import six
 
 from tempest.lib.common import http
@@ -38,16 +38,16 @@
         self.rest_client = rest_client.RestClient(
             self.fake_auth_provider, None, None)
         self.patchobject(http.ClosingHttp, 'request', self.fake_http.request)
-        self.useFixture(mockpatch.PatchObject(self.rest_client,
-                                              '_log_request'))
+        self.useFixture(fixtures.MockPatchObject(self.rest_client,
+                                                 '_log_request'))
 
 
 class TestRestClientHTTPMethods(BaseRestClientTestClass):
     def setUp(self):
         self.fake_http = fake_http.fake_httplib2()
         super(TestRestClientHTTPMethods, self).setUp()
-        self.useFixture(mockpatch.PatchObject(self.rest_client,
-                                              '_error_checker'))
+        self.useFixture(fixtures.MockPatchObject(self.rest_client,
+                                                 '_error_checker'))
 
     def test_post(self):
         __, return_dict = self.rest_client.post(self.url, {}, {})
@@ -70,8 +70,8 @@
         self.assertEqual('PUT', return_dict['method'])
 
     def test_head(self):
-        self.useFixture(mockpatch.PatchObject(self.rest_client,
-                                              'response_checker'))
+        self.useFixture(fixtures.MockPatchObject(self.rest_client,
+                                                 'response_checker'))
         __, return_dict = self.rest_client.head(self.url)
         self.assertEqual('HEAD', return_dict['method'])
 
@@ -91,18 +91,15 @@
 
 
 class TestRestClientHeadersJSON(TestRestClientHTTPMethods):
-    TYPE = "json"
 
     def _verify_headers(self, resp):
-        self.assertEqual(self.rest_client._get_type(), self.TYPE)
         resp = dict((k.lower(), v) for k, v in six.iteritems(resp))
         self.assertEqual(self.header_value, resp['accept'])
         self.assertEqual(self.header_value, resp['content-type'])
 
     def setUp(self):
         super(TestRestClientHeadersJSON, self).setUp()
-        self.rest_client.TYPE = self.TYPE
-        self.header_value = 'application/%s' % self.rest_client._get_type()
+        self.header_value = 'application/json'
 
     def test_post(self):
         resp, __ = self.rest_client.post(self.url, {})
@@ -125,8 +122,8 @@
         self._verify_headers(resp)
 
     def test_head(self):
-        self.useFixture(mockpatch.PatchObject(self.rest_client,
-                                              'response_checker'))
+        self.useFixture(fixtures.MockPatchObject(self.rest_client,
+                                                 'response_checker'))
         resp, __ = self.rest_client.head(self.url)
         self._verify_headers(resp)
 
@@ -139,8 +136,8 @@
     def setUp(self):
         self.fake_http = fake_http.fake_httplib2()
         super(TestRestClientUpdateHeaders, self).setUp()
-        self.useFixture(mockpatch.PatchObject(self.rest_client,
-                                              '_error_checker'))
+        self.useFixture(fixtures.MockPatchObject(self.rest_client,
+                                                 '_error_checker'))
         self.headers = {'X-Configuration-Session': 'session_id'}
 
     def test_post_update_headers(self):
@@ -204,8 +201,8 @@
         )
 
     def test_head_update_headers(self):
-        self.useFixture(mockpatch.PatchObject(self.rest_client,
-                                              'response_checker'))
+        self.useFixture(fixtures.MockPatchObject(self.rest_client,
+                                                 'response_checker'))
 
         __, return_dict = self.rest_client.head(self.url,
                                                 extra_headers=True,
@@ -279,6 +276,11 @@
         body = self.rest_client._parse_resp(json.dumps(self.null_dict))
         self.assertEqual(self.null_dict, body)
 
+    def test_parse_empty_list(self):
+        empty_list = []
+        body = self.rest_client._parse_resp(json.dumps(empty_list))
+        self.assertEqual(empty_list, body)
+
 
 class TestRestClientErrorCheckerJSON(base.TestCase):
     c_type = "application/json"
@@ -337,6 +339,10 @@
     def test_response_410(self):
         self._test_error_checker(exceptions.Gone, self.set_data("410"))
 
+    def test_response_412(self):
+        self._test_error_checker(exceptions.PreconditionFailed,
+                                 self.set_data("412"))
+
     def test_response_413(self):
         self._test_error_checker(exceptions.OverLimit, self.set_data("413"))
 
@@ -460,7 +466,7 @@
 
     def test_response_bigger_than_400(self):
         # Any response code, that bigger than 400, and not in
-        # (401, 403, 404, 409, 413, 422, 500, 501)
+        # (401, 403, 404, 409, 412, 413, 422, 500, 501)
         self._test_error_checker(exceptions.UnexpectedResponseCode,
                                  self.set_data("402"))
 
@@ -1144,8 +1150,8 @@
     }
 
     def test_current_json_schema_validator_version(self):
-        with mockpatch.PatchObject(jsonschema.Draft4Validator,
-                                   "check_schema") as chk_schema:
+        with fixtures.MockPatchObject(jsonschema.Draft4Validator,
+                                      "check_schema") as chk_schema:
             body = {'foo': 'test'}
             self._test_validate_pass(self.schema, body)
             chk_schema.mock.assert_called_once_with(
diff --git a/tempest/api/image/admin/__init__.py b/tempest/tests/lib/common/utils/linux/__init__.py
similarity index 100%
copy from tempest/api/image/admin/__init__.py
copy to tempest/tests/lib/common/utils/linux/__init__.py
diff --git a/tempest/tests/lib/common/utils/linux/test_remote_client.py b/tempest/tests/lib/common/utils/linux/test_remote_client.py
new file mode 100644
index 0000000..7a21a5f
--- /dev/null
+++ b/tempest/tests/lib/common/utils/linux/test_remote_client.py
@@ -0,0 +1,67 @@
+# Copyright 2017 NEC Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import mock
+
+from tempest.lib.common import ssh
+from tempest.lib.common.utils.linux import remote_client
+from tempest.lib import exceptions as lib_exc
+from tempest.tests import base
+
+
+class FakeServersClient(object):
+
+    def get_console_output(self, server_id):
+        return {"output": "fake_output"}
+
+
+class TestRemoteClient(base.TestCase):
+
+    @mock.patch.object(ssh.Client, 'exec_command', return_value='success')
+    def test_exec_command(self, mock_ssh_exec_command):
+        client = remote_client.RemoteClient('192.168.1.10', 'username')
+        client.exec_command('ls')
+        mock_ssh_exec_command.assert_called_once_with(
+            'set -eu -o pipefail; PATH=$PATH:/sbin; ls')
+
+    @mock.patch.object(ssh.Client, 'test_connection_auth')
+    def test_validate_authentication(self, mock_test_connection_auth):
+        client = remote_client.RemoteClient('192.168.1.10', 'username')
+        client.validate_authentication()
+        mock_test_connection_auth.assert_called_once_with()
+
+    @mock.patch.object(remote_client.LOG, 'debug')
+    @mock.patch.object(ssh.Client, 'exec_command')
+    def test_debug_ssh_without_console(self, mock_exec_command, mock_debug):
+        mock_exec_command.side_effect = lib_exc.SSHTimeout
+        server = {'id': 'fake_id'}
+        client = remote_client.RemoteClient('192.168.1.10', 'username',
+                                            server=server)
+        self.assertRaises(lib_exc.SSHTimeout, client.exec_command, 'ls')
+        mock_debug.assert_called_with(
+            'Caller: %s. Timeout trying to ssh to server %s',
+            'TestRemoteClient:test_debug_ssh_without_console', server)
+
+    @mock.patch.object(remote_client.LOG, 'debug')
+    @mock.patch.object(ssh.Client, 'exec_command')
+    def test_debug_ssh_with_console(self, mock_exec_command, mock_debug):
+        mock_exec_command.side_effect = lib_exc.SSHTimeout
+        server = {'id': 'fake_id'}
+        client = remote_client.RemoteClient('192.168.1.10', 'username',
+                                            server=server,
+                                            servers_client=FakeServersClient())
+        self.assertRaises(lib_exc.SSHTimeout, client.exec_command, 'ls')
+        mock_debug.assert_called_with(
+            'Console log for server %s: %s', server['id'], 'fake_output')
diff --git a/tempest/tests/lib/common/utils/test_data_utils.py b/tempest/tests/lib/common/utils/test_data_utils.py
index 4446e5c..8bdf70e 100644
--- a/tempest/tests/lib/common/utils/test_data_utils.py
+++ b/tempest/tests/lib/common/utils/test_data_utils.py
@@ -37,16 +37,20 @@
         actual2 = data_utils.rand_uuid_hex()
         self.assertNotEqual(actual, actual2)
 
-    def test_rand_name(self):
-        actual = data_utils.rand_name()
+    def test_rand_name_with_default_prefix(self):
+        actual = data_utils.rand_name('foo')
         self.assertIsInstance(actual, str)
-        actual2 = data_utils.rand_name()
+        self.assertTrue(actual.startswith('tempest-foo'))
+        actual2 = data_utils.rand_name('foo')
+        self.assertTrue(actual2.startswith('tempest-foo'))
         self.assertNotEqual(actual, actual2)
 
-        actual = data_utils.rand_name('foo')
+    def test_rand_name_with_none_prefix(self):
+        actual = data_utils.rand_name('foo', prefix=None)
+        self.assertIsInstance(actual, str)
         self.assertTrue(actual.startswith('foo'))
-        actual2 = data_utils.rand_name('foo')
-        self.assertTrue(actual.startswith('foo'))
+        actual2 = data_utils.rand_name('foo', prefix=None)
+        self.assertTrue(actual2.startswith('foo'))
         self.assertNotEqual(actual, actual2)
 
     def test_rand_name_with_prefix(self):
diff --git a/tempest/tests/lib/services/base.py b/tempest/tests/lib/services/base.py
index a244aa2..924f9f2 100644
--- a/tempest/tests/lib/services/base.py
+++ b/tempest/tests/lib/services/base.py
@@ -12,8 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import fixtures
 from oslo_serialization import jsonutils as json
-from oslotest import mockpatch
 
 from tempest.tests import base
 from tempest.tests.lib import fake_http
@@ -31,12 +31,47 @@
 
     def check_service_client_function(self, function, function2mock,
                                       body, to_utf=False, status=200,
-                                      headers=None, **kwargs):
+                                      headers=None, mock_args=None,
+                                      resp_as_string=False,
+                                      **kwargs):
+        """Mock a service client function for unit testing.
+
+        :param function: The service client function to call.
+        :param function2mock: The REST call to mock inside the service client
+               function.
+        :param body: Expected response body returned by the service client
+               function.
+        :param to_utf: Whether to use UTF-8 encoding for response.
+        :param status: Expected response status returned by the service client
+               function.
+        :param headers: Expected headers returned by the service client
+               function.
+        :param mock_args: List/dict/value of expected args/kwargs called by
+               function2mock. For example:
+               * If mock_args=['foo'] then ``assert_called_once_with('foo')``
+                 is called.
+               * If mock_args={'foo': 'bar'} then
+                 ``assert_called_once_with(foo='bar')`` is called.
+               * If mock_args='foo' then ``assert_called_once_with('foo')``
+                 is called.
+        :param resp_as_string: Whether response body is retruned as string.
+               This is for service client methods which return ResponseBodyData
+               object.
+        :param kwargs: kwargs that are passed to function.
+        """
         mocked_response = self.create_response(body, to_utf, status, headers)
-        self.useFixture(mockpatch.Patch(
+        fixture = self.useFixture(fixtures.MockPatch(
             function2mock, return_value=mocked_response))
         if kwargs:
             resp = function(**kwargs)
         else:
             resp = function()
+        if resp_as_string:
+            resp = resp.data
         self.assertEqual(body, resp)
+        if isinstance(mock_args, list):
+            fixture.mock.assert_called_once_with(*mock_args)
+        elif isinstance(mock_args, dict):
+            fixture.mock.assert_called_once_with(**mock_args)
+        elif mock_args is not None:
+            fixture.mock.assert_called_once_with(mock_args)
diff --git a/tempest/tests/lib/services/compute/test_flavors_client.py b/tempest/tests/lib/services/compute/test_flavors_client.py
index 445ee22..cbd17c6 100644
--- a/tempest/tests/lib/services/compute/test_flavors_client.py
+++ b/tempest/tests/lib/services/compute/test_flavors_client.py
@@ -14,8 +14,8 @@
 
 import copy
 
+import fixtures
 from oslo_serialization import jsonutils as json
-from oslotest import mockpatch
 
 from tempest.lib.services.compute import flavors_client
 from tempest.tests.lib import fake_auth_provider
@@ -118,7 +118,7 @@
         if bytes_body:
             body = body.encode('utf-8')
         response = fake_http.fake_http_response({}, status=200), body
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             'tempest.lib.common.rest_client.RestClient.get',
             return_value=response))
         self.assertEqual(is_deleted,
diff --git a/tempest/tests/lib/services/compute/test_floating_ips_client.py b/tempest/tests/lib/services/compute/test_floating_ips_client.py
index 92737f2..950f9ce 100644
--- a/tempest/tests/lib/services/compute/test_floating_ips_client.py
+++ b/tempest/tests/lib/services/compute/test_floating_ips_client.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslotest import mockpatch
+import fixtures
 
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.compute import floating_ips_client
@@ -99,14 +99,14 @@
             server_id='c782b7a9-33cd-45f0-b795-7f87f456408b')
 
     def test_is_resource_deleted_true(self):
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             'tempest.lib.services.compute.floating_ips_client.'
             'FloatingIPsClient.show_floating_ip',
             side_effect=lib_exc.NotFound()))
         self.assertTrue(self.client.is_resource_deleted('fake-id'))
 
     def test_is_resource_deleted_false(self):
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             'tempest.lib.services.compute.floating_ips_client.'
             'FloatingIPsClient.show_floating_ip',
             return_value={"floating_ip": TestFloatingIpsClient.floating_ip}))
diff --git a/tempest/tests/lib/services/compute/test_images_client.py b/tempest/tests/lib/services/compute/test_images_client.py
index a9a570d..c2c3b76 100644
--- a/tempest/tests/lib/services/compute/test_images_client.py
+++ b/tempest/tests/lib/services/compute/test_images_client.py
@@ -14,8 +14,7 @@
 
 import copy
 
-from oslotest import mockpatch
-
+import fixtures
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.compute import images_client
 from tempest.tests.lib import fake_auth_provider
@@ -187,15 +186,15 @@
     def _test_resource_deleted(self, bytes_body=False):
         params = {"id": self.FAKE_IMAGE_ID}
         expected_op = self.FAKE_IMAGE_DATA['show']
-        self.useFixture(mockpatch.Patch('tempest.lib.services.compute'
+        self.useFixture(fixtures.MockPatch('tempest.lib.services.compute'
                         '.images_client.ImagesClient.show_image',
-                                        side_effect=lib_exc.NotFound))
+                                           side_effect=lib_exc.NotFound))
         self.assertEqual(True, self.client.is_resource_deleted(**params))
         tempdata = copy.deepcopy(self.FAKE_IMAGE_DATA['show'])
         tempdata['image']['id'] = None
-        self.useFixture(mockpatch.Patch('tempest.lib.services.compute'
+        self.useFixture(fixtures.MockPatch('tempest.lib.services.compute'
                         '.images_client.ImagesClient.show_image',
-                                        return_value=expected_op))
+                                           return_value=expected_op))
         self.assertEqual(False, self.client.is_resource_deleted(**params))
 
     def test_list_images_with_str_body(self):
diff --git a/tempest/tests/lib/services/compute/test_quotas_client.py b/tempest/tests/lib/services/compute/test_quotas_client.py
index 4c49e8d..bbb8eb7 100644
--- a/tempest/tests/lib/services/compute/test_quotas_client.py
+++ b/tempest/tests/lib/services/compute/test_quotas_client.py
@@ -49,22 +49,35 @@
         self.client = quotas_client.QuotasClient(
             fake_auth, 'compute', 'regionOne')
 
-    def _test_show_quota_set(self, bytes_body=False, user_id=None):
+    def _get_quota_set(self, detail):
+        if not detail:
+            return self.FAKE_QUOTA_SET
+        fake_quota_set = {"quota_set": {}}
+        for key, val in self.FAKE_QUOTA_SET['quota_set'].items():
+            fake_quota_set['quota_set'][key] = \
+                {'limit': val, 'reserved': 0, 'in_use': 0}
+        fake_quota_set['quota_set']['id'] = "8421f7be61064f50b680465c07f334af"
+        return fake_quota_set
+
+    def _test_show_quota_set(self, bytes_body=False, detail=False,
+                             user_id=None):
         if user_id:
             self.check_service_client_function(
                 self.client.show_quota_set,
                 'tempest.lib.common.rest_client.RestClient.get',
-                self.FAKE_QUOTA_SET,
+                self._get_quota_set(detail),
                 to_utf=bytes_body,
                 tenant_id=self.project_id,
+                detail=detail,
                 user_id=user_id)
         else:
             self.check_service_client_function(
                 self.client.show_quota_set,
                 'tempest.lib.common.rest_client.RestClient.get',
-                self.FAKE_QUOTA_SET,
+                self._get_quota_set(detail),
                 to_utf=bytes_body,
-                tenant_id=self.project_id)
+                tenant_id=self.project_id,
+                detail=detail)
 
     def test_show_quota_set_with_str_body(self):
         self._test_show_quota_set()
@@ -78,6 +91,9 @@
     def test_show_quota_set_for_user_with_bytes_body(self):
         self._test_show_quota_set(bytes_body=True, user_id=self.fake_user_id)
 
+    def test_show_quota_set_with_details(self):
+        self._test_show_quota_set(detail=True)
+
     def _test_show_default_quota_set(self, bytes_body=False):
         self.check_service_client_function(
             self.client.show_default_quota_set,
diff --git a/tempest/tests/lib/services/compute/test_security_groups_client.py b/tempest/tests/lib/services/compute/test_security_groups_client.py
index d293a08..7bbf20e 100644
--- a/tempest/tests/lib/services/compute/test_security_groups_client.py
+++ b/tempest/tests/lib/services/compute/test_security_groups_client.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslotest import mockpatch
+import fixtures
 
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.compute import security_groups_client
@@ -103,11 +103,11 @@
     def test_is_resource_deleted_true(self):
         mod = ('tempest.lib.services.compute.security_groups_client.'
                'SecurityGroupsClient.show_security_group')
-        self.useFixture(mockpatch.Patch(mod, side_effect=lib_exc.NotFound))
+        self.useFixture(fixtures.MockPatch(mod, side_effect=lib_exc.NotFound))
         self.assertTrue(self.client.is_resource_deleted('fake-id'))
 
     def test_is_resource_deleted_false(self):
         mod = ('tempest.lib.services.compute.security_groups_client.'
                'SecurityGroupsClient.show_security_group')
-        self.useFixture(mockpatch.Patch(mod, return_value='success'))
+        self.useFixture(fixtures.MockPatch(mod, return_value='success'))
         self.assertFalse(self.client.is_resource_deleted('fake-id'))
diff --git a/tempest/tests/lib/services/compute/test_server_groups_client.py b/tempest/tests/lib/services/compute/test_server_groups_client.py
index bf03b84..9055a36 100644
--- a/tempest/tests/lib/services/compute/test_server_groups_client.py
+++ b/tempest/tests/lib/services/compute/test_server_groups_client.py
@@ -12,10 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslotest import mockpatch
-from tempest.tests.lib import fake_auth_provider
+import fixtures
 
+from tempest.lib.services.compute import base_compute_client
 from tempest.lib.services.compute import server_groups_client
+from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib import fake_http
 from tempest.tests.lib.services import base
 
@@ -36,7 +37,7 @@
             fake_auth, 'compute', 'regionOne')
 
     def _test_create_server_group(self, bytes_body=False):
-        expected = {"server_group": TestServerGroupsClient.server_group}
+        expected = {"server_group": self.server_group}
         self.check_service_client_function(
             self.client.create_server_group,
             'tempest.lib.common.rest_client.RestClient.post', expected,
@@ -50,13 +51,13 @@
 
     def test_delete_server_group(self):
         response = fake_http.fake_http_response({}, status=204), ''
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             'tempest.lib.common.rest_client.RestClient.delete',
             return_value=response))
         self.client.delete_server_group('fake-group')
 
     def _test_list_server_groups(self, bytes_body=False):
-        expected = {"server_groups": [TestServerGroupsClient.server_group]}
+        expected = {"server_groups": [self.server_group]}
         self.check_service_client_function(
             self.client.list_server_groups,
             'tempest.lib.common.rest_client.RestClient.get',
@@ -69,7 +70,7 @@
         self._test_list_server_groups(bytes_body=True)
 
     def _test_show_server_group(self, bytes_body=False):
-        expected = {"server_group": TestServerGroupsClient.server_group}
+        expected = {"server_group": self.server_group}
         self.check_service_client_function(
             self.client.show_server_group,
             'tempest.lib.common.rest_client.RestClient.get',
@@ -81,3 +82,20 @@
 
     def test_show_server_group_byte_body(self):
         self._test_show_server_group(bytes_body=True)
+
+
+class TestServerGroupsClientMinV213(TestServerGroupsClient):
+
+    server_group = {
+        "id": "5bbcc3c4-1da2-4437-a48a-66f15b1b13f9",
+        "name": "test",
+        "policies": ["anti-affinity"],
+        "members": [],
+        "metadata": {},
+        "project_id": "0beb4bffb7a445eb8eb05fee3ee7660a",
+        "user_id": "86031628064a4f99bb66ec03c507dcd8"}
+
+    def setUp(self):
+        super(TestServerGroupsClientMinV213, self).setUp()
+        self.patchobject(base_compute_client, 'COMPUTE_MICROVERSION',
+                         new='2.13')
diff --git a/tempest/tests/lib/services/compute/test_servers_client.py b/tempest/tests/lib/services/compute/test_servers_client.py
index adfaaf2..86f6ad5 100644
--- a/tempest/tests/lib/services/compute/test_servers_client.py
+++ b/tempest/tests/lib/services/compute/test_servers_client.py
@@ -1,4 +1,5 @@
 # Copyright 2015 IBM Corp.
+# Copyright 2017 AT&T Corp.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
 #    not use this file except in compliance with the License. You may obtain
@@ -14,6 +15,9 @@
 
 import copy
 
+import mock
+
+from tempest.lib.services.compute import base_compute_client
 from tempest.lib.services.compute import servers_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
@@ -164,6 +168,10 @@
         "url": "http://os.co/v2/616fb98f-46ca-475e-917e-2563e5a8cd19"
     }
 
+    FAKE_SERVER_PASSWORD = {
+        "adminPass": "fake-password",
+    }
+
     FAKE_INSTANCE_ACTION_EVENTS = {
         "event": "fake-event",
         "start_time": "2016-10-02T10:00:00-05:00",
@@ -172,12 +180,23 @@
         "traceback": "fake-trace-back"
     }
 
+    FAKE_SECURITY_GROUPS = [{
+        "description": "default",
+        "id": "3fb26eb3-581b-4420-9963-b0879a026506",
+        "name": "default",
+        "rules": [],
+        "tenant_id": "openstack"
+    }]
+
     FAKE_INSTANCE_WITH_EVENTS = copy.deepcopy(FAKE_INSTANCE_ACTIONS)
     FAKE_INSTANCE_WITH_EVENTS['events'] = [FAKE_INSTANCE_ACTION_EVENTS]
 
     FAKE_REBUILD_SERVER = copy.deepcopy(FAKE_SERVER_GET)
     FAKE_REBUILD_SERVER['server']['adminPass'] = 'fake-admin-pass'
 
+    FAKE_TAGS = ["foo", "bar"]
+    REPLACE_FAKE_TAGS = ["baz", "qux"]
+
     server_id = FAKE_SERVER_GET['server']['id']
     network_id = 'a6b0875b-6b5d-4a5a-81eb-0c3aa62e5fdb'
 
@@ -186,6 +205,7 @@
         fake_auth = fake_auth_provider.FakeAuthProvider()
         self.client = servers_client.ServersClient(
             fake_auth, 'compute', 'regionOne')
+        self.addCleanup(mock.patch.stopall)
 
     def test_list_servers_with_str_body(self):
         self._test_list_servers()
@@ -215,7 +235,7 @@
             server_id=self.server_id
             )
 
-    def test_delete_server(self, bytes_body=False):
+    def test_delete_server(self):
         self.check_service_client_function(
             self.client.delete_server,
             'tempest.lib.common.rest_client.RestClient.delete',
@@ -268,6 +288,7 @@
             self.client.list_addresses_by_network,
             'tempest.lib.common.rest_client.RestClient.get',
             self.FAKE_ADDRESS['addresses'],
+            bytes_body,
             server_id=self.server_id,
             network_id=self.network_id
             )
@@ -283,6 +304,7 @@
             self.client.action,
             'tempest.lib.common.rest_client.RestClient.post',
             {},
+            bytes_body,
             server_id=self.server_id,
             action_name='fake-action-name',
             schema={'status_code': 200}
@@ -299,6 +321,7 @@
             self.client.create_backup,
             'tempest.lib.common.rest_client.RestClient.post',
             {},
+            bytes_body,
             status=202,
             server_id=self.server_id,
             backup_type='fake-backup',
@@ -306,6 +329,22 @@
             name='fake-name'
             )
 
+    def test_evacuate_server_with_str_body(self):
+        self._test_evacuate_server()
+
+    def test_evacuate_server_with_bytes_body(self):
+        self._test_evacuate_server(bytes_body=True)
+
+    def _test_evacuate_server(self, bytes_body=False):
+        kwargs = {'server_id': self.server_id,
+                  'host': 'fake-target-host'}
+        self.check_service_client_function(
+            self.client.evacuate_server,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_SERVER_PASSWORD,
+            bytes_body,
+            **kwargs)
+
     def test_change_password_with_str_body(self):
         self._test_change_password()
 
@@ -317,6 +356,7 @@
             self.client.change_password,
             'tempest.lib.common.rest_client.RestClient.post',
             {},
+            bytes_body,
             status=202,
             server_id=self.server_id,
             adminPass='fake-admin-pass'
@@ -333,16 +373,11 @@
             self.client.show_password,
             'tempest.lib.common.rest_client.RestClient.get',
             {'password': 'fake-password'},
+            bytes_body,
             server_id=self.server_id
             )
 
-    def test_delete_password_with_str_body(self):
-        self._test_delete_password()
-
-    def test_delete_password_with_bytes_body(self):
-        self._test_delete_password(True)
-
-    def _test_delete_password(self, bytes_body=False):
+    def test_delete_password(self):
         self.check_service_client_function(
             self.client.delete_password,
             'tempest.lib.common.rest_client.RestClient.delete',
@@ -351,13 +386,7 @@
             server_id=self.server_id
             )
 
-    def test_reboot_server_with_str_body(self):
-        self._test_reboot_server()
-
-    def test_reboot_server_with_bytes_body(self):
-        self._test_reboot_server(True)
-
-    def _test_reboot_server(self, bytes_body=False):
+    def test_reboot_server(self):
         self.check_service_client_function(
             self.client.reboot_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -378,18 +407,13 @@
             self.client.rebuild_server,
             'tempest.lib.common.rest_client.RestClient.post',
             self.FAKE_REBUILD_SERVER,
+            bytes_body,
             status=202,
             server_id=self.server_id,
             image_ref='fake-image-ref'
             )
 
-    def test_resize_server_with_str_body(self):
-        self._test_resize_server()
-
-    def test_resize_server_with_bytes_body(self):
-        self._test_resize_server(True)
-
-    def _test_resize_server(self, bytes_body=False):
+    def test_resize_server(self):
         self.check_service_client_function(
             self.client.resize_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -399,13 +423,7 @@
             flavor_ref='fake-flavor-ref'
             )
 
-    def test_confirm_resize_server_with_str_body(self):
-        self._test_confirm_resize_server()
-
-    def test_confirm_resize_server_with_bytes_body(self):
-        self._test_confirm_resize_server(True)
-
-    def _test_confirm_resize_server(self, bytes_body=False):
+    def test_confirm_resize_server(self):
         self.check_service_client_function(
             self.client.confirm_resize_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -414,13 +432,7 @@
             server_id=self.server_id
             )
 
-    def test_revert_resize_server_with_str_body(self):
-        self._test_revert_resize()
-
-    def test_revert_resize_server_with_bytes_body(self):
-        self._test_revert_resize(True)
-
-    def _test_revert_resize(self, bytes_body=False):
+    def test_revert_resize(self):
         self.check_service_client_function(
             self.client.revert_resize_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -440,6 +452,7 @@
             self.client.list_server_metadata,
             'tempest.lib.common.rest_client.RestClient.get',
             {'metadata': {'fake-key': 'fake-meta-data'}},
+            bytes_body,
             server_id=self.server_id
             )
 
@@ -454,6 +467,7 @@
             self.client.set_server_metadata,
             'tempest.lib.common.rest_client.RestClient.put',
             {'metadata': {'fake-key': 'fake-meta-data'}},
+            bytes_body,
             server_id=self.server_id,
             meta='fake-meta'
             )
@@ -469,6 +483,7 @@
             self.client.update_server_metadata,
             'tempest.lib.common.rest_client.RestClient.post',
             {'metadata': {'fake-key': 'fake-meta-data'}},
+            bytes_body,
             server_id=self.server_id,
             meta='fake-meta'
             )
@@ -484,6 +499,7 @@
             self.client.show_server_metadata_item,
             'tempest.lib.common.rest_client.RestClient.get',
             {'meta': {'fake-key': 'fake-meta-data'}},
+            bytes_body,
             server_id=self.server_id,
             key='fake-key'
             )
@@ -499,18 +515,13 @@
             self.client.set_server_metadata_item,
             'tempest.lib.common.rest_client.RestClient.put',
             {'meta': {'fake-key': 'fake-meta-data'}},
+            bytes_body,
             server_id=self.server_id,
             key='fake-key',
             meta='fake-meta'
             )
 
-    def test_delete_server_metadata_item_with_str_body(self):
-        self._test_delete_server_metadata()
-
-    def test_delete_server_metadata_item_with_bytes_body(self):
-        self._test_delete_server_metadata(True)
-
-    def _test_delete_server_metadata(self, bytes_body=False):
+    def test_delete_server_metadata(self):
         self.check_service_client_function(
             self.client.delete_server_metadata_item,
             'tempest.lib.common.rest_client.RestClient.delete',
@@ -520,13 +531,7 @@
             key='fake-key'
             )
 
-    def test_stop_server_with_str_body(self):
-        self._test_stop_server()
-
-    def test_stop_server_with_bytes_body(self):
-        self._test_stop_server(True)
-
-    def _test_stop_server(self, bytes_body=False):
+    def test_stop_server(self):
         self.check_service_client_function(
             self.client.stop_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -535,13 +540,7 @@
             server_id=self.server_id
             )
 
-    def test_start_server_with_str_body(self):
-        self._test_start_server()
-
-    def test_start_server_with_bytes_body(self):
-        self._test_start_server(True)
-
-    def _test_start_server(self, bytes_body=False):
+    def test_start_server(self):
         self.check_service_client_function(
             self.client.start_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -561,6 +560,7 @@
             self.client.attach_volume,
             'tempest.lib.common.rest_client.RestClient.post',
             {'volumeAttachment': self.FAKE_COMMON_VOLUME},
+            bytes_body,
             server_id=self.server_id
             )
 
@@ -586,6 +586,7 @@
             self.client.detach_volume,
             'tempest.lib.common.rest_client.RestClient.delete',
             {},
+            bytes_body,
             status=202,
             server_id=self.server_id,
             volume_id=self.FAKE_COMMON_VOLUME['volumeId']
@@ -602,6 +603,7 @@
             self.client.show_volume_attachment,
             'tempest.lib.common.rest_client.RestClient.get',
             {'volumeAttachment': self.FAKE_COMMON_VOLUME},
+            bytes_body,
             server_id=self.server_id,
             volume_id=self.FAKE_COMMON_VOLUME['volumeId']
             )
@@ -617,6 +619,7 @@
             self.client.list_volume_attachments,
             'tempest.lib.common.rest_client.RestClient.get',
             {'volumeAttachments': [self.FAKE_COMMON_VOLUME]},
+            bytes_body,
             server_id=self.server_id
             )
 
@@ -631,18 +634,13 @@
             self.client.add_security_group,
             'tempest.lib.common.rest_client.RestClient.post',
             {},
+            bytes_body,
             status=202,
             server_id=self.server_id,
             name='fake-name'
             )
 
-    def test_remove_security_group_with_str_body(self):
-        self._test_remove_security_group()
-
-    def test_remove_security_group_with_bytes_body(self):
-        self._test_remove_security_group(True)
-
-    def _test_remove_security_group(self, bytes_body=False):
+    def test_remove_security_group(self):
         self.check_service_client_function(
             self.client.remove_security_group,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -663,6 +661,7 @@
             self.client.live_migrate_server,
             'tempest.lib.common.rest_client.RestClient.post',
             {},
+            bytes_body,
             status=202,
             server_id=self.server_id
             )
@@ -678,17 +677,12 @@
             self.client.migrate_server,
             'tempest.lib.common.rest_client.RestClient.post',
             {},
+            bytes_body,
             status=202,
             server_id=self.server_id
             )
 
-    def test_lock_server_with_str_body(self):
-        self._test_lock_server()
-
-    def test_lock_server_with_bytes_body(self):
-        self._test_lock_server(True)
-
-    def _test_lock_server(self, bytes_body=False):
+    def test_lock_server(self):
         self.check_service_client_function(
             self.client.lock_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -697,13 +691,7 @@
             server_id=self.server_id
             )
 
-    def test_unlock_server_with_str_body(self):
-        self._test_unlock_server()
-
-    def test_unlock_server_with_bytes_body(self):
-        self._test_unlock_server(True)
-
-    def _test_unlock_server(self, bytes_body=False):
+    def test_unlock_server(self):
         self.check_service_client_function(
             self.client.unlock_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -712,13 +700,7 @@
             server_id=self.server_id
             )
 
-    def test_suspend_server_with_str_body(self):
-        self._test_suspend_server()
-
-    def test_suspend_server_with_bytes_body(self):
-        self._test_suspend_server(True)
-
-    def _test_suspend_server(self, bytes_body=False):
+    def test_suspend_server(self):
         self.check_service_client_function(
             self.client.suspend_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -727,13 +709,7 @@
             server_id=self.server_id
             )
 
-    def test_resume_server_with_str_body(self):
-        self._test_resume_server()
-
-    def test_resume_server_with_bytes_body(self):
-        self._test_resume_server(True)
-
-    def _test_resume_server(self, bytes_body=False):
+    def test_resume_server(self):
         self.check_service_client_function(
             self.client.resume_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -742,13 +718,7 @@
             server_id=self.server_id
             )
 
-    def test_pause_server_with_str_body(self):
-        self._test_pause_server()
-
-    def test_pause_server_with_bytes_body(self):
-        self._test_pause_server(True)
-
-    def _test_pause_server(self, bytes_body=False):
+    def test_pause_server(self):
         self.check_service_client_function(
             self.client.pause_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -757,13 +727,7 @@
             server_id=self.server_id
             )
 
-    def test_unpause_server_with_str_body(self):
-        self._test_unpause_server()
-
-    def test_unpause_server_with_bytes_body(self):
-        self._test_unpause_server(True)
-
-    def _test_unpause_server(self, bytes_body=False):
+    def test_unpause_server(self):
         self.check_service_client_function(
             self.client.unpause_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -772,13 +736,7 @@
             server_id=self.server_id
             )
 
-    def test_reset_state_with_str_body(self):
-        self._test_reset_state()
-
-    def test_reset_state_with_bytes_body(self):
-        self._test_reset_state(True)
-
-    def _test_reset_state(self, bytes_body=False):
+    def test_reset_state(self):
         self.check_service_client_function(
             self.client.reset_state,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -788,13 +746,7 @@
             state='fake-state'
             )
 
-    def test_shelve_server_with_str_body(self):
-        self._test_shelve_server()
-
-    def test_shelve_server_with_bytes_body(self):
-        self._test_shelve_server(True)
-
-    def _test_shelve_server(self, bytes_body=False):
+    def test_shelve_server(self):
         self.check_service_client_function(
             self.client.shelve_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -803,13 +755,7 @@
             server_id=self.server_id
             )
 
-    def test_unshelve_server_with_str_body(self):
-        self._test_unshelve_server()
-
-    def test_unshelve_server_with_bytes_body(self):
-        self._test_unshelve_server(True)
-
-    def _test_unshelve_server(self, bytes_body=False):
+    def test_unshelve_server(self):
         self.check_service_client_function(
             self.client.unshelve_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -818,13 +764,7 @@
             server_id=self.server_id
             )
 
-    def test_shelve_offload_server_with_str_body(self):
-        self._test_shelve_offload_server()
-
-    def test_shelve_offload_server_with_bytes_body(self):
-        self._test_shelve_offload_server(True)
-
-    def _test_shelve_offload_server(self, bytes_body=False):
+    def test_shelve_offload_server(self):
         self.check_service_client_function(
             self.client.shelve_offload_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -844,6 +784,7 @@
             self.client.get_console_output,
             'tempest.lib.common.rest_client.RestClient.post',
             {'output': 'fake-output'},
+            bytes_body,
             server_id=self.server_id,
             length='fake-length'
             )
@@ -859,6 +800,7 @@
             self.client.list_virtual_interfaces,
             'tempest.lib.common.rest_client.RestClient.get',
             {'virtual_interfaces': [self.FAKE_VIRTUAL_INTERFACES]},
+            bytes_body,
             server_id=self.server_id
             )
 
@@ -873,16 +815,11 @@
             self.client.rescue_server,
             'tempest.lib.common.rest_client.RestClient.post',
             {'adminPass': 'fake-admin-pass'},
+            bytes_body,
             server_id=self.server_id
             )
 
-    def test_unrescue_server_with_str_body(self):
-        self._test_unrescue_server()
-
-    def test_unrescue_server_with_bytes_body(self):
-        self._test_unrescue_server(True)
-
-    def _test_unrescue_server(self, bytes_body=False):
+    def test_unrescue_server(self):
         self.check_service_client_function(
             self.client.unrescue_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -902,6 +839,7 @@
             self.client.show_server_diagnostics,
             'tempest.lib.common.rest_client.RestClient.get',
             self.FAKE_SERVER_DIAGNOSTICS,
+            bytes_body,
             status=200,
             server_id=self.server_id
             )
@@ -917,6 +855,7 @@
             self.client.list_instance_actions,
             'tempest.lib.common.rest_client.RestClient.get',
             {'instanceActions': [self.FAKE_INSTANCE_ACTIONS]},
+            bytes_body,
             server_id=self.server_id
             )
 
@@ -931,17 +870,12 @@
             self.client.show_instance_action,
             'tempest.lib.common.rest_client.RestClient.get',
             {'instanceAction': self.FAKE_INSTANCE_WITH_EVENTS},
+            bytes_body,
             server_id=self.server_id,
             request_id='fake-request-id'
             )
 
-    def test_force_delete_server_with_str_body(self):
-        self._test_force_delete_server()
-
-    def test_force_delete_server_with_bytes_body(self):
-        self._test_force_delete_server(True)
-
-    def _test_force_delete_server(self, bytes_body=False):
+    def test_force_delete_server(self):
         self.check_service_client_function(
             self.client.force_delete_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -950,13 +884,7 @@
             server_id=self.server_id
             )
 
-    def test_restore_soft_deleted_server_with_str_body(self):
-        self._test_restore_soft_deleted_server()
-
-    def test_restore_soft_deleted_server_with_bytes_body(self):
-        self._test_restore_soft_deleted_server(True)
-
-    def _test_restore_soft_deleted_server(self, bytes_body=False):
+    def test_restore_soft_deleted_server(self):
         self.check_service_client_function(
             self.client.restore_soft_deleted_server,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -965,13 +893,7 @@
             server_id=self.server_id
             )
 
-    def test_reset_network_with_str_body(self):
-        self._test_reset_network()
-
-    def test_reset_network_with_bytes_body(self):
-        self._test_reset_network(True)
-
-    def _test_reset_network(self, bytes_body=False):
+    def test_reset_network(self):
         self.check_service_client_function(
             self.client.reset_network,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -980,13 +902,7 @@
             server_id=self.server_id
             )
 
-    def test_inject_network_info_with_str_body(self):
-        self._test_inject_network_info()
-
-    def test_inject_network_info_with_bytes_body(self):
-        self._test_inject_network_info(True)
-
-    def _test_inject_network_info(self, bytes_body=False):
+    def test_inject_network_info(self):
         self.check_service_client_function(
             self.client.inject_network_info,
             'tempest.lib.common.rest_client.RestClient.post',
@@ -1006,6 +922,155 @@
             self.client.get_vnc_console,
             'tempest.lib.common.rest_client.RestClient.post',
             {'console': self.FAKE_VNC_CONSOLE},
+            bytes_body,
             server_id=self.server_id,
             type='fake-console-type'
             )
+
+    def test_list_security_groups_by_server_with_str_body(self):
+        self._test_list_security_groups_by_server()
+
+    def test_list_security_groups_by_server_with_bytes_body(self):
+        self._test_list_security_groups_by_server(True)
+
+    def _test_list_security_groups_by_server(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_security_groups_by_server,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {'security_groups': self.FAKE_SECURITY_GROUPS},
+            bytes_body,
+            server_id=self.server_id
+            )
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.26'))
+    def test_list_tags_str_body(self, _):
+        self._test_list_tags()
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.26'))
+    def test_list_tags_byte_body(self, _):
+        self._test_list_tags(bytes_body=True)
+
+    def _test_list_tags(self, bytes_body=False):
+        expected = {"tags": self.FAKE_TAGS}
+        self.check_service_client_function(
+            self.client.list_tags,
+            'tempest.lib.common.rest_client.RestClient.get',
+            expected,
+            server_id=self.server_id,
+            to_utf=bytes_body)
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.26'))
+    def test_update_all_tags_str_body(self, _):
+        self._test_update_all_tags()
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.26'))
+    def test_update_all_tags_byte_body(self, _):
+        self._test_update_all_tags(bytes_body=True)
+
+    def _test_update_all_tags(self, bytes_body=False):
+        expected = {"tags": self.REPLACE_FAKE_TAGS}
+        self.check_service_client_function(
+            self.client.update_all_tags,
+            'tempest.lib.common.rest_client.RestClient.put',
+            expected,
+            server_id=self.server_id,
+            tags=self.REPLACE_FAKE_TAGS,
+            to_utf=bytes_body)
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.26'))
+    def test_delete_all_tags(self, _):
+        self.check_service_client_function(
+            self.client.delete_all_tags,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            server_id=self.server_id,
+            status=204)
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.26'))
+    def test_check_tag_existence_str_body(self, _):
+        self._test_check_tag_existence()
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.26'))
+    def test_check_tag_existence_byte_body(self, _):
+        self._test_check_tag_existence(bytes_body=True)
+
+    def _test_check_tag_existence(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.check_tag_existence,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {},
+            server_id=self.server_id,
+            tag=self.FAKE_TAGS[0],
+            status=204,
+            to_utf=bytes_body)
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.26'))
+    def test_update_tag_str_body(self, _):
+        self._test_update_tag()
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.26'))
+    def test_update_tag_byte_body(self, _):
+        self._test_update_tag(bytes_body=True)
+
+    def _test_update_tag(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_tag,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            server_id=self.server_id,
+            tag=self.FAKE_TAGS[0],
+            status=201,
+            headers={'location': 'fake_location'},
+            to_utf=bytes_body)
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.26'))
+    def test_delete_tag(self, _):
+        self.check_service_client_function(
+            self.client.delete_tag,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            server_id=self.server_id,
+            tag=self.FAKE_TAGS[0],
+            status=204,
+            )
+
+
+class TestServersClientMinV26(base.BaseServiceTest):
+
+    def setUp(self):
+        super(TestServersClientMinV26, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = servers_client.ServersClient(fake_auth, 'compute',
+                                                   'regionOne')
+        base_compute_client.COMPUTE_MICROVERSION = '2.6'
+        self.server_id = "920eaac8-a284-4fd1-9c2c-b30f0181b125"
+
+    def tearDown(self):
+        super(TestServersClientMinV26, self).tearDown()
+        base_compute_client.COMPUTE_MICROVERSION = None
+
+    def test_get_remote_consoles(self):
+        self.check_service_client_function(
+            self.client.get_remote_console,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {
+                'remote_console': {
+                    'protocol': 'serial',
+                    'type': 'serial',
+                    'url': 'ws://127.0.0.1:6083/?token=IllAllowIt'
+                    }
+            },
+            server_id=self.server_id,
+            console_type='serial',
+            protocol='serial',
+            )
diff --git a/tempest/tests/lib/services/compute/test_services_client.py b/tempest/tests/lib/services/compute/test_services_client.py
index 41da39c..2dd981c 100644
--- a/tempest/tests/lib/services/compute/test_services_client.py
+++ b/tempest/tests/lib/services/compute/test_services_client.py
@@ -14,6 +14,9 @@
 
 import copy
 
+import mock
+
+from tempest.lib.services.compute import base_compute_client
 from tempest.lib.services.compute import services_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
@@ -44,11 +47,21 @@
         }
     }
 
+    FAKE_UPDATE_FORCED_DOWN = {
+        "service":
+        {
+            "forced_down": True,
+            "binary": "nova-conductor",
+            "host": "controller"
+        }
+    }
+
     def setUp(self):
         super(TestServicesClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
         self.client = services_client.ServicesClient(
             fake_auth, 'compute', 'regionOne')
+        self.addCleanup(mock.patch.stopall)
 
     def test_list_services_with_str_body(self):
         self.check_service_client_function(
@@ -68,7 +81,7 @@
             'tempest.lib.common.rest_client.RestClient.put',
             self.FAKE_SERVICE,
             bytes_body,
-            host_name="nova-conductor", binary="controller")
+            host="nova-conductor", binary="controller")
 
     def test_enable_service_with_str_body(self):
         self._test_enable_service()
@@ -85,10 +98,49 @@
             'tempest.lib.common.rest_client.RestClient.put',
             fake_service,
             bytes_body,
-            host_name="nova-conductor", binary="controller")
+            host="nova-conductor", binary="controller")
 
     def test_disable_service_with_str_body(self):
         self._test_disable_service()
 
     def test_disable_service_with_bytes_body(self):
         self._test_disable_service(bytes_body=True)
+
+    def _test_log_reason_disabled_service(self, bytes_body=False):
+        resp_body = copy.deepcopy(self.FAKE_SERVICE)
+        resp_body['service']['disabled_reason'] = 'test reason'
+
+        self.check_service_client_function(
+            self.client.disable_log_reason,
+            'tempest.lib.common.rest_client.RestClient.put',
+            resp_body,
+            bytes_body,
+            host="nova-conductor",
+            binary="controller",
+            disabled_reason='test reason')
+
+    def test_log_reason_disabled_service_with_str_body(self):
+        self._test_log_reason_disabled_service()
+
+    def test_log_reason_disabled_service_with_bytes_body(self):
+        self._test_log_reason_disabled_service(bytes_body=True)
+
+    def _test_update_forced_down(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_forced_down,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_FORCED_DOWN,
+            bytes_body,
+            host="nova-conductor",
+            binary="controller",
+            forced_down=True)
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.11'))
+    def test_update_forced_down_with_str_body(self, _):
+        self._test_update_forced_down()
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.11'))
+    def test_update_forced_down_with_bytes_body(self, _):
+        self._test_update_forced_down(bytes_body=True)
diff --git a/tempest/tests/lib/services/compute/test_snapshots_client.py b/tempest/tests/lib/services/compute/test_snapshots_client.py
index 1629943..1e2902c 100644
--- a/tempest/tests/lib/services/compute/test_snapshots_client.py
+++ b/tempest/tests/lib/services/compute/test_snapshots_client.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslotest import mockpatch
+import fixtures
 
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.compute import snapshots_client
@@ -91,13 +91,13 @@
     def test_is_resource_deleted_true(self):
         module = ('tempest.lib.services.compute.snapshots_client.'
                   'SnapshotsClient.show_snapshot')
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             module, side_effect=lib_exc.NotFound))
         self.assertTrue(self.client.is_resource_deleted('fake-id'))
 
     def test_is_resource_deleted_false(self):
         module = ('tempest.lib.services.compute.snapshots_client.'
                   'SnapshotsClient.show_snapshot')
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             module, return_value={}))
         self.assertFalse(self.client.is_resource_deleted('fake-id'))
diff --git a/tempest/tests/lib/services/compute/test_versions_client.py b/tempest/tests/lib/services/compute/test_versions_client.py
index 06ecdc3..40d424f 100644
--- a/tempest/tests/lib/services/compute/test_versions_client.py
+++ b/tempest/tests/lib/services/compute/test_versions_client.py
@@ -13,7 +13,8 @@
 #    under the License.
 
 import copy
-from oslotest import mockpatch
+
+import fixtures
 
 from tempest.lib.services.compute import versions_client
 from tempest.tests.lib import fake_auth_provider
@@ -72,7 +73,7 @@
             200)
 
     def _test_get_version_by_url(self, bytes_body=False):
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             "tempest.lib.common.rest_client.RestClient.token",
             return_value="Dummy Token"))
         params = {"version_url": self.versions_client._get_base_version_url()}
diff --git a/tempest/tests/lib/services/compute/test_volumes_client.py b/tempest/tests/lib/services/compute/test_volumes_client.py
index b81cdbb..4b4f02e 100644
--- a/tempest/tests/lib/services/compute/test_volumes_client.py
+++ b/tempest/tests/lib/services/compute/test_volumes_client.py
@@ -14,7 +14,7 @@
 
 import copy
 
-from oslotest import mockpatch
+import fixtures
 
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.compute import volumes_client
@@ -102,13 +102,13 @@
     def test_is_resource_deleted_true(self):
         module = ('tempest.lib.services.compute.volumes_client.'
                   'VolumesClient.show_volume')
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             module, side_effect=lib_exc.NotFound))
         self.assertTrue(self.client.is_resource_deleted('fake-id'))
 
     def test_is_resource_deleted_false(self):
         module = ('tempest.lib.services.compute.volumes_client.'
                   'VolumesClient.show_volume')
-        self.useFixture(mockpatch.Patch(
+        self.useFixture(fixtures.MockPatch(
             module, return_value={}))
         self.assertFalse(self.client.is_resource_deleted('fake-id'))
diff --git a/tempest/tests/lib/services/identity/v2/test_identity_client.py b/tempest/tests/lib/services/identity/v2/test_identity_client.py
index 96d50d7..303d1f7 100644
--- a/tempest/tests/lib/services/identity/v2/test_identity_client.py
+++ b/tempest/tests/lib/services/identity/v2/test_identity_client.py
@@ -26,6 +26,33 @@
         }
     }
 
+    FAKE_ENDPOINTS_FOR_TOKEN = {
+        "endpoints_links": [],
+        "endpoints": [
+            {
+                "name": "nova",
+                "adminURL": "https://nova.region-one.internal.com/" +
+                            "v2/be1319401cfa4a0aa590b97cc7b64d8d",
+                "region": "RegionOne",
+                "internalURL": "https://nova.region-one.internal.com/" +
+                               "v2/be1319401cfa4a0aa590b97cc7b64d8d",
+                "type": "compute",
+                "id": "11b41ee1b00841128b7333d4bf1a6140",
+                "publicURL": "https://nova.region-one.public.com/v2/" +
+                             "be1319401cfa4a0aa590b97cc7b64d8d"
+            },
+            {
+                "name": "neutron",
+                "adminURL": "https://neutron.region-one.internal.com/",
+                "region": "RegionOne",
+                "internalURL": "https://neutron.region-one.internal.com/",
+                "type": "network",
+                "id": "cdbfa3c416d741a9b5c968f2dc628acb",
+                "publicURL": "https://neutron.region-one.public.com/"
+            }
+        ]
+    }
+
     FAKE_API_INFO = {
         "name": "API_info",
         "type": "API",
@@ -148,6 +175,22 @@
             bytes_body,
             token_id="cbc36478b0bd8e67e89")
 
+    def _test_list_endpoints_for_token(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_endpoints_for_token,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ENDPOINTS_FOR_TOKEN,
+            bytes_body,
+            token_id="cbc36478b0bd8e67e89")
+
+    def _test_check_token_existence(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.check_token_existence,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            bytes_body,
+            token_id="cbc36478b0bd8e67e89")
+
     def test_show_api_description_with_str_body(self):
         self._test_show_api_description()
 
@@ -166,6 +209,18 @@
     def test_show_token_with_bytes_body(self):
         self._test_show_token(bytes_body=True)
 
+    def test_list_endpoints_for_token_with_str_body(self):
+        self._test_list_endpoints_for_token()
+
+    def test_list_endpoints_for_token_with_bytes_body(self):
+        self._test_list_endpoints_for_token(bytes_body=True)
+
+    def test_check_token_existence_with_bytes_body(self):
+        self._test_check_token_existence(bytes_body=True)
+
+    def test_check_token_existence_with_str_body(self):
+        self._test_check_token_existence()
+
     def test_delete_token(self):
         self.check_service_client_function(
             self.client.delete_token,
diff --git a/tempest/tests/lib/services/identity/v3/test_catalog_client.py b/tempest/tests/lib/services/identity/v3/test_catalog_client.py
new file mode 100644
index 0000000..0ac8fe4
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_catalog_client.py
@@ -0,0 +1,86 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from tempest.lib.services.identity.v3 import catalog_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestCatalogClient(base.BaseServiceTest):
+    FAKE_CATALOG_INFO = {
+        'catalog': [
+            {
+                'endpoints': [
+                    {
+                        'id': '39dc322ce86c4111b4f06c2eeae0841b',
+                        'interface': 'public',
+                        'region': 'RegionOne',
+                        'url': 'http://localhost:5000'
+                    },
+                ],
+                'id': 'ac58672276f848a7b1727850b3ebe826',
+                'type': 'compute',
+                'name': 'nova'
+            },
+            {
+                'endpoints': [
+                    {
+                        'id': '39dc322ce86c4111b4f06c2eeae0841b',
+                        'interface': 'public',
+                        'region': 'RegionOne',
+                        'url': 'http://localhost:5000'
+                    },
+                ],
+                'id': 'b7c5ed2b486a46dbb4c221499d22991c',
+                'type': 'image',
+                'name': 'glance'
+            },
+            {
+                'endpoints': [
+                    {
+                        'id': '39dc322ce86c4111b4f06c2eeae0841b',
+                        'interface': 'public',
+                        'region': 'RegionOne',
+                        'url': 'http://localhost:5000'
+                    },
+                ],
+                'id': '4363ae44bdf34a3981fde3b823cb9aa2',
+                'type': 'identity',
+                'name': 'keystone'
+            }
+
+        ],
+        'links': {
+            'self': 'http://localhost/identity/v3/catalog',
+            'previous': None,
+            'next': None
+        }
+    }
+
+    def setUp(self):
+        super(TestCatalogClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = catalog_client.CatalogClient(fake_auth, 'identity',
+                                                   'RegionOne')
+
+    def test_show_catalog_with_bytes_body(self):
+        self._test_show_catalog(bytes_body=True)
+
+    def test_show_catalog_with_str_body(self):
+        self._test_show_catalog()
+
+    def _test_show_catalog(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_catalog,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CATALOG_INFO,
+            bytes_body)
diff --git a/tempest/tests/lib/services/identity/v3/test_domain_configuration_client.py b/tempest/tests/lib/services/identity/v3/test_domain_configuration_client.py
new file mode 100644
index 0000000..72e5bd2
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_domain_configuration_client.py
@@ -0,0 +1,217 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from tempest.lib.services.identity.v3 import domain_configuration_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestDomainConfigurationClient(base.BaseServiceTest):
+
+    FAKE_CONFIG_SETTINGS = {
+        "config": {
+            "identity": {
+                "driver": "ldap"
+            },
+            "ldap": {
+                "url": "ldap://localhost",
+                "user": "",
+                "suffix": "cn=example,cn=com",
+            }
+        }
+    }
+
+    FAKE_DOMAIN_ID = '07ef7d04-2941-4bee-8551-f79f08a021de'
+
+    def setUp(self):
+        super(TestDomainConfigurationClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = domain_configuration_client.DomainConfigurationClient(
+            fake_auth, 'identity', 'regionOne')
+
+    def _test_show_default_config_settings(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_default_config_settings,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CONFIG_SETTINGS,
+            bytes_body)
+
+    def _test_show_default_group_config(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_default_group_config,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CONFIG_SETTINGS['config']['ldap'],
+            bytes_body,
+            group='ldap')
+
+    def _test_show_default_group_option(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_default_group_option,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {'driver': 'ldap'},
+            bytes_body,
+            group='identity',
+            option='driver')
+
+    def _test_show_domain_group_option_config(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_domain_group_option_config,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {'driver': 'ldap'},
+            bytes_body,
+            domain_id=self.FAKE_DOMAIN_ID,
+            group='identity',
+            option='driver')
+
+    def _test_update_domain_group_option_config(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_domain_group_option_config,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_CONFIG_SETTINGS,
+            bytes_body,
+            domain_id=self.FAKE_DOMAIN_ID,
+            group='identity',
+            option='driver',
+            url='http://myldap/my_other_root')
+
+    def _test_show_domain_group_config(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_domain_group_config,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CONFIG_SETTINGS['config']['ldap'],
+            bytes_body,
+            domain_id=self.FAKE_DOMAIN_ID,
+            group='ldap')
+
+    def _test_update_domain_group_config(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_domain_group_config,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_CONFIG_SETTINGS['config']['ldap'],
+            bytes_body,
+            domain_id=self.FAKE_DOMAIN_ID,
+            group='ldap',
+            **self.FAKE_CONFIG_SETTINGS['config'])
+
+    def _test_create_domain_config(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_domain_config,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_CONFIG_SETTINGS,
+            bytes_body,
+            domain_id=self.FAKE_DOMAIN_ID,
+            status=201)
+
+    def _test_show_domain_config(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_domain_config,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CONFIG_SETTINGS,
+            bytes_body,
+            domain_id=self.FAKE_DOMAIN_ID)
+
+    def _test_update_domain_config(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_domain_config,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_CONFIG_SETTINGS,
+            bytes_body,
+            domain_id=self.FAKE_DOMAIN_ID)
+
+    def test_show_default_config_settings_with_str_body(self):
+        self._test_show_default_config_settings()
+
+    def test_show_default_config_settings_with_bytes_body(self):
+        self._test_show_default_config_settings(bytes_body=True)
+
+    def test_show_default_group_config_with_str_body(self):
+        self._test_show_default_group_config()
+
+    def test_show_default_group_config_with_bytes_body(self):
+        self._test_show_default_group_config(bytes_body=True)
+
+    def test_show_default_group_option_with_str_body(self):
+        self._test_show_default_group_option()
+
+    def test_show_default_group_option_with_bytes_body(self):
+        self._test_show_default_group_option(bytes_body=True)
+
+    def test_show_domain_group_option_config_with_str_body(self):
+        self._test_show_domain_group_option_config()
+
+    def test_show_domain_group_option_config_with_bytes_body(self):
+        self._test_show_domain_group_option_config(bytes_body=True)
+
+    def test_update_domain_group_option_config_with_str_body(self):
+        self._test_update_domain_group_option_config()
+
+    def test_update_domain_group_option_config_with_bytes_body(self):
+        self._test_update_domain_group_option_config(bytes_body=True)
+
+    def test_delete_domain_group_option_config(self):
+        self.check_service_client_function(
+            self.client.delete_domain_group_option_config,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            domain_id=self.FAKE_DOMAIN_ID,
+            group='identity',
+            option='driver')
+
+    def test_show_domain_group_config_with_str_body(self):
+        self._test_show_domain_group_config()
+
+    def test_show_domain_group_config_with_bytes_body(self):
+        self._test_show_domain_group_config(bytes_body=True)
+
+    def test_test_update_domain_group_config_with_str_body(self):
+        self._test_update_domain_group_config()
+
+    def test_update_domain_group_config_with_bytes_body(self):
+        self._test_update_domain_group_config(bytes_body=True)
+
+    def test_delete_domain_group_config(self):
+        self.check_service_client_function(
+            self.client.delete_domain_group_config,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            domain_id=self.FAKE_DOMAIN_ID,
+            group='identity')
+
+    def test_create_domain_config_with_str_body(self):
+        self._test_create_domain_config()
+
+    def test_create_domain_config_with_bytes_body(self):
+        self._test_create_domain_config(bytes_body=True)
+
+    def test_show_domain_config_with_str_body(self):
+        self._test_show_domain_config()
+
+    def test_show_domain_config_with_bytes_body(self):
+        self._test_show_domain_config(bytes_body=True)
+
+    def test_update_domain_config_with_str_body(self):
+        self._test_update_domain_config()
+
+    def test_update_domain_config_with_bytes_body(self):
+        self._test_update_domain_config(bytes_body=True)
+
+    def test_delete_domain_config(self):
+        self.check_service_client_function(
+            self.client.delete_domain_config,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            domain_id=self.FAKE_DOMAIN_ID)
diff --git a/tempest/tests/lib/services/identity/v3/test_endpoint_filter_client.py b/tempest/tests/lib/services/identity/v3/test_endpoint_filter_client.py
new file mode 100644
index 0000000..7faf6a0
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_endpoint_filter_client.py
@@ -0,0 +1,165 @@
+# Copyright 2017 AT&T Corp.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.identity.v3 import endpoint_filter_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestEndPointsFilterClient(base.BaseServiceTest):
+    FAKE_LIST_PROJECTS_FOR_ENDPOINTS = {
+        "projects": [
+            {
+                "domain_id": "1777c7",
+                "enabled": True,
+                "id": "1234ab1",
+                "type": "compute",
+                "links": {
+                    "self": "http://example.com/identity/v3/projects/1234ab1"
+                },
+                "name": "Project 1",
+                "description": "Project 1 description",
+            },
+            {
+                "domain_id": "1777c7",
+                "enabled": True,
+                "id": "5678cd2",
+                "type": "compute",
+                "links": {
+                    "self": "http://example.com/identity/v3/projects/5678cd2"
+                },
+                "name": "Project 2",
+                "description": "Project 2 description",
+            }
+        ],
+        "links": {
+            "self": "http://example.com/identity/v3/OS-EP-FILTER/endpoints/\
+                    u6ay5u/projects",
+            "previous": None,
+            "next": None
+        }
+    }
+
+    FAKE_LIST_ENDPOINTS_FOR_PROJECTS = {
+        "endpoints": [
+            {
+                "id": "u6ay5u",
+                "interface": "public",
+                "url": "http://example.com/identity/",
+                "region": "north",
+                "links": {
+                    "self": "http://example.com/identity/v3/endpoints/u6ay5u"
+                },
+                "service_id": "5um4r",
+            },
+            {
+                "id": "u6ay5u",
+                "interface": "internal",
+                "url": "http://example.com/identity/",
+                "region": "south",
+                "links": {
+                    "self": "http://example.com/identity/v3/endpoints/u6ay5u"
+                },
+                "service_id": "5um4r",
+            },
+        ],
+        "links": {
+            "self": "http://example.com/identity/v3/OS-EP-FILTER/projects/\
+                    1234ab1/endpoints",
+            "previous": None,
+            "next": None
+        }
+    }
+
+    def setUp(self):
+        super(TestEndPointsFilterClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = endpoint_filter_client.EndPointsFilterClient(
+            fake_auth, 'identity', 'regionOne')
+
+    def _test_add_endpoint_to_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.add_endpoint_to_project,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            status=204,
+            project_id=3,
+            endpoint_id=4)
+
+    def _test_check_endpoint_in_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.check_endpoint_in_project,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            bytes_body,
+            status=204,
+            project_id=3,
+            endpoint_id=4)
+
+    def _test_list_projects_for_endpoint(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_projects_for_endpoint,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_PROJECTS_FOR_ENDPOINTS,
+            bytes_body,
+            status=200,
+            endpoint_id=3)
+
+    def _test_list_endpoints_in_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_endpoints_in_project,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ENDPOINTS_FOR_PROJECTS,
+            bytes_body,
+            status=200,
+            project_id=4)
+
+    def _test_delete_endpoint_from_project(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.delete_endpoint_from_project,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            bytes_body,
+            status=204,
+            project_id=3,
+            endpoint_id=4)
+
+    def test_add_endpoint_to_project_with_str_body(self):
+        self._test_add_endpoint_to_project()
+
+    def test_add_endpoint_to_project_with_bytes_body(self):
+        self._test_add_endpoint_to_project(bytes_body=True)
+
+    def test_check_endpoint_in_project_with_str_body(self):
+        self._test_check_endpoint_in_project()
+
+    def test_check_endpoint_in_project_with_bytes_body(self):
+        self._test_check_endpoint_in_project(bytes_body=True)
+
+    def test_list_projects_for_endpoint_with_str_body(self):
+        self._test_list_projects_for_endpoint()
+
+    def test_list_projects_for_endpoint_with_bytes_body(self):
+        self._test_list_projects_for_endpoint(bytes_body=True)
+
+    def test_list_endpoints_in_project_with_str_body(self):
+        self._test_list_endpoints_in_project()
+
+    def test_list_endpoints_in_project_with_bytes_body(self):
+        self._test_list_endpoints_in_project(bytes_body=True)
+
+    def test_delete_endpoint_from_project(self):
+        self._test_delete_endpoint_from_project()
diff --git a/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py b/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py
new file mode 100644
index 0000000..c724f0a
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py
@@ -0,0 +1,162 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.identity.v3 import endpoint_groups_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestEndPointGroupsClient(base.BaseServiceTest):
+    FAKE_CREATE_ENDPOINT_GROUP = {
+        "endpoint_group": {
+            "id": 1,
+            "name": "FAKE_ENDPOINT_GROUP",
+            "description": "FAKE SERVICE ENDPOINT GROUP",
+            "filters": {
+                "service_id": 1
+            }
+        }
+    }
+
+    FAKE_ENDPOINT_GROUP_INFO = {
+        "endpoint_group": {
+            "id": 1,
+            "name": "FAKE_ENDPOINT_GROUP",
+            "description": "FAKE SERVICE ENDPOINT GROUP",
+            "links": {
+                "self": "http://example.com/identity/v3/OS-EP-FILTER/" +
+                        "endpoint_groups/1"
+            },
+            "filters": {
+                "service_id": 1
+            }
+        }
+    }
+
+    FAKE_LIST_ENDPOINT_GROUPS = {
+        "endpoint_groups": [
+            {
+                "id": 1,
+                "name": "SERVICE_GROUP1",
+                "description": "FAKE SERVICE ENDPOINT GROUP",
+                "links": {
+                    "self": "http://example.com/identity/v3/OS-EP-FILTER/" +
+                            "endpoint_groups/1"
+                },
+                "filters": {
+                    "service_id": 1
+                }
+            },
+            {
+                "id": 2,
+                "name": "SERVICE_GROUP2",
+                "description": "FAKE SERVICE ENDPOINT GROUP",
+                "links": {
+                    "self": "http://example.com/identity/v3/OS-EP-FILTER/" +
+                            "endpoint_groups/2"
+                },
+                "filters": {
+                    "service_id": 2
+                }
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestEndPointGroupsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = endpoint_groups_client.EndPointGroupsClient(
+            fake_auth, 'identity', 'regionOne')
+
+    def _test_create_endpoint_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_endpoint_group,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_ENDPOINT_GROUP,
+            bytes_body,
+            status=201,
+            name="FAKE_ENDPOINT_GROUP",
+            filters={'service_id': "1"})
+
+    def _test_show_endpoint_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_endpoint_group,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ENDPOINT_GROUP_INFO,
+            bytes_body,
+            endpoint_group_id="1")
+
+    def _test_check_endpoint_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.check_endpoint_group,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            bytes_body,
+            status=200,
+            endpoint_group_id="1")
+
+    def _test_update_endpoint_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_endpoint_group,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_ENDPOINT_GROUP_INFO,
+            bytes_body,
+            endpoint_group_id="1",
+            name="NewName")
+
+    def _test_list_endpoint_groups(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_endpoint_groups,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ENDPOINT_GROUPS,
+            bytes_body)
+
+    def test_create_endpoint_group_with_str_body(self):
+        self._test_create_endpoint_group()
+
+    def test_create_endpoint_group_with_bytes_body(self):
+        self._test_create_endpoint_group(bytes_body=True)
+
+    def test_show_endpoint_group_with_str_body(self):
+        self._test_show_endpoint_group()
+
+    def test_show_endpoint_group_with_bytes_body(self):
+        self._test_show_endpoint_group(bytes_body=True)
+
+    def test_check_endpoint_group_with_str_body(self):
+        self._test_check_endpoint_group()
+
+    def test_check_endpoint_group_with_bytes_body(self):
+        self._test_check_endpoint_group(bytes_body=True)
+
+    def test_list_endpoint_groups_with_str_body(self):
+        self._test_list_endpoint_groups()
+
+    def test_list_endpoint_groups_with_bytes_body(self):
+        self._test_list_endpoint_groups(bytes_body=True)
+
+    def test_update_endpoint_group_with_str_body(self):
+        self._test_update_endpoint_group()
+
+    def test_update_endpoint_group_with_bytes_body(self):
+        self._test_update_endpoint_group(bytes_body=True)
+
+    def test_delete_endpoint_group(self):
+        self.check_service_client_function(
+            self.client.delete_endpoint_group,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            endpoint_group_id="1",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_endpoints_client.py b/tempest/tests/lib/services/identity/v3/test_endpoints_client.py
index f8c553f..ca15dd1 100644
--- a/tempest/tests/lib/services/identity/v3/test_endpoints_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_endpoints_client.py
@@ -53,6 +53,8 @@
         ]
     }
 
+    FAKE_SERVICE_ID = "a4dc5060-f757-4662-b658-edd2aefbb41d"
+
     def setUp(self):
         super(TestEndpointsClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -72,12 +74,15 @@
             adminurl="https://compute.north.internal.com/v1",
             internalurl="https://compute.north.internal.com/v1")
 
-    def _test_list_endpoints(self, bytes_body=False):
+    def _test_list_endpoints(self, bytes_body=False, mock_args='endpoints',
+                             **params):
         self.check_service_client_function(
             self.client.list_endpoints,
             'tempest.lib.common.rest_client.RestClient.get',
             self.FAKE_LIST_ENDPOINTS,
-            bytes_body)
+            bytes_body,
+            mock_args=[mock_args],
+            **params)
 
     def test_create_endpoint_with_str_body(self):
         self._test_create_endpoint()
@@ -91,6 +96,16 @@
     def test_list_endpoints_with_bytes_body(self):
         self._test_list_endpoints(bytes_body=True)
 
+    def test_list_endpoints_with_params(self):
+        # Run the test separately for each param, to avoid assertion error
+        # resulting from randomized params order.
+        mock_args = 'endpoints?service_id=%s' % self.FAKE_SERVICE_ID
+        self._test_list_endpoints(mock_args=mock_args,
+                                  service_id=self.FAKE_SERVICE_ID)
+
+        mock_args = 'endpoints?interface=public'
+        self._test_list_endpoints(mock_args=mock_args, interface='public')
+
     def test_delete_endpoint(self):
         self.check_service_client_function(
             self.client.delete_endpoint,
diff --git a/tempest/tests/lib/services/identity/v3/test_identity_client.py b/tempest/tests/lib/services/identity/v3/test_identity_client.py
index 9eaaaaf..6572947 100644
--- a/tempest/tests/lib/services/identity/v3/test_identity_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_identity_client.py
@@ -32,6 +32,34 @@
         "description": "test_description"
     }
 
+    FAKE_AUTH_PROJECTS = {
+        "projects": [
+            {
+                "domain_id": "1789d1",
+                "enabled": True,
+                "id": "263fd9",
+                "links": {
+                    "self": "https://example.com/identity/v3/projects/263fd9"
+                },
+                "name": "Test Group"
+            },
+            {
+                "domain_id": "1789d1",
+                "enabled": True,
+                "id": "50ef01",
+                "links": {
+                    "self": "https://example.com/identity/v3/projects/50ef01"
+                },
+                "name": "Build Group"
+            }
+        ],
+        "links": {
+            "self": "https://example.com/identity/v3/auth/projects",
+            "previous": None,
+            "next": None
+        }
+    }
+
     def setUp(self):
         super(TestIdentityClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -54,6 +82,13 @@
             bytes_body,
             resp_token="cbc36478b0bd8e67e89")
 
+    def _test_list_auth_projects(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_auth_projects,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_AUTH_PROJECTS,
+            bytes_body)
+
     def test_show_api_description_with_str_body(self):
         self._test_show_api_description()
 
@@ -73,3 +108,17 @@
             {},
             resp_token="cbc36478b0bd8e67e89",
             status=204)
+
+    def test_check_token_existence(self):
+        self.check_service_client_function(
+            self.client.check_token_existence,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            resp_token="cbc36478b0bd8e67e89",
+            status=200)
+
+    def test_list_auth_projects_with_str_body(self):
+        self._test_list_auth_projects()
+
+    def test_list_auth_projects_with_bytes_body(self):
+        self._test_list_auth_projects(bytes_body=True)
diff --git a/tempest/tests/lib/services/identity/v3/test_oauth_consumers_client.py b/tempest/tests/lib/services/identity/v3/test_oauth_consumers_client.py
new file mode 100644
index 0000000..8d53792
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_oauth_consumers_client.py
@@ -0,0 +1,160 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.identity.v3 import oauth_consumers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestOAUTHConsumerClient(base.BaseServiceTest):
+    FAKE_CREATE_CONSUMER = {
+        "consumer": {
+            'description': 'A fake description 1'
+        }
+
+    }
+
+    FAKE_CONSUMER_INFO = {
+        "consumer": {
+            'id': '6392c7d3b7a2062e09a07aa377',
+            'links': {
+                'self': 'http://example.com/identity/v3/' +
+                        'OS-OAUTH1/consumers/g6f2l9'
+            },
+            'description': 'A description that is fake'
+        }
+
+    }
+
+    FAKE_LIST_CONSUMERS = {
+        'links': {
+            'self': 'http://example.com/identity/v3/OS-OAUTH1/consumers/',
+            'next': None,
+            'previous': None
+        },
+        'consumers': [
+            {
+                'id': '6392c7d3b7a2062e09a07aa377',
+                'links': {
+                    'self': 'http://example.com/identity/v3/' +
+                            'OS-OAUTH1/consumers/6b9f2g5'
+                },
+                'description': 'A description that is fake'
+            },
+            {
+                'id': '677a855c9e3eb3a3954b36aca6',
+                'links': {
+                    'self': 'http://example.com/identity/v3/' +
+                            'OS-OAUTH1/consumers/6a9f2366'
+                },
+                'description': 'A very fake description 2'
+            },
+            {
+                'id': '9d3ac57b08d65e07826b5e506',
+                'links': {
+                    'self': 'http://example.com/identity/v3/' +
+                            'OS-OAUTH1/consumers/626b5e506'
+                },
+                'description': 'A very fake description 3'
+            },
+            {
+                'id': 'b522d163b1a18e928aca9y426',
+                'links': {
+                    'self': 'http://example.com/identity/v3/' +
+                            'OS-OAUTH1/consumers/g7ca9426'
+                },
+                'description': 'A very fake description 4'
+            },
+            {
+                'id': 'b7e47321b5ef9051f93c2049e',
+                'links': {
+                    'self': 'http://example.com/identity/v3/' +
+                            'OS-OAUTH1/consumers/23d82049e'
+                },
+                'description': 'A very fake description 5'
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestOAUTHConsumerClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = oauth_consumers_client.OAUTHConsumerClient(fake_auth,
+                                                                 'identity',
+                                                                 'regionOne')
+
+    def _test_create_consumer(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_consumer,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_CONSUMER,
+            bytes_body,
+            description=self.FAKE_CREATE_CONSUMER["consumer"]["description"],
+            status=201)
+
+    def _test_show_consumer(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_consumer,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CONSUMER_INFO,
+            bytes_body,
+            consumer_id="6392c7d3b7a2062e09a07aa377")
+
+    def _test_list_consumers(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_consumers,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_CONSUMERS,
+            bytes_body)
+
+    def _test_update_consumer(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_consumer,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_CONSUMER_INFO,
+            bytes_body,
+            consumer_id="6392c7d3b7a2062e09a07aa377")
+
+    def test_create_consumer_with_str_body(self):
+        self._test_create_consumer()
+
+    def test_create_consumer_with_bytes_body(self):
+        self._test_create_consumer(bytes_body=True)
+
+    def test_show_consumer_with_str_body(self):
+        self._test_show_consumer()
+
+    def test_show_consumer_with_bytes_body(self):
+        self._test_show_consumer(bytes_body=True)
+
+    def test_list_consumers_with_str_body(self):
+        self._test_list_consumers()
+
+    def test_list_consumers_with_bytes_body(self):
+        self._test_list_consumers(bytes_body=True)
+
+    def test_update_consumer_with_str_body(self):
+        self._test_update_consumer()
+
+    def test_update_consumer_with_bytes_body(self):
+        self._test_update_consumer(bytes_body=True)
+
+    def test_delete_consumer(self):
+        self.check_service_client_function(
+            self.client.delete_consumer,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            consumer_id="6392c7d3b7a2062e09a07aa377",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_oauth_token_client.py b/tempest/tests/lib/services/identity/v3/test_oauth_token_client.py
new file mode 100644
index 0000000..420ea5f
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_oauth_token_client.py
@@ -0,0 +1,215 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import fixtures
+
+from tempest.lib.services.identity.v3 import oauth_token_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib import fake_http
+from tempest.tests.lib.services import base
+
+
+class TestOAUTHTokenClient(base.BaseServiceTest):
+    FAKE_CREATE_REQUEST_TOKEN = {
+        'oauth_token': '29971f',
+        'oauth_token_secret': '238eb8',
+        'oauth_expires_at': '2013-09-11T06:07:51.501805Z'
+    }
+
+    FAKE_AUTHORIZE_REQUEST_TOKEN = {
+        'token': {
+            'oauth_verifier': '8171'
+        }
+    }
+
+    FAKE_CREATE_ACCESS_TOKEN = {
+        'oauth_token': 'accd36',
+        'oauth_token_secret': 'aa47da',
+        'oauth_expires_at': '2013-09-11T06:07:51.501805Z'
+    }
+
+    FAKE_ACCESS_TOKEN_INFO = {
+        'access_token': {
+            'consumer_id': '7fea2d',
+            'id': '6be26a',
+            'expires_at': '2013-09-11T06:07:51.501805Z',
+            'links': {
+                'roles': 'http://example.com/identity/v3/' +
+                         'users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles',
+                'self': 'http://example.com/identity/v3/' +
+                        'users/ce9e07/OS-OAUTH1/access_tokens/6be26a'
+            },
+            'project_id': 'b9fca3',
+            'authorizing_user_id': 'ce9e07'
+        }
+    }
+
+    FAKE_LIST_ACCESS_TOKENS = {
+        'access_tokens': [
+            {
+                'consumer_id': '7fea2d',
+                'id': '6be26a',
+                'expires_at': '2013-09-11T06:07:51.501805Z',
+                'links': {
+                    'roles': 'http://example.com/identity/v3/' +
+                             'users/ce9e07/OS-OAUTH1/access_tokens/' +
+                             '6be26a/roles',
+                    'self': 'http://example.com/identity/v3/' +
+                            'users/ce9e07/OS-OAUTH1/access_tokens/6be26a'
+                },
+                'project_id': 'b9fca3',
+                'authorizing_user_id': 'ce9e07'
+            }
+        ],
+        'links': {
+            'next': None,
+            'previous': None,
+            'self': 'http://example.com/identity/v3/' +
+                    'users/ce9e07/OS-OAUTH1/access_tokens'
+        }
+    }
+
+    FAKE_LIST_ACCESS_TOKEN_ROLES = {
+        'roles': [
+            {
+                'id': '26b860',
+                'domain_id': 'fake_domain',
+                'links': {
+                    'self': 'http://example.com/identity/v3/' +
+                            'roles/26b860'
+                },
+                'name': 'fake_role'
+            }
+        ],
+        'links': {
+            'next': None,
+            'previous': None,
+            'self': 'http://example.com/identity/v3/' +
+                    'users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles'
+        }
+    }
+
+    FAKE_ACCESS_TOKEN_ROLE_INFO = {
+        'role': {
+            'id': '26b860',
+            'domain_id': 'fake_domain',
+            'links': {
+                'self': 'http://example.com/identity/v3/' +
+                        'roles/26b860'
+            },
+            'name': 'fake_role'
+        }
+    }
+
+    def setUp(self):
+        super(TestOAUTHTokenClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = oauth_token_client.OAUTHTokenClient(fake_auth,
+                                                          'identity',
+                                                          'regionOne')
+
+    def _mock_token_response(self, body):
+        temp_response = [key + '=' + value for key, value in body.items()]
+        return '&'.join(temp_response)
+
+    def _test_authorize_request_token(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.authorize_request_token,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_AUTHORIZE_REQUEST_TOKEN,
+            bytes_body,
+            request_token_id=self.FAKE_CREATE_REQUEST_TOKEN['oauth_token'],
+            role_ids=['26b860'],
+            status=200)
+
+    def test_create_request_token(self):
+        mock_resp = self._mock_token_response(self.FAKE_CREATE_REQUEST_TOKEN)
+        resp = fake_http.fake_http_response(None, status=201), mock_resp
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.rest_client.RestClient.post',
+            return_value=resp))
+
+        resp = self.client.create_request_token(
+            consumer_key='12345',
+            consumer_secret='23456',
+            project_id='c8f58432c6f00162f04d3250f')
+        self.assertEqual(self.FAKE_CREATE_REQUEST_TOKEN, resp)
+
+    def test_authorize_token_request_with_str_body(self):
+        self._test_authorize_request_token()
+
+    def test_authorize_token_request_with_bytes_body(self):
+        self._test_authorize_request_token(bytes_body=True)
+
+    def test_create_access_token(self):
+        mock_resp = self._mock_token_response(self.FAKE_CREATE_ACCESS_TOKEN)
+        req_secret = self.FAKE_CREATE_REQUEST_TOKEN['oauth_token_secret']
+        resp = fake_http.fake_http_response(None, status=201), mock_resp
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.rest_client.RestClient.post',
+            return_value=resp))
+
+        resp = self.client.create_access_token(
+            consumer_key='12345',
+            consumer_secret='23456',
+            request_key=self.FAKE_CREATE_REQUEST_TOKEN['oauth_token'],
+            request_secret=req_secret,
+            oauth_verifier='8171')
+        self.assertEqual(self.FAKE_CREATE_ACCESS_TOKEN, resp)
+
+    def test_get_access_token(self):
+        self.check_service_client_function(
+            self.client.get_access_token,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ACCESS_TOKEN_INFO,
+            user_id='ce9e07',
+            access_token_id=self.FAKE_ACCESS_TOKEN_INFO['access_token']['id'],
+            status=200)
+
+    def test_list_access_tokens(self):
+        self.check_service_client_function(
+            self.client.list_access_tokens,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ACCESS_TOKENS,
+            user_id='ce9e07',
+            status=200)
+
+    def test_revoke_access_token(self):
+        self.check_service_client_function(
+            self.client.revoke_access_token,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            user_id=self.FAKE_ACCESS_TOKEN_INFO['access_token']['consumer_id'],
+            access_token_id=self.FAKE_ACCESS_TOKEN_INFO['access_token']['id'],
+            status=204)
+
+    def test_list_access_token_roles(self):
+        self.check_service_client_function(
+            self.client.list_access_token_roles,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ACCESS_TOKEN_ROLES,
+            user_id='ce9e07',
+            access_token_id=self.FAKE_ACCESS_TOKEN_INFO['access_token']['id'],
+            status=200)
+
+    def test_get_access_token_role(self):
+        self.check_service_client_function(
+            self.client.get_access_token_role,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ACCESS_TOKEN_ROLE_INFO,
+            user_id='ce9e07',
+            access_token_id=self.FAKE_ACCESS_TOKEN_INFO['access_token']['id'],
+            role_id=self.FAKE_ACCESS_TOKEN_ROLE_INFO['role']['id'],
+            status=200)
diff --git a/tempest/tests/lib/services/identity/v3/test_roles_client.py b/tempest/tests/lib/services/identity/v3/test_roles_client.py
index 41cea85..8d6bb42 100644
--- a/tempest/tests/lib/services/identity/v3/test_roles_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_roles_client.py
@@ -26,6 +26,18 @@
     FAKE_ROLE_ID_2 = "2"
     FAKE_ROLE_NAME_2 = "test2"
 
+    FAKE_ROLE_ID_3 = "3"
+    FAKE_ROLE_NAME_3 = "test3"
+
+    FAKE_ROLE_ID_4 = "4"
+    FAKE_ROLE_NAME_4 = "test4"
+
+    FAKE_ROLE_ID_5 = "5"
+    FAKE_ROLE_NAME_5 = "test5"
+
+    FAKE_ROLE_ID_6 = "6"
+    FAKE_ROLE_NAME_6 = "test6"
+
     FAKE_ROLE_INFO = {
         "role": {
             "domain_id": FAKE_DOMAIN_ID,
@@ -77,8 +89,8 @@
         }
     }
 
-    FAKE_LIST_ROLE_INFERENCES_RULES = {
-        "role_inference": {
+    COMMON_FAKE_LIST_ROLE_INFERENCE_RULES = [
+        {
             "prior_role": {
                 "id": FAKE_ROLE_ID,
                 "name": FAKE_ROLE_NAME,
@@ -97,20 +109,60 @@
                     }
                 },
                 {
-                    "id": "3",
-                    "name": "test3",
+                    "id": FAKE_ROLE_ID_3,
+                    "name": FAKE_ROLE_NAME_3,
                     "links": {
-                        "self": "http://example.com/identity/v3/roles/3"
+                        "self": "http://example.com/identity/v3/roles/%s" % (
+                            FAKE_ROLE_ID_3)
                     }
                 }
             ]
         },
+        {
+            "prior_role": {
+                "id": FAKE_ROLE_ID_4,
+                "name": FAKE_ROLE_NAME_4,
+                "links": {
+                    "self": "http://example.com/identity/v3/roles/%s" % (
+                        FAKE_ROLE_ID_4)
+                }
+            },
+            "implies": [
+                {
+                    "id": FAKE_ROLE_ID_5,
+                    "name": FAKE_ROLE_NAME_5,
+                    "links": {
+                        "self": "http://example.com/identity/v3/roles/%s" % (
+                            FAKE_ROLE_ID_5)
+                    }
+                },
+                {
+                    "id": FAKE_ROLE_ID_6,
+                    "name": FAKE_ROLE_NAME_6,
+                    "links": {
+                        "self": "http://example.com/identity/v3/roles/%s" % (
+                            FAKE_ROLE_ID_6)
+                    }
+                }
+            ]
+        }
+    ]
+
+    FAKE_LIST_ROLE_INFERENCE_RULES = {
+        "role_inference": COMMON_FAKE_LIST_ROLE_INFERENCE_RULES[0],
         "links": {
             "self": "http://example.com/identity/v3/roles/"
                     "%s/implies" % FAKE_ROLE_ID
         }
     }
 
+    FAKE_LIST_ALL_ROLE_INFERENCE_RULES = {
+        "role_inferences": COMMON_FAKE_LIST_ROLE_INFERENCE_RULES,
+        "links": {
+            "self": "http://example.com/identity/v3/role_inferences"
+        }
+    }
+
     def setUp(self):
         super(TestRolesClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -254,10 +306,17 @@
         self.check_service_client_function(
             self.client.list_role_inferences_rules,
             'tempest.lib.common.rest_client.RestClient.get',
-            self.FAKE_LIST_ROLE_INFERENCES_RULES,
+            self.FAKE_LIST_ROLE_INFERENCE_RULES,
             bytes_body,
             prior_role=self.FAKE_ROLE_ID)
 
+    def _test_list_all_role_inference_rules(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_all_role_inference_rules,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ALL_ROLE_INFERENCE_RULES,
+            bytes_body)
+
     def test_create_role_with_str_body(self):
         self._test_create_role()
 
@@ -441,3 +500,9 @@
             status=204,
             prior_role=self.FAKE_ROLE_ID,
             implies_role=self.FAKE_ROLE_ID_2)
+
+    def test_list_all_role_inference_rules_with_str_body(self):
+        self._test_list_all_role_inference_rules()
+
+    def test_list_all_role_inference_rules_with_bytes_body(self):
+        self._test_list_all_role_inference_rules(bytes_body=True)
diff --git a/tempest/tests/lib/services/identity/v3/test_services_client.py b/tempest/tests/lib/services/identity/v3/test_services_client.py
index f87fcce..b464644 100644
--- a/tempest/tests/lib/services/identity/v3/test_services_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_services_client.py
@@ -101,12 +101,15 @@
             bytes_body,
             service_id="686766")
 
-    def _test_list_services(self, bytes_body=False):
+    def _test_list_services(self, bytes_body=False, mock_args='services',
+                            **params):
         self.check_service_client_function(
             self.client.list_services,
             'tempest.lib.common.rest_client.RestClient.get',
             self.FAKE_LIST_SERVICES,
-            bytes_body)
+            bytes_body,
+            mock_args=[mock_args],
+            **params)
 
     def _test_update_service(self, bytes_body=False):
         self.check_service_client_function(
@@ -134,6 +137,10 @@
     def test_list_services_with_bytes_body(self):
         self._test_list_services(bytes_body=True)
 
+    def test_list_services_with_params(self):
+        self._test_list_services(
+            type='fake-type', mock_args='services?type=fake-type')
+
     def test_update_service_with_str_body(self):
         self._test_update_service()
 
diff --git a/tempest/tests/lib/services/identity/v3/test_versions_client.py b/tempest/tests/lib/services/identity/v3/test_versions_client.py
new file mode 100644
index 0000000..3bfaf1e
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_versions_client.py
@@ -0,0 +1,70 @@
+# Copyright 2017 NEC Corporation.  All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.identity.v3 import versions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestIdentityClient(base.BaseServiceTest):
+
+    FAKE_VERSIONS_INFO = {
+        "versions": {
+            "values": [
+                {"status": "stable", "updated": "2017-02-22T00:00:00Z",
+                 "media-types": [
+                     {"base": "application/json", "type":
+                      "application/vnd.openstack.identity-v3+json"}
+                 ],
+                 "id": "v3.8",
+                 "links": [
+                     {"href": "https://15.184.67.226/identity_admin/v3/",
+                      "rel": "self"}
+                 ]},
+                {"status": "deprecated", "updated": "2016-08-04T00:00:00Z",
+                 "media-types": [
+                     {"base": "application/json",
+                      "type": "application/vnd.openstack.identity-v2.0+json"}
+                 ],
+                 "id": "v2.0",
+                 "links": [
+                     {"href": "https://15.184.67.226/identity_admin/v2.0/",
+                      "rel": "self"},
+                     {"href": "https://docs.openstack.org/",
+                      "type": "text/html", "rel": "describedby"}
+                 ]}
+            ]
+        }
+    }
+
+    def setUp(self):
+        super(TestIdentityClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = versions_client.VersionsClient(fake_auth,
+                                                     'identity',
+                                                     'regionOne')
+
+    def _test_list_versions(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_versions,
+            'tempest.lib.common.rest_client.RestClient.raw_request',
+            self.FAKE_VERSIONS_INFO,
+            bytes_body,
+            300)
+
+    def test_list_versions_with_str_body(self):
+        self._test_list_versions()
+
+    def test_list_versions_with_bytes_body(self):
+        self._test_list_versions(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
index 9648985..ee4d4cb 100644
--- a/tempest/tests/lib/services/image/v2/test_images_client.py
+++ b/tempest/tests/lib/services/image/v2/test_images_client.py
@@ -12,6 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import six
+
+from tempest.lib.common.utils import data_utils
 from tempest.lib.services.image.v2 import images_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
@@ -42,6 +45,57 @@
         "container_format": None
     }
 
+    FAKE_LIST_IMAGES = {
+        "images": [
+            {
+                "status": "active",
+                "name": "cirros-0.3.2-x86_64-disk",
+                "tags": [],
+                "container_format": "bare",
+                "created_at": "2014-11-07T17:07:06Z",
+                "disk_format": "qcow2",
+                "updated_at": "2014-11-07T17:19:09Z",
+                "visibility": "public",
+                "self": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27",
+                "min_disk": 0,
+                "protected": False,
+                "id": "1bea47ed-f6a9-463b-b423-14b9cca9ad27",
+                "file": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27/file",
+                "checksum": "64d7c1cd2b6f60c92c14662941cb7913",
+                "owner": "5ef70662f8b34079a6eddb8da9d75fe8",
+                "size": 13167616,
+                "min_ram": 0,
+                "schema": "/v2/schemas/image",
+                "virtual_size": None
+            },
+            {
+                "status": "active",
+                "name": "F17-x86_64-cfntools",
+                "tags": [],
+                "container_format": "bare",
+                "created_at": "2014-10-30T08:23:39Z",
+                "disk_format": "qcow2",
+                "updated_at": "2014-11-03T16:40:10Z",
+                "visibility": "public",
+                "self": "/v2/images/781b3762-9469-4cec-b58d-3349e5de4e9c",
+                "min_disk": 0,
+                "protected": False,
+                "id": "781b3762-9469-4cec-b58d-3349e5de4e9c",
+                "file": "/v2/images/781b3762-9469-4cec-b58d-3349e5de4e9c/file",
+                "checksum": "afab0f79bac770d61d24b4d0560b5f70",
+                "owner": "5ef70662f8b34079a6eddb8da9d75fe8",
+                "size": 476704768,
+                "min_ram": 0,
+                "schema": "/v2/schemas/image",
+                "virtual_size": None
+            }
+        ],
+        "schema": "/v2/schemas/images",
+        "first": "/v2/images"
+    }
+
+    FAKE_TAG_NAME = "fake tag"
+
     def setUp(self):
         super(TestImagesClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -74,6 +128,14 @@
             bytes_body,
             image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8")
 
+    def _test_list_images(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_images,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_IMAGES,
+            bytes_body,
+            mock_args=['images'])
+
     def test_create_image_with_str_body(self):
         self._test_create_image()
 
@@ -104,8 +166,56 @@
             'tempest.lib.common.rest_client.RestClient.delete',
             {}, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8", status=204)
 
+    def test_store_image_file(self):
+        data = six.BytesIO(data_utils.random_bytes())
+
+        self.check_service_client_function(
+            self.client.store_image_file,
+            'tempest.lib.common.rest_client.RestClient.raw_request',
+            {},
+            image_id=self.FAKE_CREATE_UPDATE_SHOW_IMAGE["id"],
+            status=204,
+            data=data)
+
+    def test_show_image_file(self):
+        # NOTE: The response for this API returns raw binary data, but an error
+        # is thrown if random bytes are used for the resp body since
+        # ``create_response`` then calls ``json.dumps``.
+        self.check_service_client_function(
+            self.client.show_image_file,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {},
+            resp_as_string=True,
+            image_id=self.FAKE_CREATE_UPDATE_SHOW_IMAGE["id"],
+            headers={'Content-Type': 'application/octet-stream'},
+            status=200)
+
+    def test_add_image_tag(self):
+        self.check_service_client_function(
+            self.client.add_image_tag,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            image_id=self.FAKE_CREATE_UPDATE_SHOW_IMAGE["id"],
+            status=204,
+            tag=self.FAKE_TAG_NAME)
+
+    def test_delete_image_tag(self):
+        self.check_service_client_function(
+            self.client.delete_image_tag,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            image_id=self.FAKE_CREATE_UPDATE_SHOW_IMAGE["id"],
+            status=204,
+            tag=self.FAKE_TAG_NAME)
+
     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)
+
+    def test_list_images_with_str_body(self):
+        self._test_list_images()
+
+    def test_list_images_with_bytes_body(self):
+        self._test_list_images(bytes_body=True)
diff --git a/tempest/tests/lib/services/image/v2/test_versions_client.py b/tempest/tests/lib/services/image/v2/test_versions_client.py
new file mode 100644
index 0000000..6234b06
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_versions_client.py
@@ -0,0 +1,94 @@
+# Copyright 2017 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 versions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVersionsClient(base.BaseServiceTest):
+
+    FAKE_VERSIONS_INFO = {
+        "versions": [
+            {
+                "status": "CURRENT", "id": "v2.5",
+                "links": [
+                    {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+                ]
+            },
+            {
+                "status": "SUPPORTED", "id": "v2.4",
+                "links": [
+                    {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+                ]
+            },
+            {
+                "status": "SUPPORTED", "id": "v2.3",
+                "links": [
+                    {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+                ]
+            },
+            {
+                "status": "SUPPORTED", "id": "v2.2",
+                "links": [
+                    {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+                ]
+            },
+            {
+                "status": "SUPPORTED", "id": "v2.1",
+                "links": [
+                    {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+                ]
+            },
+            {
+                "status": "SUPPORTED", "id": "v2.0",
+                "links": [
+                    {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+                ]
+            },
+            {
+                "status": "DEPRECATED", "id": "v1.1",
+                "links": [
+                    {"href": "https://10.220.1.21:9292/v1/", "rel": "self"}
+                ]
+            },
+            {
+                "status": "DEPRECATED", "id": "v1.0",
+                "links": [
+                    {"href": "https://10.220.1.21:9292/v1/", "rel": "self"}
+                ]
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestVersionsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = versions_client.VersionsClient(fake_auth,
+                                                     'image',
+                                                     'regionOne')
+
+    def _test_list_versions(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_versions,
+            'tempest.lib.common.rest_client.RestClient.raw_request',
+            self.FAKE_VERSIONS_INFO,
+            bytes_body,
+            300)
+
+    def test_list_versions_with_str_body(self):
+        self._test_list_versions()
+
+    def test_list_versions_with_bytes_body(self):
+        self._test_list_versions(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_base_network_client.py b/tempest/tests/lib/services/network/test_base_network_client.py
new file mode 100644
index 0000000..e121cec
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_base_network_client.py
@@ -0,0 +1,96 @@
+# Copyright 2017 AT&T Corporation.
+# All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import mock
+
+from tempest.lib.services.network import base as base_network_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib import fake_http
+from tempest.tests.lib.services import base
+
+
+class TestBaseNetworkClient(base.BaseServiceTest):
+
+    def setUp(self):
+        super(TestBaseNetworkClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = base_network_client.BaseNetworkClient(
+            fake_auth, 'compute', 'regionOne')
+
+        self.mock_expected_success = mock.patch.object(
+            self.client, 'expected_success').start()
+
+    def _assert_empty(self, resp):
+        self.assertEqual([], list(resp.keys()))
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient.post')
+    def test_create_resource(self, mock_post):
+        response = fake_http.fake_http_response(headers=None, status=201)
+        mock_post.return_value = response, '{"baz": "qux"}'
+
+        post_data = {'foo': 'bar'}
+        resp = self.client.create_resource('/fake_url', post_data)
+
+        self.assertEqual({'status': '201'}, resp.response)
+        self.assertEqual("qux", resp["baz"])
+        mock_post.assert_called_once_with('v2.0/fake_url', '{"foo": "bar"}')
+        self.mock_expected_success.assert_called_once_with(
+            201, 201)
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient.post')
+    def test_create_resource_expect_different_values(self, mock_post):
+        response = fake_http.fake_http_response(headers=None, status=200)
+        mock_post.return_value = response, '{}'
+
+        post_data = {'foo': 'bar'}
+        resp = self.client.create_resource('/fake_url', post_data,
+                                           expect_response_code=200,
+                                           expect_empty_body=True)
+
+        self.assertEqual({'status': '200'}, resp.response)
+        self._assert_empty(resp)
+        mock_post.assert_called_once_with('v2.0/fake_url', '{"foo": "bar"}')
+        self.mock_expected_success.assert_called_once_with(
+            200, 200)
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient.put')
+    def test_update_resource(self, mock_put):
+        response = fake_http.fake_http_response(headers=None, status=200)
+        mock_put.return_value = response, '{"baz": "qux"}'
+
+        put_data = {'foo': 'bar'}
+        resp = self.client.update_resource('/fake_url', put_data)
+
+        self.assertEqual({'status': '200'}, resp.response)
+        self.assertEqual("qux", resp["baz"])
+        mock_put.assert_called_once_with('v2.0/fake_url', '{"foo": "bar"}')
+        self.mock_expected_success.assert_called_once_with(
+            200, 200)
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient.put')
+    def test_update_resource_expect_different_values(self, mock_put):
+        response = fake_http.fake_http_response(headers=None, status=201)
+        mock_put.return_value = response, '{}'
+
+        put_data = {'foo': 'bar'}
+        resp = self.client.update_resource('/fake_url', put_data,
+                                           expect_response_code=201,
+                                           expect_empty_body=True)
+
+        self.assertEqual({'status': '201'}, resp.response)
+        self._assert_empty(resp)
+        mock_put.assert_called_once_with('v2.0/fake_url', '{"foo": "bar"}')
+        self.mock_expected_success.assert_called_once_with(
+            201, 201)
diff --git a/tempest/tests/lib/services/network/test_extensions_client.py b/tempest/tests/lib/services/network/test_extensions_client.py
new file mode 100644
index 0000000..27eb485
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_extensions_client.py
@@ -0,0 +1,201 @@
+# Copyright 2017 AT&T Corporation.
+# All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.network import extensions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestExtensionsClient(base.BaseServiceTest):
+
+    FAKE_EXTENSIONS = {
+        "extensions": [
+            {
+                "updated": "2013-01-20T00:00:00-00:00",
+                "name": "Neutron Service Type Management",
+                "links": [],
+                "alias": "service-type",
+                "description": "API for retrieving service providers for"
+                " Neutron advanced services"
+            },
+            {
+                "updated": "2012-10-05T10:00:00-00:00",
+                "name": "security-group",
+                "links": [],
+                "alias": "security-group",
+                "description": "The security groups extension."
+            },
+            {
+                "updated": "2013-02-07T10:00:00-00:00",
+                "name": "L3 Agent Scheduler",
+                "links": [],
+                "alias": "l3_agent_scheduler",
+                "description": "Schedule routers among l3 agents"
+            },
+            {
+                "updated": "2013-02-07T10:00:00-00:00",
+                "name": "Loadbalancer Agent Scheduler",
+                "links": [],
+                "alias": "lbaas_agent_scheduler",
+                "description": "Schedule pools among lbaas agents"
+            },
+            {
+                "updated": "2013-03-28T10:00:00-00:00",
+                "name": "Neutron L3 Configurable external gateway mode",
+                "links": [],
+                "alias": "ext-gw-mode",
+                "description":
+                "Extension of the router abstraction for specifying whether"
+                " SNAT should occur on the external gateway"
+            },
+            {
+                "updated": "2014-02-03T10:00:00-00:00",
+                "name": "Port Binding",
+                "links": [],
+                "alias": "binding",
+                "description": "Expose port bindings of a virtual port to"
+                " external application"
+            },
+            {
+                "updated": "2012-09-07T10:00:00-00:00",
+                "name": "Provider Network",
+                "links": [],
+                "alias": "provider",
+                "description": "Expose mapping of virtual networks to"
+                " physical networks"
+            },
+            {
+                "updated": "2013-02-03T10:00:00-00:00",
+                "name": "agent",
+                "links": [],
+                "alias": "agent",
+                "description": "The agent management extension."
+            },
+            {
+                "updated": "2012-07-29T10:00:00-00:00",
+                "name": "Quota management support",
+                "links": [],
+                "alias": "quotas",
+                "description": "Expose functions for quotas management per"
+                " tenant"
+            },
+            {
+                "updated": "2013-02-07T10:00:00-00:00",
+                "name": "DHCP Agent Scheduler",
+                "links": [],
+                "alias": "dhcp_agent_scheduler",
+                "description": "Schedule networks among dhcp agents"
+            },
+            {
+                "updated": "2013-06-27T10:00:00-00:00",
+                "name": "Multi Provider Network",
+                "links": [],
+                "alias": "multi-provider",
+                "description": "Expose mapping of virtual networks to"
+                " multiple physical networks"
+            },
+            {
+                "updated": "2013-01-14T10:00:00-00:00",
+                "name": "Neutron external network",
+                "links": [],
+                "alias": "external-net",
+                "description": "Adds external network attribute to network"
+                " resource."
+            },
+            {
+                "updated": "2012-07-20T10:00:00-00:00",
+                "name": "Neutron L3 Router",
+                "links": [],
+                "alias": "router",
+                "description": "Router abstraction for basic L3 forwarding"
+                " between L2 Neutron networks and access to external"
+                " networks via a NAT gateway."
+            },
+            {
+                "updated": "2013-07-23T10:00:00-00:00",
+                "name": "Allowed Address Pairs",
+                "links": [],
+                "alias": "allowed-address-pairs",
+                "description": "Provides allowed address pairs"
+            },
+            {
+                "updated": "2013-03-17T12:00:00-00:00",
+                "name": "Neutron Extra DHCP opts",
+                "links": [],
+                "alias": "extra_dhcp_opt",
+                "description": "Extra options configuration for DHCP. For"
+                " example PXE boot options to DHCP clients can be specified"
+                " (e.g. tftp-server, server-ip-address, bootfile-name)"
+            },
+            {
+                "updated": "2012-10-07T10:00:00-00:00",
+                "name": "LoadBalancing service",
+                "links": [],
+                "alias": "lbaas",
+                "description": "Extension for LoadBalancing service"
+            },
+            {
+                "updated": "2013-02-01T10:00:00-00:00",
+                "name": "Neutron Extra Route",
+                "links": [],
+                "alias": "extraroute",
+                "description": "Extra routes configuration for L3 router"
+            },
+            {
+                "updated": "2016-01-24T10:00:00-00:00",
+                "name": "Neutron Port Data Plane Status",
+                "links": [],
+                "alias": "data-plane-status",
+                "description": "Status of the underlying data plane."
+            }
+        ]
+    }
+
+    FAKE_EXTENSION_ALIAS = "service-type"
+
+    def setUp(self):
+        super(TestExtensionsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.extensions_client = extensions_client.ExtensionsClient(
+            fake_auth, "network", "regionOne")
+
+    def _test_list_extensions(self, bytes_body=False):
+        self.check_service_client_function(
+            self.extensions_client.list_extensions,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_EXTENSIONS,
+            bytes_body,
+            200)
+
+    def _test_show_extension(self, bytes_body=False):
+        self.check_service_client_function(
+            self.extensions_client.show_extension,
+            "tempest.lib.common.rest_client.RestClient.get",
+            {"extension": self.FAKE_EXTENSIONS["extensions"][0]},
+            bytes_body,
+            200,
+            ext_alias=self.FAKE_EXTENSION_ALIAS)
+
+    def test_list_extensions_with_str_body(self):
+        self._test_list_extensions()
+
+    def test_list_extensions_with_bytes_body(self):
+        self._test_list_extensions(bytes_body=True)
+
+    def test_show_extension_with_str_body(self):
+        self._test_show_extension()
+
+    def test_show_extension_with_bytes_body(self):
+        self._test_show_extension(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_floating_ips_client.py b/tempest/tests/lib/services/network/test_floating_ips_client.py
new file mode 100644
index 0000000..c5b1845
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_floating_ips_client.py
@@ -0,0 +1,145 @@
+# Copyright 2017 AT&T Corporation.
+# All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.services.network import floating_ips_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestFloatingIPsClient(base.BaseServiceTest):
+
+    FAKE_FLOATING_IPS = {
+        "floatingips": [
+            {
+                "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
+                "description": "for test",
+                "created_at": "2016-12-21T10:55:50Z",
+                "updated_at": "2016-12-21T10:55:53Z",
+                "revision_number": 1,
+                "project_id": "4969c491a3c74ee4af974e6d800c62de",
+                "tenant_id": "4969c491a3c74ee4af974e6d800c62de",
+                "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
+                "fixed_ip_address": "10.0.0.3",
+                "floating_ip_address": "172.24.4.228",
+                "port_id": "ce705c24-c1ef-408a-bda3-7bbd946164ab",
+                "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7",
+                "status": "ACTIVE"
+            },
+            {
+                "router_id": None,
+                "description": "for test",
+                "created_at": "2016-12-21T11:55:50Z",
+                "updated_at": "2016-12-21T11:55:53Z",
+                "revision_number": 2,
+                "project_id": "4969c491a3c74ee4af974e6d800c62de",
+                "tenant_id": "4969c491a3c74ee4af974e6d800c62de",
+                "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
+                "fixed_ip_address": None,
+                "floating_ip_address": "172.24.4.227",
+                "port_id": None,
+                "id": "61cea855-49cb-4846-997d-801b70c71bdd",
+                "status": "DOWN"
+            }
+        ]
+    }
+
+    FAKE_FLOATING_IP_ID = "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
+
+    def setUp(self):
+        super(TestFloatingIPsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.floating_ips_client = floating_ips_client.FloatingIPsClient(
+            fake_auth, "compute", "regionOne")
+
+    def _test_list_floatingips(self, bytes_body=False):
+        self.check_service_client_function(
+            self.floating_ips_client.list_floatingips,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_FLOATING_IPS,
+            bytes_body,
+            200)
+
+    def _test_create_floatingip(self, bytes_body=False):
+        self.check_service_client_function(
+            self.floating_ips_client.create_floatingip,
+            "tempest.lib.common.rest_client.RestClient.post",
+            {"floatingip": self.FAKE_FLOATING_IPS["floatingips"][1]},
+            bytes_body,
+            201,
+            floating_network_id="172.24.4.228")
+
+    def _test_show_floatingip(self, bytes_body=False):
+        self.check_service_client_function(
+            self.floating_ips_client.show_floatingip,
+            "tempest.lib.common.rest_client.RestClient.get",
+            {"floatingip": self.FAKE_FLOATING_IPS["floatingips"][0]},
+            bytes_body,
+            200,
+            floatingip_id=self.FAKE_FLOATING_IP_ID)
+
+    def _test_update_floatingip(self, bytes_body=False):
+        update_kwargs = {
+            "port_id": "fc861431-0e6c-4842-a0ed-e2363f9bc3a8"
+        }
+
+        resp_body = {
+            "floatingip": copy.deepcopy(
+                self.FAKE_FLOATING_IPS["floatingips"][0]
+            )
+        }
+        resp_body["floatingip"].update(update_kwargs)
+
+        self.check_service_client_function(
+            self.floating_ips_client.update_floatingip,
+            "tempest.lib.common.rest_client.RestClient.put",
+            resp_body,
+            bytes_body,
+            200,
+            floatingip_id=self.FAKE_FLOATING_IP_ID,
+            **update_kwargs)
+
+    def test_list_floatingips_with_str_body(self):
+        self._test_list_floatingips()
+
+    def test_list_floatingips_with_bytes_body(self):
+        self._test_list_floatingips(bytes_body=True)
+
+    def test_create_floatingip_with_str_body(self):
+        self._test_create_floatingip()
+
+    def test_create_floatingip_with_bytes_body(self):
+        self._test_create_floatingip(bytes_body=True)
+
+    def test_show_floatingips_with_str_body(self):
+        self._test_show_floatingip()
+
+    def test_show_floatingips_with_bytes_body(self):
+        self._test_show_floatingip(bytes_body=True)
+
+    def test_update_floatingip_with_str_body(self):
+        self._test_update_floatingip()
+
+    def test_update_floatingip_with_bytes_body(self):
+        self._test_update_floatingip(bytes_body=True)
+
+    def test_delete_floatingip(self):
+        self.check_service_client_function(
+            self.floating_ips_client.delete_floatingip,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            floatingip_id=self.FAKE_FLOATING_IP_ID)
diff --git a/tempest/tests/lib/services/network/test_metering_label_rules_client.py b/tempest/tests/lib/services/network/test_metering_label_rules_client.py
new file mode 100644
index 0000000..047c34f
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_metering_label_rules_client.py
@@ -0,0 +1,110 @@
+# Copyright 2017 AT&T Corporation.
+# All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.network import metering_label_rules_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestMeteringLabelRulesClient(base.BaseServiceTest):
+
+    FAKE_METERING_LABEL_RULES = {
+        "metering_label_rules": [
+            {
+                "remote_ip_prefix": "20.0.0.0/24",
+                "direction": "ingress",
+                "metering_label_id": "e131d186-b02d-4c0b-83d5-0c0725c4f812",
+                "id": "9536641a-7d14-4dc5-afaf-93a973ce0eb8",
+                "excluded": False
+            },
+            {
+                "remote_ip_prefix": "10.0.0.0/24",
+                "direction": "ingress",
+                "metering_label_id": "e131d186-b02d-4c0b-83d5-0c0725c4f812",
+                "id": "ffc6fd15-40de-4e7d-b617-34d3f7a93aec",
+                "excluded": False
+            }
+        ]
+    }
+
+    FAKE_METERING_LABEL_RULE = {
+        "remote_ip_prefix": "20.0.0.0/24",
+        "direction": "ingress",
+        "metering_label_id": "e131d186-b02d-4c0b-83d5-0c0725c4f812"
+    }
+
+    FAKE_METERING_LABEL_ID = "e131d186-b02d-4c0b-83d5-0c0725c4f812"
+    FAKE_METERING_LABEL_RULE_ID = "9536641a-7d14-4dc5-afaf-93a973ce0eb8"
+
+    def setUp(self):
+        super(TestMeteringLabelRulesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.metering_label_rules_client = \
+            metering_label_rules_client.MeteringLabelRulesClient(
+                fake_auth, "network", "regionOne")
+
+    def _test_list_metering_label_rules(self, bytes_body=False):
+        self.check_service_client_function(
+            self.metering_label_rules_client.list_metering_label_rules,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_METERING_LABEL_RULES,
+            bytes_body,
+            200)
+
+    def _test_create_metering_label_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.metering_label_rules_client.create_metering_label_rule,
+            "tempest.lib.common.rest_client.RestClient.post",
+            {"metering_label_rule": self.FAKE_METERING_LABEL_RULES[
+                "metering_label_rules"][0]},
+            bytes_body,
+            201,
+            **self.FAKE_METERING_LABEL_RULE)
+
+    def _test_show_metering_label_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.metering_label_rules_client.show_metering_label_rule,
+            "tempest.lib.common.rest_client.RestClient.get",
+            {"metering_label_rule": self.FAKE_METERING_LABEL_RULES[
+                "metering_label_rules"][0]},
+            bytes_body,
+            200,
+            metering_label_rule_id=self.FAKE_METERING_LABEL_RULE_ID)
+
+    def test_delete_metering_label_rule(self):
+        self.check_service_client_function(
+            self.metering_label_rules_client.delete_metering_label_rule,
+            "tempest.lib.common.rest_client.RestClient.delete",
+            {},
+            status=204,
+            metering_label_rule_id=self.FAKE_METERING_LABEL_RULE_ID)
+
+    def test_list_metering_label_rules_with_str_body(self):
+        self._test_list_metering_label_rules()
+
+    def test_list_metering_label_rules_with_bytes_body(self):
+        self._test_list_metering_label_rules(bytes_body=True)
+
+    def test_create_metering_label_rule_with_str_body(self):
+        self._test_create_metering_label_rule()
+
+    def test_create_metering_label_rule_with_bytes_body(self):
+        self._test_create_metering_label_rule(bytes_body=True)
+
+    def test_show_metering_label_rule_with_str_body(self):
+        self._test_show_metering_label_rule()
+
+    def test_show_metering_label_rule_with_bytes_body(self):
+        self._test_show_metering_label_rule(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_metering_labels_client.py b/tempest/tests/lib/services/network/test_metering_labels_client.py
new file mode 100644
index 0000000..a048326
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_metering_labels_client.py
@@ -0,0 +1,107 @@
+# Copyright 2017 AT&T Corporation.
+# All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.network import metering_labels_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestMeteringLabelsClient(base.BaseServiceTest):
+
+    FAKE_METERING_LABELS = {
+        "metering_labels": [
+            {
+                "project_id": "45345b0ee1ea477fac0f541b2cb79cd4",
+                "tenant_id": "45345b0ee1ea477fac0f541b2cb79cd4",
+                "description": "label1 description",
+                "name": "label1",
+                "id": "a6700594-5b7a-4105-8bfe-723b346ce866",
+                "shared": False
+            },
+            {
+                "project_id": "45345b0ee1ea477fac0f541b2cb79cd4",
+                "tenant_id": "45345b0ee1ea477fac0f541b2cb79cd4",
+                "description": "label2 description",
+                "name": "label2",
+                "id": "e131d186-b02d-4c0b-83d5-0c0725c4f812",
+                "shared": False
+            }
+        ]
+    }
+
+    FAKE_METERING_LABEL_ID = "a6700594-5b7a-4105-8bfe-723b346ce866"
+
+    def setUp(self):
+        super(TestMeteringLabelsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.metering_labels_client = \
+            metering_labels_client.MeteringLabelsClient(
+                fake_auth, "network", "regionOne")
+
+    def _test_list_metering_labels(self, bytes_body=False):
+        self.check_service_client_function(
+            self.metering_labels_client.list_metering_labels,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_METERING_LABELS,
+            bytes_body,
+            200)
+
+    def _test_create_metering_label(self, bytes_body=False):
+        self.check_service_client_function(
+            self.metering_labels_client.create_metering_label,
+            "tempest.lib.common.rest_client.RestClient.post",
+            {"metering_label": self.FAKE_METERING_LABELS[
+                "metering_labels"][1]},
+            bytes_body,
+            201,
+            name="label1",
+            description="label1 description",
+            shared=False)
+
+    def _test_show_metering_label(self, bytes_body=False):
+        self.check_service_client_function(
+            self.metering_labels_client.show_metering_label,
+            "tempest.lib.common.rest_client.RestClient.get",
+            {"metering_label": self.FAKE_METERING_LABELS[
+                "metering_labels"][0]},
+            bytes_body,
+            200,
+            metering_label_id=self.FAKE_METERING_LABEL_ID)
+
+    def test_delete_metering_label(self):
+        self.check_service_client_function(
+            self.metering_labels_client.delete_metering_label,
+            "tempest.lib.common.rest_client.RestClient.delete",
+            {},
+            status=204,
+            metering_label_id=self.FAKE_METERING_LABEL_ID)
+
+    def test_list_metering_labels_with_str_body(self):
+        self._test_list_metering_labels()
+
+    def test_list_metering_labels_with_bytes_body(self):
+        self._test_list_metering_labels(bytes_body=True)
+
+    def test_create_metering_label_with_str_body(self):
+        self._test_create_metering_label()
+
+    def test_create_metering_label_with_bytes_body(self):
+        self._test_create_metering_label(bytes_body=True)
+
+    def test_show_metering_label_with_str_body(self):
+        self._test_show_metering_label()
+
+    def test_show_metering_label_with_bytes_body(self):
+        self._test_show_metering_label(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_ports_client.py b/tempest/tests/lib/services/network/test_ports_client.py
new file mode 100644
index 0000000..20ef3f1
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_ports_client.py
@@ -0,0 +1,198 @@
+# Copyright 2017 AT&T Corporation.
+# All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.services.network import ports_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestPortsClient(base.BaseServiceTest):
+
+    FAKE_PORTS = {
+        "ports": [
+            {
+                "admin_state_up": True,
+                "allowed_address_pairs": [],
+                "data_plane_status": None,
+                "description": "",
+                "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824",
+                "device_owner": "network:router_gateway",
+                "extra_dhcp_opts": [],
+                "fixed_ips": [
+                    {
+                        "ip_address": "172.24.4.2",
+                        "subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062"
+                    }
+                ],
+                "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
+                "mac_address": "fa:16:3e:58:42:ed",
+                "name": "",
+                "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3",
+                "project_id": "",
+                "security_groups": [],
+                "status": "ACTIVE",
+                "tenant_id": ""
+            },
+            {
+                "admin_state_up": True,
+                "allowed_address_pairs": [],
+                "data_plane_status": None,
+                "description": "",
+                "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824",
+                "device_owner": "network:router_interface",
+                "extra_dhcp_opts": [],
+                "fixed_ips": [
+                    {
+                        "ip_address": "10.0.0.1",
+                        "subnet_id": "288bf4a1-51ba-43b6-9d0a-520e9005db17"
+                    }
+                ],
+                "id": "f71a6703-d6de-4be1-a91a-a570ede1d159",
+                "mac_address": "fa:16:3e:bb:3c:e4",
+                "name": "",
+                "network_id": "f27aa545-cbdd-4907-b0c6-c9e8b039dcc2",
+                "project_id": "d397de8a63f341818f198abb0966f6f3",
+                "security_groups": [],
+                "status": "ACTIVE",
+                "tenant_id": "d397de8a63f341818f198abb0966f6f3"
+            }
+        ]
+    }
+
+    FAKE_PORT_ID = "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b"
+
+    FAKE_PORT1 = {
+        "admin_state_up": True,
+        "name": "",
+        "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3"
+    }
+
+    FAKE_PORT2 = {
+        "admin_state_up": True,
+        "name": "",
+        "network_id": "f27aa545-cbdd-4907-b0c6-c9e8b039dcc2"
+    }
+
+    FAKE_PORTS_REQ = {
+        "ports": [
+            FAKE_PORT1,
+            FAKE_PORT2
+        ]
+    }
+
+    def setUp(self):
+        super(TestPortsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.ports_client = ports_client.PortsClient(
+            fake_auth, "network", "regionOne")
+
+    def _test_list_ports(self, bytes_body=False):
+        self.check_service_client_function(
+            self.ports_client.list_ports,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_PORTS,
+            bytes_body,
+            200)
+
+    def _test_create_port(self, bytes_body=False):
+        self.check_service_client_function(
+            self.ports_client.create_port,
+            "tempest.lib.common.rest_client.RestClient.post",
+            {"port": self.FAKE_PORTS["ports"][0]},
+            bytes_body,
+            201,
+            **self.FAKE_PORT1)
+
+    def _test_create_bulk_ports(self, bytes_body=False):
+        self.check_service_client_function(
+            self.ports_client.create_bulk_ports,
+            "tempest.lib.common.rest_client.RestClient.post",
+            self.FAKE_PORTS,
+            bytes_body,
+            201,
+            ports=self.FAKE_PORTS_REQ)
+
+    def _test_show_port(self, bytes_body=False):
+        self.check_service_client_function(
+            self.ports_client.show_port,
+            "tempest.lib.common.rest_client.RestClient.get",
+            {"port": self.FAKE_PORTS["ports"][0]},
+            bytes_body,
+            200,
+            port_id=self.FAKE_PORT_ID)
+
+    def _test_update_port(self, bytes_body=False):
+        update_kwargs = {
+            "admin_state_up": True,
+            "device_id": "d90a13da-be41-461f-9f99-1dbcf438fdf2",
+            "device_owner": "compute:nova",
+            "name": "test-for-port-update"
+        }
+
+        resp_body = {
+            "port": copy.deepcopy(
+                self.FAKE_PORTS["ports"][0]
+            )
+        }
+        resp_body["port"].update(update_kwargs)
+
+        self.check_service_client_function(
+            self.ports_client.update_port,
+            "tempest.lib.common.rest_client.RestClient.put",
+            resp_body,
+            bytes_body,
+            200,
+            port_id=self.FAKE_PORT_ID,
+            **update_kwargs)
+
+    def test_delete_port(self):
+        self.check_service_client_function(
+            self.ports_client.delete_port,
+            "tempest.lib.common.rest_client.RestClient.delete",
+            {},
+            status=204,
+            port_id=self.FAKE_PORT_ID)
+
+    def test_list_ports_with_str_body(self):
+        self._test_list_ports()
+
+    def test_list_ports_with_bytes_body(self):
+        self._test_list_ports(bytes_body=True)
+
+    def test_create_port_with_str_body(self):
+        self._test_create_port()
+
+    def test_create_port_with_bytes_body(self):
+        self._test_create_port(bytes_body=True)
+
+    def test_create_bulk_port_with_str_body(self):
+        self._test_create_bulk_ports()
+
+    def test_create_bulk_port_with_bytes_body(self):
+        self._test_create_bulk_ports(bytes_body=True)
+
+    def test_show_port_with_str_body(self):
+        self._test_show_port()
+
+    def test_show_port_with_bytes_body(self):
+        self._test_show_port(bytes_body=True)
+
+    def test_update_port_with_str_body(self):
+        self._test_update_port()
+
+    def test_update_port_with_bytes_body(self):
+        self._test_update_port(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_quotas_client.py b/tempest/tests/lib/services/network/test_quotas_client.py
new file mode 100644
index 0000000..e76bc9c
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_quotas_client.py
@@ -0,0 +1,99 @@
+# Copyright 2017 AT&T Corporation.
+# All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.network import quotas_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQuotasClient(base.BaseServiceTest):
+
+    FAKE_QUOTAS = {
+        "quotas": [
+            {
+                "floatingip": 50,
+                "network": 15,
+                "port": 50,
+                "project_id": "bab7d5c60cd041a0a36f7c4b6e1dd978",
+                "rbac_policy": -1,
+                "router": 10,
+                "security_group": 10,
+                "security_group_rule": 100,
+                "subnet": 10,
+                "subnetpool": -1,
+                "tenant_id": "bab7d5c60cd041a0a36f7c4b6e1dd978"
+            }
+        ]
+    }
+
+    FAKE_QUOTA_TENANT_ID = "bab7d5c60cd041a0a36f7c4b6e1dd978"
+
+    def setUp(self):
+        super(TestQuotasClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.quotas_client = quotas_client.QuotasClient(
+            fake_auth, "network", "regionOne")
+
+    def _test_list_quotas(self, bytes_body=False):
+        self.check_service_client_function(
+            self.quotas_client.list_quotas,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_QUOTAS,
+            bytes_body,
+            200)
+
+    def _test_show_quotas(self, bytes_body=False):
+        self.check_service_client_function(
+            self.quotas_client.show_quotas,
+            "tempest.lib.common.rest_client.RestClient.get",
+            {"quota": self.FAKE_QUOTAS["quotas"][0]},
+            bytes_body,
+            200,
+            tenant_id=self.FAKE_QUOTA_TENANT_ID)
+
+    def _test_update_quotas(self, bytes_body=False):
+        self.check_service_client_function(
+            self.quotas_client.update_quotas,
+            "tempest.lib.common.rest_client.RestClient.put",
+            {"quota": self.FAKE_QUOTAS["quotas"][0]},
+            bytes_body,
+            200,
+            tenant_id=self.FAKE_QUOTA_TENANT_ID)
+
+    def test_reset_quotas(self):
+        self.check_service_client_function(
+            self.quotas_client.reset_quotas,
+            "tempest.lib.common.rest_client.RestClient.delete",
+            {},
+            status=204,
+            tenant_id=self.FAKE_QUOTA_TENANT_ID)
+
+    def test_list_quotas_with_str_body(self):
+        self._test_list_quotas()
+
+    def test_list_quotas_with_bytes_body(self):
+        self._test_list_quotas(bytes_body=True)
+
+    def test_show_quotas_with_str_body(self):
+        self._test_show_quotas()
+
+    def test_show_quotas_with_bytes_body(self):
+        self._test_show_quotas(bytes_body=True)
+
+    def test_update_quotas_with_str_body(self):
+        self._test_update_quotas()
+
+    def test_update_quotas_with_bytes_body(self):
+        self._test_update_quotas(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_security_group_rules_client.py b/tempest/tests/lib/services/network/test_security_group_rules_client.py
new file mode 100644
index 0000000..b9c17a1
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_security_group_rules_client.py
@@ -0,0 +1,138 @@
+# Copyright 2017 AT&T 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 mock
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.services.network import base as network_base
+from tempest.lib.services.network import security_group_rules_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSecurityGroupsClient(base.BaseServiceTest):
+
+    FAKE_SEC_GROUP_RULE_ID = "3c0e45ff-adaf-4124-b083-bf390e5482ff"
+
+    FAKE_SECURITY_GROUP_RULES = {
+        "security_group_rules": [
+            {
+                "direction": "egress",
+                "ethertype": "IPv6",
+                "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
+                "port_range_max": None,
+                "port_range_min": None,
+                "protocol": None,
+                "remote_group_id": None,
+                "remote_ip_prefix": None,
+                "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+                "project_id": "e4f50856753b4dc6afee5fa6b9b6c550",
+                "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550",
+                "description": ""
+            },
+            {
+                "direction": "egress",
+                "ethertype": "IPv4",
+                "id": "93aa42e5-80db-4581-9391-3a608bd0e448",
+                "port_range_max": None,
+                "port_range_min": None,
+                "protocol": None,
+                "remote_group_id": None,
+                "remote_ip_prefix": None,
+                "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+                "project_id": "e4f50856753b4dc6afee5fa6b9b6c550",
+                "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550",
+                "description": ""
+            }
+        ]
+    }
+
+    FAKE_SECURITY_GROUP_RULE = copy.copy(
+        FAKE_SECURITY_GROUP_RULES['security_group_rules'][0])
+
+    def setUp(self):
+        super(TestSecurityGroupsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = security_group_rules_client.SecurityGroupRulesClient(
+            fake_auth, 'network', 'regionOne')
+
+    def _test_list_security_group_rules(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_security_group_rules,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SECURITY_GROUP_RULES,
+            bytes_body,
+            mock_args='v2.0/security-group-rules')
+
+    def _test_create_security_group_rule(self, bytes_body=False):
+        kwargs = {'direction': 'egress',
+                  'security_group_id': '85cc3048-abc3-43cc-89b3-377341426ac5',
+                  'remote_ip_prefix': None}
+        payload = json.dumps({"security_group_rule": kwargs}, sort_keys=True)
+        json_dumps = json.dumps
+
+        # NOTE: Use sort_keys for json.dumps so that the expected and actual
+        # payloads are guaranteed to be identical for mock_args assert check.
+        with mock.patch.object(network_base.json, 'dumps') as mock_dumps:
+            mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+            self.check_service_client_function(
+                self.client.create_security_group_rule,
+                'tempest.lib.common.rest_client.RestClient.post',
+                self.FAKE_SECURITY_GROUP_RULE,
+                bytes_body,
+                status=201,
+                mock_args=['v2.0/security-group-rules', payload],
+                **kwargs)
+
+    def _test_show_security_group_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_security_group_rule,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SECURITY_GROUP_RULE,
+            bytes_body,
+            security_group_rule_id=self.FAKE_SEC_GROUP_RULE_ID,
+            mock_args='v2.0/security-group-rules/%s'
+                      % self.FAKE_SEC_GROUP_RULE_ID)
+
+    def test_list_security_group_rules_with_str_body(self):
+        self._test_list_security_group_rules()
+
+    def test_list_security_group_rules_with_bytes_body(self):
+        self._test_list_security_group_rules(bytes_body=True)
+
+    def test_create_security_group_rule_with_str_body(self):
+        self._test_create_security_group_rule()
+
+    def test_create_security_group_rule_with_bytes_body(self):
+        self._test_create_security_group_rule(bytes_body=True)
+
+    def test_show_security_group_rule_with_str_body(self):
+        self._test_show_security_group_rule()
+
+    def test_show_security_group_rule_with_bytes_body(self):
+        self._test_show_security_group_rule(bytes_body=True)
+
+    def test_delete_security_group_rule(self):
+        self.check_service_client_function(
+            self.client.delete_security_group_rule,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            security_group_rule_id=self.FAKE_SEC_GROUP_RULE_ID,
+            mock_args='v2.0/security-group-rules/%s'
+                      % self.FAKE_SEC_GROUP_RULE_ID)
diff --git a/tempest/tests/lib/services/network/test_security_groups_client.py b/tempest/tests/lib/services/network/test_security_groups_client.py
new file mode 100644
index 0000000..f96805f
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_security_groups_client.py
@@ -0,0 +1,174 @@
+# Copyright 2017 AT&T 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 mock
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.services.network import base as network_base
+from tempest.lib.services.network import security_groups_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSecurityGroupsClient(base.BaseServiceTest):
+
+    FAKE_SEC_GROUP_ID = "85cc3048-abc3-43cc-89b3-377341426ac5"
+
+    FAKE_SECURITY_GROUPS = {
+        "security_groups": [
+            {
+                "description": "default",
+                "id": FAKE_SEC_GROUP_ID,
+                "name": "fake-security-group-name",
+                "security_group_rules": [
+                    {
+                        "direction": "egress",
+                        "ethertype": "IPv4",
+                        "id": "38ce2d8e-e8f1-48bd-83c2-d33cb9f50c3d",
+                        "port_range_max": None,
+                        "port_range_min": None,
+                        "protocol": None,
+                        "remote_group_id": None,
+                        "remote_ip_prefix": None,
+                        "security_group_id": FAKE_SEC_GROUP_ID,
+                        "project_id": "e4f50856753b4dc6afee5fa6b9b6c550",
+                        "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550",
+                        "description": ""
+                    },
+                    {
+                        "direction": "egress",
+                        "ethertype": "IPv6",
+                        "id": "565b9502-12de-4ffd-91e9-68885cff6ae1",
+                        "port_range_max": None,
+                        "port_range_min": None,
+                        "protocol": None,
+                        "remote_group_id": None,
+                        "remote_ip_prefix": None,
+                        "security_group_id": FAKE_SEC_GROUP_ID,
+                        "project_id": "e4f50856753b4dc6afee5fa6b9b6c550",
+                        "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550",
+                        "description": ""
+                    }
+                ],
+                "project_id": "e4f50856753b4dc6afee5fa6b9b6c550",
+                "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+            }
+        ]
+    }
+
+    FAKE_SECURITY_GROUP = {
+        "security_group": copy.deepcopy(
+            FAKE_SECURITY_GROUPS["security_groups"][0])
+    }
+
+    def setUp(self):
+        super(TestSecurityGroupsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = security_groups_client.SecurityGroupsClient(
+            fake_auth, 'network', 'regionOne')
+
+    def _test_list_security_groups(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_security_groups,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SECURITY_GROUPS,
+            bytes_body,
+            mock_args='v2.0/security-groups')
+
+    def _test_create_security_group(self, bytes_body=False):
+        kwargs = {'name': 'fake-security-group-name'}
+        payload = json.dumps({"security_group": kwargs}, sort_keys=True)
+        json_dumps = json.dumps
+
+        # NOTE: Use sort_keys for json.dumps so that the expected and actual
+        # payloads are guaranteed to be identical for mock_args assert check.
+        with mock.patch.object(network_base.json, 'dumps') as mock_dumps:
+            mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+            self.check_service_client_function(
+                self.client.create_security_group,
+                'tempest.lib.common.rest_client.RestClient.post',
+                self.FAKE_SECURITY_GROUP,
+                bytes_body,
+                status=201,
+                mock_args=['v2.0/security-groups', payload],
+                **kwargs)
+
+    def _test_show_security_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_security_group,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SECURITY_GROUP,
+            bytes_body,
+            security_group_id=self.FAKE_SEC_GROUP_ID,
+            mock_args='v2.0/security-groups/%s' % self.FAKE_SEC_GROUP_ID)
+
+    def _test_update_security_group(self, bytes_body=False):
+        kwargs = {'name': 'updated-security-group-name'}
+        resp_body = copy.deepcopy(self.FAKE_SECURITY_GROUP)
+        resp_body["security_group"]["name"] = 'updated-security-group-name'
+
+        payload = json.dumps({'security_group': kwargs}, sort_keys=True)
+        json_dumps = json.dumps
+
+        # NOTE: Use sort_keys for json.dumps so that the expected and actual
+        # payloads are guaranteed to be identical for mock_args assert check.
+        with mock.patch.object(network_base.json, 'dumps') as mock_dumps:
+            mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+            self.check_service_client_function(
+                self.client.update_security_group,
+                'tempest.lib.common.rest_client.RestClient.put',
+                resp_body,
+                bytes_body,
+                security_group_id=self.FAKE_SEC_GROUP_ID,
+                mock_args=['v2.0/security-groups/%s' % self.FAKE_SEC_GROUP_ID,
+                           payload],
+                **kwargs)
+
+    def test_list_security_groups_with_str_body(self):
+        self._test_list_security_groups()
+
+    def test_list_security_groups_with_bytes_body(self):
+        self._test_list_security_groups(bytes_body=True)
+
+    def test_create_security_group_with_str_body(self):
+        self._test_create_security_group()
+
+    def test_create_security_group_with_bytes_body(self):
+        self._test_create_security_group(bytes_body=True)
+
+    def test_show_security_group_with_str_body(self):
+        self._test_show_security_group()
+
+    def test_show_security_group_with_bytes_body(self):
+        self._test_show_security_group(bytes_body=True)
+
+    def test_update_security_group_with_str_body(self):
+        self._test_update_security_group()
+
+    def test_update_security_group_with_bytes_body(self):
+        self._test_update_security_group(bytes_body=True)
+
+    def test_delete_security_group(self):
+        self.check_service_client_function(
+            self.client.delete_security_group,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            security_group_id=self.FAKE_SEC_GROUP_ID,
+            mock_args='v2.0/security-groups/%s' % self.FAKE_SEC_GROUP_ID)
diff --git a/tempest/tests/lib/services/network/test_subnetpools_client.py b/tempest/tests/lib/services/network/test_subnetpools_client.py
new file mode 100644
index 0000000..3abb438
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_subnetpools_client.py
@@ -0,0 +1,163 @@
+# Copyright 2017 AT&T Corporation.
+# All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.services.network import subnetpools_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSubnetsClient(base.BaseServiceTest):
+
+    FAKE_SUBNETPOOLS = {
+        "subnetpools": [
+            {
+                "min_prefixlen": "64",
+                "address_scope_id": None,
+                "default_prefixlen": "64",
+                "id": "03f761e6-eee0-43fc-a921-8acf64c14988",
+                "max_prefixlen": "64",
+                "name": "my-subnet-pool-ipv6",
+                "default_quota": None,
+                "is_default": False,
+                "project_id": "9fadcee8aa7c40cdb2114fff7d569c08",
+                "tenant_id": "9fadcee8aa7c40cdb2114fff7d569c08",
+                "prefixes": [
+                    "2001:db8:0:2::/64",
+                    "2001:db8::/63"
+                ],
+                "ip_version": 6,
+                "shared": False,
+                "description": "",
+                "revision_number": 2
+            },
+            {
+                "min_prefixlen": "24",
+                "address_scope_id": None,
+                "default_prefixlen": "25",
+                "id": "f49a1319-423a-4ee6-ba54-1d95a4f6cc68",
+                "max_prefixlen": "30",
+                "name": "my-subnet-pool-ipv4",
+                "default_quota": None,
+                "is_default": False,
+                "project_id": "9fadcee8aa7c40cdb2114fff7d569c08",
+                "tenant_id": "9fadcee8aa7c40cdb2114fff7d569c08",
+                "prefixes": [
+                    "10.10.0.0/21",
+                    "192.168.0.0/16"
+                ],
+                "ip_version": 4,
+                "shared": False,
+                "description": "",
+                "revision_number": 2
+            }
+        ]
+    }
+
+    FAKE_SUBNETPOOL_ID = "03f761e6-eee0-43fc-a921-8acf64c14988"
+
+    def setUp(self):
+        super(TestSubnetsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.subnetpools_client = subnetpools_client.SubnetpoolsClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_list_subnetpools(self, bytes_body=False):
+        self.check_service_client_function(
+            self.subnetpools_client.list_subnetpools,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SUBNETPOOLS,
+            bytes_body,
+            200)
+
+    def _test_create_subnetpool(self, bytes_body=False):
+        self.check_service_client_function(
+            self.subnetpools_client.create_subnetpool,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {'subnetpool': self.FAKE_SUBNETPOOLS['subnetpools'][1]},
+            bytes_body,
+            201,
+            name="my-subnet-pool-ipv4",
+            prefixes=["192.168.0.0/16", "10.10.0.0/21"])
+
+    def _test_show_subnetpool(self, bytes_body=False):
+        self.check_service_client_function(
+            self.subnetpools_client.show_subnetpool,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {'subnetpool': self.FAKE_SUBNETPOOLS['subnetpools'][0]},
+            bytes_body,
+            200,
+            subnetpool_id=self.FAKE_SUBNETPOOL_ID)
+
+    def _test_update_subnetpool(self, bytes_body=False):
+        update_kwargs = {
+            "name": "my-new-subnetpool-name",
+            "prefixes": [
+                "2001:db8::/64",
+                "2001:db8:0:1::/64",
+                "2001:db8:0:2::/64"
+            ],
+            "min_prefixlen": 64,
+            "default_prefixlen": 64,
+            "max_prefixlen": 64
+        }
+
+        resp_body = {
+            'subnetpool': copy.deepcopy(
+                self.FAKE_SUBNETPOOLS['subnetpools'][0])
+        }
+        resp_body['subnetpool'].update(update_kwargs)
+
+        self.check_service_client_function(
+            self.subnetpools_client.update_subnetpool,
+            'tempest.lib.common.rest_client.RestClient.put',
+            resp_body,
+            bytes_body,
+            200,
+            subnetpool_id=self.FAKE_SUBNETPOOL_ID,
+            **update_kwargs)
+
+    def test_list_subnetpools_with_str_body(self):
+        self._test_list_subnetpools()
+
+    def test_list_subnetpools_with_bytes_body(self):
+        self._test_list_subnetpools(bytes_body=True)
+
+    def test_create_subnetpool_with_str_body(self):
+        self._test_create_subnetpool()
+
+    def test_create_subnetpool_with_bytes_body(self):
+        self._test_create_subnetpool(bytes_body=True)
+
+    def test_show_subnetpool_with_str_body(self):
+        self._test_show_subnetpool()
+
+    def test_show_subnetpool_with_bytes_body(self):
+        self._test_show_subnetpool(bytes_body=True)
+
+    def test_update_subnet_with_str_body(self):
+        self._test_update_subnetpool()
+
+    def test_update_subnet_with_bytes_body(self):
+        self._test_update_subnetpool(bytes_body=True)
+
+    def test_delete_subnetpool(self):
+        self.check_service_client_function(
+            self.subnetpools_client.delete_subnetpool,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            subnetpool_id=self.FAKE_SUBNETPOOL_ID)
diff --git a/tempest/tests/lib/services/network/test_subnets_client.py b/tempest/tests/lib/services/network/test_subnets_client.py
new file mode 100644
index 0000000..0aadf54
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_subnets_client.py
@@ -0,0 +1,250 @@
+# Copyright 2017 AT&T Corporation.
+# All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.services.network import subnets_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSubnetsClient(base.BaseServiceTest):
+
+    FAKE_SUBNET = {
+        "subnet": {
+            "name": "",
+            "enable_dhcp": True,
+            "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+            "segment_id": None,
+            "project_id": "4fd44f30292945e481c7b8a0c8908869",
+            "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+            "dns_nameservers": [],
+            "allocation_pools": [
+                {
+                    "start": "192.168.199.2",
+                    "end": "192.168.199.254"
+                }
+            ],
+            "host_routes": [],
+            "ip_version": 4,
+            "gateway_ip": "192.168.199.1",
+            "cidr": "192.168.199.0/24",
+            "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126",
+            "created_at": "2016-10-10T14:35:47Z",
+            "description": "",
+            "ipv6_address_mode": None,
+            "ipv6_ra_mode": None,
+            "revision_number": 2,
+            "service_types": [],
+            "subnetpool_id": None,
+            "updated_at": "2016-10-10T14:35:47Z"
+        }
+    }
+
+    FAKE_UPDATED_SUBNET = {
+        "subnet": {
+            "name": "my_subnet",
+            "enable_dhcp": True,
+            "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
+            "segment_id": None,
+            "project_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
+            "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
+            "dns_nameservers": [],
+            "allocation_pools": [
+                {
+                    "start": "10.0.0.2",
+                    "end": "10.0.0.254"
+                }
+            ],
+            "host_routes": [],
+            "ip_version": 4,
+            "gateway_ip": "10.0.0.1",
+            "cidr": "10.0.0.0/24",
+            "id": "08eae331-0402-425a-923c-34f7cfe39c1b",
+            "description": ""
+        }
+    }
+
+    FAKE_SUBNETS = {
+        "subnets": [
+            {
+                "name": "private-subnet",
+                "enable_dhcp": True,
+                "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
+                "segment_id": None,
+                "project_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
+                "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
+                "dns_nameservers": [],
+                "allocation_pools": [
+                    {
+                        "start": "10.0.0.2",
+                        "end": "10.0.0.254"
+                    }
+                ],
+                "host_routes": [],
+                "ip_version": 4,
+                "gateway_ip": "10.0.0.1",
+                "cidr": "10.0.0.0/24",
+                "id": "08eae331-0402-425a-923c-34f7cfe39c1b",
+                "created_at": "2016-10-10T14:35:34Z",
+                "description": "",
+                "ipv6_address_mode": None,
+                "ipv6_ra_mode": None,
+                "revision_number": 2,
+                "service_types": [],
+                "subnetpool_id": None,
+                "updated_at": "2016-10-10T14:35:34Z"
+            },
+            {
+                "name": "my_subnet",
+                "enable_dhcp": True,
+                "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+                "segment_id": None,
+                "project_id": "4fd44f30292945e481c7b8a0c8908869",
+                "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+                "dns_nameservers": [],
+                "allocation_pools": [
+                    {
+                        "start": "192.0.0.2",
+                        "end": "192.255.255.254"
+                    }
+                ],
+                "host_routes": [],
+                "ip_version": 4,
+                "gateway_ip": "192.0.0.1",
+                "cidr": "192.0.0.0/8",
+                "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b",
+                "created_at": "2016-10-10T14:35:47Z",
+                "description": "",
+                "ipv6_address_mode": None,
+                "ipv6_ra_mode": None,
+                "revision_number": 2,
+                "service_types": [],
+                "subnetpool_id": None,
+                "updated_at": "2016-10-10T14:35:47Z"
+            }
+        ]
+    }
+
+    FAKE_BULK_SUBNETS = copy.deepcopy(FAKE_SUBNETS)
+
+    FAKE_SUBNET_ID = "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+
+    FAKE_NETWORK_ID = "d32019d3-bc6e-4319-9c1d-6722fc136a22"
+
+    def setUp(self):
+        super(TestSubnetsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.subnets_client = subnets_client.SubnetsClient(
+            fake_auth, 'compute', 'regionOne')
+
+    def _test_create_subnet(self, bytes_body=False):
+        self.check_service_client_function(
+            self.subnets_client.create_subnet,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_SUBNET,
+            bytes_body,
+            201,
+            network_id=self.FAKE_NETWORK_ID,
+            ip_version=4,
+            cidr="192.168.199.0/24")
+
+    def _test_update_subnet(self, bytes_body=False):
+        self.check_service_client_function(
+            self.subnets_client.update_subnet,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATED_SUBNET,
+            bytes_body,
+            200,
+            subnet_id=self.FAKE_SUBNET_ID,
+            name="fake_updated_subnet_name")
+
+    def _test_show_subnet(self, bytes_body=False):
+        self.check_service_client_function(
+            self.subnets_client.show_subnet,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SUBNET,
+            bytes_body,
+            200,
+            subnet_id=self.FAKE_SUBNET_ID)
+
+    def _test_list_subnets(self, bytes_body=False):
+        self.check_service_client_function(
+            self.subnets_client.list_subnets,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SUBNETS,
+            bytes_body,
+            200)
+
+    def _test_create_bulk_subnets(self, bytes_body=False):
+        kwargs = {
+            "subnets": [
+                {
+                    "cidr": "192.168.199.0/24",
+                    "ip_version": 4,
+                    "network_id": "e6031bc2-901a-4c66-82da-f4c32ed89406"
+                },
+                {
+                    "cidr": "10.56.4.0/22",
+                    "ip_version": 4,
+                    "network_id": "64239a54-dcc4-4b39-920b-b37c2144effa"
+                }
+            ]
+        }
+        self.check_service_client_function(
+            self.subnets_client.create_bulk_subnets,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_SUBNETS,
+            bytes_body,
+            201,
+            **kwargs)
+
+    def test_create_subnet_with_str_body(self):
+        self._test_create_subnet()
+
+    def test_create_subnet_with_bytes_body(self):
+        self._test_create_subnet(bytes_body=True)
+
+    def test_update_subnet_with_str_body(self):
+        self._test_update_subnet()
+
+    def test_update_subnet_with_bytes_body(self):
+        self._test_update_subnet(bytes_body=True)
+
+    def test_show_subnet_with_str_body(self):
+        self._test_show_subnet()
+
+    def test_show_subnet_with_bytes_body(self):
+        self._test_show_subnet(bytes_body=True)
+
+    def test_list_subnets_with_str_body(self):
+        self._test_list_subnets()
+
+    def test_list_subnets_with_bytes_body(self):
+        self._test_list_subnets(bytes_body=True)
+
+    def test_delete_subnet(self):
+        self.check_service_client_function(
+            self.subnets_client.delete_subnet,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            subnet_id=self.FAKE_SUBNET_ID)
+
+    def test_create_bulk_subnets_with_str_body(self):
+        self._test_create_bulk_subnets()
+
+    def test_create_bulk_subnets_with_bytes_body(self):
+        self._test_create_bulk_subnets(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_tags_client.py b/tempest/tests/lib/services/network/test_tags_client.py
new file mode 100644
index 0000000..dbe50a0
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_tags_client.py
@@ -0,0 +1,123 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+from tempest.lib.services.network import tags_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestTagsClient(base.BaseServiceTest):
+
+    FAKE_TAGS = {
+        "tags": [
+            "red",
+            "blue"
+        ]
+    }
+
+    FAKE_RESOURCE_TYPE = 'network'
+
+    FAKE_RESOURCE_ID = '7a8f904b-c1ed-4446-a87d-60440c02934b'
+
+    def setUp(self):
+        super(TestTagsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = tags_client.TagsClient(
+            fake_auth, 'network', 'regionOne')
+
+    def _test_update_all_tags(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_all_tags,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_TAGS,
+            bytes_body,
+            resource_type=self.FAKE_RESOURCE_TYPE,
+            resource_id=self.FAKE_RESOURCE_ID,
+            tags=self.FAKE_TAGS)
+
+    def _test_check_tag_existence(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.check_tag_existence,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {},
+            bytes_body,
+            resource_type=self.FAKE_RESOURCE_TYPE,
+            resource_id=self.FAKE_RESOURCE_ID,
+            tag=self.FAKE_TAGS['tags'][0],
+            status=204)
+
+    def _test_create_tag(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_tag,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            resource_type=self.FAKE_RESOURCE_TYPE,
+            resource_id=self.FAKE_RESOURCE_ID,
+            tag=self.FAKE_TAGS['tags'][0],
+            status=201)
+
+    def _test_list_tags(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_tags,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_TAGS,
+            bytes_body,
+            resource_type=self.FAKE_RESOURCE_TYPE,
+            resource_id=self.FAKE_RESOURCE_ID)
+
+    def test_update_all_tags_with_str_body(self):
+        self._test_update_all_tags()
+
+    def test_update_all_tags_with_bytes_body(self):
+        self._test_update_all_tags(bytes_body=True)
+
+    def test_delete_all_tags(self):
+        self.check_service_client_function(
+            self.client.delete_all_tags,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            resource_type=self.FAKE_RESOURCE_TYPE,
+            resource_id=self.FAKE_RESOURCE_ID,
+            status=204)
+
+    def test_check_tag_existence_with_str_body(self):
+        self._test_check_tag_existence()
+
+    def test_check_tag_existence_with_bytes_body(self):
+        self._test_check_tag_existence(bytes_body=True)
+
+    def test_create_tag_with_str_body(self):
+        self._test_create_tag()
+
+    def test_create_tag_with_bytes_body(self):
+        self._test_create_tag(bytes_body=True)
+
+    def test_list_tags_with_str_body(self):
+        self._test_list_tags()
+
+    def test_list_tags_with_bytes_body(self):
+        self._test_list_tags(bytes_body=True)
+
+    def test_delete_tag(self):
+        self.check_service_client_function(
+            self.client.delete_tag,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            resource_type=self.FAKE_RESOURCE_TYPE,
+            resource_id=self.FAKE_RESOURCE_ID,
+            tag=self.FAKE_TAGS['tags'][0],
+            status=204)
diff --git a/tempest/api/image/admin/__init__.py b/tempest/tests/lib/services/object_storage/__init__.py
similarity index 100%
copy from tempest/api/image/admin/__init__.py
copy to tempest/tests/lib/services/object_storage/__init__.py
diff --git a/tempest/tests/lib/services/object_storage/test_bulk_middleware_client.py b/tempest/tests/lib/services/object_storage/test_bulk_middleware_client.py
new file mode 100644
index 0000000..08028c3
--- /dev/null
+++ b/tempest/tests/lib/services/object_storage/test_bulk_middleware_client.py
@@ -0,0 +1,66 @@
+# Copyright 2017 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.object_storage import bulk_middleware_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestBulkMiddlewareClient(base.BaseServiceTest):
+
+    def setUp(self):
+        super(TestBulkMiddlewareClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = bulk_middleware_client.BulkMiddlewareClient(
+            fake_auth, 'object-storage', 'regionOne')
+
+    def test_upload_archive(self):
+        url = 'test_path?extract-archive=tar'
+        data = 'test_data'
+        self.check_service_client_function(
+            self.client.upload_archive,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            mock_args=[url, data, {}],
+            resp_as_string=True,
+            upload_path='test_path', data=data, archive_file_format='tar')
+
+    def test_delete_bulk_data(self):
+        url = '?bulk-delete'
+        data = 'test_data'
+        self.check_service_client_function(
+            self.client.delete_bulk_data,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            mock_args=[url, {}, data],
+            resp_as_string=True,
+            data=data)
+
+    def _test_delete_bulk_data_with_post(self, status):
+        url = '?bulk-delete'
+        data = 'test_data'
+        self.check_service_client_function(
+            self.client.delete_bulk_data_with_post,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            mock_args=[url, data, {}],
+            resp_as_string=True,
+            status=status,
+            data=data)
+
+    def test_delete_bulk_data_with_post_200(self):
+        self._test_delete_bulk_data_with_post(200)
+
+    def test_delete_bulk_data_with_post_204(self):
+        self._test_delete_bulk_data_with_post(204)
diff --git a/tempest/tests/lib/services/object_storage/test_capabilities_client.py b/tempest/tests/lib/services/object_storage/test_capabilities_client.py
new file mode 100644
index 0000000..b7f972a
--- /dev/null
+++ b/tempest/tests/lib/services/object_storage/test_capabilities_client.py
@@ -0,0 +1,54 @@
+# Copyright 2016 IBM Corp.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+from tempest.lib.services.object_storage import capabilities_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestCapabilitiesClient(base.BaseServiceTest):
+
+    def setUp(self):
+        super(TestCapabilitiesClient, self).setUp()
+        self.fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.url = self.fake_auth.base_url(None)
+        self.client = capabilities_client.CapabilitiesClient(
+            self.fake_auth, 'swift', 'region1')
+
+    def _test_list_capabilities(self, bytes_body=False):
+        resp = {
+            "swift": {
+                "version": "1.11.0"
+            },
+            "slo": {
+                "max_manifest_segments": 1000,
+                "max_manifest_size": 2097152,
+                "min_segment_size": 1
+            },
+            "staticweb": {},
+            "tempurl": {}
+        }
+        self.check_service_client_function(
+            self.client.list_capabilities,
+            'tempest.lib.common.rest_client.RestClient.get',
+            resp,
+            bytes_body)
+
+    def test_list_capabilities_with_str_body(self):
+        self._test_list_capabilities()
+
+    def test_list_capabilities_with_bytes_body(self):
+        self._test_list_capabilities(True)
diff --git a/tempest/tests/lib/services/test_clients.py b/tempest/tests/lib/services/test_clients.py
index 5db932c..6d0f27a 100644
--- a/tempest/tests/lib/services/test_clients.py
+++ b/tempest/tests/lib/services/test_clients.py
@@ -12,10 +12,12 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
+import types
+
 import fixtures
 import mock
+import six
 import testtools
-import types
 
 from tempest.lib import auth
 from tempest.lib import exceptions
@@ -99,9 +101,8 @@
     def test___init___no_module(self):
         auth_provider = fake_auth_provider.FakeAuthProvider()
         class_names = ['FakeServiceClient1', 'FakeServiceClient2']
-        with testtools.ExpectedException(ImportError, '.*fake_module.*'):
-            clients.ClientsFactory('fake_module', class_names,
-                                   auth_provider)
+        self.assertRaises(ImportError, clients.ClientsFactory,
+                          'fake_module', class_names, auth_provider)
 
     def test___init___not_a_class(self):
         class_names = ['FakeServiceClient1', 'FakeServiceClient2']
@@ -258,6 +259,58 @@
             clients.ServiceClients(creds, identity_uri=uri,
                                    client_parameters=params)
 
+    def test___init___plugin_service_clients_cannot_load(self):
+        creds = fake_credentials.FakeKeystoneV3Credentials()
+        uri = 'fake_uri'
+        fake_service_clients = {
+            'service1': [{'name': 'client1',
+                          'service_version': 'client1.v1',
+                          'module_path': 'I cannot load this',
+                          'client_names': ['SomeClient1']}],
+            'service2': [{'name': 'client2',
+                          'service_version': 'client2.v1',
+                          'module_path': 'This neither',
+                          'client_names': ['SomeClient1']}]}
+        msg = "(?=.*{0})(?=.*{1})".format(
+            *[x[1][0]['module_path'] for x in six.iteritems(
+                fake_service_clients)])
+        self.useFixture(fixtures.MockPatchObject(
+            clients.ClientsRegistry(), 'get_service_clients',
+            return_value=fake_service_clients))
+        with testtools.ExpectedException(
+                testtools.MultipleExceptions, value_re=msg):
+            clients.ServiceClients(creds, identity_uri=uri)
+
+    def test___init___plugin_service_clients_name_conflict(self):
+        creds = fake_credentials.FakeKeystoneV3Credentials()
+        uri = 'fake_uri'
+        fake_service_clients = {
+            'serviceA': [{'name': 'client1',
+                          'service_version': 'client1.v1',
+                          'module_path': 'fake_path_1',
+                          'client_names': ['SomeClient1']}],
+            'serviceB': [{'name': 'client1',
+                          'service_version': 'client1.v2',
+                          'module_path': 'fake_path_2',
+                          'client_names': ['SomeClient2']}],
+            'serviceC': [{'name': 'client1',
+                          'service_version': 'client1.v1',
+                          'module_path': 'fake_path_2',
+                          'client_names': ['SomeClient1']}],
+            'serviceD': [{'name': 'client1',
+                          'service_version': 'client1.v2',
+                          'module_path': 'fake_path_2',
+                          'client_names': ['SomeClient2']}]}
+        msg = "(?=.*{0})(?=.*{1})".format(
+            *[x[1][0]['service_version'] for x in six.iteritems(
+                fake_service_clients)])
+        self.useFixture(fixtures.MockPatchObject(
+            clients.ClientsRegistry(), 'get_service_clients',
+            return_value=fake_service_clients))
+        with testtools.ExpectedException(
+                testtools.MultipleExceptions, value_re=msg):
+            clients.ServiceClients(creds, identity_uri=uri)
+
     def _get_manager(self, init_region='fake_region'):
         # Get a manager to invoke _setup_parameters on
         creds = fake_credentials.FakeKeystoneV2Credentials()
diff --git a/tempest/tests/lib/services/volume/v2/test_availability_zone_client.py b/tempest/tests/lib/services/volume/v2/test_availability_zone_client.py
new file mode 100644
index 0000000..770565c
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_availability_zone_client.py
@@ -0,0 +1,51 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v2 import availability_zone_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestAvailabilityZoneClient(base.BaseServiceTest):
+
+    FAKE_AZ_LIST = {
+        "availabilityZoneInfo": [
+            {
+                "zoneState": {
+                    "available": True
+                },
+                "zoneName": "nova"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestAvailabilityZoneClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = availability_zone_client.AvailabilityZoneClient(
+            fake_auth, 'volume', 'regionOne')
+
+    def _test_list_availability_zones(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_availability_zones,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_AZ_LIST,
+            bytes_body)
+
+    def test_list_availability_zones_with_str_body(self):
+        self._test_list_availability_zones()
+
+    def test_list_availability_zones_with_bytes_body(self):
+        self._test_list_availability_zones(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_backups_client.py b/tempest/tests/lib/services/volume/v2/test_backups_client.py
new file mode 100644
index 0000000..14e5fb0
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_backups_client.py
@@ -0,0 +1,117 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v2 import backups_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestBackupsClient(base.BaseServiceTest):
+
+    FAKE_BACKUP_LIST = {
+        "backups": [
+            {
+                "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
+                "links": [
+                    {
+                        "href": "fake-url-1",
+                        "rel": "self"
+                    },
+                    {
+                        "href": "fake-url-2",
+                        "rel": "bookmark"
+                    }
+                ],
+                "name": "backup001"
+            }
+        ]
+    }
+
+    FAKE_BACKUP_LIST_WITH_DETAIL = {
+        "backups": [
+            {
+                "availability_zone": "az1",
+                "container": "volumebackups",
+                "created_at": "2013-04-02T10:35:27.000000",
+                "description": None,
+                "fail_reason": None,
+                "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
+                "links": [
+                    {
+                        "href": "fake-url-1",
+                        "rel": "self"
+                    },
+                    {
+                        "href": "fake-url-2",
+                        "rel": "bookmark"
+                    }
+                ],
+                "name": "backup001",
+                "object_count": 22,
+                "size": 1,
+                "status": "available",
+                "volume_id": "e5185058-943a-4cb4-96d9-72c184c337d6",
+                "is_incremental": True,
+                "has_dependent_backups": False
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestBackupsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = backups_client.BackupsClient(fake_auth,
+                                                   'volume',
+                                                   'regionOne')
+
+    def _test_list_backups(self, detail=False, mock_args='backups',
+                           bytes_body=False, **params):
+        if detail:
+            resp_body = self.FAKE_BACKUP_LIST_WITH_DETAIL
+        else:
+            resp_body = self.FAKE_BACKUP_LIST
+        self.check_service_client_function(
+            self.client.list_backups,
+            'tempest.lib.common.rest_client.RestClient.get',
+            resp_body,
+            to_utf=bytes_body,
+            mock_args=[mock_args],
+            detail=detail,
+            **params)
+
+    def test_list_backups_with_str_body(self):
+        self._test_list_backups()
+
+    def test_list_backups_with_bytes_body(self):
+        self._test_list_backups(bytes_body=True)
+
+    def test_list_backups_with_detail_with_str_body(self):
+        mock_args = "backups/detail"
+        self._test_list_backups(detail=True, mock_args=mock_args)
+
+    def test_list_backups_with_detail_with_bytes_body(self):
+        mock_args = "backups/detail"
+        self._test_list_backups(detail=True, mock_args=mock_args,
+                                bytes_body=True)
+
+    def test_list_backups_with_params(self):
+        # Run the test separately for each param, to avoid assertion error
+        # resulting from randomized params order.
+        mock_args = 'backups?sort_key=name'
+        self._test_list_backups(mock_args=mock_args, sort_key='name')
+
+        mock_args = 'backups/detail?limit=10'
+        self._test_list_backups(detail=True, mock_args=mock_args,
+                                bytes_body=True, limit=10)
diff --git a/tempest/tests/lib/services/volume/v2/test_capabilities_client.py b/tempest/tests/lib/services/volume/v2/test_capabilities_client.py
new file mode 100644
index 0000000..3d3f1e1
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_capabilities_client.py
@@ -0,0 +1,77 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v2 import capabilities_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestCapabilitiesClient(base.BaseServiceTest):
+
+    FAKE_BACKEND_CAPABILITIES = {
+        "namespace": "OS::Storage::Capabilities::fake",
+        "vendor_name": "OpenStack",
+        "volume_backend_name": "lvmdriver-1",
+        "pool_name": "pool",
+        "driver_version": "2.0.0",
+        "storage_protocol": "iSCSI",
+        "display_name": "Capabilities of Cinder LVM driver",
+        "description": (
+            "These are volume type options provided by Cinder LVM driver."),
+        "visibility": "public",
+        "replication_targets": [],
+        "properties": {
+            "compression": {
+                "title": "Compression",
+                "description": "Enables compression.",
+                "type": "boolean"
+            },
+            "qos": {
+                "title": "QoS",
+                "description": "Enables QoS.",
+                "type": "boolean"
+            },
+            "replication": {
+                "title": "Replication",
+                "description": "Enables replication.",
+                "type": "boolean"
+            },
+            "thin_provisioning": {
+                "title": "Thin Provisioning",
+                "description": "Sets thin provisioning.",
+                "type": "boolean"
+            }
+        }
+    }
+
+    def setUp(self):
+        super(TestCapabilitiesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = capabilities_client.CapabilitiesClient(
+            fake_auth, 'volume', 'regionOne')
+
+    def _test_show_backend_capabilities(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_backend_capabilities,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_BACKEND_CAPABILITIES,
+            bytes_body,
+            host='lvmdriver-1')
+
+    def test_show_backend_capabilities_with_str_body(self):
+        self._test_show_backend_capabilities()
+
+    def test_show_backend_capabilities_with_bytes_body(self):
+        self._test_show_backend_capabilities(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_extensions_client.py b/tempest/tests/lib/services/volume/v2/test_extensions_client.py
new file mode 100644
index 0000000..c0ee421
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_extensions_client.py
@@ -0,0 +1,70 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v2 import extensions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestExtensionsClient(base.BaseServiceTest):
+
+    FAKE_EXTENSION_LIST = {
+        "extensions": [
+            {
+                "updated": "2012-03-12T00:00:00+00:00",
+                "name": "QuotaClasses",
+                "links": [],
+                "namespace": "fake-namespace-1",
+                "alias": "os-quota-class-sets",
+                "description": "Quota classes management support."
+            },
+            {
+                "updated": "2013-05-29T00:00:00+00:00",
+                "name": "VolumeTransfer",
+                "links": [],
+                "namespace": "fake-namespace-2",
+                "alias": "os-volume-transfer",
+                "description": "Volume transfer management support."
+            },
+            {
+                "updated": "2014-02-10T00:00:00+00:00",
+                "name": "VolumeManage",
+                "links": [],
+                "namespace": "fake-namespace-3",
+                "alias": "os-volume-manage",
+                "description": "Manage existing backend storage by Cinder."
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestExtensionsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = extensions_client.ExtensionsClient(fake_auth,
+                                                         'volume',
+                                                         'regionOne')
+
+    def _test_list_extensions(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_extensions,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_EXTENSION_LIST,
+            bytes_body)
+
+    def test_list_extensions_with_str_body(self):
+        self._test_list_extensions()
+
+    def test_list_extensions_with_bytes_body(self):
+        self._test_list_extensions(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_hosts_client.py b/tempest/tests/lib/services/volume/v2/test_hosts_client.py
new file mode 100644
index 0000000..e107910
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_hosts_client.py
@@ -0,0 +1,97 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v2 import hosts_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQuotasClient(base.BaseServiceTest):
+    FAKE_LIST_HOSTS = {
+        "hosts": [
+            {
+                "service-status": "available",
+                "service": "cinder-scheduler",
+                "zone": "nova",
+                "service-state": "enabled",
+                "host_name": "fake-host",
+                "last-update": "2017-04-12T04:26:03.000000"
+            },
+            {
+                "service-status": "available",
+                "service": "cinder-volume",
+                "zone": "nova",
+                "service-state": "enabled",
+                "host_name": "fake-host@rbd",
+                "last-update": "2017-04-12T04:26:07.000000"
+            }
+        ]
+    }
+
+    FAKE_HOST_INFO = {
+        "host": [
+            {
+                "resource": {
+                    "volume_count": "2",
+                    "total_volume_gb": "2",
+                    "total_snapshot_gb": "0",
+                    "project": "(total)",
+                    "host": "fake-host",
+                    "snapshot_count": "0"
+                }
+            },
+            {
+                "resource": {
+                    "volume_count": "2",
+                    "total_volume_gb": "2",
+                    "total_snapshot_gb": "0",
+                    "project": "f21a9c86d7114bf99c711f4874d80474",
+                    "host": "fake-host",
+                    "snapshot_count": "0"
+                }
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestQuotasClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = hosts_client.HostsClient(fake_auth,
+                                               'volume',
+                                               'regionOne')
+
+    def _test_list_hosts(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_hosts,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_HOSTS, bytes_body)
+
+    def _test_show_host(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_host,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_HOST_INFO, bytes_body, host_name='fake-host')
+
+    def test_list_hosts_with_str_body(self):
+        self._test_list_hosts()
+
+    def test_list_hosts_with_bytes_body(self):
+        self._test_list_hosts(bytes_body=True)
+
+    def test_show_host_with_str_body(self):
+        self._test_show_host()
+
+    def test_show_host_with_bytes_body(self):
+        self._test_show_host(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_limits_client.py b/tempest/tests/lib/services/volume/v2/test_limits_client.py
new file mode 100644
index 0000000..202054c
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_limits_client.py
@@ -0,0 +1,59 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v2 import limits_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestLimitsClient(base.BaseServiceTest):
+
+    FAKE_LIMIT_INFO = {
+        "limits": {
+            "rate": [],
+            "absolute": {
+                "totalSnapshotsUsed": 0,
+                "maxTotalBackups": 10,
+                "maxTotalVolumeGigabytes": 1000,
+                "maxTotalSnapshots": 10,
+                "maxTotalBackupGigabytes": 1000,
+                "totalBackupGigabytesUsed": 0,
+                "maxTotalVolumes": 10,
+                "totalVolumesUsed": 0,
+                "totalBackupsUsed": 0,
+                "totalGigabytesUsed": 0
+            }
+        }
+    }
+
+    def setUp(self):
+        super(TestLimitsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = limits_client.LimitsClient(fake_auth,
+                                                 'volume',
+                                                 'regionOne')
+
+    def _test_show_limits(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_limits,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIMIT_INFO,
+            bytes_body)
+
+    def test_show_limits_with_str_body(self):
+        self._test_show_limits()
+
+    def test_show_limits_with_bytes_body(self):
+        self._test_show_limits(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_quota_classes_client.py b/tempest/tests/lib/services/volume/v2/test_quota_classes_client.py
new file mode 100644
index 0000000..e715fcc
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_quota_classes_client.py
@@ -0,0 +1,71 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    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.volume.v2 import quota_classes_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQuotaClassesClient(base.BaseServiceTest):
+
+    FAKE_QUOTA_CLASS_SET = {
+        "id": "test",
+        "gigabytes": 2000,
+        "volumes": 200,
+        "snapshots": 50,
+        "backups": 20,
+        "backup_gigabytes": 1500,
+        "per_volume_gigabytes": 500,
+    }
+
+    def setUp(self):
+        super(TestQuotaClassesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = quota_classes_client.QuotaClassesClient(
+            fake_auth, 'volume', 'regionOne')
+
+    def _test_show_quota_class_set(self, bytes_body=False):
+        fake_body = {'quota_class_set': self.FAKE_QUOTA_CLASS_SET}
+        self.check_service_client_function(
+            self.client.show_quota_class_set,
+            'tempest.lib.common.rest_client.RestClient.get',
+            fake_body,
+            bytes_body,
+            quota_class_id="test")
+
+    def _test_update_quota_class_set(self, bytes_body=False):
+        fake_quota_class_set = copy.deepcopy(self.FAKE_QUOTA_CLASS_SET)
+        fake_quota_class_set.pop("id")
+        fake_body = {'quota_class_set': fake_quota_class_set}
+        self.check_service_client_function(
+            self.client.update_quota_class_set,
+            'tempest.lib.common.rest_client.RestClient.put',
+            fake_body,
+            bytes_body,
+            quota_class_id="test")
+
+    def test_show_quota_class_set_with_str_body(self):
+        self._test_show_quota_class_set()
+
+    def test_show_quota_class_set_with_bytes_body(self):
+        self._test_show_quota_class_set(bytes_body=True)
+
+    def test_update_quota_class_set_with_str_boy(self):
+        self._test_update_quota_class_set()
+
+    def test_update_quota_class_set_with_bytes_body(self):
+        self._test_update_quota_class_set(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py b/tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py
new file mode 100644
index 0000000..8a5f25f
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py
@@ -0,0 +1,83 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v2 import scheduler_stats_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSchedulerStatsClient(base.BaseServiceTest):
+    FAKE_POOLS_LIST = {
+        "pools": [
+            {
+                "name": "pool1",
+                "capabilities": {
+                    "updated": "2014-10-28T00:00:00-00:00",
+                    "total_capacity": 1024,
+                    "free_capacity": 100,
+                    "volume_backend_name": "pool1",
+                    "reserved_percentage": 0,
+                    "driver_version": "1.0.0",
+                    "storage_protocol": "iSCSI",
+                    "QoS_support": False
+                }
+            },
+            {
+                "name": "pool2",
+                "capabilities": {
+                    "updated": "2014-10-28T00:00:00-00:00",
+                    "total_capacity": 512,
+                    "free_capacity": 200,
+                    "volume_backend_name": "pool2",
+                    "reserved_percentage": 0,
+                    "driver_version": "1.0.2",
+                    "storage_protocol": "iSER",
+                    "QoS_support": True
+                }
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestSchedulerStatsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = scheduler_stats_client.SchedulerStatsClient(
+            fake_auth, 'volume', 'regionOne')
+
+    def _test_list_pools(self, bytes_body=False, detail=False):
+        resp_body = []
+        if detail:
+            resp_body = self.FAKE_POOLS_LIST
+        else:
+            resp_body = {'pools': [{'name': pool['name']}
+                         for pool in self.FAKE_POOLS_LIST['pools']]}
+        self.check_service_client_function(
+            self.client.list_pools,
+            'tempest.lib.common.rest_client.RestClient.get',
+            resp_body,
+            bytes_body,
+            detail=detail)
+
+    def test_list_pools_with_str_body(self):
+        self._test_list_pools()
+
+    def test_list_pools_with_str_body_and_detail(self):
+        self._test_list_pools(detail=True)
+
+    def test_list_pools_with_bytes_body(self):
+        self._test_list_pools(bytes_body=True)
+
+    def test_list_pools_with_bytes_body_and_detail(self):
+        self._test_list_pools(bytes_body=True, detail=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py b/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
new file mode 100644
index 0000000..3fe8970
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
@@ -0,0 +1,83 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import mock
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.services.volume.v2 import snapshot_manage_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSnapshotManageClient(base.BaseServiceTest):
+
+    SNAPSHOT_MANAGE_REQUEST = {
+        "snapshot": {
+            "description": "snapshot-manage-description",
+            "metadata": None,
+            "ref": {
+                "source-name": "_snapshot-22b71da0-94f9-4aca-ad45-7522b3fa96bb"
+            },
+            "name": "snapshot-managed",
+            "volume_id": "7c064b34-1e4b-40bd-93ca-4ac5a973661b"
+        }
+    }
+
+    SNAPSHOT_MANAGE_RESPONSE = {
+        "snapshot": {
+            "status": "creating",
+            "description": "snapshot-manage-description",
+            "updated_at": None,
+            "volume_id": "32bafcc8-7109-42cd-8342-70d8de2bedef",
+            "id": "8fd6eb9d-0a82-456d-b1ec-dea4ac7f1ee2",
+            "size": 1,
+            "name": "snapshot-managed",
+            "created_at": "2017-07-11T10:07:58.000000",
+            "metadata": {}
+        }
+    }
+
+    def setUp(self):
+        super(TestSnapshotManageClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = snapshot_manage_client.SnapshotManageClient(fake_auth,
+                                                                  'volume',
+                                                                  'regionOne')
+
+    def _test_manage_snapshot(self, bytes_body=False):
+        payload = json.dumps(self.SNAPSHOT_MANAGE_REQUEST, sort_keys=True)
+        json_dumps = json.dumps
+
+        # NOTE: Use sort_keys for json.dumps so that the expected and actual
+        # payloads are guaranteed to be identical for mock_args assert check.
+        with mock.patch.object(snapshot_manage_client.json,
+                               'dumps') as mock_dumps:
+            mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+            self.check_service_client_function(
+                self.client.manage_snapshot,
+                'tempest.lib.common.rest_client.RestClient.post',
+                self.SNAPSHOT_MANAGE_RESPONSE,
+                to_utf=bytes_body,
+                status=202,
+                mock_args=['os-snapshot-manage', payload],
+                **self.SNAPSHOT_MANAGE_REQUEST['snapshot'])
+
+    def test_manage_snapshot_with_str_body(self):
+        self._test_manage_snapshot()
+
+    def test_manage_snapshot_with_bytes_body(self):
+        self._test_manage_snapshot(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshots_client.py b/tempest/tests/lib/services/volume/v2/test_snapshots_client.py
index 7d656f1..c9f57a0 100644
--- a/tempest/tests/lib/services/volume/v2/test_snapshots_client.py
+++ b/tempest/tests/lib/services/volume/v2/test_snapshots_client.py
@@ -72,6 +72,12 @@
         ]
     }
 
+    FAKE_SNAPSHOT_METADATA_ITEM = {
+        "meta": {
+            "key1": "value1"
+        }
+    }
+
     def setUp(self):
         super(TestSnapshotsClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -142,6 +148,15 @@
             self.FAKE_INFO_SNAPSHOT,
             bytes_body, volume_type_id="cbc36478b0bd8e67e89")
 
+    def _test_show_snapshot_metadata_item(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_snapshot_metadata_item,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SNAPSHOT_METADATA_ITEM,
+            bytes_body,
+            snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+            id="key1")
+
     def test_create_snapshot_with_str_body(self):
         self._test_create_snapshot()
 
@@ -184,6 +199,12 @@
     def test_update_snapshot_metadata_with_bytes_body(self):
         self._test_update_snapshot_metadata(bytes_body=True)
 
+    def test_show_snapshot_metadata_item_with_str_body(self):
+        self._test_show_snapshot_metadata_item()
+
+    def test_show_snapshot_metadata_item_with_bytes_body(self):
+        self._test_show_snapshot_metadata_item(bytes_body=True)
+
     def test_force_delete_snapshot(self):
         self.check_service_client_function(
             self.client.force_delete_snapshot,
diff --git a/tempest/tests/lib/services/volume/v2/test_transfers_client.py b/tempest/tests/lib/services/volume/v2/test_transfers_client.py
new file mode 100644
index 0000000..84f4992
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_transfers_client.py
@@ -0,0 +1,158 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    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 mock
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.services.volume.v2 import transfers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestTransfersClient(base.BaseServiceTest):
+
+    FAKE_VOLUME_TRANSFER_ID = "0e89cdd1-6249-421b-96d8-25fac0623d42"
+
+    FAKE_VOLUME_TRANSFER_INFO = {
+        "transfer": {
+            "id": FAKE_VOLUME_TRANSFER_ID,
+            "name": "fake-volume-transfer",
+            "volume_id": "47bf04ef-1ea5-4c5f-a375-430a086d6747",
+            "created_at": "2017-04-18T09:10:03.000000",
+            "links": [
+                {
+                    "href": "fake-url-1",
+                    "rel": "self"
+                },
+                {
+                    "href": "fake-url-2",
+                    "rel": "bookmark"
+                }
+            ]
+        }
+    }
+
+    def setUp(self):
+        super(TestTransfersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = transfers_client.TransfersClient(fake_auth,
+                                                       'volume',
+                                                       'regionOne')
+
+    def _test_create_volume_transfer(self, bytes_body=False):
+        resp_body = copy.deepcopy(self.FAKE_VOLUME_TRANSFER_INFO)
+        resp_body['transfer'].update({"auth_key": "fake-auth-key"})
+        kwargs = {"name": "fake-volume-transfer",
+                  "volume_id": "47bf04ef-1ea5-4c5f-a375-430a086d6747"}
+        payload = json.dumps({"transfer": kwargs}, sort_keys=True)
+        json_dumps = json.dumps
+
+        # NOTE: Use sort_keys for json.dumps so that the expected and actual
+        # payloads are guaranteed to be identical for mock_args assert check.
+        with mock.patch.object(transfers_client.json, 'dumps') as mock_dumps:
+            mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+            self.check_service_client_function(
+                self.client.create_volume_transfer,
+                'tempest.lib.common.rest_client.RestClient.post',
+                resp_body,
+                to_utf=bytes_body,
+                status=202,
+                mock_args=['os-volume-transfer', payload],
+                **kwargs)
+
+    def _test_accept_volume_transfer(self, bytes_body=False):
+        resp_body = copy.deepcopy(self.FAKE_VOLUME_TRANSFER_INFO)
+        resp_body['transfer'].pop('created_at')
+        kwargs = {"auth_key": "fake-auth-key"}
+        payload = json.dumps({"accept": kwargs}, sort_keys=True)
+        json_dumps = json.dumps
+
+        # NOTE: Use sort_keys for json.dumps so that the expected and actual
+        # payloads are guaranteed to be identical for mock_args assert check.
+        with mock.patch.object(transfers_client.json, 'dumps') as mock_dumps:
+            mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+            self.check_service_client_function(
+                self.client.accept_volume_transfer,
+                'tempest.lib.common.rest_client.RestClient.post',
+                resp_body,
+                to_utf=bytes_body,
+                status=202,
+                mock_args=['os-volume-transfer/%s/accept' %
+                           self.FAKE_VOLUME_TRANSFER_ID, payload],
+                transfer_id=self.FAKE_VOLUME_TRANSFER_ID,
+                **kwargs)
+
+    def _test_show_volume_transfer(self, bytes_body=False):
+        resp_body = self.FAKE_VOLUME_TRANSFER_INFO
+        self.check_service_client_function(
+            self.client.show_volume_transfer,
+            'tempest.lib.common.rest_client.RestClient.get',
+            resp_body,
+            to_utf=bytes_body,
+            transfer_id="0e89cdd1-6249-421b-96d8-25fac0623d42")
+
+    def _test_list_volume_transfers(self, detail=False, bytes_body=False):
+        resp_body = copy.deepcopy(self.FAKE_VOLUME_TRANSFER_INFO)
+        if not detail:
+            resp_body['transfer'].pop('created_at')
+        resp_body = {"transfers": [resp_body['transfer']]}
+        self.check_service_client_function(
+            self.client.list_volume_transfers,
+            'tempest.lib.common.rest_client.RestClient.get',
+            resp_body,
+            to_utf=bytes_body,
+            detail=detail)
+
+    def test_create_volume_transfer_with_str_body(self):
+        self._test_create_volume_transfer()
+
+    def test_create_volume_transfer_with_bytes_body(self):
+        self._test_create_volume_transfer(bytes_body=True)
+
+    def test_accept_volume_transfer_with_str_body(self):
+        self._test_accept_volume_transfer()
+
+    def test_accept_volume_transfer_with_bytes_body(self):
+        self._test_accept_volume_transfer(bytes_body=True)
+
+    def test_show_volume_transfer_with_str_body(self):
+        self._test_show_volume_transfer()
+
+    def test_show_volume_transfer_with_bytes_body(self):
+        self._test_show_volume_transfer(bytes_body=True)
+
+    def test_list_volume_transfers_with_str_body(self):
+        self._test_list_volume_transfers()
+
+    def test_list_volume_transfers_with_bytes_body(self):
+        self._test_list_volume_transfers(bytes_body=True)
+
+    def test_list_volume_transfers_with_detail_with_str_body(self):
+        self._test_list_volume_transfers(detail=True)
+
+    def test_list_volume_transfers_with_detail_with_bytes_body(self):
+        self._test_list_volume_transfers(detail=True, bytes_body=True)
+
+    def test_delete_volume_transfer(self):
+        self.check_service_client_function(
+            self.client.delete_volume_transfer,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=202,
+            transfer_id="0e89cdd1-6249-421b-96d8-25fac0623d42")
diff --git a/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py b/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
new file mode 100644
index 0000000..ea4a9f9
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
@@ -0,0 +1,111 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import mock
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.services.volume.v2 import volume_manage_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVolumeManageClient(base.BaseServiceTest):
+
+    VOLUME_MANAGE_REQUEST = {
+        "volume": {
+            "host": "controller1@rbd#rbd",
+            "name": "volume-managed",
+            "availability_zone": "nova",
+            "bootable": False,
+            "metadata": None,
+            "ref": {
+                "source-name": "volume-2ce6ca46-e6c1-4fe5-8268-3a1c536fcbf3"
+            },
+            "volume_type": None,
+            "description": "volume-manage-description"
+        }
+    }
+
+    VOLUME_MANAGE_RESPONSE = {
+        "volume": {
+            "migration_status": None,
+            "attachments": [],
+            "links": [
+                {
+                    "href": "fake-url-1",
+                    "rel": "self"
+                },
+                {
+                    "href": "fake-url-2",
+                    "rel": "bookmark"
+                }
+            ],
+            "availability_zone": "nova",
+            "os-vol-host-attr:host": "controller1@rbd#rbd",
+            "encrypted": False,
+            "updated_at": None,
+            "replication_status": None,
+            "snapshot_id": None,
+            "id": "c07cd4a4-b52b-4511-a176-fbaa2011a227",
+            "size": 0,
+            "user_id": "142d8663efce464c89811c63e45bd82e",
+            "os-vol-tenant-attr:tenant_id": "f21a9c86d7114bf99c711f4874d80474",
+            "os-vol-mig-status-attr:migstat": None,
+            "metadata": {},
+            "status": "creating",
+            "description": "volume-manage-description",
+            "multiattach": False,
+            "source_volid": None,
+            "consistencygroup_id": None,
+            "os-vol-mig-status-attr:name_id": None,
+            "name": "volume-managed",
+            "bootable": "false",
+            "created_at": "2017-07-11T09:14:01.000000",
+            "volume_type": None
+        }
+    }
+
+    def setUp(self):
+        super(TestVolumeManageClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = volume_manage_client.VolumeManageClient(fake_auth,
+                                                              'volume',
+                                                              'regionOne')
+
+    def _test_manage_volume(self, bytes_body=False):
+        payload = json.dumps(self.VOLUME_MANAGE_REQUEST, sort_keys=True)
+        json_dumps = json.dumps
+
+        # NOTE: Use sort_keys for json.dumps so that the expected and actual
+        # payloads are guaranteed to be identical for mock_args assert check.
+        with mock.patch.object(volume_manage_client.json,
+                               'dumps') as mock_dumps:
+            mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+            self.check_service_client_function(
+                self.client.manage_volume,
+                'tempest.lib.common.rest_client.RestClient.post',
+                self.VOLUME_MANAGE_RESPONSE,
+                to_utf=bytes_body,
+                status=202,
+                mock_args=['os-volume-manage', payload],
+                **self.VOLUME_MANAGE_REQUEST['volume'])
+
+    def test_manage_volume_with_str_body(self):
+        self._test_manage_volume()
+
+    def test_manage_volume_with_bytes_body(self):
+        self._test_manage_volume(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_volumes_client.py b/tempest/tests/lib/services/volume/v2/test_volumes_client.py
new file mode 100644
index 0000000..d7b042e
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_volumes_client.py
@@ -0,0 +1,127 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES 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.services.volume.v2 import volumes_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVolumesClient(base.BaseServiceTest):
+
+    FAKE_VOLUME_METADATA_ITEM = {
+        "meta": {
+            "key1": "value1"
+        }
+    }
+
+    FAKE_VOLUME_IMAGE_METADATA = {
+        "metadata": {
+            "container_format": "bare",
+            "min_ram": "0",
+            "disk_format": "raw",
+            "image_name": "xly-ubuntu16-server",
+            "image_id": "3e087b0c-10c5-4255-b147-6e8e9dbad6fc",
+            "checksum": "008f5d22fe3cb825d714da79607a90f9",
+            "min_disk": "0",
+            "size": "8589934592"
+        }
+    }
+
+    def setUp(self):
+        super(TestVolumesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = volumes_client.VolumesClient(fake_auth,
+                                                   'volume',
+                                                   'regionOne')
+
+    def _test_retype_volume(self, bytes_body=False):
+        kwargs = {
+            "new_type": "dedup-tier-replication",
+            "migration_policy": "never"
+        }
+
+        self.check_service_client_function(
+            self.client.retype_volume,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            to_utf=bytes_body,
+            status=202,
+            volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
+            **kwargs
+        )
+
+    def _test_force_detach_volume(self, bytes_body=False):
+        kwargs = {
+            'attachment_id': '6980e295-920f-412e-b189-05c50d605acd',
+            'connector': {
+                'initiator': 'iqn.2017-04.org.fake:01'
+            }
+        }
+
+        self.check_service_client_function(
+            self.client.force_detach_volume,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            to_utf=bytes_body,
+            status=202,
+            volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
+            **kwargs
+        )
+
+    def _test_show_volume_metadata_item(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_volume_metadata_item,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_VOLUME_METADATA_ITEM,
+            to_utf=bytes_body,
+            volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
+            id="key1")
+
+    def _test_show_volume_image_metadata(self, bytes_body=False):
+        fake_volume_id = "a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8"
+        self.check_service_client_function(
+            self.client.show_volume_image_metadata,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_VOLUME_IMAGE_METADATA,
+            to_utf=bytes_body,
+            mock_args=['volumes/%s/action' % fake_volume_id,
+                       json.dumps({"os-show_image_metadata": {}})],
+            volume_id=fake_volume_id)
+
+    def test_force_detach_volume_with_str_body(self):
+        self._test_force_detach_volume()
+
+    def test_force_detach_volume_with_bytes_body(self):
+        self._test_force_detach_volume(bytes_body=True)
+
+    def test_show_volume_metadata_item_with_str_body(self):
+        self._test_show_volume_metadata_item()
+
+    def test_show_volume_metadata_item_with_bytes_body(self):
+        self._test_show_volume_metadata_item(bytes_body=True)
+
+    def test_show_volume_image_metadata_with_str_body(self):
+        self._test_show_volume_image_metadata()
+
+    def test_show_volume_image_metadata_with_bytes_body(self):
+        self._test_show_volume_image_metadata(bytes_body=True)
+
+    def test_retype_volume_with_str_body(self):
+        self._test_retype_volume()
+
+    def test_retype_volume_with_bytes_body(self):
+        self._test_retype_volume(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v3/test_backups_client.py b/tempest/tests/lib/services/volume/v3/test_backups_client.py
new file mode 100644
index 0000000..f1ce987
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_backups_client.py
@@ -0,0 +1,50 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v3 import backups_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestBackupsClient(base.BaseServiceTest):
+
+    FAKE_BACKUP_UPDATE = {
+        "backup": {
+            "id": "4c65c15f-a5c5-464b-b92a-90e4c04636a7",
+            "name": "fake-backup-name",
+            "links": "fake-links"
+        }
+    }
+
+    def setUp(self):
+        super(TestBackupsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = backups_client.BackupsClient(fake_auth,
+                                                   'volume',
+                                                   'regionOne')
+
+    def _test_update_backup(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_backup,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_BACKUP_UPDATE,
+            bytes_body,
+            backup_id='4c65c15f-a5c5-464b-b92a-90e4c04636a7')
+
+    def test_update_backup_with_str_body(self):
+        self._test_update_backup()
+
+    def test_update_backup_with_bytes_body(self):
+        self._test_update_backup(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v3/test_group_snapshots_client.py b/tempest/tests/lib/services/volume/v3/test_group_snapshots_client.py
new file mode 100644
index 0000000..5ac5c08
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_group_snapshots_client.py
@@ -0,0 +1,141 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.volume.v3 import group_snapshots_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestGroupSnapshotsClient(base.BaseServiceTest):
+    FAKE_CREATE_GROUP_SNAPSHOT = {
+        "group_snapshot": {
+            "group_id": "49c8c114-0d68-4e89-b8bc-3f5a674d54be",
+            "name": "group-snapshot-001",
+            "description": "Test group snapshot 1"
+        }
+    }
+
+    FAKE_INFO_GROUP_SNAPSHOT = {
+        "group_snapshot": {
+            "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
+            "group_id": "49c8c114-0d68-4e89-b8bc-3f5a674d54be",
+            "name": "group-snapshot-001",
+            "description": "Test group snapshot 1",
+            "group_type_id": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+            "status": "available",
+            "created_at": "20127-06-20T03:50:07Z"
+        }
+    }
+
+    FAKE_LIST_GROUP_SNAPSHOTS = {
+        "group_snapshots": [
+            {
+                "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
+                "group_id": "49c8c114-0d68-4e89-b8bc-3f5a674d54be",
+                "name": "group-snapshot-001",
+                "description": "Test group snapshot 1",
+                "group_type_id": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+                "status": "available",
+                "created_at": "2017-06-20T03:50:07Z",
+            },
+            {
+                "id": "e479997c-650b-40a4-9dfe-77655818b0d2",
+                "group_id": "49c8c114-0d68-4e89-b8bc-3f5a674d54be",
+                "name": "group-snapshot-002",
+                "description": "Test group snapshot 2",
+                "group_type_id": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+                "status": "available",
+                "created_at": "2017-06-19T01:52:47Z",
+            },
+            {
+                "id": "c5c4769e-213c-40a6-a568-8e797bb691d4",
+                "group_id": "49c8c114-0d68-4e89-b8bc-3f5a674d54be",
+                "name": "group-snapshot-003",
+                "description": "Test group snapshot 3",
+                "group_type_id": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+                "status": "available",
+                "created_at": "2017-06-18T06:34:32Z",
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestGroupSnapshotsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = group_snapshots_client.GroupSnapshotsClient(
+            fake_auth, 'volume', 'regionOne')
+
+    def _test_create_group_snapshot(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_group_snapshot,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_GROUP_SNAPSHOT,
+            bytes_body,
+            group_id="49c8c114-0d68-4e89-b8bc-3f5a674d54be",
+            status=202)
+
+    def _test_show_group_snapshot(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_group_snapshot,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_INFO_GROUP_SNAPSHOT,
+            bytes_body,
+            group_snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+    def _test_list_group_snapshots(self, bytes_body=False, detail=False):
+        resp_body = []
+        if detail:
+            resp_body = self.FAKE_LIST_GROUP_SNAPSHOTS
+        else:
+            resp_body = {
+                'group_snapshots': [{
+                    'id': group_snapshot['id'],
+                    'name': group_snapshot['name'],
+                    'group_type_id': group_snapshot['group_type_id']}
+                    for group_snapshot in
+                    self.FAKE_LIST_GROUP_SNAPSHOTS['group_snapshots']
+                ]
+            }
+        self.check_service_client_function(
+            self.client.list_group_snapshots,
+            'tempest.lib.common.rest_client.RestClient.get',
+            resp_body,
+            bytes_body,
+            detail=detail)
+
+    def test_create_group_snapshot_with_str_body(self):
+        self._test_create_group_snapshot()
+
+    def test_create_group_snapshot_with_bytes_body(self):
+        self._test_create_group_snapshot(bytes_body=True)
+
+    def test_show_group_snapshot_with_str_body(self):
+        self._test_show_group_snapshot()
+
+    def test_show_group_snapshot_with_bytes_body(self):
+        self._test_show_group_snapshot(bytes_body=True)
+
+    def test_list_group_snapshots_with_str_body(self):
+        self._test_list_group_snapshots()
+
+    def test_list_group_snapshots_with_bytes_body(self):
+        self._test_list_group_snapshots(bytes_body=True)
+
+    def test_delete_group_snapshot(self):
+        self.check_service_client_function(
+            self.client.delete_group_snapshot,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            group_snapshot_id='0e701ab8-1bec-4b9f-b026-a7ba4af13578',
+            status=202)
diff --git a/tempest/tests/lib/services/volume/v3/test_group_types_client.py b/tempest/tests/lib/services/volume/v3/test_group_types_client.py
new file mode 100644
index 0000000..0f456a2
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_group_types_client.py
@@ -0,0 +1,124 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.volume.v3 import group_types_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestGroupTypesClient(base.BaseServiceTest):
+    FAKE_CREATE_GROUP_TYPE = {
+        "group_type": {
+            "name": "group-type-001",
+            "description": "Test group type 1",
+            "group_specs": {},
+            "is_public": True,
+        }
+    }
+
+    FAKE_INFO_GROUP_TYPE = {
+        "group_type": {
+            "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
+            "name": "group-type-001",
+            "description": "Test group type 1",
+            "is_public": True,
+            "created_at": "20127-06-20T03:50:07Z",
+            "group_specs": {},
+        }
+    }
+
+    FAKE_LIST_GROUP_TYPES = {
+        "group_types": [
+            {
+                "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
+                "name": "group-type-001",
+                "description": "Test group type 1",
+                "is_public": True,
+                "created_at": "2017-06-20T03:50:07Z",
+                "group_specs": {},
+            },
+            {
+                "id": "e479997c-650b-40a4-9dfe-77655818b0d2",
+                "name": "group-type-002",
+                "description": "Test group type 2",
+                "is_public": True,
+                "created_at": "2017-06-19T01:52:47Z",
+                "group_specs": {},
+            },
+            {
+                "id": "c5c4769e-213c-40a6-a568-8e797bb691d4",
+                "name": "group-type-003",
+                "description": "Test group type 3",
+                "is_public": True,
+                "created_at": "2017-06-18T06:34:32Z",
+                "group_specs": {},
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestGroupTypesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = group_types_client.GroupTypesClient(fake_auth,
+                                                          'volume',
+                                                          'regionOne')
+
+    def _test_create_group_type(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_group_type,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_GROUP_TYPE,
+            bytes_body,
+            status=202)
+
+    def _test_show_group_type(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_group_type,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_INFO_GROUP_TYPE,
+            bytes_body,
+            group_type_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+    def _test_list_group_types(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_group_types,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_GROUP_TYPES,
+            bytes_body)
+
+    def test_create_group_type_with_str_body(self):
+        self._test_create_group_type()
+
+    def test_create_group_type_with_bytes_body(self):
+        self._test_create_group_type(bytes_body=True)
+
+    def test_delete_group_type(self):
+        self.check_service_client_function(
+            self.client.delete_group_type,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            group_type_id='0e58433f-d108-4bf3-a22c-34e6b71ef86b',
+            status=202)
+
+    def test_show_group_type_with_str_body(self):
+        self._test_show_group_type()
+
+    def test_show_group_type_with_bytes_body(self):
+        self._test_show_group_type(bytes_body=True)
+
+    def test_list_group_types_with_str_body(self):
+        self._test_list_group_types()
+
+    def test_list_group_types_with_bytes_body(self):
+        self._test_list_group_types(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v3/test_groups_client.py b/tempest/tests/lib/services/volume/v3/test_groups_client.py
new file mode 100644
index 0000000..0884e5a
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_groups_client.py
@@ -0,0 +1,186 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.volume.v3 import groups_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestGroupsClient(base.BaseServiceTest):
+    FAKE_CREATE_GROUP = {
+        "group": {
+            "name": "group-001",
+            "description": "Test group 1",
+            "group_type": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+            "volume_types": ["2103099d-7cc3-4e52-a2f1-23a5284416f3"],
+            "availability_zone": "az1",
+        }
+    }
+
+    FAKE_CREATE_GROUP_FROM_GROUP_SNAPSHOT = {
+        "create-from-src": {
+            "name": "group-002",
+            "description": "Test group 2",
+            "group_snapshot_id": "79c9afdb-7e46-4d71-9249-1f022886963c",
+        }
+    }
+
+    FAKE_CREATE_GROUP_FROM_GROUP = {
+        "create-from-src": {
+            "name": "group-003",
+            "description": "Test group 3",
+            "source_group_id": "e92f9dc7-0b20-492d-8ab2-3ad8fdac270e",
+        }
+    }
+
+    FAKE_UPDATE_GROUP = {
+        "group": {
+            "name": "new-group",
+            "description": "New test group",
+            "add_volumes": "27d45037-ade3-4a87-b729-dba3293c06f3,"
+                           "6e7cd916-d961-41cc-b3bd-0601ca0c701f",
+            "remove_volumes": "4d580519-6467-448e-95e9-5b25c94d83c7,"
+                              "ea22464c-f095-4a87-a31f-c5d34e0c6fc9"
+        }
+    }
+
+    FAKE_INFO_GROUP = {
+        "group": {
+            "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
+            "name": "group-001",
+            "description": "Test group 1",
+            "group_type": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+            "volume_types": ["2103099d-7cc3-4e52-a2f1-23a5284416f3"],
+            "status": "available",
+            "availability_zone": "az1",
+            "created_at": "20127-06-20T03:50:07Z"
+        }
+    }
+
+    FAKE_LIST_GROUPS = {
+        "groups": [
+            {
+                "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
+                "name": "group-001",
+                "description": "Test group 1",
+                "group_type": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+                "volume_types": ["2103099d-7cc3-4e52-a2f1-23a5284416f3"],
+                "status": "available",
+                "availability_zone": "az1",
+                "created_at": "2017-06-20T03:50:07Z",
+            },
+            {
+                "id": "e479997c-650b-40a4-9dfe-77655818b0d2",
+                "name": "group-002",
+                "description": "Test group 2",
+                "group_snapshot_id": "79c9afdb-7e46-4d71-9249-1f022886963c",
+                "group_type": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+                "volume_types": ["2103099d-7cc3-4e52-a2f1-23a5284416f3"],
+                "status": "available",
+                "availability_zone": "az1",
+                "created_at": "2017-06-19T01:52:47Z",
+            },
+            {
+                "id": "c5c4769e-213c-40a6-a568-8e797bb691d4",
+                "name": "group-003",
+                "description": "Test group 3",
+                "source_group_id": "e92f9dc7-0b20-492d-8ab2-3ad8fdac270e",
+                "group_type": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+                "volume_types": ["2103099d-7cc3-4e52-a2f1-23a5284416f3"],
+                "status": "available",
+                "availability_zone": "az1",
+                "created_at": "2017-06-18T06:34:32Z",
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestGroupsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = groups_client.GroupsClient(fake_auth,
+                                                 'volume',
+                                                 'regionOne')
+
+    def _test_create_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_group,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_GROUP,
+            bytes_body,
+            status=202)
+
+    def _test_show_group(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_group,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_INFO_GROUP,
+            bytes_body,
+            group_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+    def _test_list_groups(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_groups,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_GROUPS,
+            bytes_body,
+            detail=True)
+
+    def test_create_group_with_str_body(self):
+        self._test_create_group()
+
+    def test_create_group_with_bytes_body(self):
+        self._test_create_group(bytes_body=True)
+
+    def test_show_group_with_str_body(self):
+        self._test_show_group()
+
+    def test_show_group_with_bytes_body(self):
+        self._test_show_group(bytes_body=True)
+
+    def test_list_groups_with_str_body(self):
+        self._test_list_groups()
+
+    def test_list_groups_with_bytes_body(self):
+        self._test_list_groups(bytes_body=True)
+
+    def test_delete_group(self):
+        self.check_service_client_function(
+            self.client.delete_group,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            group_id='0e701ab8-1bec-4b9f-b026-a7ba4af13578',
+            status=202)
+
+    def test_create_group_from_group_snapshot(self):
+        self.check_service_client_function(
+            self.client.create_group_from_source,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_GROUP_FROM_GROUP_SNAPSHOT,
+            status=202)
+
+    def test_create_group_from_group(self):
+        self.check_service_client_function(
+            self.client.create_group_from_source,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_GROUP_FROM_GROUP,
+            status=202)
+
+    def test_update_group(self):
+        self.check_service_client_function(
+            self.client.update_group,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            group_id='0e701ab8-1bec-4b9f-b026-a7ba4af13578',
+            status=202,
+            **self.FAKE_UPDATE_GROUP['group'])
diff --git a/tempest/tests/lib/services/volume/v3/test_versions_client.py b/tempest/tests/lib/services/volume/v3/test_versions_client.py
new file mode 100644
index 0000000..9627b9a
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_versions_client.py
@@ -0,0 +1,91 @@
+# Copyright 2017 NEC Corporation.  All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.volume.v3 import versions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVersionsClient(base.BaseServiceTest):
+
+    FAKE_VERSIONS_INFO = {
+        "versions": [
+            {
+                "status": "DEPRECATED", "updated": "2016-05-02T20:25:19Z",
+                "links": [
+                    {"href": "http://docs.openstack.org/", "type": "text/html",
+                     "rel": "describedby"},
+                    {"href": "https://10.30.197.39:8776/v1/", "rel": "self"}
+                ],
+                "min_version": "",
+                "version": "",
+                "media-types": [
+                    {"base": "application/json",
+                     "type": "application/vnd.openstack.volume+json;version=1"}
+                ],
+                "id": "v1.0"
+            },
+            {
+                "status": "DEPRECATED", "updated": "2017-02-25T12:00:00Z",
+                "links": [
+                    {"href": "http://docs.openstack.org/", "type": "text/html",
+                     "rel": "describedby"},
+                    {"href": "https://10.30.197.39:8776/v2/", "rel": "self"}
+                ],
+                "min_version": "",
+                "version": "",
+                "media-types": [
+                    {"base": "application/json",
+                     "type": "application/vnd.openstack.volume+json;version=1"}
+                ],
+                "id": "v2.0"
+            },
+            {
+                "status": "CURRENT", "updated": "2016-02-08T12:20:21Z",
+                "links": [
+                    {"href": "http://docs.openstack.org/", "type": "text/html",
+                     "rel": "describedby"},
+                    {"href": "https://10.30.197.39:8776/v3/", "rel": "self"}
+                ],
+                "min_version": "3.0",
+                "version": "3.28",
+                "media-types": [
+                    {"base": "application/json",
+                     "type": "application/vnd.openstack.volume+json;version=1"}
+                ],
+                "id": "v3.0"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestVersionsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = versions_client.VersionsClient(fake_auth,
+                                                     'volume',
+                                                     'regionOne')
+
+    def _test_list_versions(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_versions,
+            'tempest.lib.common.rest_client.RestClient.raw_request',
+            self.FAKE_VERSIONS_INFO,
+            bytes_body,
+            300)
+
+    def test_list_versions_with_str_body(self):
+        self._test_list_versions()
+
+    def test_list_versions_with_bytes_body(self):
+        self._test_list_versions(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v3/test_volumes_client.py b/tempest/tests/lib/services/volume/v3/test_volumes_client.py
new file mode 100644
index 0000000..a515fd3
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_volumes_client.py
@@ -0,0 +1,48 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v3 import volumes_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVolumesClient(base.BaseServiceTest):
+
+    FAKE_VOLUME_SUMMARY = {
+        "volume-summary": {
+            "total_size": 20,
+            "total_count": 5
+        }
+    }
+
+    def setUp(self):
+        super(TestVolumesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = volumes_client.VolumesClient(fake_auth,
+                                                   'volume',
+                                                   'regionOne')
+
+    def _test_show_volume_summary(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_volume_summary,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_VOLUME_SUMMARY,
+            bytes_body)
+
+    def test_show_volume_summary_with_str_body(self):
+        self._test_show_volume_summary()
+
+    def test_show_volume_summary_with_bytes_body(self):
+        self._test_show_volume_summary(bytes_body=True)
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
index 2f975d2..c3a792f 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -15,9 +15,9 @@
 
 import copy
 import datetime
-import testtools
 
-from oslotest import mockpatch
+import fixtures
+import testtools
 
 from tempest.lib import auth
 from tempest.lib import exceptions
@@ -82,9 +82,9 @@
 
     def test_auth_data_property_when_cache_exists(self):
         self.auth_provider.cache = 'foo'
-        self.useFixture(mockpatch.PatchObject(self.auth_provider,
-                                              'is_expired',
-                                              return_value=False))
+        self.useFixture(fixtures.MockPatchObject(self.auth_provider,
+                                                 'is_expired',
+                                                 return_value=False))
         self.assertEqual('foo', getattr(self.auth_provider, 'auth_data'))
 
     def test_delete_auth_data_property_through_deleter(self):
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
index f3a4e9c..bbebcd3 100644
--- a/tempest/tests/lib/test_decorators.py
+++ b/tempest/tests/lib/test_decorators.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import mock
 import testtools
 
 from tempest.lib import base as test
@@ -21,6 +22,34 @@
 from tempest.tests import base
 
 
+class TestAttrDecorator(base.TestCase):
+    def _test_attr_helper(self, expected_attrs, **decorator_args):
+        @decorators.attr(**decorator_args)
+        def foo():
+            pass
+
+        # By our decorators.attr decorator the attribute __testtools_attrs
+        # will be set only for 'type' argument, so we test it first.
+        if 'type' in decorator_args:
+            # this is what testtools sets
+            self.assertEqual(getattr(foo, '__testtools_attrs'),
+                             set(expected_attrs))
+
+    def test_attr_without_type(self):
+        self._test_attr_helper(expected_attrs='baz', bar='baz')
+
+    def test_attr_decorator_with_list_type(self):
+        # if type is 'smoke' we'll get the original list of types
+        self._test_attr_helper(expected_attrs=['smoke', 'foo'],
+                               type=['smoke', 'foo'])
+
+    def test_attr_decorator_with_unknown_type(self):
+        self._test_attr_helper(expected_attrs=['foo'], type='foo')
+
+    def test_attr_decorator_with_duplicated_type(self):
+        self._test_attr_helper(expected_attrs=['foo'], type=['foo', 'foo'])
+
+
 class TestSkipBecauseDecorator(base.TestCase):
     def _test_skip_because_helper(self, expected_to_skip=True,
                                   **decorator_args):
@@ -123,3 +152,33 @@
 
     def test_no_skip_for_attr_exist_and_true(self):
         self._test_skip_unless_attr('expected_attr', expected_to_skip=False)
+
+
+class TestRelatedBugDecorator(base.TestCase):
+    def test_relatedbug_when_no_exception(self):
+        f = mock.Mock()
+        sentinel = object()
+
+        @decorators.related_bug(bug="1234", status_code=500)
+        def test_foo(self):
+            f(self)
+
+        test_foo(sentinel)
+        f.assert_called_once_with(sentinel)
+
+    def test_relatedbug_when_exception(self):
+        class MyException(Exception):
+            def __init__(self, status_code):
+                self.status_code = status_code
+
+        def f(self):
+            raise MyException(status_code=500)
+
+        @decorators.related_bug(bug="1234", status_code=500)
+        def test_foo(self):
+            f(self)
+
+        with mock.patch.object(decorators.LOG, 'error') as m_error:
+            self.assertRaises(MyException, test_foo, object())
+
+        m_error.assert_called_once_with(mock.ANY, '1234', '1234')
diff --git a/tempest/tests/lib/test_ssh.py b/tempest/tests/lib/test_ssh.py
index a16da1c..37fe646 100644
--- a/tempest/tests/lib/test_ssh.py
+++ b/tempest/tests/lib/test_ssh.py
@@ -12,11 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from io import StringIO
 import socket
 
 import mock
 import six
+from six import StringIO
 import testtools
 
 from tempest.lib.common import ssh
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
index 01b8a72..3ece11d 100644
--- a/tempest/tests/test_base_test.py
+++ b/tempest/tests/test_base_test.py
@@ -13,11 +13,11 @@
 #    under the License.
 
 import mock
+from oslo_config import cfg
 
 from tempest import clients
 from tempest.common import credentials_factory as credentials
-from tempest.common import fixed_network
-from tempest import config
+from tempest.lib.common import fixed_network
 from tempest import test
 from tempest.tests import base
 from tempest.tests import fake_config
@@ -28,8 +28,9 @@
         super(TestBaseTestCase, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
         self.fixed_network_name = 'fixed-net'
-        config.CONF.compute.fixed_network_name = self.fixed_network_name
-        config.CONF.service_available.neutron = True
+        cfg.CONF.set_default('fixed_network_name', self.fixed_network_name,
+                             'compute')
+        cfg.CONF.set_default('neutron', True, 'service_available')
 
     @mock.patch.object(test.BaseTestCase, 'get_client_manager')
     @mock.patch.object(test.BaseTestCase, '_get_credentials_provider')
@@ -56,7 +57,7 @@
     def test_get_tenant_network_with_nova_net(self, mock_man, mock_iaa,
                                               mock_giv, mock_gtn, mock_gcp,
                                               mock_gcm):
-        config.CONF.service_available.neutron = False
+        cfg.CONF.set_default('neutron', False, 'service_available')
         mock_prov = mock.Mock()
         mock_admin_man = mock.Mock()
         mock_iaa.return_value = True
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index ae2f2a3..bf04280 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -12,10 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import fixtures
 from oslo_config import cfg
-from oslotest import mockpatch
 import testtools
 
+from tempest.common import utils
 from tempest import config
 from tempest import exceptions
 from tempest.lib.common.utils import data_utils
@@ -32,34 +33,10 @@
                          fake_config.FakePrivate)
 
 
-class TestAttrDecorator(BaseDecoratorsTest):
-    def _test_attr_helper(self, expected_attrs, **decorator_args):
-        @test.attr(**decorator_args)
-        def foo():
-            pass
-
-        # By our test.attr decorator the attribute __testtools_attrs will be
-        # set only for 'type' argument, so we test it first.
-        if 'type' in decorator_args:
-            # this is what testtools sets
-            self.assertEqual(getattr(foo, '__testtools_attrs'),
-                             set(expected_attrs))
-
-    def test_attr_without_type(self):
-        self._test_attr_helper(expected_attrs='baz', bar='baz')
-
-    def test_attr_decorator_with_list_type(self):
-        # if type is 'smoke' we'll get the original list of types
-        self._test_attr_helper(expected_attrs=['smoke', 'foo'],
-                               type=['smoke', 'foo'])
-
-    def test_attr_decorator_with_unknown_type(self):
-        self._test_attr_helper(expected_attrs=['foo'], type='foo')
-
-    def test_attr_decorator_with_duplicated_type(self):
-        self._test_attr_helper(expected_attrs=['foo'], type=['foo', 'foo'])
-
-
+# NOTE: The test module is for tempest.test.idempotent_id.
+# After all projects switch to use decorators.idempotent_id,
+# we can remove tempest.test.idempotent_id as well as this
+# test module
 class TestIdempotentIdDecorator(BaseDecoratorsTest):
 
     def _test_helper(self, _id, **decorator_args):
@@ -100,7 +77,7 @@
 class TestServicesDecorator(BaseDecoratorsTest):
     def _test_services_helper(self, *decorator_args):
         class TestFoo(test.BaseTestCase):
-            @test.services(*decorator_args)
+            @utils.services(*decorator_args)
             def test_bar(self):
                 return 0
 
@@ -124,14 +101,14 @@
                           'bad_service')
 
     def test_services_decorator_with_service_valid_and_unavailable(self):
-        self.useFixture(mockpatch.PatchObject(test.CONF.service_available,
-                                              'cinder', False))
+        self.useFixture(fixtures.MockPatchObject(test.CONF.service_available,
+                                                 'cinder', False))
         self.assertRaises(testtools.TestCase.skipException,
                           self._test_services_helper, 'compute',
                           'volume')
 
     def test_services_list(self):
-        service_list = test.get_service_list()
+        service_list = utils.get_service_list()
         for service in service_list:
             try:
                 self._test_services_helper(service)
@@ -162,7 +139,7 @@
     def _test_requires_ext_helper(self, expected_to_skip=True,
                                   **decorator_args):
         class TestFoo(test.BaseTestCase):
-            @test.requires_ext(**decorator_args)
+            @utils.requires_ext(**decorator_args)
             def test_bar(self):
                 return 0
 
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index f005c21..bc3a753 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -86,13 +86,13 @@
     def test_scenario_tests_need_service_tags(self):
         self.assertFalse(checks.scenario_tests_need_service_tags(
             'def test_fake:', './tempest/scenario/test_fake.py',
-            "@test.services('compute')"))
+            "@utils.services('compute')"))
         self.assertFalse(checks.scenario_tests_need_service_tags(
             'def test_fake_test:', './tempest/api/compute/test_fake.py',
-            "@test.services('image')"))
+            "@utils.services('image')"))
         self.assertFalse(checks.scenario_tests_need_service_tags(
             'def test_fake:', './tempest/scenario/orchestration/test_fake.py',
-            "@test.services('compute')"))
+            "@utils.services('compute')"))
         self.assertTrue(checks.scenario_tests_need_service_tags(
             'def test_fake_test:', './tempest/scenario/test_fake.py',
             '\n'))
@@ -113,12 +113,13 @@
 
     def test_service_tags_not_in_module_path(self):
         self.assertTrue(checks.service_tags_not_in_module_path(
-            "@test.services('compute')", './tempest/api/compute/fake_test.py'))
+            "@utils.services('compute')",
+            './tempest/api/compute/fake_test.py'))
         self.assertFalse(checks.service_tags_not_in_module_path(
-            "@test.services('compute')",
+            "@utils.services('compute')",
             './tempest/scenario/compute/fake_test.py'))
         self.assertFalse(checks.service_tags_not_in_module_path(
-            "@test.services('compute')", './tempest/api/image/fake_test.py'))
+            "@utils.services('compute')", './tempest/api/image/fake_test.py'))
 
     def test_no_hyphen_at_end_of_rand_name(self):
         self.assertIsNone(checks.no_hyphen_at_end_of_rand_name(
@@ -180,3 +181,15 @@
             'from oslo_config import cfg', './tempest/lib/decorators.py')))
         self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
             'import tempest.config', './tempest/lib/common/rest_client.py')))
+
+    def test_unsupported_exception_attribute_PY3(self):
+        self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
+            "raise TestCase.failureException(e.message)"))), 1)
+        self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
+            "raise TestCase.failureException(ex.message)"))), 1)
+        self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
+            "raise TestCase.failureException(exc.message)"))), 1)
+        self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
+            "raise TestCase.failureException(exception.message)"))), 1)
+        self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
+            "raise TestCase.failureException(ee.message)"))), 0)
diff --git a/tempest/tests/test_list_tests.py b/tempest/tests/test_list_tests.py
index 38d4c5c..a238879 100644
--- a/tempest/tests/test_list_tests.py
+++ b/tempest/tests/test_list_tests.py
@@ -14,9 +14,10 @@
 
 import os
 import re
-import six
 import subprocess
 
+import six
+
 from tempest.tests import base
 
 
diff --git a/tempest/tests/test_microversions.py b/tempest/tests/test_microversions.py
index 173accb..ee6db71 100644
--- a/tempest/tests/test_microversions.py
+++ b/tempest/tests/test_microversions.py
@@ -13,6 +13,7 @@
 #    under the License.
 
 from oslo_config import cfg
+import six
 import testtools
 
 from tempest.api.compute import base as compute_base
@@ -74,7 +75,7 @@
                 self.assertRaises(testtools.TestCase.skipException,
                                   test_class.skip_checks)
         except testtools.TestCase.skipException as e:
-            raise testtools.TestCase.failureException(e.message)
+            raise testtools.TestCase.failureException(six.text_type(e))
 
     def test_config_version_none_none(self):
         expected_pass_tests = [VersionTestNoneTolatest, VersionTestNoneTo2_2]
diff --git a/tempest/tests/test_wrappers.py b/tempest/tests/test_wrappers.py
deleted file mode 100644
index a4ef699..0000000
--- a/tempest/tests/test_wrappers.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# Copyright 2013 IBM Corp.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import os
-import shutil
-import subprocess
-import tempfile
-
-import six
-
-from tempest.tests import base
-
-DEVNULL = open(os.devnull, 'wb')
-
-
-class TestWrappers(base.TestCase):
-    def setUp(self):
-        super(TestWrappers, 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)
-        # copy over the pretty_tox scripts
-        shutil.copy('tools/pretty_tox.sh',
-                    os.path.join(self.directory, 'pretty_tox.sh'))
-        shutil.copy('tools/pretty_tox_serial.sh',
-                    os.path.join(self.directory, 'pretty_tox_serial.sh'))
-
-        self.stdout = six.StringIO()
-        self.stderr = six.StringIO()
-        # 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(
-            "bash %s" % cmd, shell=True,
-            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-        out, err = p.communicate()
-
-        self.assertEqual(
-            p.returncode, expected,
-            "Stdout: %s; Stderr: %s" % (out, err))
-
-    def test_pretty_tox(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('pretty_tox.sh passing', 0)
-
-    def test_pretty_tox_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('pretty_tox.sh', 1)
-
-    def test_pretty_tox_serial(self):
-        self.assertRunExit('pretty_tox_serial.sh passing', 0)
-
-    def test_pretty_tox_serial_fails(self):
-        self.assertRunExit('pretty_tox_serial.sh', 1)
diff --git a/test-requirements.txt b/test-requirements.txt
index 475fb16..29f0865 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,11 +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.13,>=0.12.0 # Apache-2.0
+hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
 # needed for doc build
-sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
-oslosphinx>=4.7.0 # Apache-2.0
-reno>=1.8.0 # Apache-2.0
-mock>=2.0 # BSD
-coverage>=4.0 # Apache-2.0
+sphinx>=1.6.2 # BSD
+openstackdocstheme>=1.16.0 # Apache-2.0
+reno>=2.5.0 # Apache-2.0
+mock>=2.0.0 # BSD
+coverage!=4.4,>=4.0 # Apache-2.0
 oslotest>=1.10.0 # Apache-2.0
+flake8-import-order==0.11 # LGPLv3
diff --git a/tools/check_logs.py b/tools/check_logs.py
index caad85c..fc21f75 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -19,13 +19,12 @@
 import gzip
 import os
 import re
-import six
-import six.moves.urllib.request as urlreq
 import sys
 
+import six
+import six.moves.urllib.request as urlreq
 import yaml
 
-
 # DEVSTACK_GATE_GRENADE is either unset if grenade is not running
 # or a string describing what type of grenade run to perform.
 is_grenade = os.environ.get('DEVSTACK_GATE_GRENADE') is not None
@@ -137,7 +136,7 @@
     with open(WHITELIST_FILE) as stream:
         loaded = yaml.safe_load(stream)
         if loaded:
-            for (name, l) in loaded.iteritems():
+            for (name, l) in six.iteritems(loaded):
                 for w in l:
                     assert 'module' in w, 'no module in %s' % name
                     assert 'message' in w, 'no message in %s' % name
diff --git a/tools/find_stack_traces.py b/tools/find_stack_traces.py
index f2da27a..1f2b88b 100755
--- a/tools/find_stack_traces.py
+++ b/tools/find_stack_traces.py
@@ -18,9 +18,10 @@
 import gzip
 import pprint
 import re
+import sys
+
 import six
 import six.moves.urllib.request as urlreq
-import sys
 
 
 pp = pprint.PrettyPrinter()
@@ -125,8 +126,8 @@
 
 
 def print_stats(items, fname, verbose=False):
-    errors = len(filter(lambda x: x.level == "ERROR", items))
-    traces = len(filter(lambda x: x.level == "TRACE", items))
+    errors = len([x for x in items if x.level == "ERROR"])
+    traces = len([x for x in items if x.level == "TRACE"])
     print("%d ERRORS found in %s" % (errors, fname))
     print("%d TRACES found in %s" % (traces, fname))
 
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
index 03e838e..99df0d1 100644
--- a/tools/generate-tempest-plugins-list.py
+++ b/tools/generate-tempest-plugins-list.py
@@ -25,7 +25,16 @@
 
 import json
 import re
-import requests
+
+try:
+    # For Python 3.0 and later
+    from urllib.error import HTTPError as HTTPError
+    import urllib.request as urllib
+except ImportError:
+    # Fall back to Python 2's urllib2
+    import urllib2 as urllib
+    from urllib2 import HTTPError as HTTPError
+
 
 url = 'https://review.openstack.org/projects/'
 
@@ -48,23 +57,31 @@
 
 
 def has_tempest_plugin(proj):
-    if proj.startswith('openstack/deb-'):
-        return False
-    r = requests.get(
-        "https://git.openstack.org/cgit/%s/plain/setup.cfg" % proj)
+    try:
+        r = urllib.urlopen(
+            "https://git.openstack.org/cgit/%s/plain/setup.cfg" % proj)
+    except HTTPError as err:
+        if err.code == 404:
+            return False
     p = re.compile('^tempest\.test_plugins', re.M)
-    if p.findall(r.text):
+    if p.findall(r.read().decode('utf-8')):
         return True
     else:
         False
 
-r = requests.get(url)
+r = urllib.urlopen(url)
 # Gerrit prepends 4 garbage octets to the JSON, in order to counter
 # cross-site scripting attacks.  Therefore we must discard it so the
 # json library won't choke.
-projects = sorted(filter(is_in_openstack_namespace, json.loads(r.text[4:])))
+projects = sorted(filter(is_in_openstack_namespace, json.loads(r.read()[4:])))
 
-found_plugins = filter(has_tempest_plugin, projects)
+# Retrieve projects having no deb, ui or spec namespace as those namespaces
+# do not contains tempest plugins.
+projects_list = [i for i in projects if not (i.startswith('openstack/deb-') or
+                                             i.endswith('-ui') or
+                                             i.endswith('-specs'))]
+
+found_plugins = list(filter(has_tempest_plugin, projects_list))
 
 # Every element of the found_plugins list begins with "openstack/".
 # We drop those initial 10 octets when printing the list.
diff --git a/tools/generate-tempest-plugins-list.sh b/tools/generate-tempest-plugins-list.sh
index ecff508..e6aad86 100755
--- a/tools/generate-tempest-plugins-list.sh
+++ b/tools/generate-tempest-plugins-list.sh
@@ -1,4 +1,4 @@
-#!/bin/bash -ex
+#!/usr/bin/env bash
 
 # Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
 #
@@ -38,6 +38,8 @@
 # current working directory, it will be prepended or appended to
 # the generated reStructuredText plugins table respectively.
 
+set -ex
+
 (
 declare -A plugins
 
diff --git a/tools/pretty_tox.sh b/tools/pretty_tox.sh
deleted file mode 100755
index 0b83b91..0000000
--- a/tools/pretty_tox.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-
-echo "WARNING: This script is deprecated and will be removed in the near future. Please migrate to tempest run or another method of launching a test runner"
-
-set -o pipefail
-
-TESTRARGS=$1
-python setup.py testr --testr-args="--subunit $TESTRARGS" | subunit-trace --no-failure-debug -f
-retval=$?
-# NOTE(mtreinish) The pipe above would eat the slowest display from pbr's testr
-# wrapper so just manually print the slowest tests.
-echo -e "\nSlowest Tests:\n"
-testr slowest
-exit $retval
diff --git a/tools/pretty_tox_serial.sh b/tools/pretty_tox_serial.sh
deleted file mode 100755
index 1f8204e..0000000
--- a/tools/pretty_tox_serial.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-
-echo "WARNING: This script is deprecated and will be removed in the near future. Please migrate to tempest run or another method of launching a test runner"
-
-set -o pipefail
-
-TESTRARGS=$@
-
-if [ ! -d .testrepository ]; then
-    testr init
-fi
-testr run --subunit $TESTRARGS | subunit-trace -f -n
-retval=$?
-testr slowest
-
-exit $retval
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index 9735f77..44f5874 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -20,127 +20,10 @@
 is fixed but a skip is still in the Tempest test code
 """
 
-import os
-import re
-
-from launchpadlib import launchpad
-from oslo_log import log as logging
-
-BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
-TESTDIR = os.path.join(BASEDIR, 'tempest')
-LPCACHEDIR = os.path.expanduser('~/.launchpadlib/cache')
-
-LOG = logging.getLogger(__name__)
-
-
-def info(msg, *args, **kwargs):
-    LOG.info(msg, *args, **kwargs)
-
-
-def debug(msg, *args, **kwargs):
-    LOG.debug(msg, *args, **kwargs)
-
-
-def find_skips(start=TESTDIR):
-    """Find skipped tests
-
-    Returns a list of tuples (method, bug) that represent
-    test methods that have been decorated to skip because of
-    a particular bug.
-    """
-    results = {}
-    debug("Searching in %s", start)
-    for root, _dirs, files in os.walk(start):
-        for name in files:
-            if name.startswith('test_') and name.endswith('py'):
-                path = os.path.join(root, name)
-                debug("Searching in %s", path)
-                temp_result = find_skips_in_file(path)
-                for method_name, bug_no in temp_result:
-                    if results.get(bug_no):
-                        result_dict = results.get(bug_no)
-                        if result_dict.get(name):
-                            result_dict[name].append(method_name)
-                        else:
-                            result_dict[name] = [method_name]
-                        results[bug_no] = result_dict
-                    else:
-                        results[bug_no] = {name: [method_name]}
-    return results
-
-
-def find_skips_in_file(path):
-    """Return the skip tuples in a test file"""
-    BUG_RE = re.compile(r'\s*@.*skip_because\(bug=[\'"](\d+)[\'"]')
-    DEF_RE = re.compile(r'\s*def (\w+)\(')
-    bug_found = False
-    results = []
-    with open(path, 'rb') as content:
-        lines = content.readlines()
-        for x, line in enumerate(lines):
-            if not bug_found:
-                res = BUG_RE.match(line)
-                if res:
-                    bug_no = int(res.group(1))
-                    debug("Found bug skip %s on line %d", bug_no, x + 1)
-                    bug_found = True
-            else:
-                res = DEF_RE.match(line)
-                if res:
-                    method = res.group(1)
-                    debug("Found test method %s skips for bug %d",
-                          method, bug_no)
-                    results.append((method, bug_no))
-                    bug_found = False
-    return results
-
-
-def get_results(result_dict):
-    results = []
-    for bug_no in result_dict:
-        for method in result_dict[bug_no]:
-            results.append((method, bug_no))
-    return results
+from tempest.lib.cmd import skip_tracker
 
 
 if __name__ == '__main__':
-    results = find_skips()
-    unique_bugs = sorted(set([bug for (method, bug) in get_results(results)]))
-    unskips = []
-    duplicates = []
-    info("Total bug skips found: %d", len(results))
-    info("Total unique bugs causing skips: %d", len(unique_bugs))
-    lp = launchpad.Launchpad.login_anonymously('grabbing bugs',
-                                               'production',
-                                               LPCACHEDIR)
-    for bug_no in unique_bugs:
-        bug = lp.bugs[bug_no]
-        duplicate = bug.duplicate_of_link
-        if duplicate is not None:
-            dup_id = duplicate.split('/')[-1]
-            duplicates.append((bug_no, dup_id))
-        for task in bug.bug_tasks:
-            info("Bug #%7s (%12s - %12s)", bug_no,
-                 task.importance, task.status)
-            if task.status in ('Fix Released', 'Fix Committed'):
-                unskips.append(bug_no)
-
-    for bug_id, dup_id in duplicates:
-        if bug_id not in unskips:
-            dup_bug = lp.bugs[dup_id]
-            for task in dup_bug.bug_tasks:
-                info("Bug #%7s is a duplicate of Bug#%7s (%12s - %12s)",
-                     bug_id, dup_id, task.importance, task.status)
-                if task.status in ('Fix Released', 'Fix Committed'):
-                    unskips.append(bug_id)
-
-    unskips = sorted(set(unskips))
-    if unskips:
-        print("The following bugs have been fixed and the corresponding skips")
-        print("should be removed from the test cases:")
-        print()
-        for bug in unskips:
-            message = "  %7s in " % bug
-            locations = ["%s" % x for x in results[bug].keys()]
-            message += " and ".join(locations)
-            print(message)
+    print("DEPRECATED: `skip_tracker.py` is already deprecated, "
+          "use `skip-tracker` command instead.")
+    skip_tracker.main()
diff --git a/tools/tempest-plugin-sanity.sh b/tools/tempest-plugin-sanity.sh
new file mode 100644
index 0000000..a4f706e
--- /dev/null
+++ b/tools/tempest-plugin-sanity.sh
@@ -0,0 +1,122 @@
+#!/usr/bin/env bash
+
+# Copyright 2017 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.
+
+# This script is intended to check the sanity of tempest plugins against
+# tempest master.
+# What it does:
+# * Creates the virtualenv
+# * Install tempest
+# * Retrive the project lists having tempest plugin if project name is
+#   given.
+# * For each project in a list, It does:
+#   * Clone the Project
+#   * Install the Project and also installs dependencies from
+#     test-requirements.txt.
+#   * Create Tempest workspace
+#   * List tempest plugins
+#   * List tempest plugins tests
+#   * Uninstall the project and its dependencies
+#   * Again Install tempest
+#   * Again repeat the step from cloning project
+#
+# If one of the step fails, The script will exit with failure.
+
+if [ "$1" == "-h" ]; then
+    echo -e "This script performs the sanity of tempest plugins to find
+configuration and dependency issues with the tempest.\n
+Usage: sh ./tools/tempest-plugin-sanity.sh [Run sanity on tempest plugins]"
+    exit 0
+fi
+
+set -ex
+
+# retrieve a list of projects having tempest plugins
+PROJECT_LIST="$(python tools/generate-tempest-plugins-list.py)"
+# List of projects having tempest plugin stale or unmaintained from long time
+BLACKLIST="trio2o"
+
+# Function to clone project using zuul-cloner or from git
+function clone_project() {
+    if [ -e /usr/zuul-env/bin/zuul-cloner ]; then
+        /usr/zuul-env/bin/zuul-cloner --cache-dir /opt/git \
+        git://git.openstack.org \
+        openstack/"$1"
+
+    elif [ -e /usr/bin/git ]; then
+        /usr/bin/git clone git://git.openstack.org/openstack/"$1" \
+        openstack/"$1"
+
+    fi
+}
+
+# Create virtualenv to perform sanity operation
+SANITY_DIR=$(pwd)
+virtualenv "$SANITY_DIR"/.venv
+export TVENV="$SANITY_DIR/tools/with_venv.sh"
+cd "$SANITY_DIR"
+
+# Install tempest in a venv
+"$TVENV" pip install .
+
+# Function to install project
+function install_project() {
+    "$TVENV" pip install "$SANITY_DIR"/openstack/"$1"
+    # Check for test-requirements.txt file in a project then install it.
+    if [ -e "$SANITY_DIR"/openstack/"$1"/test-requirements.txt ]; then
+        "$TVENV" pip install -r "$SANITY_DIR"/openstack/"$1"/test-requirements.txt
+    fi
+}
+
+# Function to perform sanity checking on Tempest plugin
+function tempest_sanity() {
+    "$TVENV" tempest init "$SANITY_DIR"/tempest_sanity
+    cd "$SANITY_DIR"/tempest_sanity
+    "$TVENV" tempest list-plugins
+    "$TVENV" tempest run -l
+    # Delete tempest workspace
+    "$TVENV" tempest workspace remove --name tempest_sanity --rmdir
+    cd "$SANITY_DIR"
+}
+
+# Function to uninstall project
+function uninstall_project() {
+    "$TVENV" pip uninstall -y "$SANITY_DIR"/openstack/"$1"
+    # Check for *requirements.txt file in a project then uninstall it.
+    if [ -e "$SANITY_DIR"/openstack/"$1"/*requirements.txt ]; then
+        "$TVENV" pip uninstall -y -r "$SANITY_DIR"/openstack/"$1"/*requirements.txt
+    fi
+    # Remove the project directory after sanity run
+    rm -fr "$SANITY_DIR"/openstack/"$1"
+}
+
+# Function to run sanity check on each project
+function plugin_sanity_check() {
+        clone_project "$1"  &&  install_project "$1"  &&  tempest_sanity "$1" \
+        &&  uninstall_project "$1"  &&  "$TVENV" pip install .
+}
+
+# Log status
+passed_plugin=''
+failed_plugin=''
+# Perform sanity on all tempest plugin projects
+for project in $PROJECT_LIST; do
+    # Remove blacklisted tempest plugins
+    if ! [[ `echo $BLACKLIST | grep -c $project ` -gt 0 ]]; then
+        plugin_sanity_check $project && passed_plugin+=", $project" || \
+        failed_plugin+=", $project"
+    fi
+done
diff --git a/tools/tox_install.sh b/tools/tox_install.sh
new file mode 100755
index 0000000..43468e4
--- /dev/null
+++ b/tools/tox_install.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+# Client constraint file contains this client version pin that is in conflict
+# with installing the client from source. We should remove the version pin in
+# the constraints file before applying it for from-source installation.
+
+CONSTRAINTS_FILE=$1
+shift 1
+
+set -e
+
+# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get
+# published to logs.openstack.org for easy debugging.
+localfile="$VIRTUAL_ENV/log/upper-constraints.txt"
+
+if [[ $CONSTRAINTS_FILE != http* ]]; then
+    CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE
+fi
+# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep
+curl $CONSTRAINTS_FILE --insecure --progress-bar --output $localfile
+
+pip install -c$localfile openstack-requirements
+
+# This is the main purpose of the script: Allow local installation of
+# the current repo. It is listed in constraints file and thus any
+# install will be constrained and we need to unconstrain it.
+edit-constraints $localfile -- $CLIENT_NAME
+
+pip install -c$localfile -U $*
+exit $?
diff --git a/tools/with_venv.sh b/tools/with_venv.sh
index 165c883..408b5f1 100755
--- a/tools/with_venv.sh
+++ b/tools/with_venv.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 TOOLS_PATH=${TOOLS_PATH:-$(dirname $0)/../}
 VENV_PATH=${VENV_PATH:-${TOOLS_PATH}}
 VENV_DIR=${VENV_DIR:-/.venv}
diff --git a/tox.ini b/tox.ini
index 46823d8..24e3b5d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = pep8,py35,py34,py27,pip-check-reqs
+envlist = pep8,py35,py27,pip-check-reqs
 minversion = 2.3.1
 skipsdist = True
 
@@ -8,8 +8,9 @@
 setenv =
     VIRTUAL_ENV={envdir}
     OS_TEST_PATH=./tempest/test_discover
+    BRANCH_NAME=master
+    CLIENT_NAME=tempest
 deps =
-    setuptools
     -r{toxinidir}/requirements.txt
 
 [testenv]
@@ -17,9 +18,12 @@
     VIRTUAL_ENV={envdir}
     OS_TEST_PATH=./tempest/tests
     PYTHONWARNINGS=default::DeprecationWarning
-passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH OS_TEST_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY
+    BRANCH_NAME=master
+    CLIENT_NAME=tempest
+passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH OS_TEST_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION GENERATE_TEMPEST_PLUGIN_LIST
 usedevelop = True
-install_command = pip install -U {opts} {packages}
+install_command =
+    {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
 whitelist_externals = *
 deps =
     -r{toxinidir}/requirements.txt
@@ -32,7 +36,6 @@
 commands = oslo-config-generator --config-file tempest/cmd/config-generator.tempest.conf
 
 [testenv:cover]
-setenv = OS_TEST_PATH=./tempest/tests
 commands = python setup.py testr --coverage --testr-arg='tempest\.tests {posargs}'
 
 [testenv:all]
@@ -78,7 +81,8 @@
 # See the testrepository bug: https://bugs.launchpad.net/testrepository/+bug/1208610
 commands =
     find . -type f -name "*.pyc" -delete
-    tempest run --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))' {posargs}
+    tempest run --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.api)' {posargs}
+    tempest run --combine --serial --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.scenario)' {posargs}
 
 [testenv:full-serial]
 envdir = .tox/tempest
@@ -91,6 +95,16 @@
     find . -type f -name "*.pyc" -delete
     tempest run --serial --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))' {posargs}
 
+[testenv:scenario]
+envdir = .tox/tempest
+sitepackages = {[tempestenv]sitepackages}
+setenv = {[tempestenv]setenv}
+deps = {[tempestenv]deps}
+# The regex below is used to select all scenario tests
+commands =
+    find . -type f -name "*.pyc" -delete
+    tempest run --serial --regex '(^tempest\.scenario)' {posargs}
+
 [testenv:smoke]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
@@ -124,6 +138,7 @@
 
 [testenv:docs]
 commands =
+    rm -rf doc/build
     python setup.py build_sphinx {posargs}
 
 [testenv:pep8]
@@ -145,11 +160,14 @@
 # E129 skipped because it is too limiting when combined with other rules
 ignore = E125,E123,E129
 show-source = True
-exclude = .git,.venv,.tox,dist,doc,*egg
+exclude = .git,.venv,.tox,dist,doc,*egg,build
 enable-extensions = H106,H203,H904
+import-order-style = pep8
 
 [testenv:releasenotes]
-commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
+commands =
+    rm -rf releasenotes/build
+    sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
 
 [testenv:pip-check-reqs]
 # Do not install test-requirements as that will pollute the virtualenv for
@@ -170,3 +188,9 @@
 # separately, outside of the requirements files.
 deps = bindep
 commands = bindep test
+
+[testenv:plugin-sanity-check]
+# perform tempest plugin sanity
+whitelist_externals = bash
+commands =
+  bash tools/tempest-plugin-sanity.sh