Merge "Add test for cinder volume extend when volume has snapshot"
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 f97f97a..c0a857c 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -246,7 +246,7 @@
 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
 ----------
diff --git a/README.rst b/README.rst
index c1c6a10..ac93992 100644
--- a/README.rst
+++ b/README.rst
@@ -172,7 +172,7 @@
 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.
@@ -258,11 +258,11 @@
 
 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/bindep.txt b/bindep.txt
index 6a28348..8914ade 100644
--- a/bindep.txt
+++ b/bindep.txt
@@ -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 2597f04..cbfcc09 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.
 #
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index adbd2dc..b516055 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: http://docs.openstack.org/developer/tempest/library/api_microversion_testing.html
+
+
 Step1: Add skip logic based on configured Microversion range
 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
 
diff --git a/doc/source/write_tests.rst b/doc/source/write_tests.rst
index 768bf0f..2363fa6 100644
--- a/doc/source/write_tests.rst
+++ b/doc/source/write_tests.rst
@@ -1,7 +1,7 @@
 .. _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
@@ -99,7 +99,7 @@
            specific situations you should not need to use this.
         """
         super(TestExampleCase, cls).setup_clients()
-        cls.servers_client = cls.os.servers_client
+        cls.servers_client = cls.os_primary.servers_client
 
     @classmethod
     def resource_setup(cls):
@@ -112,6 +112,7 @@
         super(TestExampleCase, cls).resource_setup()
         cls.shared_server = cls.servers_client.create_server(...)
 
+.. _credentials:
 
 Allocating Credentials
 ''''''''''''''''''''''
@@ -142,10 +143,10 @@
 
 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 and cls.os_adm 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.
+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.
@@ -187,7 +188,7 @@
 +-------------------+---------------------+
 
 By default cls.os is available since it is allocated in the base tempest test
-class. (located in tempest/test.py) If your TestCase inherits from a different
+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.
 
@@ -198,10 +199,10 @@
 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)
+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::
@@ -241,3 +242,70 @@
 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.servers_client.create_server(...)
+
+is all you need to do. As described previously, in the above example the ``self.os``
+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``. 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.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.credentials
+      username = credentials.username
+      user_id = credentials.user_id
+      password = credentials.password
+      tenant_id = credentials.tenant_id
diff --git a/releasenotes/notes/add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml b/releasenotes/notes/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
similarity index 100%
rename from releasenotes/notes/add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
rename to releasenotes/notes/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
diff --git a/releasenotes/notes/add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml b/releasenotes/notes/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
similarity index 100%
rename from releasenotes/notes/add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
rename to releasenotes/notes/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
diff --git a/releasenotes/notes/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml b/releasenotes/notes/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml
new file mode 100644
index 0000000..6801858
--- /dev/null
+++ b/releasenotes/notes/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/add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml b/releasenotes/notes/16.0.0-add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
similarity index 100%
rename from releasenotes/notes/add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
rename to releasenotes/notes/16.0.0-add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
diff --git a/releasenotes/notes/add-content-type-without-spaces-b2c9b91b257814f3.yaml b/releasenotes/notes/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
similarity index 100%
rename from releasenotes/notes/add-content-type-without-spaces-b2c9b91b257814f3.yaml
rename to releasenotes/notes/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
diff --git a/releasenotes/notes/add-list-auth-project-client-5905076d914a3943.yaml b/releasenotes/notes/16.0.0-add-list-auth-project-client-5905076d914a3943.yaml
similarity index 100%
rename from releasenotes/notes/add-list-auth-project-client-5905076d914a3943.yaml
rename to releasenotes/notes/16.0.0-add-list-auth-project-client-5905076d914a3943.yaml
diff --git a/releasenotes/notes/add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml b/releasenotes/notes/16.0.0-add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
similarity index 100%
rename from releasenotes/notes/add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
rename to releasenotes/notes/16.0.0-add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
diff --git a/releasenotes/notes/add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml b/releasenotes/notes/16.0.0-add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml
similarity index 100%
rename from releasenotes/notes/add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml
rename to releasenotes/notes/16.0.0-add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml
diff --git a/releasenotes/notes/add-list-version-to-identity-client-944cb7396088a575.yaml b/releasenotes/notes/16.0.0-add-list-version-to-identity-client-944cb7396088a575.yaml
similarity index 100%
rename from releasenotes/notes/add-list-version-to-identity-client-944cb7396088a575.yaml
rename to releasenotes/notes/16.0.0-add-list-version-to-identity-client-944cb7396088a575.yaml
diff --git a/releasenotes/notes/add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml b/releasenotes/notes/16.0.0-add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
similarity index 100%
rename from releasenotes/notes/add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
rename to releasenotes/notes/16.0.0-add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
diff --git a/releasenotes/notes/add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml b/releasenotes/notes/16.0.0-add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
similarity index 100%
rename from releasenotes/notes/add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
rename to releasenotes/notes/16.0.0-add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
diff --git a/releasenotes/notes/add-tempest-lib-remote-client-adbeb3f42a36910b.yaml b/releasenotes/notes/16.0.0-add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
similarity index 100%
rename from releasenotes/notes/add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
rename to releasenotes/notes/16.0.0-add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
diff --git a/releasenotes/notes/add-tempest-run-combine-option-e94c1049ba8985d5.yaml b/releasenotes/notes/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
similarity index 100%
rename from releasenotes/notes/add-tempest-run-combine-option-e94c1049ba8985d5.yaml
rename to releasenotes/notes/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
diff --git a/releasenotes/notes/add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml b/releasenotes/notes/16.0.0-add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
similarity index 100%
rename from releasenotes/notes/add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
rename to releasenotes/notes/16.0.0-add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
diff --git a/releasenotes/notes/add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml b/releasenotes/notes/16.0.0-add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
similarity index 100%
rename from releasenotes/notes/add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
rename to releasenotes/notes/16.0.0-add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
diff --git a/releasenotes/notes/create-server-tags-client-8c0042a77e859af6.yaml b/releasenotes/notes/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
similarity index 100%
rename from releasenotes/notes/create-server-tags-client-8c0042a77e859af6.yaml
rename to releasenotes/notes/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
diff --git a/releasenotes/notes/deprecate-deactivate_image-config-7a282c471937bbcb.yaml b/releasenotes/notes/16.0.0-deprecate-deactivate_image-config-7a282c471937bbcb.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-deactivate_image-config-7a282c471937bbcb.yaml
rename to releasenotes/notes/16.0.0-deprecate-deactivate_image-config-7a282c471937bbcb.yaml
diff --git a/releasenotes/notes/deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml b/releasenotes/notes/16.0.0-deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
rename to releasenotes/notes/16.0.0-deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
diff --git a/releasenotes/notes/deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml b/releasenotes/notes/16.0.0-deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
rename to releasenotes/notes/16.0.0-deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
diff --git a/releasenotes/notes/deprecate-resources-prefix-option-ad490c0a30a0266b.yaml b/releasenotes/notes/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
rename to releasenotes/notes/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
diff --git a/releasenotes/notes/deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml b/releasenotes/notes/16.0.0-deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml
rename to releasenotes/notes/16.0.0-deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml
diff --git a/releasenotes/notes/deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml b/releasenotes/notes/16.0.0-deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml
rename to releasenotes/notes/16.0.0-deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml
diff --git a/releasenotes/notes/deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml b/releasenotes/notes/16.0.0-deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
similarity index 100%
rename from releasenotes/notes/deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
rename to releasenotes/notes/16.0.0-deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
diff --git a/releasenotes/notes/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml b/releasenotes/notes/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml
new file mode 100644
index 0000000..4081f6a
--- /dev/null
+++ b/releasenotes/notes/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/fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml b/releasenotes/notes/16.0.0-fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
similarity index 100%
rename from releasenotes/notes/fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
rename to releasenotes/notes/16.0.0-fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
diff --git a/releasenotes/notes/16.0.0-mitaka-eol-88ff8355fff81b55.yaml b/releasenotes/notes/16.0.0-mitaka-eol-88ff8355fff81b55.yaml
new file mode 100644
index 0000000..24ec512
--- /dev/null
+++ b/releasenotes/notes/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/remove-call_until_true-of-test-de9c13bc8f969921.yaml b/releasenotes/notes/16.0.0-remove-call_until_true-of-test-de9c13bc8f969921.yaml
similarity index 100%
rename from releasenotes/notes/remove-call_until_true-of-test-de9c13bc8f969921.yaml
rename to releasenotes/notes/16.0.0-remove-call_until_true-of-test-de9c13bc8f969921.yaml
diff --git a/releasenotes/notes/remove-cinder-v1-api-tests-71e266b8d55d475f.yaml b/releasenotes/notes/16.0.0-remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
similarity index 100%
rename from releasenotes/notes/remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
rename to releasenotes/notes/16.0.0-remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
diff --git a/releasenotes/notes/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml b/releasenotes/notes/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.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/remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml b/releasenotes/notes/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
similarity index 100%
rename from releasenotes/notes/remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
rename to releasenotes/notes/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
diff --git a/releasenotes/notes/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml b/releasenotes/notes/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
new file mode 100644
index 0000000..889e862
--- /dev/null
+++ b/releasenotes/notes/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.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml b/releasenotes/notes/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml
new file mode 100644
index 0000000..8085694
--- /dev/null
+++ b/releasenotes/notes/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/remove-sahara-service-available-44a642aa9c634ab4.yaml b/releasenotes/notes/16.0.0-remove-sahara-service-available-44a642aa9c634ab4.yaml
similarity index 100%
rename from releasenotes/notes/remove-sahara-service-available-44a642aa9c634ab4.yaml
rename to releasenotes/notes/16.0.0-remove-sahara-service-available-44a642aa9c634ab4.yaml
diff --git a/releasenotes/notes/remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml b/releasenotes/notes/16.0.0-remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml
similarity index 100%
rename from releasenotes/notes/remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml
rename to releasenotes/notes/16.0.0-remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml
diff --git a/releasenotes/notes/use-keystone-v3-api-935860d30ddbb8e9.yaml b/releasenotes/notes/16.0.0-use-keystone-v3-api-935860d30ddbb8e9.yaml
similarity index 100%
rename from releasenotes/notes/use-keystone-v3-api-935860d30ddbb8e9.yaml
rename to releasenotes/notes/16.0.0-use-keystone-v3-api-935860d30ddbb8e9.yaml
diff --git a/releasenotes/notes/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml b/releasenotes/notes/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml
new file mode 100644
index 0000000..e5e479b
--- /dev/null
+++ b/releasenotes/notes/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-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-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-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-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-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-volume-backup-force-delete-af0156651a0cbf7f.yaml b/releasenotes/notes/add-volume-backup-force-delete-af0156651a0cbf7f.yaml
new file mode 100644
index 0000000..71bbfcb
--- /dev/null
+++ b/releasenotes/notes/add-volume-backup-force-delete-af0156651a0cbf7f.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    As in the [doc]:
+    https://developer.openstack.org/api-ref/block-storage/v3/
+    #force-delete-a-backup.
+
+      * Force-deletes a backup(v2)
+
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/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..dc4ed27
--- /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:
+  - |
+    Swith 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/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/source/conf.py b/releasenotes/source/conf.py
index eec42cd..97e3a4d 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
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index cea76b4..adec7a7 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
     :maxdepth: 1
 
     unreleased
+    v16.0.0
     v15.0.0
     v14.0.0
     v13.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/requirements.txt b/requirements.txt
index 7c934c2..26e1f02 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,8 +1,8 @@
 # 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>=2.0.0 # Apache-2.0
-cliff>=2.3.0 # Apache-2.0
+pbr!=2.1.0,>=2.0.0 # Apache-2.0
+cliff>=2.6.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+
diff --git a/setup.cfg b/setup.cfg
index b2035bc..b292970 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -39,7 +39,11 @@
     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
diff --git a/tempest/api/compute/admin/test_agents.py b/tempest/api/compute/admin/test_agents.py
index 4ae4372..31976ec 100644
--- a/tempest/api/compute/admin/test_agents.py
+++ b/tempest/api/compute/admin/test_agents.py
@@ -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):
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 2f5382e..79d03f4 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -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:
@@ -226,7 +226,7 @@
         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 e682570..41be620 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_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 AggregatesAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -27,7 +26,7 @@
     @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
@@ -35,7 +34,7 @@
         super(AggregatesAdminNegativeTestJSON, cls).resource_setup()
         cls.aggregate_name_prefix = 'test_aggregate'
 
-        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]
@@ -47,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.
@@ -56,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
@@ -64,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
@@ -73,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
@@ -82,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.
@@ -91,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.
@@ -107,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')
@@ -135,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.
@@ -144,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'))
@@ -157,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.
@@ -172,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..c4db5e3 100644
--- a/tempest/api/compute/admin/test_auto_allocate_network.py
+++ b/tempest/api/compute/admin/test_auto_allocate_network.py
@@ -66,10 +66,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):
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..3449aba
--- /dev/null
+++ b/tempest/api/compute/admin/test_create_server.py
@@ -0,0 +1,109 @@
+# 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):
+    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.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..2569161
--- /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_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'])
diff --git a/tempest/api/compute/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index 10ed519..1e09eeb 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -33,7 +33,7 @@
     @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):
diff --git a/tempest/api/compute/admin/test_fixed_ips_negative.py b/tempest/api/compute/admin/test_fixed_ips_negative.py
index 073f152..a77011e 100644
--- a/tempest/api/compute/admin/test_fixed_ips_negative.py
+++ b/tempest/api/compute/admin/test_fixed_ips_negative.py
@@ -33,7 +33,7 @@
     @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,14 +49,14 @@
             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):
@@ -64,7 +64,7 @@
                           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):
@@ -72,7 +72,7 @@
                           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):
@@ -85,7 +85,7 @@
                           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):
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 3821895..36ebc25 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -163,7 +163,7 @@
 
         # 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'])
 
diff --git a/tempest/api/compute/admin/test_flavors_access_negative.py b/tempest/api/compute/admin/test_flavors_access_negative.py
index 12e4587..be165cb 100644
--- a/tempest/api/compute/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/admin/test_flavors_access_negative.py
@@ -43,7 +43,7 @@
         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
@@ -53,7 +53,7 @@
                           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.
@@ -64,7 +64,7 @@
                           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.
@@ -81,7 +81,7 @@
                           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.
@@ -101,7 +101,7 @@
                           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.
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 a728711..f39feb9 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -62,7 +62,7 @@
         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.
@@ -71,7 +71,7 @@
                           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
@@ -85,7 +85,7 @@
                           '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.admin_flavors_client.set_flavor_extra_spec(
@@ -96,7 +96,7 @@
                           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,
@@ -104,7 +104,7 @@
                           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,
@@ -112,7 +112,7 @@
                           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
@@ -122,7 +122,7 @@
                           "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
diff --git a/tempest/api/compute/admin/test_floating_ips_bulk.py b/tempest/api/compute/admin/test_floating_ips_bulk.py
index f38af56..056b4bd 100644
--- a/tempest/api/compute/admin/test_floating_ips_bulk.py
+++ b/tempest/api/compute/admin/test_floating_ips_bulk.py
@@ -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):
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index b664955..8e2f6ed 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):
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..4544267 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -23,7 +23,7 @@
     @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
@@ -104,7 +104,7 @@
             try:
                 uptime = (self.client.show_hypervisor_uptime(hyper['id'])
                           ['hypervisor'])
-                if len(uptime) > 0:
+                if uptime:
                     has_valid_uptime = True
                     break
             except Exception:
diff --git a/tempest/api/compute/admin/test_hypervisor_negative.py b/tempest/api/compute/admin/test_hypervisor_negative.py
index b8a67b9..af87287 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -17,7 +17,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 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,7 +43,7 @@
             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()
@@ -55,7 +54,7 @@
             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()
@@ -66,7 +65,7 @@
             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,7 +92,7 @@
             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()
@@ -104,7 +103,7 @@
             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,7 +127,7 @@
             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()
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 b6c2c3d..e24c7c1 100644
--- a/tempest/api/compute/admin/test_keypairs_v210.py
+++ b/tempest/api/compute/admin/test_keypairs_v210.py
@@ -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/test_live_block_migration_negative.py b/tempest/api/compute/admin/test_live_block_migration_negative.py
similarity index 96%
rename from tempest/api/compute/test_live_block_migration_negative.py
rename to tempest/api/compute/admin/test_live_block_migration_negative.py
index 0f5ea57..ab63154 100644
--- a/tempest/api/compute/test_live_block_migration_negative.py
+++ b/tempest/api/compute/admin/test_live_block_migration_negative.py
@@ -19,7 +19,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
 
 CONF = config.CONF
 
@@ -37,7 +36,7 @@
             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
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index 3ffd238..3859e64 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -13,16 +13,20 @@
 #    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 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):
@@ -44,8 +48,8 @@
     @classmethod
     def setup_clients(cls):
         super(LiveBlockMigrationTestJSON, cls).setup_clients()
-        cls.admin_hosts_client = cls.os_adm.hosts_client
-        cls.admin_migration_client = cls.os_adm.migrations_client
+        cls.admin_hosts_client = cls.os_admin.hosts_client
+        cls.admin_migration_client = cls.os_admin.migrations_client
 
     @classmethod
     def _get_compute_hostnames(cls):
@@ -80,6 +84,22 @@
             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.
 
@@ -94,27 +114,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(source_host)
 
         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):
@@ -149,11 +165,91 @@
 
         # 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')
+
+        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 LiveBlockMigrationRemoteConsolesV26TestJson(LiveBlockMigrationTestJSON):
+    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(
+        test.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 LiveAutoBlockMigrationV225TestJSON(LiveBlockMigrationTestJSON):
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index df8b175..a626ebb 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -29,7 +29,7 @@
     @classmethod
     def setup_clients(cls):
         super(MigrationsAdminTest, cls).setup_clients()
-        cls.client = cls.os_adm.migrations_client
+        cls.client = cls.os_admin.migrations_client
 
     @decorators.idempotent_id('75c0b83d-72a0-4cf8-a153-631e83e7d53f')
     def test_list_migrations(self):
diff --git a/tempest/api/compute/admin/test_networks.py b/tempest/api/compute/admin/test_networks.py
index 12ae864..0ea0a78 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):
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index c9d7722..937540e 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -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,7 +153,7 @@
     @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")
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index a1dd2e7..747f320 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -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,7 +87,7 @@
 
     @decorators.skip_because(bug="1186354",
                              condition=CONF.service_available.neutron)
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7c6c8f3b-2bf6-4918-b240-57b136a66aa0')
     @test.services('network')
     def test_security_groups_exceed_limit(self):
@@ -106,7 +106,7 @@
 
     @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')
     def test_security_groups_rules_exceed_limit(self):
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..6c7cde2 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,
diff --git a/tempest/api/compute/admin/test_security_groups.py b/tempest/api/compute/admin/test_security_groups.py
index b4d0f2a..8abe03a 100644
--- a/tempest/api/compute/admin/test_security_groups.py
+++ b/tempest/api/compute/admin/test_security_groups.py
@@ -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):
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 79777d0..98bf4bf 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -18,7 +18,6 @@
 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 +26,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 +34,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):
@@ -92,7 +92,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
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index ebdceef..b0f18d7 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -21,7 +21,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
 
 CONF = config.CONF
 
@@ -32,9 +31,9 @@
     @classmethod
     def setup_clients(cls):
         super(ServersAdminNegativeTestJSON, cls).setup_clients()
-        cls.client = cls.os_adm.servers_client
+        cls.client = cls.os_admin.servers_client
         cls.non_adm_client = cls.servers_client
-        cls.quotas_client = cls.os_adm.quotas_client
+        cls.quotas_client = cls.os_admin.quotas_client
 
     @classmethod
     def resource_setup(cls):
@@ -47,7 +46,7 @@
     @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'))
@@ -69,7 +68,7 @@
     @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'))
@@ -88,27 +87,27 @@
                           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.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
@@ -116,7 +115,7 @@
                           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
@@ -129,7 +128,7 @@
                           'Resize 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..6a2e5e9 100644
--- a/tempest/api/compute/admin/test_servers_on_multinodes.py
+++ b/tempest/api/compute/admin/test_servers_on_multinodes.py
@@ -33,7 +33,7 @@
                 "Less than 2 compute nodes, skipping multi-nodes test.")
 
     def _get_host(self, server_id):
-        return self.os_adm.servers_client.show_server(
+        return self.os_admin.servers_client.show_server(
             server_id)['server']['OS-EXT-SRV-ATTR:host']
 
     @decorators.idempotent_id('26a9d5df-6890-45f2-abc4-a659290cb130')
diff --git a/tempest/api/compute/admin/test_services.py b/tempest/api/compute/admin/test_services.py
index c1c1c82..1dfc13e 100644
--- a/tempest/api/compute/admin/test_services.py
+++ b/tempest/api/compute/admin/test_services.py
@@ -24,7 +24,7 @@
     @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):
diff --git a/tempest/api/compute/admin/test_services_negative.py b/tempest/api/compute/admin/test_services_negative.py
index 9447a09..38bb5ec 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,7 +41,7 @@
                         ['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']
@@ -51,7 +50,7 @@
                                              binary='xxx')['services']
         self.assertEqual(0, len(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']
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_volumes_negative.py b/tempest/api/compute/admin/test_volumes_negative.py
index 06b0893..4a7f36f 100644
--- a/tempest/api/compute/admin/test_volumes_negative.py
+++ b/tempest/api/compute/admin/test_volumes_negative.py
@@ -17,7 +17,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
 
 CONF = config.CONF
 
@@ -36,7 +35,7 @@
         super(VolumesAdminNegativeTest, cls).resource_setup()
         cls.server = cls.create_test_server(wait_until='ACTIVE')
 
-    @test.attr(type=['negative'])
+    @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()
@@ -46,8 +45,8 @@
                           self.server['id'], nonexistent_volume,
                           volumeId=volume['id'])
 
-    @test.related_bug('1629110', status_code=400)
-    @test.attr(type=['negative'])
+    @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()
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 1736463..a5ee716 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -63,38 +63,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
+            cls.os_primary.security_group_default_rules_client)
+        cls.versions_client = cls.os_primary.compute_versions_client
 
-        cls.volumes_client = cls.os.volumes_v2_client
+        cls.volumes_client = cls.os_primary.volumes_v2_client
 
     @classmethod
     def resource_setup(cls):
@@ -470,9 +473,9 @@
     def setup_clients(cls):
         super(BaseV2ComputeAdminTest, cls).setup_clients()
         cls.availability_zone_admin_client = (
-            cls.os_adm.availability_zone_client)
-        cls.admin_flavors_client = cls.os_adm.flavors_client
-        cls.admin_servers_client = cls.os_adm.servers_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):
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index 89051c1..bf4c958 100644
--- a/tempest/api/compute/flavors/test_flavors.py
+++ b/tempest/api/compute/flavors/test_flavors.py
@@ -15,14 +15,13 @@
 
 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'
 
-    @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
@@ -39,7 +38,7 @@
         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
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index eee6bd2..ebb9d2e 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -34,15 +34,15 @@
     def setup_clients(cls):
         super(FlavorsV2NegativeTest, cls).setup_clients()
         if CONF.image_feature_enabled.api_v1:
-            cls.images_client = cls.os.image_client
+            cls.images_client = cls.os_primary.image_client
         elif CONF.image_feature_enabled.api_v2:
-            cls.images_client = cls.os.image_client_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].')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @test.services('image')
     @decorators.idempotent_id('90f0d93a-91c1-450c-91e6-07d18172cefe')
     def test_boot_with_low_ram(self):
@@ -82,9 +82,9 @@
         self.assertEqual(min_img_ram, image['min_ram'])
 
         # Try to create server with flavor of insufficient ram size
-        self.assertRaisesRegexp(lib_exc.BadRequest,
-                                "Flavor's memory is too small for "
-                                "requested image",
-                                self.create_test_server,
-                                image_id=image['id'],
-                                flavor=flavor['id'])
+        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_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
index b3c3e17..96983b0 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,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute.floating_ips import base
 from tempest import config
 from tempest.lib.common.utils import data_utils
@@ -48,7 +50,7 @@
             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):
@@ -58,7 +60,7 @@
                           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):
@@ -69,7 +71,7 @@
         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):
@@ -80,7 +82,7 @@
                           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):
@@ -90,7 +92,7 @@
                           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):
@@ -99,3 +101,27 @@
         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')
+    @test.services('network')
+    @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_negative.py b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
index 388db0b..9d70bf7 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
@@ -30,7 +30,7 @@
         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):
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index dcc44d8..8d503dc 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -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 9cb4b57..03d0789 100644
--- a/tempest/api/compute/images/test_image_metadata_negative.py
+++ b/tempest/api/compute/images/test_image_metadata_negative.py
@@ -17,7 +17,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 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_negative.py b/tempest/api/compute/images/test_images_negative.py
index 86013d4..e292389 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/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
 
 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
@@ -56,7 +55,7 @@
                           self.create_image_from_server,
                           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
@@ -65,7 +64,7 @@
         self.assertRaises(lib_exc.NotFound, self.create_image_from_server,
                           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_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 68563bd..cf32ba3 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -22,7 +22,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
 
 CONF = config.CONF
 
@@ -77,7 +76,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('55d1d38c-dd66-4933-9c8e-7d92aeb60ddc')
     def test_create_image_specify_invalid_metadata(self):
         # Return an error when creating image with invalid metadata
@@ -85,7 +84,7 @@
         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
@@ -93,7 +92,7 @@
         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
@@ -110,7 +109,7 @@
         self.client.delete_image(image_id)
         self.images.remove(image_id)
 
-    @test.attr(type=['negative'])
+    @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
@@ -119,7 +118,7 @@
         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
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index 7168a8c..6677aa2 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -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 '
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 403961f..d37f8fc 100644
--- a/tempest/api/compute/images/test_list_image_filters_negative.py
+++ b/tempest/api/compute/images/test_list_image_filters_negative.py
@@ -17,7 +17,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
 
 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/keypairs/test_keypairs_negative.py b/tempest/api/compute/keypairs/test_keypairs_negative.py
index 8b5a35b..205076c 100644
--- a/tempest/api/compute/keypairs/test_keypairs_negative.py
+++ b/tempest/api/compute/keypairs/test_keypairs_negative.py
@@ -18,11 +18,10 @@
 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/limits/test_absolute_limits_negative.py b/tempest/api/compute/limits/test_absolute_limits_negative.py
index 21b4b1c..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
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 a50933b..3378ef8 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -53,7 +53,7 @@
             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):
@@ -123,7 +123,7 @@
                                   '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):
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 6b8dfdf..4efb8b7 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
@@ -27,7 +27,7 @@
         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):
@@ -44,7 +44,7 @@
                           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):
@@ -61,7 +61,7 @@
                           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):
@@ -86,7 +86,7 @@
                           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):
@@ -106,7 +106,7 @@
                           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):
@@ -125,7 +125,7 @@
                           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):
@@ -144,7 +144,7 @@
                           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):
@@ -163,7 +163,7 @@
                           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):
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index ed0e722..930a58e 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -28,7 +28,7 @@
         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):
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 3593237..207778a 100644
--- a/tempest/api/compute/security_groups/test_security_groups_negative.py
+++ b/tempest/api/compute/security_groups/test_security_groups_negative.py
@@ -32,7 +32,7 @@
         super(SecurityGroupsNegativeTestJSON, cls).setup_clients()
         cls.client = cls.security_groups_client
 
-    @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):
@@ -44,7 +44,7 @@
 
     @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):
@@ -67,7 +67,7 @@
 
     @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):
@@ -84,7 +84,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'])
+    @decorators.attr(type=['negative'])
     @test.services('network')
     def test_security_group_create_with_duplicate_name(self):
         # Negative test:Security Group with duplicate name should not
@@ -97,7 +97,7 @@
                           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):
@@ -113,7 +113,7 @@
                           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):
@@ -122,7 +122,7 @@
         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):
@@ -134,7 +134,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'])
+    @decorators.attr(type=['negative'])
     @test.services('network')
     def test_update_security_group_with_invalid_sg_id(self):
         # Update security_group with invalid sg_id should fail
@@ -149,7 +149,7 @@
     @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'])
+    @decorators.attr(type=['negative'])
     @test.services('network')
     def test_update_security_group_with_invalid_sg_name(self):
         # Update security_group with invalid sg_name should fail
@@ -165,7 +165,7 @@
     @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'])
+    @decorators.attr(type=['negative'])
     @test.services('network')
     def test_update_security_group_with_invalid_sg_des(self):
         # Update security_group with invalid sg_des should fail
@@ -178,7 +178,7 @@
                           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):
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index e0c8887..e50b29a 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -46,8 +46,8 @@
     @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):
@@ -219,7 +219,7 @@
         _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')
     def test_add_remove_fixed_ip(self):
@@ -231,7 +231,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
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index eb23e4b..db42c6c 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -38,8 +38,8 @@
     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
+        cls.networks_client = cls.os_primary.networks_client
+        cls.subnets_client = cls.os_primary.subnets_client
 
     @classmethod
     def resource_setup(cls):
@@ -77,7 +77,7 @@
                         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
@@ -91,7 +91,7 @@
         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
@@ -226,91 +226,6 @@
             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.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)
-
-
 class ServersTestManualDisk(ServersTestJSON):
     disk_config = 'MANUAL'
 
diff --git a/tempest/api/compute/servers/test_delete_server.py b/tempest/api/compute/servers/test_delete_server.py
index 8ed55e0..2b03b2b 100644
--- a/tempest/api/compute/servers/test_delete_server.py
+++ b/tempest/api/compute/servers/test_delete_server.py
@@ -117,34 +117,3 @@
         waiters.wait_for_server_termination(self.client, server['id'])
         waiters.wait_for_volume_resource_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'])
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 57aa72e..9ab508d 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -55,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):
@@ -263,7 +263,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 512bd1e..1d3a790 100644
--- a/tempest/api/compute/servers/test_instance_actions_negative.py
+++ b/tempest/api/compute/servers/test_instance_actions_negative.py
@@ -17,7 +17,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 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_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index 3010caf..d89f44c 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):
@@ -36,15 +35,14 @@
         # servers are cleaned up automatically in the
         # tearDownClass method of the super-class.
         cls.deleted_fixtures = []
-        for _ in range(2):
-            srv = cls.create_test_server(wait_until='ACTIVE')
+        cls.create_test_server(wait_until='ACTIVE', min_count=2)
 
         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)
 
-    @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
@@ -56,7 +54,7 @@
                   if srv['id'] in deleted_ids]
         self.assertEqual([], 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
@@ -64,7 +62,7 @@
         servers = body['servers']
         self.assertEqual([], 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
@@ -72,7 +70,7 @@
         servers = body['servers']
         self.assertEqual([], 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
@@ -80,7 +78,7 @@
         servers = body['servers']
         self.assertEqual([], 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
@@ -94,7 +92,7 @@
         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
@@ -104,21 +102,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
@@ -126,7 +124,7 @@
         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
@@ -134,7 +132,7 @@
         body = self.client.list_servers(**changes_since)
         self.assertEqual(0, len(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
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 6354c57..90b0665 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -82,7 +82,7 @@
         """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
