Merge "Create a snapshot from a in-use volume with force=False"
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/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/write_tests.rst b/doc/source/write_tests.rst
index 8488fb1..2363fa6 100644
--- a/doc/source/write_tests.rst
+++ b/doc/source/write_tests.rst
@@ -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):
@@ -143,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.
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/add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml b/releasenotes/notes/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml
similarity index 100%
rename from releasenotes/notes/add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml
rename to releasenotes/notes/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml
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/dreprecate_client_parameters-cb8d069e62957f7e.yaml b/releasenotes/notes/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml
similarity index 100%
rename from releasenotes/notes/dreprecate_client_parameters-cb8d069e62957f7e.yaml
rename to releasenotes/notes/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml
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/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/remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml b/releasenotes/notes/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
similarity index 100%
rename from releasenotes/notes/remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
rename to releasenotes/notes/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
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/remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml b/releasenotes/notes/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
similarity index 100%
rename from releasenotes/notes/remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
rename to releasenotes/notes/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
diff --git a/releasenotes/notes/remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml b/releasenotes/notes/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml
similarity index 100%
rename from releasenotes/notes/remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml
rename to releasenotes/notes/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml
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/volume-transfers-client-e5ed3f5464c0cdc0.yaml b/releasenotes/notes/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml
similarity index 100%
rename from releasenotes/notes/volume-transfers-client-e5ed3f5464c0cdc0.yaml
rename to releasenotes/notes/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml
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-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/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 69689a7..41be620 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -26,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
@@ -34,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]
@@ -124,7 +124,7 @@
     @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')
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_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 5134b81..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
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_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 684f656..5bd8104 100644
--- a/tempest/api/compute/admin/test_hosts_negative.py
+++ b/tempest/api/compute/admin/test_hosts_negative.py
@@ -23,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):
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index e0e5a29..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
diff --git a/tempest/api/compute/admin/test_hypervisor_negative.py b/tempest/api/compute/admin/test_hypervisor_negative.py
index 2addce9..af87287 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -25,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):
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 92235cc..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
@@ -27,7 +27,7 @@
     @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
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a9d33178-d2c9-4131-ad3b-f4ca8d0308a2')
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 100%
rename from tempest/api/compute/test_live_block_migration_negative.py
rename to tempest/api/compute/admin/test_live_block_migration_negative.py
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index 4d0f12a..3859e64 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -13,10 +13,13 @@
 #    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
@@ -45,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):
@@ -175,6 +178,80 @@
         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):
     min_microversion = '2.25'
     max_microversion = 'latest'
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 cc4654d..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
 
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 aff61bf..98bf4bf 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -26,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
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 677972e..b0f18d7 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -31,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):
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 1edfc89..38bb5ec 100644
--- a/tempest/api/compute/admin/test_services_negative.py
+++ b/tempest/api/compute/admin/test_services_negative.py
@@ -23,7 +23,7 @@
     @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
 
     @decorators.attr(type=['negative'])
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 222d28c..cb60b8d 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
@@ -25,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):
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_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index b2c33e2..ebb9d2e 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -34,9 +34,9 @@
     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 '
@@ -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 cce9856..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
@@ -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/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_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/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 06a47d1..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):
@@ -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 d54c3ec..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):
@@ -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_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 751d33a..8808510 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -337,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/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/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 86a5764..cddba53 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -31,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
@@ -152,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)
 
 
diff --git a/tempest/api/identity/admin/v3/test_domains_negative.py b/tempest/api/identity/admin/v3/test_domains_negative.py
index 61b4fa2..1a0b851 100644
--- a/tempest/api/identity/admin/v3/test_domains_negative.py
+++ b/tempest/api/identity/admin/v3/test_domains_negative.py
@@ -25,11 +25,7 @@
     @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)
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index f630f74..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-'),
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index ac56fc6..adb467c 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -38,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'),
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index 1a9502a..8c5e63b 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -96,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'],
@@ -122,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'])
@@ -134,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'])
@@ -146,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.
@@ -156,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 339b4bb..3e6a2de 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -47,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)
 
@@ -62,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
@@ -96,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
 
@@ -232,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)
@@ -276,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 4f271cb..751962f 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -147,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 09d1b9b..11dcdb0 100644
--- a/tempest/api/identity/admin/v3/test_users_negative.py
+++ b/tempest/api/identity/admin/v3/test_users_negative.py
@@ -14,10 +14,13 @@
 #    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
 