@@ -181,6 +181,6 @@
         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.")
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index d810ff7..8808510 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -133,7 +133,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 +145,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'])
@@ -325,9 +337,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 '
diff --git a/tempest/api/compute/servers/test_server_addresses.py b/tempest/api/compute/servers/test_server_addresses.py
index cf4ed85..92b1ff1 100644
--- a/tempest/api/compute/servers/test_server_addresses.py
+++ b/tempest/api/compute/servers/test_server_addresses.py
@@ -37,7 +37,7 @@
 
         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')
     def test_list_server_addresses(self):
@@ -55,7 +55,7 @@
                 self.assertTrue(address['addr'])
                 self.assertTrue(address['version'])
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('87bbc374-5538-4f64-b673-2b0e4443cc30')
     @test.services('network')
     def test_list_server_addresses_by_network(self):
diff --git a/tempest/api/compute/servers/test_server_addresses_negative.py b/tempest/api/compute/servers/test_server_addresses_negative.py
index 1884e4f..76a102b 100644
--- a/tempest/api/compute/servers/test_server_addresses_negative.py
+++ b/tempest/api/compute/servers/test_server_addresses_negative.py
@@ -36,7 +36,7 @@
         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')
     def test_list_server_addresses_invalid_server_id(self):
@@ -44,7 +44,7 @@
         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')
     def test_list_server_addresses_by_network_neg(self):
diff --git a/tempest/api/compute/servers/test_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index 22ce37d..482ba09 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -17,7 +17,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 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_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index 565d76d..5fac433 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -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,
@@ -110,7 +110,7 @@
 
     @decorators.idempotent_id('d0ccac79-0091-4cf4-a1ce-26162d0cc55f')
     @test.services('volume')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_rescued_vm_attach_volume(self):
         volume = self.create_volume()
 
@@ -130,7 +130,7 @@
 
     @decorators.idempotent_id('f56e465b-fe10-48bf-b75d-646cda3a8bc9')
     @test.services('volume')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_rescued_vm_detach_volume(self):
         volume = self.create_volume()
 
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index c6b3b40..a480a15 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -54,7 +54,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('dbbfd247-c40c-449e-8f6c-d2aa7c7da7cf')
     def test_server_name_blank(self):
         # Create a server with name parameter empty
@@ -63,7 +63,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 +78,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 +87,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 +96,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 +105,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 +118,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 +129,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 +139,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 +156,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,7 +166,7 @@
                           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
@@ -178,8 +178,8 @@
                           self.client.rebuild_server,
                           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
@@ -190,7 +190,7 @@
         self.assertRaises(lib_exc.NotFound, self.client.reboot_server,
                           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
@@ -200,7 +200,7 @@
                           nonexistent_server,
                           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 +208,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 +218,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)
+    @test.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 +239,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 +250,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 +260,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 +270,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 +282,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 +292,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 +303,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 +312,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 +327,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 +337,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 +345,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 +356,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 +366,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 +376,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 +386,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 +396,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 +410,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 +420,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 +436,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 +445,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 +454,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 +465,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 +475,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 +496,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,14 +506,14 @@
     @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')
     def test_create_server_from_non_bootable_volume(self):
@@ -566,7 +567,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 +577,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_negative.py b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
index b1374d2..173784a 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces_negative.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
@@ -33,7 +33,7 @@
         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')
     def test_list_virtual_interfaces_invalid_server_id(self):
diff --git a/tempest/api/compute/test_extensions.py b/tempest/api/compute/test_extensions.py
index f87bf6d..3b41775 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -31,7 +31,7 @@
     @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]
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_tenant_networks.py b/tempest/api/compute/test_tenant_networks.py
index b203c7e..18c5d38 100644
--- a/tempest/api/compute/test_tenant_networks.py
+++ b/tempest/api/compute/test_tenant_networks.py
@@ -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
diff --git a/tempest/api/compute/test_versions.py b/tempest/api/compute/test_versions.py
index dcab067..6141553 100644
--- a/tempest/api/compute/test_versions.py
+++ b/tempest/api/compute/test_versions.py
@@ -14,13 +14,12 @@
 
 from tempest.api.compute import base
 from tempest.lib import decorators
-from tempest import test
 
 
 class TestVersions(base.BaseV2ComputeTest):
 
     @decorators.idempotent_id('6c0a0990-43b6-4529-9b61-5fd8daf7c55c')
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     def test_list_api_versions(self):
         """Test that a get of the unversioned url returns the choices doc.
 
@@ -39,7 +38,7 @@
                          "The first listed version should be v2.0")
 
     @decorators.idempotent_id('b953a29e-929c-4a8e-81be-ec3a7e03cb76')
-    @test.attr(type='smoke')
+    @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_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_volumes_negative.py b/tempest/api/compute/volumes/test_volumes_negative.py
index 7676ee8..80db1be 100644
--- a/tempest/api/compute/volumes/test_volumes_negative.py
+++ b/tempest/api/compute/volumes/test_volumes_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
 
 CONF = config.CONF
 
@@ -37,7 +36,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 +45,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 +54,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 +64,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 +74,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 +83,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 +98,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_roles_negative.py b/tempest/api/identity/admin/v2/test_roles_negative.py
index f80721c..86d06e2 100644
--- a/tempest/api/identity/admin/v2/test_roles_negative.py
+++ b/tempest/api/identity/admin/v2/test_roles_negative.py
@@ -17,7 +17,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 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,7 +139,7 @@
             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
@@ -150,7 +149,7 @@
                           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
@@ -160,7 +159,7 @@
                           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,7 +240,7 @@
             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
diff --git a/tempest/api/identity/admin/v2/test_services.py b/tempest/api/identity/admin/v2/test_services.py
index f6d4a24..634cf21 100644
--- a/tempest/api/identity/admin/v2/test_services.py
+++ b/tempest/api/identity/admin/v2/test_services.py
@@ -17,7 +17,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 ServicesTestJSON(base.BaseIdentityV2AdminTest):
@@ -78,7 +77,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 69f0920..49bb949 100644
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ b/tempest/api/identity/admin/v2/test_tenant_negative.py
@@ -17,19 +17,18 @@
 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 6b7413c..ad9b983 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -15,7 +15,6 @@
 
 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
 
 
@@ -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']
@@ -48,14 +42,8 @@
     @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 3428c07..b4c9389 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -26,17 +26,12 @@
         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,
@@ -65,32 +60,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'],
diff --git a/tempest/api/identity/admin/v2/test_users.py b/tempest/api/identity/admin/v2/test_users.py
index df4ded8..2711a2d 100644
--- a/tempest/api/identity/admin/v2/test_users.py
+++ b/tempest/api/identity/admin/v2/test_users.py
@@ -19,9 +19,7 @@
 
 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 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'])
@@ -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
diff --git a/tempest/api/identity/admin/v2/test_users_negative.py b/tempest/api/identity/admin/v2/test_users_negative.py
index 442c1a7..4f47e41 100644
--- a/tempest/api/identity/admin/v2/test_users_negative.py
+++ b/tempest/api/identity/admin/v2/test_users_negative.py
@@ -17,7 +17,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 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 c752532..75e877a 100644
--- a/tempest/api/identity/admin/v3/test_credentials.py
+++ b/tempest/api/identity/admin/v3/test_credentials.py
@@ -17,7 +17,6 @@
 from tempest.api.identity import base
 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"}' % (
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 c2ab488..ac2faa9 100644
--- a/tempest/api/identity/admin/v3/test_default_project_id.py
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -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
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 d04e774..cddba53 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -18,7 +18,6 @@
 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
@@ -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
@@ -153,11 +149,7 @@
         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 +160,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..1a0b851 100644
--- a/tempest/api/identity/admin/v3/test_domains_negative.py
+++ b/tempest/api/identity/admin/v3/test_domains_negative.py
@@ -17,20 +17,15 @@
 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 +34,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 +50,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_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 0786d82..09f92e2 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -16,7 +16,6 @@
 from tempest.api.identity import base
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class EndPointsTestJSON(base.BaseIdentityV3AdminTest):
@@ -109,7 +108,7 @@
         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
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index d46e1f0..70dd7b5 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_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 EndpointsNegativeTestJSON(base.BaseIdentityV3AdminTest):
@@ -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 a423a12..2694402 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -16,7 +16,6 @@
 from tempest.api.identity import base
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class GroupsV3TestJSON(base.BaseIdentityV3AdminTest):
@@ -72,7 +71,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 +81,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 +100,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):
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index 3fe591b..49b6585 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -31,9 +31,7 @@
         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-'),
@@ -65,9 +63,7 @@
     @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 +87,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 +111,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 +126,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 +142,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 +186,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_policies.py b/tempest/api/identity/admin/v3/test_policies.py
index f74cb1c..730d469 100644
--- a/tempest/api/identity/admin/v3/test_policies.py
+++ b/tempest/api/identity/admin/v3/test_policies.py
@@ -16,7 +16,6 @@
 from tempest.api.identity import base
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class PoliciesTestJSON(base.BaseIdentityV3AdminTest):
@@ -44,7 +43,7 @@
         missing_pols = [p for p in policy_ids if p not in fetched_ids]
         self.assertEqual(0, len(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
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index 77a5c69..1b1d3f7 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
-
 from tempest.api.identity import base
 from tempest import config
 from tempest.lib.common.utils import data_utils
@@ -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 31e7302..33a9c8c 100644
--- a/tempest/api/identity/admin/v3/test_projects_negative.py
+++ b/tempest/api/identity/admin/v3/test_projects_negative.py
@@ -17,30 +17,28 @@
 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 56ee496..ac550a7 100644
--- a/tempest/api/identity/admin/v3/test_regions.py
+++ b/tempest/api/identity/admin/v3/test_regions.py
@@ -17,7 +17,6 @@
 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
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index 04be00b..adb467c 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -19,7 +19,6 @@
 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
 
@@ -39,9 +38,7 @@
         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'),
@@ -74,7 +71,7 @@
         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')
diff --git a/tempest/api/identity/admin/v3/test_services.py b/tempest/api/identity/admin/v3/test_services.py
index 53e005d..ad4e549 100644
--- a/tempest/api/identity/admin/v3/test_services.py
+++ b/tempest/api/identity/admin/v3/test_services.py
@@ -17,7 +17,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 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
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index fabb91c..8c5e63b 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -32,12 +32,9 @@
         # 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
@@ -63,27 +60,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'],
@@ -107,18 +96,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'],
@@ -133,10 +122,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'])
@@ -145,9 +133,10 @@
         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'])
@@ -157,7 +146,7 @@
     @decorators.idempotent_id('08ed85ce-2ba8-4864-b442-bcc61f16ae89')
     def test_get_available_project_scopes(self):
         manager_project_id = self.manager.credentials.project_id
-        admin_user_id = self.os_adm.credentials.user_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.
@@ -167,7 +156,7 @@
             self.roles_client.delete_role_from_user_on_project,
             manager_project_id, admin_user_id, admin_role_id)
 
-        assigned_project_ids = [self.os_adm.credentials.project_id,
+        assigned_project_ids = [self.os_admin.credentials.project_id,
                                 manager_project_id]
 
         # Get available project scopes
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index ec64e0c..3e6a2de 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -22,7 +22,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
 
 CONF = config.CONF
 
@@ -48,7 +47,8 @@
         # create a project that trusts will be granted on
         trustor_project_name = data_utils.rand_name(name='project')
         project = self.projects_client.create_project(
-            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)
 
@@ -63,7 +63,7 @@
             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
@@ -97,10 +97,10 @@
             identity_version='v3',
             username=trustor_username,
             password=trustor_password,
-            user_domain_id='default',
+            user_domain_id=CONF.identity.default_domain_id,
             tenant_name=trustor_project_name,
-            project_domain_id='default',
-            domain_id='default')
+            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
 
@@ -233,10 +233,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 +267,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 +279,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 0d12ba9..751962f 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -42,11 +42,7 @@
         # 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'
@@ -102,11 +98,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 +110,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'],
@@ -163,8 +147,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 93241c5..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 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 8317535..785485b 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -47,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
@@ -57,20 +57,24 @@
         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):
+    def create_test_user(self, **kwargs):
         if kwargs.get('password', None) is None:
-            user_password = data_utils.rand_password()
-            kwargs['password'] = user_password
+            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(
@@ -78,9 +82,9 @@
             self.users_client.delete_user, user['id'])
         return user
 
-    def setup_test_role(self, domain_id=None):
+    def setup_test_role(self, name=None, domain_id=None):
         """Set up a test role."""
-        params = {'name': data_utils.rand_name('test_role')}
+        params = {'name': name or data_utils.rand_name('test_role')}
         if domain_id:
             params['domain_id'] = domain_id
 
@@ -103,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):
@@ -123,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):
@@ -145,17 +155,16 @@
     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(
             test_utils.call_and_ignore_notfound_exc,
@@ -174,11 +183,12 @@
     @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_versions_client = cls.os.identity_versions_v3_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_versions_client =\
+            cls.os_primary.identity_versions_v3_client
 
 
 class BaseIdentityV3AdminTest(BaseIdentityV3Test):
@@ -197,28 +207,31 @@
     @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_adm.oauth_consumers_client
+        cls.oauth_consumers_client = cls.os_admin.oauth_consumers_client
+        cls.domain_config_client = cls.os_admin.domain_config_client
+        cls.endpoint_filter_client = cls.os_admin.endpoint_filter_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):
@@ -226,11 +239,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):
@@ -242,18 +257,17 @@
     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(
             test_utils.call_and_ignore_notfound_exc,
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..599b784 100644
--- a/tempest/api/identity/v2/test_ec2_credentials.py
+++ b/tempest/api/identity/v2/test_ec2_credentials.py
@@ -31,7 +31,7 @@
     @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/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 177a49d..c04c21b 100644
--- a/tempest/api/identity/v3/test_api_discovery.py
+++ b/tempest/api/identity/v3/test_api_discovery.py
@@ -15,14 +15,13 @@
 
 from tempest.api.identity import base
 from tempest.lib import decorators
-from tempest import test
 
 
 class TestApiDiscovery(base.BaseIdentityV3Test):
     """Tests for API discovery features."""
 
     @decorators.idempotent_id('721f480f-35b6-46c7-846e-047e6acea0dc')
-    @test.attr(type='smoke')
+    @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
@@ -35,7 +34,7 @@
             for res in expected_resources:
                 self.assertIn(res, version)
 
-    @test.attr(type='smoke')
+    @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']
@@ -46,7 +45,7 @@
         for res in expected_resources:
             self.assertIn(res, keys)
 
-    @test.attr(type='smoke')
+    @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']
@@ -61,7 +60,7 @@
         for s_type in supported_types:
             self.assertIn(s_type, media_types)
 
-    @test.attr(type='smoke')
+    @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']
diff --git a/tempest/api/identity/v3/test_projects.py b/tempest/api/identity/v3/test_projects.py
index 570be99..0ae35ea 100644
--- a/tempest/api/identity/v3/test_projects.py
+++ b/tempest/api/identity/v3/test_projects.py
@@ -25,9 +25,9 @@
     @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 1dc1df6..c9d7a4d 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -24,7 +24,7 @@
     @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
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index e7998ee..1f099df 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -32,7 +32,7 @@
     @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
 
     def _update_password(self, original_password, 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 c586960..70ba2fe 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -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,14 +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.versions_client = cls.os.image_versions_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,
@@ -167,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
 
@@ -196,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):
@@ -206,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_negative.py b/tempest/api/image/v1/test_image_members_negative.py
index f075cab..2748bd5 100644
--- a/tempest/api/image/v1/test_image_members_negative.py
+++ b/tempest/api/image/v1/test_image_members_negative.py
@@ -16,12 +16,11 @@
 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_negative.py b/tempest/api/image/v1/test_images_negative.py
index 42ff02f..690b8da 100644
--- a/tempest/api/image/v1/test_images_negative.py
+++ b/tempest/api/image/v1/test_images_negative.py
@@ -18,13 +18,12 @@
 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 30f9ae2..2e68efd 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 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
@@ -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"""
@@ -328,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_negative.py b/tempest/api/image/v2/test_images_negative.py
index 6e5e726..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,14 +91,14 @@
                           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')
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ab980a34-8410-40eb-872b-f264752f46e5')
     def test_delete_protected_image(self):
         # Create a protected image