+CONF = config.CONF
+
 
 class UsersNegativeTest(base.BaseIdentityV3AdminTest):
 
@@ -43,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 aff3238..785485b 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -107,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):
@@ -127,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):
@@ -177,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):
@@ -200,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):
@@ -229,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):
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_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/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/v2/test_images.py b/tempest/api/image/v2/test_images.py
index a5d9773..2e68efd 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -364,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/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/base.py b/tempest/api/network/base.py
index 7ceeb50..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):
@@ -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/object_storage/base.py b/tempest/api/object_storage/base.py
index c075b92..2bac8d3 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -79,11 +79,11 @@
     @classmethod
     def setup_clients(cls):
         super(BaseObjectTest, cls).setup_clients()
-        cls.object_client = cls.os.object_client
-        cls.bulk_client = cls.os.bulk_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_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 9077f88..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
@@ -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 f955d34..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
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_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/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 58ca92f..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,16 +27,21 @@
 
     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.volume_transfers_v2_client
+        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')
@@ -80,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)
diff --git a/tempest/api/volume/admin/test_volume_quotas_negative.py b/tempest/api/volume/admin/test_volume_quotas_negative.py
index 63f61e4..d127b5f 100644
--- a/tempest/api/volume/admin/test_volume_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_quotas_negative.py
@@ -27,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):
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 fa8c7ab..0f4e90f 100644
--- a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
@@ -33,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):
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 767b3c7..c877969 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -64,18 +64,21 @@
     @classmethod
     def setup_clients(cls):
         super(BaseVolumeTest, cls).setup_clients()
-        cls.servers_client = cls.os.servers_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()
@@ -113,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:
@@ -142,7 +144,8 @@
                                                 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
@@ -152,9 +155,12 @@
 
         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
@@ -241,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_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_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 75f2a73..5fd1904 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -28,10 +28,10 @@
     def setup_clients(cls):
         super(VolumesTransfersTest, cls).setup_clients()
 
-        cls.client = cls.os.volume_transfers_v2_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_adm.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):
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 1e05f22..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()
 
@@ -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_get.py b/tempest/api/volume/test_volumes_get.py
index 1d9b846..712254e 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -124,9 +124,8 @@
     @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_negative.py b/tempest/api/volume/test_volumes_negative.py
index 5ba72c3..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,6 +37,25 @@
         cls.volume = cls.create_volume()
         cls.mountpoint = "/dev/vdc"
 
+    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):
@@ -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/clients.py b/tempest/clients.py
index dbaacda..73a4b20 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -229,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
@@ -273,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 = \
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/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/config.py b/tempest/config.py
index fe2ba54..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'),
@@ -357,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?"),
@@ -388,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 '
@@ -403,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 '
diff --git a/tempest/lib/api_schema/response/compute/v2_6/__init__.py b/tempest/lib/api_schema/response/compute/v2_6/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/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/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/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/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/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
index 9434896..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
@@ -40,4 +42,5 @@
            'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
            'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
            'LimitsClient', 'CapabilitiesClient', 'SchedulerStatsClient',
-           'SnapshotManageClient', 'VolumeManageClient', 'TransfersClient']
+           '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/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/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index c67ddfb..8b5c96f 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -118,11 +118,16 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def delete_volume(self, volume_id, cascade=False):
-        """Deletes the Specified Volume."""
+    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 cascade:
-            url += '?cascade=True'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
         resp, body = self.delete(url)
         self.expected_success(202, resp.status)
         return rest_client.ResponseBody(resp, body)
@@ -281,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 3418a2f..ef4506c 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -1242,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_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index ae2ba2f..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):
@@ -185,9 +185,6 @@
     @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)
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 6005e28..6d6318c 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -45,11 +45,6 @@
     @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
diff --git a/tempest/scenario/test_server_multinode.py b/tempest/scenario/test_server_multinode.py
index bf2858b..d9bff09 100644
--- a/tempest/scenario/test_server_multinode.py
+++ b/tempest/scenario/test_server_multinode.py
@@ -49,7 +49,7 @@
     @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_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index a3cb3eb..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):
diff --git a/tempest/test.py b/tempest/test.py
index 2ebdc60..f6b17ad 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -331,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)
@@ -455,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__):
@@ -464,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
diff --git a/tempest/test_discover/plugins.py b/tempest/test_discover/plugins.py
index 9f75962..1206e3f 100644
--- a/tempest/test_discover/plugins.py
+++ b/tempest/test_discover/plugins.py
@@ -92,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 []
 
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/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_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_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/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()