diff --git a/tempest/api/image/v2/test_images_tags_negative.py b/tempest/api/image/v2/test_images_tags_negative.py
index 7032dd4..440fa36 100644
--- a/tempest/api/image/v2/test_images_tags_negative.py
+++ b/tempest/api/image/v2/test_images_tags_negative.py
@@ -16,12 +16,11 @@
 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
index 24f104c..84f1068 100644
--- a/tempest/api/image/v2/test_versions.py
+++ b/tempest/api/image/v2/test_versions.py
@@ -14,13 +14,12 @@
 
 from tempest.api.image import base
 from tempest.lib import decorators
-from tempest import test
 
 
 class VersionsTest(base.BaseV2ImageTest):
 
     @decorators.idempotent_id('659ea30a-a17c-4317-832c-0f68ed23c31d')
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     def test_list_versions(self):
         versions = self.versions_client.list_versions()['versions']
         expected_resources = ('id', 'links', 'status')
diff --git a/tempest/api/network/admin/test_external_networks_negative.py b/tempest/api/network/admin/test_external_networks_negative.py
index 770d91f..0709d2a 100644
--- a/tempest/api/network/admin/test_external_networks_negative.py
+++ b/tempest/api/network/admin/test_external_networks_negative.py
@@ -19,14 +19,13 @@
 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.')
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 9a17817..11f520a 100644
--- a/tempest/api/network/admin/test_floating_ips_admin_actions.py
+++ b/tempest/api/network/admin/test_floating_ips_admin_actions.py
@@ -38,7 +38,7 @@
     @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):
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index 42a8f34..85b2472 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -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.
@@ -53,14 +48,10 @@
     @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,46 +59,6 @@
             raise exceptions.InvalidConfiguration(msg)
         cls.router = cls.create_router()
 
-        # TODO(ylobankov): Delete this 'if' block once 'dvr_extra_resources'
-        # option is deleted. Currently this option is deprecated for removal.
-        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.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)
-
-    # TODO(ylobankov): Delete this cleanup block once 'dvr_extra_resources'
-    # option is deleted. Currently this option is deprecated for removal.
-    @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/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index 2c639da..21688d2 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -39,7 +39,7 @@
             msg = "quotas extension not enabled."
             raise cls.skipException(msg)
 
-    @test.attr(type=['negative'])
+    @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/base.py b/tempest/api/network/base.py
index a724dc9..359a444 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -68,20 +68,21 @@
     @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
 
     @classmethod
     def resource_setup(cls):
@@ -106,7 +107,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,16 +259,16 @@
     @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)
+            cls.os_admin.metering_label_rules_client)
 
     @classmethod
     def create_metering_label(cls, name, description):
diff --git a/tempest/api/network/test_dhcp_ipv6.py b/tempest/api/network/test_dhcp_ipv6.py
index a7ae765..9d6d700 100644
--- a/tempest/api/network/test_dhcp_ipv6.py
+++ b/tempest/api/network/test_dhcp_ipv6.py
@@ -16,6 +16,7 @@
 import random
 
 import netaddr
+from oslo_utils import netutils
 
 from tempest.api.network import base
 from tempest.common.utils import net_info
@@ -92,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')
@@ -188,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']])
@@ -236,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..014d064 100644
--- a/tempest/api/network/test_extensions.py
+++ b/tempest/api/network/test_extensions.py
@@ -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
diff --git a/tempest/api/network/test_floating_ips.py b/tempest/api/network/test_floating_ips.py
index 1dc574b..f0f92ac 100644
--- a/tempest/api/network/test_floating_ips.py
+++ b/tempest/api/network/test_floating_ips.py
@@ -64,7 +64,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
@@ -171,7 +171,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 cb29d3d..f5830ab 100644
--- a/tempest/api/network/test_floating_ips_negative.py
+++ b/tempest/api/network/test_floating_ips_negative.py
@@ -52,7 +52,7 @@
         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(
@@ -61,7 +61,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,
@@ -71,7 +71,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 fc9f465..269f2c2 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -174,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
@@ -196,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
@@ -220,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
@@ -241,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
@@ -263,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
@@ -368,7 +368,7 @@
             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')
     @testtools.skipUnless(CONF.network.public_network_id,
@@ -452,7 +452,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
@@ -468,7 +468,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()]
@@ -503,7 +503,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 5cb12fe..bc4f41f 100644
--- a/tempest/api/network/test_networks_negative.py
+++ b/tempest/api/network/test_networks_negative.py
@@ -18,33 +18,32 @@
 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 376d4ae..69a1441 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -52,7 +52,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 +100,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 +125,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 +153,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
@@ -342,7 +342,7 @@
         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'),
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 8df5248..97ad4d4 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -41,7 +41,7 @@
                            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.')
@@ -122,7 +122,7 @@
             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()
@@ -141,7 +141,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()
@@ -376,7 +376,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 8d680e9..fdd8dd8 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -45,7 +45,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,
@@ -54,7 +54,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()
@@ -66,7 +66,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(
@@ -82,7 +82,7 @@
                           self.router['id'],
                           subnet02['id'])
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7101cc02-058a-11e7-93e1-fa163e4fa634')
     @test.requires_ext(extension='ext-gw-mode', service='network')
     @testtools.skipUnless(CONF.network.public_network_id,
@@ -107,7 +107,7 @@
                           self.admin_routers_client.create_router,
                           external_gateway_info=external_gateway_info)
 
-    @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'],
@@ -116,21 +116,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')
@@ -158,7 +158,7 @@
         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 d7e5149..0d5e230 100644
--- a/tempest/api/network/test_security_groups.py
+++ b/tempest/api/network/test_security_groups.py
@@ -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,7 +80,7 @@
         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()
@@ -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 218a79a..f51fb33 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -33,7 +33,7 @@
             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,7 @@
                           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.
@@ -185,7 +185,7 @@
                           group_create_body['security_group']['id'],
                           name="default")
 
-    @test.attr(type=['negative'])
+    @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.
@@ -211,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.
@@ -227,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_subnetpools_extensions.py b/tempest/api/network/test_subnetpools_extensions.py
index be6bffc..01d7db2 100644
--- a/tempest/api/network/test_subnetpools_extensions.py
+++ b/tempest/api/network/test_subnetpools_extensions.py
@@ -46,7 +46,7 @@
             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_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..2bac8d3 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -79,10 +79,11 @@
     @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):
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index d882731..0a72d75 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -56,11 +56,10 @@
 
     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)
+            resp, body = self.bulk_client.upload_archive(
+                upload_path='', data=mydata, archive_file_format='tar')
         return resp, body
 
     def _check_contents_deleted(self, container_name):
@@ -113,9 +112,7 @@
         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, body = 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,
@@ -140,10 +137,8 @@
         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, body = 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,
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 84e4508..092d369 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -54,8 +54,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 +67,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,7 +76,7 @@
         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')
     def test_upload_valid_object(self):
@@ -87,7 +87,7 @@
 
         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')
     def test_admin_modify_quota(self):
@@ -108,9 +108,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..55a6c7a 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -53,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
@@ -66,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(AccountQuotasNegativeTest, self).tearDown()
 
     @classmethod
@@ -75,18 +75,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')
     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 de64dfe..e54b6e7 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -23,7 +23,6 @@
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -54,7 +53,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
@@ -265,7 +264,7 @@
         self.assertEqual(sorted(orig_container_list, reverse=True),
                          container_list)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('4894c312-6056-4587-8d6f-86ffbf861f80')
     def test_list_account_metadata(self):
         # list all account metadata
@@ -273,13 +272,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):
@@ -292,7 +293,8 @@
     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()
@@ -300,14 +302,17 @@
         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_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()
@@ -318,7 +323,8 @@
         # 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()
@@ -329,9 +335,11 @@
         # 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()
@@ -341,11 +349,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, body = (
+            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()
@@ -354,4 +364,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..e98a4f5 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
 
@@ -32,7 +31,7 @@
         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_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index c0f7b3a..9c9d821 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -17,7 +17,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
 
 CONF = config.CONF
 
@@ -47,7 +46,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,7 +60,7 @@
                           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
@@ -78,7 +77,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 +92,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 +110,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,7 +128,7 @@
                           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
@@ -153,7 +152,7 @@
                           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
@@ -174,7 +173,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
@@ -199,7 +198,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
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index 7ff337e..8266341 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -50,7 +50,7 @@
 
     @decorators.idempotent_id('9a0fb034-86af-4df0-86fa-f8bd7db21ae0')
     @test.requires_ext(extension='container_quotas', service='object')
-    @test.attr(type="smoke")
+    @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")
@@ -67,7 +67,7 @@
 
     @decorators.idempotent_id('22eeeb2b-3668-4160-baef-44790f65a5a0')
     @test.requires_ext(extension='container_quotas', service='object')
-    @test.attr(type="smoke")
+    @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")
@@ -84,7 +84,7 @@
 
     @decorators.idempotent_id('3a387039-697a-44fc-a9c0-935de31f426b')
     @test.requires_ext(extension='container_quotas', service='object')
-    @test.attr(type="smoke")
+    @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 a82d2cc..2b5692d 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,7 +23,7 @@
         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')
@@ -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
@@ -271,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..a8d70c5 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
 
@@ -36,7 +35,7 @@
             _, 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_sync.py b/tempest/api/object_storage/test_container_sync.py
index 1c45a98..3c11a51 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -22,7 +22,6 @@
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -128,7 +127,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..9eae138 100644
--- a/tempest/api/object_storage/test_container_sync_middleware.py
+++ b/tempest/api/object_storage/test_container_sync_middleware.py
@@ -37,7 +37,7 @@
         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')
     def test_container_synchronization(self):
diff --git a/tempest/api/object_storage/test_object_formpost.py b/tempest/api/object_storage/test_object_formpost.py
index c100629..3a2233a 100644
--- a/tempest/api/object_storage/test_object_formpost.py
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -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()
 
diff --git a/tempest/api/object_storage/test_object_formpost_negative.py b/tempest/api/object_storage/test_object_formpost_negative.py
index c4e6a3a..c56d91a 100644
--- a/tempest/api/object_storage/test_object_formpost_negative.py
+++ b/tempest/api/object_storage/test_object_formpost_negative.py
@@ -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()
 
@@ -108,7 +110,7 @@
 
     @decorators.idempotent_id('d3fb3c4d-e627-48ce-9379-a1631f21336d')
     @test.requires_ext(extension='formpost', service='object')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     def test_post_object_using_form_expired(self):
         body, content_type = self.get_multipart_form(expires=1)
         time.sleep(2)
@@ -125,7 +127,7 @@
 
     @decorators.idempotent_id('b277257f-113c-4499-b8d1-5fead79f7360')
     @test.requires_ext(extension='formpost', service='object')
-    @test.attr(type=['negative'])
+    @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 d463827..21ea6ae 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -24,7 +24,6 @@
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 CONF = config.CONF
 
@@ -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
@@ -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
@@ -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
@@ -530,7 +529,7 @@
         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)
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 912deab..4b506f8 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -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()
 
@@ -110,7 +111,8 @@
     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
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 9c1393f..f4d63fd 100644
--- a/tempest/api/object_storage/test_object_temp_url_negative.py
+++ b/tempest/api/object_storage/test_object_temp_url_negative.py
@@ -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)
+        resp, _ = cls.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=cls.metadata)
 
         cls.delete_containers()
 
@@ -91,7 +92,7 @@
 
         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')
     def test_get_object_after_expiration_time(self):
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index 8d3d344..d9d8017 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -42,13 +42,13 @@
     @classmethod
     def setup_clients(cls):
         super(BaseOrchestrationTest, cls).setup_clients()
-        cls.orchestration_client = cls.os.orchestration_client
+        cls.orchestration_client = cls.os_primary.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.images_v2_client = cls.os.image_client_v2
-        cls.volumes_client = cls.os.volumes_v2_client
+        cls.servers_client = cls.os_primary.servers_client
+        cls.keypairs_client = cls.os_primary.keypairs_client
+        cls.networks_client = cls.os_primary.networks_client
+        cls.images_v2_client = cls.os_primary.image_client_v2
+        cls.volumes_client = cls.os_primary.volumes_v2_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index 6faca71..36881bf 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -41,9 +41,9 @@
     @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
+        cls.subnets_client = cls.os_primary.subnets_client
+        cls.ports_client = cls.os_primary.ports_client
+        cls.routers_client = cls.os_primary.routers_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/orchestration/stacks/test_resource_types.py b/tempest/api/orchestration/stacks/test_resource_types.py
index 63376d5..6d0707e 100644
--- a/tempest/api/orchestration/stacks/test_resource_types.py
+++ b/tempest/api/orchestration/stacks/test_resource_types.py
@@ -12,12 +12,11 @@
 
 from tempest.api.orchestration import base
 from tempest.lib import decorators
-from tempest import test
 
 
 class ResourceTypesTest(base.BaseOrchestrationTest):
 
-    @test.attr(type='smoke')
+    @decorators.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."""
@@ -25,7 +24,7 @@
         self.assertIsInstance(resource_types, list)
         self.assertIn('OS::Nova::Server', resource_types)
 
-    @test.attr(type='smoke')
+    @decorators.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."""
@@ -38,7 +37,7 @@
                                        'attributes', 'resource_type')
             self.assertEqual(resource_type, type_schema['resource_type'])
 
-    @test.attr(type='smoke')
+    @decorators.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."""
diff --git a/tempest/api/orchestration/stacks/test_soft_conf.py b/tempest/api/orchestration/stacks/test_soft_conf.py
index dae0bab..8192999 100644
--- a/tempest/api/orchestration/stacks/test_soft_conf.py
+++ b/tempest/api/orchestration/stacks/test_soft_conf.py
@@ -14,7 +14,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 TestSoftwareConfig(base.BaseOrchestrationTest):
@@ -75,7 +74,7 @@
         self.assertRaises(
             lib_exc.NotFound, self.client.show_software_config, config_id)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('136162ed-9445-4b9c-b7fc-306af8b5da99')
     def test_get_software_config(self):
         """Testing software config get."""
@@ -83,7 +82,7 @@
             api_config = self.client.show_software_config(conf['id'])
             self._validate_config(conf, api_config)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('1275c835-c967-4a2c-8d5d-ad533447ed91')
     def test_get_deployment_list(self):
         """Getting a list of all deployments"""
@@ -92,7 +91,7 @@
                       deploy_list['software_deployments']]
         self.assertIn(self.deployment_id, deploy_ids)
 
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('fe7cd9f9-54b1-429c-a3b7-7df8451db913')
     def test_get_deployment_metadata(self):
         """Testing deployment metadata get"""
@@ -110,7 +109,7 @@
         self.assertEqual(config_id,
                          deployment['software_deployment']['config_id'])
 
-    @test.attr(type='smoke')
+    @decorators.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."""
@@ -123,7 +122,7 @@
         self._validate_deployment(self.action, self.status,
                                   self.status_reason, self.configs[0]['id'])
 
-    @test.attr(type='smoke')
+    @decorators.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."""
@@ -149,7 +148,7 @@
                 metadata['metadata'][0][key],
                 test_metadata['metadata'][0][key])
 
-    @test.attr(type='smoke')
+    @decorators.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."""
diff --git a/tempest/api/orchestration/stacks/test_stacks.py b/tempest/api/orchestration/stacks/test_stacks.py
index 0be135b..dc1bb4f 100644
--- a/tempest/api/orchestration/stacks/test_stacks.py
+++ b/tempest/api/orchestration/stacks/test_stacks.py
@@ -13,19 +13,18 @@
 from tempest.api.orchestration import base
 from tempest.lib.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.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.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')
diff --git a/tempest/api/orchestration/stacks/test_swift_resources.py b/tempest/api/orchestration/stacks/test_swift_resources.py
index 97fdac4..d63cde8 100644
--- a/tempest/api/orchestration/stacks/test_swift_resources.py
+++ b/tempest/api/orchestration/stacks/test_swift_resources.py
@@ -40,8 +40,8 @@
     @classmethod
     def setup_clients(cls):
         super(SwiftResourcesTestJSON, cls).setup_clients()
-        cls.account_client = cls.os.account_client
-        cls.container_client = cls.os.container_client
+        cls.account_client = cls.os_primary.account_client
+        cls.container_client = cls.os_primary.container_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/orchestration/stacks/test_templates_negative.py b/tempest/api/orchestration/stacks/test_templates_negative.py
index a90abe2..eb93d95 100644
--- a/tempest/api/orchestration/stacks/test_templates_negative.py
+++ b/tempest/api/orchestration/stacks/test_templates_negative.py
@@ -15,7 +15,6 @@
 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):
@@ -35,7 +34,7 @@
         super(TemplateYAMLNegativeTestJSON, cls).resource_setup()
         cls.parameters = {}
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5586cbca-ddc4-4152-9db8-fa1ce5fc1876')
     def test_validate_template_url(self):
         """Validating template passing url to it."""
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..016d87a
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_quota_classes.py
@@ -0,0 +1,89 @@
+# 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_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']
+        body.pop('id')
+
+        # Restore the defaults when the test is done
+        self.addCleanup(self._restore_default_quotas, body.copy())
+
+        # 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 body.items():
+            if default >= 0:
+                body[quota] = default + 1
+
+        LOG.debug("Update limits for the default quota class set")
+        update_body = self.admin_quota_classes_client.update_quota_class_set(
+            'default', **body)['quota_class_set']
+        self.assertThat(update_body.items(),
+                        matchers.ContainsAll(body.items()))
+
+        # Verify current project's default quotas
+        default_quotas = self.admin_quotas_client.show_default_quota_set(
+            self.os_adm.credentials.tenant_id)['quota_set']
+        self.assertThat(default_quotas.items(),
+                        matchers.ContainsAll(body.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(body.items()))
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 8bf416a..ae4b579 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -13,6 +13,7 @@
 #    under the License.
 
 from tempest.api.volume import base
+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
@@ -26,12 +27,23 @@
 
     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(BaseVolumeQuotasAdminTestJSON, cls).setup_credentials()
-        cls.demo_tenant_id = cls.os.credentials.tenant_id
+        cls.demo_tenant_id = cls.os_primary.credentials.tenant_id
         cls.alt_client = cls.os_alt.volumes_client
 
+    @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):
         quotas = (self.admin_quotas_client.show_quota_set(self.demo_tenant_id)
@@ -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)
@@ -136,13 +148,13 @@
             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(
+        self.alt_transfer_client.accept_volume_transfer(
             transfer_id, auth_key=auth_key)['transfer']
 
         # Verify volume transferred is available
diff --git a/tempest/api/volume/admin/test_volume_quotas_negative.py b/tempest/api/volume/admin/test_volume_quotas_negative.py
index 10cf890..d127b5f 100644
--- a/tempest/api/volume/admin/test_volume_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_quotas_negative.py
@@ -17,7 +17,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
 
@@ -28,7 +27,7 @@
     @classmethod
     def setup_credentials(cls):
         super(BaseVolumeQuotasNegativeTestJSON, cls).setup_credentials()
-        cls.demo_tenant_id = cls.os.credentials.tenant_id
+        cls.demo_tenant_id = cls.os_primary.credentials.tenant_id
 
     @classmethod
     def resource_setup(cls):
@@ -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
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 e5c78cb..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,7 +17,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
 
@@ -34,7 +33,7 @@
     @classmethod
     def setup_credentials(cls):
         super(VolumeSnapshotQuotasNegativeTestJSON, cls).setup_credentials()
-        cls.demo_tenant_id = cls.os.credentials.tenant_id
+        cls.demo_tenant_id = cls.os_primary.credentials.tenant_id
 
     @classmethod
     def resource_setup(cls):
@@ -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,
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 4efc44b..4fa934e 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
@@ -17,7 +17,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 ExtraSpecsNegativeTest(base.BaseVolumeAdminTest):
@@ -28,7 +27,7 @@
         extra_specs = {"spec1": "val1"}
         cls.volume_type = cls.create_volume_type(extra_specs=extra_specs)
 
-    @test.attr(type=['negative'])
+    @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
@@ -37,7 +36,7 @@
             self.admin_volume_types_client.update_volume_type_extra_specs,
             self.volume_type['id'], "spec1", None)
 
-    @test.attr(type=['negative'])
+    @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.
@@ -48,7 +47,7 @@
             self.volume_type['id'], data_utils.rand_uuid(),
             extra_spec)
 
-    @test.attr(type=['negative'])
+    @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.
@@ -58,7 +57,7 @@
             self.admin_volume_types_client.update_volume_type_extra_specs,
             self.volume_type['id'], None, extra_spec)
 
-    @test.attr(type=['negative'])
+    @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
@@ -70,7 +69,7 @@
             self.volume_type['id'], list(extra_spec)[0],
             extra_spec)
 
-    @test.attr(type=['negative'])
+    @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
@@ -81,7 +80,7 @@
             self.admin_volume_types_client.create_volume_type_extra_specs,
             data_utils.rand_uuid(), extra_specs)
 
-    @test.attr(type=['negative'])
+    @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.
@@ -90,7 +89,7 @@
             self.admin_volume_types_client.create_volume_type_extra_specs,
             self.volume_type['id'], None)
 
-    @test.attr(type=['negative'])
+    @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.
@@ -99,7 +98,7 @@
             self.admin_volume_types_client.create_volume_type_extra_specs,
             self.volume_type['id'], extra_specs=['invalid'])
 
-    @test.attr(type=['negative'])
+    @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
@@ -109,7 +108,7 @@
             self.admin_volume_types_client.delete_volume_type_extra_specs,
             data_utils.rand_uuid(), "spec1")
 
-    @test.attr(type=['negative'])
+    @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.
@@ -118,7 +117,7 @@
             self.admin_volume_types_client.list_volume_types_extra_specs,
             data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @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.
@@ -127,7 +126,7 @@
             self.admin_volume_types_client.show_volume_type_extra_specs,
             data_utils.rand_uuid(), "spec1")
 
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c881797d-12ff-4f1a-b09d-9f6212159753')
     def test_get_nonexistent_extra_spec_id(self):
         # Should not get volume type extra spec for nonexistent extra spec
diff --git a/tempest/api/volume/admin/test_volume_types_negative.py b/tempest/api/volume/admin/test_volume_types_negative.py
index bac2ea3..4cad52a 100644
--- a/tempest/api/volume/admin/test_volume_types_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_negative.py
@@ -17,12 +17,11 @@
 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 VolumeTypesNegativeTest(base.BaseVolumeAdminTest):
 
-    @test.attr(type=['negative'])
+    @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.
@@ -31,7 +30,7 @@
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.create_volume, **params)
 
-    @test.attr(type=['negative'])
+    @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.
@@ -39,7 +38,7 @@
             lib_exc.BadRequest,
             self.admin_volume_types_client.create_volume_type, name='')
 
-    @test.attr(type=['negative'])
+    @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.
@@ -47,7 +46,7 @@
                           self.admin_volume_types_client.show_volume_type,
                           data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @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.
@@ -55,7 +54,7 @@
                           self.admin_volume_types_client.delete_volume_type,
                           data_utils.rand_uuid())
 
-    @test.attr(type=['negative'])
+    @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.
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index 7f291e9..acff7cd 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -14,7 +14,12 @@
 #    under the License.
 
 from tempest.api.volume import base
+from tempest.common import waiters
+from tempest import config
 from tempest.lib import decorators
+from tempest import test
+
+CONF = config.CONF
 
 
 class VolumesActionsTest(base.BaseVolumeAdminTest):
@@ -60,3 +65,36 @@
     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')
+
+    @decorators.idempotent_id('d38285d9-929d-478f-96a5-00e66a115b81')
+    @test.services('compute')
+    def test_force_detach_volume(self):
+        # Create a server and a volume
+        server_id = self.create_server(wait_until='ACTIVE')['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')
+
+        # 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 afc3281..a6f9246 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -121,7 +121,7 @@
                                                 'available')
 
     @decorators.idempotent_id('47a35425-a891-4e13-961c-c45deea21e94')
-    def test_volume_backup_reset_status(self):
+    def test_volume_backup_reset_status_force_delete(self):
         # Create a volume
         volume = self.create_volume()
         # Create a backup
@@ -136,3 +136,6 @@
                                                       status="error")
         waiters.wait_for_volume_resource_status(self.admin_backups_client,
                                                 backup['id'], 'error')
+        # Force delete a backup volume when backup is in error state.
+        self.admin_backups_client.force_delete_backup(backup['id'])
+        self.admin_backups_client.wait_for_resource_deletion(backup['id'])
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 0cd5ec7..c877969 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -64,19 +64,21 @@
     @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
 
-        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
+        if CONF.service_available.glance:
+            cls.images_client = cls.os_primary.image_client_v2
+
+        cls.snapshots_client = cls.os_primary.snapshots_v2_client
+        cls.volumes_client = cls.os_primary.volumes_v2_client
+        cls.backups_client = cls.os_primary.backups_v2_client
+        cls.volumes_extension_client =\
+            cls.os_primary.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.messages_client = cls.os.volume_v3_messages_client
-        cls.versions_client = cls.os.volume_v3_versions_client
+            cls.os_primary.volume_v2_availability_zone_client)
+        cls.volume_limits_client = cls.os_primary.volume_v2_limits_client
+        cls.messages_client = cls.os_primary.volume_v3_messages_client
+        cls.versions_client = cls.os_primary.volume_v3_versions_client
 
     def setUp(self):
         super(BaseVolumeTest, self).setUp()
@@ -114,9 +116,8 @@
             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)
 
         if 'name' not in kwargs:
@@ -143,16 +144,23 @@
                                                 snapshot['id'], 'available')
         return snapshot
 
-    def create_backup(self, volume_id, backup_client=None, **kwargs):
+    def create_backup(self, volume_id, backup_client=None,
+                      wait_until="available", **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_volume_resource_status(backup_client, backup['id'],
-                                                'available')
+
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        backup_client.delete_backup, backup['id'])
+        waiters.wait_for_volume_resource_status(backup_client,
+                                                backup['id'],
+                                                wait_until)
         return backup
 
     # NOTE(afazekas): these create_* and clean_* could be defined
@@ -239,27 +247,28 @@
     def setup_clients(cls):
         super(BaseVolumeAdminTest, cls).setup_clients()
 
-        cls.admin_volume_qos_client = cls.os_adm.volume_qos_v2_client
+        cls.admin_volume_qos_client = cls.os_admin.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_manage_client = cls.os_adm.volume_manage_v2_client
-        cls.admin_volume_client = cls.os_adm.volumes_v2_client
-        cls.admin_hosts_client = cls.os_adm.volume_hosts_v2_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
+        cls.admin_hosts_client = cls.os_admin.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.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_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.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_adm.volume_capabilities_v2_client
+            cls.os_admin.volume_capabilities_v2_client
         cls.admin_scheduler_stats_client = \
-            cls.os_adm.volume_scheduler_stats_v2_client
-        cls.admin_messages_client = cls.os_adm.volume_v3_messages_client
-        cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
+            cls.os_admin.volume_scheduler_stats_v2_client
+        cls.admin_messages_client = cls.os_admin.volume_v3_messages_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/volume/test_extensions.py b/tempest/api/volume/test_extensions.py
index 91bfa33..39ce00c 100644
--- a/tempest/api/volume/test_extensions.py
+++ b/tempest/api/volume/test_extensions.py
@@ -32,7 +32,7 @@
         # 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))
diff --git a/tempest/api/volume/test_versions.py b/tempest/api/volume/test_versions.py
index 76f2a99..0083a3b 100644
--- a/tempest/api/volume/test_versions.py
+++ b/tempest/api/volume/test_versions.py
@@ -14,7 +14,6 @@
 
 from tempest.api.volume import base
 from tempest.lib import decorators
-from tempest import test
 
 
 class VersionsTest(base.BaseVolumeTest):
@@ -22,7 +21,7 @@
     _api_version = 3
 
     @decorators.idempotent_id('77838fc4-b49b-4c64-9533-166762517369')
-    @test.attr(type='smoke')
+    @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
diff --git a/tempest/api/volume/test_volume_absolute_limits.py b/tempest/api/volume/test_volume_absolute_limits.py
index 836e489..870b9f0 100644
--- a/tempest/api/volume/test_volume_absolute_limits.py
+++ b/tempest/api/volume/test_volume_absolute_limits.py
@@ -21,7 +21,10 @@
 CONF = config.CONF
 
 
-class AbsoluteLimitsTests(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):
 
     # avoid existing volumes of pre-defined tenant
     force_tenant_isolation = True
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_transfers.py b/tempest/api/volume/test_volume_transfers.py
index bfb42c6..5fd1904 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -28,15 +28,18 @@
     def setup_clients(cls):
         super(VolumesTransfersTest, cls).setup_clients()
 
-        cls.client = cls.volumes_client
-        cls.alt_client = cls.os_alt.volumes_client
-        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(
@@ -44,7 +47,7 @@
         transfer_id = transfer['id']
         auth_key = transfer['auth_key']
         waiters.wait_for_volume_resource_status(
-            self.client, volume['id'], 'awaiting-transfer')
+            self.volumes_client, volume['id'], 'awaiting-transfer')
 
         # Get a volume transfer
         body = self.client.show_volume_transfer(transfer_id)['transfer']
@@ -58,31 +61,34 @@
         # 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_resource_status(self.alt_client,
+        waiters.wait_for_volume_resource_status(self.alt_volumes_client,
                                                 volume['id'], 'available')
 
     @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']
+        transfer_id = self.client.create_volume_transfer(
+            volume_id=volume['id'])['transfer']['id']
         waiters.wait_for_volume_resource_status(
-            self.client, volume['id'], 'awaiting-transfer')
+            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_resource_status(
-            self.client, volume['id'], 'available')
+            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 a2a3d27..0e7f1e9 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -19,7 +19,6 @@
 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
@@ -28,20 +27,6 @@
 class VolumesActionsTest(base.BaseVolumeTest):
 
     @classmethod
-    def setup_clients(cls):
-        super(VolumesActionsTest, cls).setup_clients()
-        if CONF.service_available.glance:
-            # Check if glance v1 is available to determine which client to use.
-            if CONF.image_feature_enabled.api_v1:
-                cls.image_client = cls.os.image_client
-            elif CONF.image_feature_enabled.api_v2:
-                cls.image_client = cls.os.image_client_v2
-            else:
-                raise exceptions.InvalidConfiguration(
-                    'Either api_v1 or api_v2 must be True in '
-                    '[image-feature-enabled].')
-
-    @classmethod
     def resource_setup(cls):
         super(VolumesActionsTest, cls).resource_setup()
 
@@ -49,7 +34,7 @@
         cls.volume = cls.create_volume()
 
     @decorators.idempotent_id('fff42874-7db5-4487-a8e1-ddda5fb5288d')
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @test.services('compute')
     def test_attach_detach_volume_to_instance(self):
         # Create a server
@@ -114,16 +99,16 @@
         # 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.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_image_status(self.images_client, image_id, 'active')
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 self.volume['id'], 'available')
 
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index 925beee..5ad209c 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -50,6 +50,8 @@
                                                 '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 a volume with metadata
diff --git a/tempest/api/volume/test_volumes_clone_negative.py b/tempest/api/volume/test_volumes_clone_negative.py
index 9169c19..bba7a0b 100644
--- a/tempest/api/volume/test_volumes_clone_negative.py
+++ b/tempest/api/volume/test_volumes_clone_negative.py
@@ -17,7 +17,6 @@
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions
-from tempest import test
 
 CONF = config.CONF
 
@@ -30,7 +29,7 @@
         if not CONF.volume_feature_enabled.clone:
             raise cls.skipException("Cinder volume clones are disabled")
 
-    @test.attr(type=['negative'])
+    @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
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 65027bf..712254e 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -115,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')
     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)
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 0570797..9787160 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -13,14 +13,16 @@
 #    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.lib.common.utils import data_utils
 from tempest.lib import decorators
-from tempest import test
 
 
 class VolumesListTestJSON(base.BaseVolumeTest):
@@ -44,7 +46,7 @@
             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):
@@ -59,6 +61,10 @@
     def resource_setup(cls):
         super(VolumesListTestJSON, cls).resource_setup()
         cls.name = cls.VOLUME_FIELDS[1]
+
+        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 +72,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"""
@@ -91,7 +98,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
@@ -217,3 +224,157 @@
         params = {self.name: volume[self.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))
+
+        _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')
+    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 609a031..fc3bcab 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -13,12 +13,19 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import six
+
 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
 from tempest.lib import exceptions as lib_exc
 from tempest import test
 
+CONF = config.CONF
+
 
 class VolumesNegativeTest(base.BaseVolumeTest):
 
@@ -30,28 +37,47 @@
         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
         self.assertRaises(lib_exc.BadRequest,
                           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
@@ -59,88 +85,88 @@
         self.assertRaises(lib_exc.BadRequest,
                           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
         self.assertRaises(lib_exc.BadRequest,
                           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
         self.assertRaises(lib_exc.BadRequest,
                           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
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
                           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
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
                           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
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
                           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):
         self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           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):
         self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           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):
         self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           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')
     def test_attach_volumes_with_nonexistent_volume_id(self):
@@ -152,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.
@@ -168,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.
@@ -177,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.
@@ -186,7 +212,7 @@
                           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.
@@ -194,7 +220,7 @@
         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.
@@ -202,21 +228,21 @@
         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.
@@ -228,7 +254,7 @@
         # 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')
@@ -237,7 +263,7 @@
             params=params)['volumes']
         self.assertEqual(0, len(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')
@@ -247,7 +273,7 @@
                 detail=True, params=params)['volumes']
         self.assertEqual(0, len(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'}
@@ -255,7 +281,7 @@
             params=params)['volumes']
         self.assertEqual(0, len(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'}
@@ -263,3 +289,34 @@
             self.volumes_client.list_volumes(detail=True,
                                              params=params)['volumes']
         self.assertEqual(0, len(fetched_volume))
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('5b810c91-0ad1-47ce-aee8-615f789be78f')
+    @test.services('image')
+    def test_create_volume_from_image_with_decreasing_size(self):
+        # Create image
+        image = self.create_image()
+
+        # 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')
+    @test.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 504875b..be3f1f2 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -16,6 +16,7 @@
 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
@@ -42,6 +43,10 @@
         server = self.create_server(wait_until='ACTIVE')
         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 even if it's attached to an instance
         snapshot = self.create_snapshot(self.volume_origin['id'],
                                         force=True)
@@ -102,6 +107,7 @@
         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(),
@@ -135,7 +141,8 @@
 
     @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)
diff --git a/tempest/api/volume/test_volumes_snapshots_list.py b/tempest/api/volume/test_volumes_snapshots_list.py
index 68eb181..507df1f 100644
--- a/tempest/api/volume/test_volumes_snapshots_list.py
+++ b/tempest/api/volume/test_volumes_snapshots_list.py
@@ -28,10 +28,14 @@
     @classmethod
     def resource_setup(cls):
         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(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."""
@@ -101,3 +105,56 @@
     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)
+
+    @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 2e30d04..ea5f036 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -15,7 +15,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
 
 CONF = config.CONF
 
@@ -28,7 +27,7 @@
         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,7 +46,7 @@
                           self.snapshots_client.create_snapshot,
                           volume_id=None, display_name=s_name)
 
-    @test.attr(type=['negative'])
+    @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
@@ -62,21 +61,21 @@
                           size=src_size - 1,
                           snapshot_id=src_snap['id'])
 
-    @test.attr(type=['negative'])
+    @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')
 
-    @test.attr(type=['negative'])
+    @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')
 
-    @test.attr(type=['negative'])
+    @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,
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 e7adcd6..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 VolumesListTestJSON(base.BaseVolumeTest):
-    """volumes 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(VolumesListTestJSON, cls).resource_setup()
-
-        # Create 3 test volumes
-        # 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()
-            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 bfed67b..0000000
--- a/tempest/api/volume/v2/test_volumes_snapshots_list.py
+++ /dev/null
@@ -1,93 +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.lib import decorators
-
-CONF = config.CONF
-
-
-class VolumesSnapshotListTestJSON(base.BaseVolumeTest):
-
-    @classmethod
-    def skip_checks(cls):
-        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(VolumesSnapshotListTestJSON, cls).resource_setup()
-        cls.snapshot_id_list = []
-        # Create a volume
-        volume_origin = cls.create_volume()
-        # Create 3 snapshots
-        for _ in range(3):
-            snapshot = cls.create_snapshot(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 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)
-
-    @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/clients.py b/tempest/clients.py
index 71c3d41..73a4b20 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -159,7 +159,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 = (
@@ -168,11 +167,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(
@@ -231,6 +229,10 @@
             **params_v3)
         self.oauth_consumers_client = self.identity_v3.OAUTHConsumerClient(
             **params_v3)
+        self.domain_config_client = self.identity_v3.DomainConfigurationClient(
+            **params_v3)
+        self.endpoint_filter_client = \
+            self.identity_v3.EndPointsFilterClient(**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
@@ -275,6 +277,8 @@
         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.volume_quota_classes_v2_client = \
+            self.volume_v2.QuotaClassesClient()
         self.volumes_extension_client = self.volume_v1.ExtensionsClient()
         self.volumes_v2_extension_client = self.volume_v2.ExtensionsClient()
         self.volume_availability_zone_client = \
@@ -287,6 +291,8 @@
             self.volume_v2.CapabilitiesClient()
         self.volume_scheduler_stats_v2_client = \
             self.volume_v2.SchedulerStatsClient()
+        self.volume_transfers_v2_client = \
+            self.volume_v2.TransfersClient()
 
     def _set_object_storage_clients(self):
         # Mandatory parameters (always defined)
@@ -294,6 +300,8 @@
 
         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/cleanup.py b/tempest/cmd/cleanup.py
index ec76103..ac73cbf 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
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index a632726..f1c0a3e 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -105,7 +105,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]):
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/workspace.py b/tempest/cmd/workspace.py
index d2dc00d..96d2300 100644
--- a/tempest/cmd/workspace.py
+++ b/tempest/cmd/workspace.py
@@ -52,8 +52,8 @@
 import sys
 
 from cliff import command
+from cliff import lister
 from oslo_concurrency import lockutils
-import prettytable
 import yaml
 
 from tempest import config
@@ -154,76 +154,97 @@
             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)
+
+        return parser
+
+    def take_action(self, parsed_args):
+        self.manager = WorkspaceManager(parsed_args.workspace_path)
+        self.manager.remove_workspace(parsed_args.name)
+        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 38daffe..9587ffe 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -258,7 +258,7 @@
         while True:
             header = self._socket.recv(2)
             # If we didn't receive any data, just return None
-            if len(header) == 0:
+            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
@@ -313,6 +313,6 @@
         self._socket.sendall(reqdata.encode('utf8'))
         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(b'\r\n\r\n') < 0:
+        while data and self.response.find(b'\r\n\r\n') < 0:
             data = self._socket.recv(4096)
             self.response += data
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index a92d16a..8053cac 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -241,7 +241,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)
diff --git a/tempest/config.py b/tempest/config.py
index 24651f1..f5b2f0d 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -208,6 +208,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'),
@@ -217,21 +221,17 @@
                      "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?',
-                deprecated_for_removal=True,
-                deprecated_reason="All supported versions of OpenStack now "
-                                  "support the 'reseller' feature"),
     # 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?'),
+                     'domain specific ones?',
+                deprecated_for_removal=True,
+                deprecated_reason="This feature flag was introduced to "
+                                  "support testing of old OpenStack versions, "
+                                  "which are not supported anymore"),
     cfg.BoolOpt('security_compliance',
                 default=False,
                 help='Does the environment have the security compliance '
@@ -336,15 +336,6 @@
                                       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"),
@@ -370,7 +361,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?"),
@@ -386,6 +380,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? "
@@ -396,9 +395,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 '
@@ -411,6 +410,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 '
@@ -613,17 +617,6 @@
                 default=False,
                 help="The environment does not support network separation "
                      "between tenants."),
-    cfg.BoolOpt('dvr_extra_resources',
-                default=False,
-                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.",
-                deprecated_for_removal=True,
-                deprecated_reason='This config switch was added for Liberty '
-                                  'which is not supported anymore.')
 ]
 
 network_feature_group = cfg.OptGroup(name='network-feature-enabled',
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index f48d7ac..a437761 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"
 
diff --git a/tempest/api/image/admin/__init__.py b/tempest/lib/api_schema/response/compute/v2_6/__init__.py
similarity index 100%
rename from tempest/api/image/admin/__init__.py
rename 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/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 e911776..101d692 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -355,7 +355,7 @@
     if errors:
         sys.exit("@decorators.idempotent_id existence and uniqueness checks "
                  "failed\n"
-                 "Run 'tox -v -euuidgen' to automatically fix tests with\n"
+                 "Run 'tox -v -e uuidgen' to automatically fix tests with\n"
                  "missing @decorators.idempotent_id decorators.")
 
 if __name__ == '__main__':
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/rest_client.py b/tempest/lib/common/rest_client.py
index 99ba6ab..d72b4dd 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -70,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
@@ -105,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
 
@@ -125,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}
 
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index 92f9698..f82f707 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -16,9 +16,12 @@
 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
@@ -45,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):
@@ -79,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 dea3289..68ce57a 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
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index eefac66..cd3bab0 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -17,7 +17,9 @@
 import copy
 import importlib
 import inspect
+import warnings
 
+from debtcollector import removals
 from oslo_log import log as logging
 
 from tempest.lib import auth
@@ -29,7 +31,7 @@
 from tempest.lib.services import network
 from tempest.lib.services import volume
 
-
+warnings.simplefilter("once")
 LOG = logging.getLogger(__name__)
 
 
@@ -70,15 +72,15 @@
     :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())
@@ -161,17 +163,17 @@
             parameters cannot be deleted.
         :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
@@ -242,13 +244,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
@@ -257,6 +265,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):
@@ -272,7 +281,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
@@ -287,14 +301,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
@@ -310,15 +328,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
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/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 0d355a1..ff65b25 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -27,6 +27,7 @@
 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_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
@@ -37,7 +38,8 @@
 
     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},
@@ -598,6 +600,29 @@
         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,
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 75984ec..8fbb136 100644
--- a/tempest/lib/services/compute/versions_client.py
+++ b/tempest/lib/services/compute/versions_client.py
@@ -12,6 +12,8 @@
 # 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.compute.v2_1 import versions as schema
@@ -23,7 +25,13 @@
 
     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/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/users_client.py b/tempest/lib/services/identity/v2/users_client.py
index cfd97bb..44bb5fd 100644
--- a/tempest/lib/services/identity/v2/users_client.py
+++ b/tempest/lib/services/identity/v2/users_client.py
@@ -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 1489b50..6f498d9 100644
--- a/tempest/lib/services/identity/v3/__init__.py
+++ b/tempest/lib/services/identity/v3/__init__.py
@@ -14,7 +14,11 @@
 
 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.endpoints_client import EndPointsClient
 from tempest.lib.services.identity.v3.groups_client import GroupsClient
 from tempest.lib.services.identity.v3.identity_client import IdentityClient
@@ -34,9 +38,9 @@
 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',
+__all__ = ['CredentialsClient', 'DomainsClient', 'DomainConfigurationClient',
+           'EndPointsClient', 'EndPointsFilterClient', 'GroupsClient',
+           'IdentityClient', 'InheritedRolesClient', 'OAUTHConsumerClient',
            'PoliciesClient', 'ProjectsClient', 'RegionsClient',
            'RoleAssignmentsClient', 'RolesClient', 'ServicesClient',
-           'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient',
-           'OAUTHConsumerClient']
+           'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient']
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/oauth_consumers_client.py b/tempest/lib/services/identity/v3/oauth_consumers_client.py
index 582c9e0..97fb141 100644
--- a/tempest/lib/services/identity/v3/oauth_consumers_client.py
+++ b/tempest/lib/services/identity/v3/oauth_consumers_client.py
@@ -58,7 +58,7 @@
 
         For more information, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/identity/v3-ext/#update-consumers
+        https://developer.openstack.org/api-ref/identity/v3-ext/#update-consumer
         """
         post_body = {"description": description}
         post_body = json.dumps({'consumer': post_body})
diff --git a/tempest/lib/services/image/v1/images_client.py b/tempest/lib/services/image/v1/images_client.py
index faafb4a..42f8cf2 100644
--- a/tempest/lib/services/image/v1/images_client.py
+++ b/tempest/lib/services/image/v1/images_client.py
@@ -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/namespace_tags_client.py b/tempest/lib/services/image/v2/namespace_tags_client.py
index a7f8c39..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)
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
index b4eb771..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,6 +32,7 @@
 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
@@ -39,4 +42,5 @@
            'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
            'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
            'LimitsClient', 'CapabilitiesClient', 'SchedulerStatsClient',
-           'SnapshotManageClient', 'VolumeManageClient']
+           '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 2b5e82d..197d57e 100644
--- a/tempest/lib/services/volume/v2/backups_client.py
+++ b/tempest/lib/services/volume/v2/backups_client.py
@@ -55,6 +55,14 @@
         self.expected_success(202, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def force_delete_backup(self, backup_id):
+        """Force delete a backup volume."""
+        post_body = json.dumps({'os-force_delete': {}})
+        url = 'backups/%s/action' % backup_id
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp)
+
     def show_backup(self, backup_id):
         """Returns the details of a single backup."""
         url = "backups/%s" % backup_id
diff --git a/tempest/lib/services/volume/v2/capabilities_client.py b/tempest/lib/services/volume/v2/capabilities_client.py
index 40cb8bf..240be13 100644
--- a/tempest/lib/services/volume/v2/capabilities_client.py
+++ b/tempest/lib/services/volume/v2/capabilities_client.py
@@ -24,9 +24,9 @@
     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/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/scheduler_stats_client.py b/tempest/lib/services/volume/v2/scheduler_stats_client.py
index 3f56f82..0d04f85 100644
--- a/tempest/lib/services/volume/v2/scheduler_stats_client.py
+++ b/tempest/lib/services/volume/v2/scheduler_stats_client.py
@@ -24,8 +24,9 @@
     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/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/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index 72823c0..8b5c96f 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,43 @@
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v2 import transfers_client
 
 
 class VolumesClient(rest_client.RestClient):
     """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.
 
@@ -86,9 +118,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)
 
@@ -183,62 +223,6 @@
         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
-        """
-        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
-        """
-        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
-        """
-        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})
@@ -302,6 +286,19 @@
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
         self.expected_success(202, resp.status)
 
+    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.
 
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index c1270c7..ef4506c 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -411,16 +411,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)
@@ -1241,9 +1242,9 @@
     @classmethod
     def setup_clients(cls):
         super(EncryptionScenarioTest, cls).setup_clients()
-        cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
+        cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
         cls.admin_encryption_types_client =\
-            cls.os_adm.encryption_types_v2_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,
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index cefa119..8408a1e 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -96,7 +96,7 @@
         return aggregate
 
     @decorators.idempotent_id('cb2b4c4f-0c7c-4164-bdde-6285b302a081')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute')
     def test_aggregate_basic_ops(self):
         self.useFixture(fixtures.LockFixture('availability_zone'))
diff --git a/tempest/scenario/test_encrypted_cinder_volumes.py b/tempest/scenario/test_encrypted_cinder_volumes.py
index a05b1b1..d7b86f6 100644
--- a/tempest/scenario/test_encrypted_cinder_volumes.py
+++ b/tempest/scenario/test_encrypted_cinder_volumes.py
@@ -62,7 +62,7 @@
         self.nova_volume_detach(server, attached_volume)
 
     @decorators.idempotent_id('79165fb4-5534-4b9d-8429-97ccffb8f86e')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'volume', 'image')
     def test_encrypted_cinder_volumes_luks(self):
         server = self.launch_instance()
@@ -72,7 +72,7 @@
         self.attach_detach_volume(server, volume)
 
     @decorators.idempotent_id('cbc752ed-b716-4717-910f-956cce965722')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'volume', 'image')
     def test_encrypted_cinder_volumes_cryptsetup(self):
         server = self.launch_instance()
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 6665fa7..c2fc1a7 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -38,7 +38,7 @@
     @classmethod
     def setup_clients(cls):
         super(TestNetworkAdvancedServerOps, cls).setup_clients()
-        cls.admin_servers_client = cls.os_adm.servers_client
+        cls.admin_servers_client = cls.os_admin.servers_client
 
     @classmethod
     def skip_checks(cls):
@@ -104,7 +104,7 @@
         return body['OS-EXT-SRV-ATTR:host']
 
     @decorators.idempotent_id('61f1aa9a-1573-410e-9054-afa557cab021')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_server_connectivity_stop_start(self):
         keypair = self.create_keypair()
@@ -130,7 +130,7 @@
             server, keypair, floating_ip)
 
     @decorators.idempotent_id('88a529c2-1daa-4c85-9aec-d541ba3eb699')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_server_connectivity_rebuild(self):
         keypair = self.create_keypair()
@@ -145,7 +145,7 @@
     @decorators.idempotent_id('2b2642db-6568-4b35-b812-eceed3fa20ce')
     @testtools.skipUnless(CONF.compute_feature_enabled.pause,
                           'Pause is not available.')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_server_connectivity_pause_unpause(self):
         keypair = self.create_keypair()
@@ -163,7 +163,7 @@
     @decorators.idempotent_id('5cdf9499-541d-4923-804e-b9a60620a7f0')
     @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
                           'Suspend is not available.')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_server_connectivity_suspend_resume(self):
         keypair = self.create_keypair()
@@ -181,13 +181,10 @@
     @decorators.idempotent_id('719eb59d-2f42-4b66-b8b1-bb1254473967')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize is not available.')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.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)
@@ -205,7 +202,7 @@
     @testtools.skipUnless(CONF.compute.min_compute_nodes > 1,
                           'Less than 2 compute nodes, skipping multinode '
                           'tests.')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_server_connectivity_cold_migration(self):
         keypair = self.create_keypair()
@@ -231,7 +228,7 @@
     @testtools.skipUnless(CONF.compute.min_compute_nodes > 1,
                           'Less than 2 compute nodes, skipping multinode '
                           'tests.')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_server_connectivity_cold_migration_revert(self):
         keypair = self.create_keypair()
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index dec0ad0..756ca4d 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -352,7 +352,7 @@
             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')
     def test_network_basic_ops(self):
@@ -408,7 +408,7 @@
     @decorators.idempotent_id('b158ea55-472e-4086-8fa9-c64ac0c6c1d0')
     @testtools.skipUnless(test.is_extension_enabled('net-mtu', 'network'),
                           'No way to calculate MTU for networks')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_mtu_sized_frames(self):
         """Validate that network MTU sized frames fit through."""
@@ -421,7 +421,7 @@
                       'Connectivity can only be tested when in a '
                       'multitenant network environment')
     @decorators.skip_because(bug="1610994")
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_connectivity_between_vms_on_different_networks(self):
         """Test connectivity between VMs on different networks
@@ -497,7 +497,7 @@
     @testtools.skipIf(CONF.network.shared_physical_network,
                       'Router state can be altered only with multitenant '
                       'networks capabilities')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_update_router_admin_state(self):
         """Test to update admin state up of router
@@ -531,7 +531,7 @@
                       'network isolation not available')
     @testtools.skipUnless(CONF.scenario.dhcp_client,
                           "DHCP client is not available.")
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_subnet_details(self):
         """Tests that subnet's extra configuration details are affecting VMs.
@@ -615,7 +615,7 @@
     @testtools.skipUnless(CONF.network_feature_enabled.port_admin_state_change,
                           "Changing a port's admin state is not supported "
                           "by the test environment")
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_update_instance_port_admin_state(self):
         """Test to update admin_state_up attribute of instance port
@@ -661,7 +661,7 @@
                                        should_succeed=True)
 
     @decorators.idempotent_id('759462e1-8535-46b0-ab3a-33aa45c55aaa')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_preserve_preexisting_port(self):
         """Test preserve pre-existing port
@@ -713,7 +713,7 @@
 
     @test.requires_ext(service='network', extension='l3_agent_scheduler')
     @decorators.idempotent_id('2e788c46-fb3f-4ac9-8f82-0561555bea73')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_router_rescheduling(self):
         """Tests that router can be removed from agent and add to a new agent.
@@ -793,7 +793,7 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.interface_attach,
                           'NIC hotplug not available')
     @decorators.idempotent_id('7c0bb1a2-d053-49a4-98f9-ca1a1d849f63')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_port_security_macspoofing_port(self):
         """Tests port_security extension enforces mac spoofing
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 4e8b004..daf4d13 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -206,43 +206,43 @@
             self.check_remote_connectivity(sshv4_2,
                                            self.subnets_v6[i]['gateway_ip'])
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('2c92df61-29f0-4eaa-bee3-7c65bef62a43')
     @test.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')
     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')
     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')
     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')
     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')
     def test_dualnet_dhcp6_stateless_from_os(self):
         self._prepare_and_test(address6_mode='dhcpv6-stateless', dualnet=True)
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('cf1c4425-766b-45b8-be35-e2959728eb00')
     @test.services('compute', 'network')
     def test_dualnet_multi_prefix_dhcpv6_stateless(self):
diff --git a/tempest/scenario/test_object_storage_basic_ops.py b/tempest/scenario/test_object_storage_basic_ops.py
index 7fd8c91..25e9f5c 100644
--- a/tempest/scenario/test_object_storage_basic_ops.py
+++ b/tempest/scenario/test_object_storage_basic_ops.py
@@ -46,7 +46,7 @@
         self.delete_container(container_name)
 
     @decorators.idempotent_id('916c7111-cb1f-44b2-816d-8f760e4ea910')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.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 72b61c8..2116fe8 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -122,6 +122,7 @@
             self.router = None
             self.security_groups = {}
             self.servers = list()
+            self.access_point = None
 
         def set_network(self, network, subnet, router):
             self.network = network
@@ -458,6 +459,14 @@
         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')
     def test_cross_tenant_traffic(self):
@@ -475,8 +484,7 @@
             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')
@@ -489,12 +497,11 @@
             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.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_port_update_new_security_group(self):
         """Verifies the traffic after updating the vm port
@@ -544,12 +551,11 @@
                 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.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'network')
     def test_multiple_security_groups(self):
         """Verify multiple security groups and checks that rules
@@ -581,7 +587,7 @@
                                    private_key=private_key,
                                    should_connect=True)
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.requires_ext(service='network', extension='port-security')
     @decorators.idempotent_id('7c811dcc-263b-49a3-92d2-1b4d8405f50c')
     @test.services('compute', 'network')
@@ -618,16 +624,12 @@
                 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.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.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
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index cc3687f..6d6318c 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -41,15 +41,10 @@
         cls.set_network_resources()
         super(TestServerAdvancedOps, cls).setup_credentials()
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('e6c28180-7454-4b59-b188-0257af08a63b')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize is not available.')
-    @testtools.skipUnless(CONF.compute.flavor_ref !=
-                          CONF.compute.flavor_ref_alt
-                          and CONF.compute.flavor_ref_alt != "",
-                          'The flavor_ref_alt option should not be empty and '
-                          'should not be identical with flavor_ref')
     @test.services('compute', 'volume')
     def test_resize_volume_backed_server_confirm(self):
         # We create an instance for use in this test
@@ -68,7 +63,7 @@
         waiters.wait_for_server_status(self.servers_client, instance_id,
                                        'ACTIVE')
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('949da7d5-72c8-4808-8802-e3d70df98e2c')
     @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
                           'Suspend is not available.')
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index ddbaf5a..2be9e06 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -124,7 +124,7 @@
             # instance info and do direct comparison.
 
     @decorators.idempotent_id('7fff3fb3-91d8-4fd0-bd7d-0204f1f180ba')
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @test.services('compute', 'network')
     def test_server_basic_ops(self):
         keypair = self.create_keypair()
diff --git a/tempest/scenario/test_server_multinode.py b/tempest/scenario/test_server_multinode.py
index db91a21..d9bff09 100644
--- a/tempest/scenario/test_server_multinode.py
+++ b/tempest/scenario/test_server_multinode.py
@@ -45,11 +45,11 @@
         cls.servers_client = cls.admin_manager.servers_client
 
     @decorators.idempotent_id('9cecbe35-b9d4-48da-a37e-7ce70aa43d30')
-    @test.attr(type='smoke')
+    @decorators.attr(type='smoke')
     @test.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:
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 9e763f8..fc04b44 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -74,7 +74,7 @@
                                         private_key=keypair['private_key'])
         self.assertEqual(timestamp, timestamp2)
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('1164e700-0af0-4a4c-8792-35909a88743c')
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
@@ -82,7 +82,7 @@
     def test_shelve_instance(self):
         self._create_server_then_shelve_and_unshelve()
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('c1b6318c-b9da-490b-9c67-9339b627271f')
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index a699de2..52767dc 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -41,7 +41,7 @@
             raise cls.skipException("Snapshotting is not available.")
 
     @decorators.idempotent_id('608e604b-1d63-4a82-8e3e-91bc665c90b4')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
     @test.services('compute', 'network', 'image')
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 5f5d701..debd664 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -85,7 +85,7 @@
                                           CONF.compute.build_interval):
             raise lib_exc.TimeoutException
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.skip_because(bug="1664793")
     @decorators.idempotent_id('10fd234a-515c-41e5-b092-8323060598c5')
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 888bff2..3dfbf18 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -170,7 +170,7 @@
         self.assertEqual(timestamp, timestamp3)
 
     @decorators.idempotent_id('05795fb2-b2a7-4c9f-8fac-ff25aedb1489')
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @test.services('compute', 'image', 'volume')
     def test_create_server_from_volume_snapshot(self):
         # Create a volume from an image
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index f04947c..63dc23d 100644
--- a/tempest/scenario/test_volume_migrate_attached.py
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -40,7 +40,7 @@
     @classmethod
     def setup_clients(cls):
         super(TestVolumeMigrateRetypeAttached, cls).setup_clients()
-        cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
+        cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
 
     @classmethod
     def skip_checks(cls):
@@ -88,7 +88,7 @@
         waiters.wait_for_volume_retype(self.volumes_client,
                                        volume_id, new_volume_type)
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('deadd2c2-beef-4dce-98be-f86765ff311b')
     @test.services('compute', 'volume')
     def test_volume_migrate_attached(self):
diff --git a/tempest/services/object_storage/__init__.py b/tempest/services/object_storage/__init__.py
index d1a61d6..1738566 100644
--- a/tempest/services/object_storage/__init__.py
+++ b/tempest/services/object_storage/__init__.py
@@ -13,10 +13,12 @@
 # the License.
 
 from tempest.services.object_storage.account_client import AccountClient
+from tempest.services.object_storage.bulk_middleware_client import \
+    BulkMiddlewareClient
 from tempest.services.object_storage.capabilities_client import \
     CapabilitiesClient
 from tempest.services.object_storage.container_client import ContainerClient
 from tempest.services.object_storage.object_client import ObjectClient
 
-__all__ = ['AccountClient', 'CapabilitiesClient', 'ContainerClient',
-           'ObjectClient']
+__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 4859e75..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
 
diff --git a/tempest/services/object_storage/bulk_middleware_client.py b/tempest/services/object_storage/bulk_middleware_client.py
new file mode 100644
index 0000000..c194ea9
--- /dev/null
+++ b/tempest/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:
+        http://docs.openstack.org/developer/swift/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 resp, body
+
+    def delete_bulk_data(self, data=None, headers=None):
+        """Delete multiple objects or containers from their account.
+
+        For More information please check:
+        http://docs.openstack.org/developer/swift/middleware.html#module-swift.common.middleware.bulk
+        """
+        url = '?bulk-delete'
+
+        if headers is None:
+            headers = {}
+        resp, body = self.delete(url, headers=headers, body=data)
+        self.expected_success(200, resp.status)
+        return 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:
+        http://docs.openstack.org/developer/swift/middleware.html#module-swift.common.middleware.bulk
+        """
+        url = '?bulk-delete'
+
+        if headers is None:
+            headers = {}
+        resp, body = self.post(url, headers=headers, body=data)
+        self.expected_success([200, 204], resp.status)
+        return resp, body
diff --git a/tempest/test.py b/tempest/test.py
index 52994ac..f6b17ad 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -29,7 +29,6 @@
 from tempest.common import fixed_network
 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 import decorators
 from tempest.lib import exceptions as lib_exc
@@ -45,22 +44,18 @@
     version='Mitaka', removal_version='?')
 
 
-def attr(**kwargs):
-    """A decorator which applies the testtools attr decorator
+related_bug = debtcollector.moves.moved_function(
+    decorators.related_bug, 'related_bug', __name__,
+    version='Pike', removal_version='?')
 
-    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
+attr = debtcollector.moves.moved_function(
+    decorators.attr, 'attr', __name__,
+    version='Pike', removal_version='?')
 
-    return decorator
+
+class InvalidServiceTag(lib_exc.TempestException):
+    message = "Invalid service tag"
 
 
 def get_service_list():
@@ -82,13 +77,12 @@
     exercised by a test case.
     """
     def decorator(f):
-        services = ['compute', 'image', 'baremetal', 'volume',
-                    'network', 'identity', 'object_storage']
+        known_services = get_service_list()
+
         for service in args:
-            if service not in services:
-                raise exceptions.InvalidServiceTag('%s is not a valid '
-                                                   'service' % service)
-        attr(type=list(args))(f)
+            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):
@@ -134,7 +128,7 @@
         'object': CONF.object_storage_feature_enabled.discoverable_apis,
         'identity': CONF.identity_feature_enabled.api_extensions
     }
-    if len(config_dict[service]) == 0:
+    if not config_dict[service]:
         return False
     if config_dict[service][0] == 'all':
         return True
@@ -143,28 +137,6 @@
     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.
 
@@ -177,7 +149,7 @@
     """
 
     filters = CONF.compute_feature_enabled.scheduler_available_filters
-    if len(filters) == 0:
+    if not filters:
         return False
     if 'all' in filters:
         return True
@@ -359,16 +331,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='Ocata')
+                    cls.manager =\
+                        debtcollector.moves.moved_read_only_property(
+                            'manager', 'os_primary', version='Pike',
+                            removal_version='Ocata')
                 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='Ocata')
+                    cls.admin_manager =\
+                        debtcollector.moves.moved_read_only_property(
+                            'admin_manager', 'os_admin', version='Pike',
+                            removal_version='Ocata')
                 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='Ocata')
             elif isinstance(credentials_type, list):
                 manager = cls.get_client_manager(roles=credentials_type[1:],
                                                  force_new=True)
@@ -483,7 +471,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__):
@@ -492,8 +482,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
@@ -642,12 +631,24 @@
         return fixed_network.get_tenant_network(
             cred_provider, networks_client, CONF.compute.fixed_network_name)
 
-    def assertEmpty(self, list, msg=None):
-        if msg is None:
-            msg = "list is not empty: %s" % list
-        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):
+        :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 = "list is empty."
-        self.assertGreater(len(list), 0, msg)
+            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
+
+        :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 613ab92..1206e3f 100644
--- a/tempest/test_discover/plugins.py
+++ b/tempest/test_discover/plugins.py
@@ -51,36 +51,37 @@
         :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)
+        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
 
@@ -91,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 []
 
@@ -107,37 +133,41 @@
           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 []
 
diff --git a/tempest/tests/cmd/test_workspace.py b/tempest/tests/cmd/test_workspace.py
index 6ca4d42..dc6c0c8 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,17 @@
     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]
+        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))
 
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/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/lib/services/compute/test_servers_client.py b/tempest/tests/lib/services/compute/test_servers_client.py
index a277dfe..a857329 100644
--- a/tempest/tests/lib/services/compute/test_servers_client.py
+++ b/tempest/tests/lib/services/compute/test_servers_client.py
@@ -1168,3 +1168,34 @@
             tag=self.FAKE_TAGS[0],
             status=204,
             to_utf=bytes_body)
+
+
+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/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/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_transfers_client.py b/tempest/tests/lib/services/volume/v2/test_transfers_client.py
new file mode 100644
index 0000000..0c59bf2
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_transfers_client.py
@@ -0,0 +1,61 @@
+# 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 transfers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestTransfersClient(base.BaseServiceTest):
+
+    FAKE_LIST_VOLUME_TRANSFERS_WITH_DETAIL = {
+        "transfers": [{
+            "created_at": "2017-04-18T09:10:03.000000",
+            "volume_id": "47bf04ef-1ea5-4c5f-a375-430a086d6747",
+            "id": "0e89cdd1-6249-421b-96d8-25fac0623d42",
+            "links": [
+                {
+                    "href": "fake-url-1",
+                    "rel": "self"
+                },
+                {
+                    "href": "fake-url-2",
+                    "rel": "bookmark"
+                }
+            ],
+            "name": "fake-volume-transfer"
+        }]
+    }
+
+    def setUp(self):
+        super(TestTransfersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = transfers_client.TransfersClient(fake_auth,
+                                                       'volume',
+                                                       'regionOne')
+
+    def _test_list_volume_transfers_with_detail(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_volume_transfers,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_VOLUME_TRANSFERS_WITH_DETAIL,
+            bytes_body,
+            detail=True)
+
+    def test_list_volume_transfers_with_detail_with_str_body(self):
+        self._test_list_volume_transfers_with_detail()
+
+    def test_list_volume_transfers_with_detail_with_bytes_body(self):
+        self._test_list_volume_transfers_with_detail(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..498b963
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_volumes_client.py
@@ -0,0 +1,52 @@
+# 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 volumes_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVolumesClient(base.BaseServiceTest):
+
+    def setUp(self):
+        super(TestVolumesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = volumes_client.VolumesClient(fake_auth,
+                                                   'volume',
+                                                   'regionOne')
+
+    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_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)
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_rest_client.py b/tempest/tests/lib/test_rest_client.py
index 4a83631..43bb6d0 100644
--- a/tempest/tests/lib/test_rest_client.py
+++ b/tempest/tests/lib/test_rest_client.py
@@ -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, {})
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index ae2f2a3..8b6472b 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -17,7 +17,6 @@
 import testtools
 
 from tempest import config
-from tempest import exceptions
 from tempest.lib.common.utils import data_utils
 from tempest import test
 from tempest.tests import base
@@ -32,34 +31,6 @@
                          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'])
-
-
 class TestIdempotentIdDecorator(BaseDecoratorsTest):
 
     def _test_helper(self, _id, **decorator_args):
@@ -119,7 +90,7 @@
         self._test_services_helper('compute', 'compute')
 
     def test_services_decorator_with_invalid_service(self):
-        self.assertRaises(exceptions.InvalidServiceTag,
+        self.assertRaises(test.InvalidServiceTag,
                           self._test_services_helper, 'compute',
                           'bad_service')
 
@@ -135,7 +106,7 @@
         for service in service_list:
             try:
                 self._test_services_helper(service)
-            except exceptions.InvalidServiceTag:
+            except test.InvalidServiceTag:
                 self.fail('%s is not listed in the valid service tag list'
                           % service)
             except KeyError:
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/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/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}