Merge "Make delete_volume in volumes_client.py use **params"
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index adbd2dc..b516055 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -1,13 +1,83 @@
-===================================
-How To Implement Microversion Tests
-===================================
+=================================
+Microversion Testing With Tempest
+=================================
 
-Tempest provides stable interfaces to test API Microversion.
-For Details, see: `API Microversion testing Framework`_
-This document explains how to implement Microversion tests using those
-interfaces.
+Many OpenStack Services provide their APIs with `microversion`_
+support and want to test them in Tempest.
 
-.. _API Microversion testing Framework: http://docs.openstack.org/developer/tempest/library/api_microversion_testing.html
+.. _microversion: http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html
+
+This document covers how to test microversions for each project and
+whether tests should live in Tempest or on project side.
+
+Tempest Scope For Microversion Testing
+""""""""""""""""""""""""""""""""""""""
+APIs microversions for any OpenStack service grow rapidly and
+testing each and every microversion in Tempest is not feasible and
+efficient way.
+Also not every API microversion changes the complete system behavior
+and many of them only change the API or DB layer to accept and return more
+data on API.
+
+Tempest is an integration test suite, but not all API microversion testing fall under this category.
+As a result, Tempest mainly covers integration test cases for microversions, Other testing coverage
+for microversion should be hosted on project side as functional tests or via Tempest plugin as per
+project guidelines.
+
+.. note:: Integration tests are those tests which involve more than one service to
+     verify the expected behavior by single or combination of API requests.
+     If a test is just to verify the API behavior as success and failure cases
+     or verify its expected response object, then it does not fall under integration
+     tests.
+
+Tempest will cover only integration testing of applicable microversions with
+below exceptions:
+
+  #. Test covers a feature which is important for interoperability. This covers tests requirement
+     from Defcore.
+  #. Test needed to fill Schema gaps.
+     Tempest validates API responses with defined JSON schema. API responses can be different on
+     each microversion and the JSON schemas need to be defined separately for the microversion.
+     While implementing new integration tests for a specific microversion, there
+     may be a gap in the JSON schemas (caused by previous microversions) implemented
+     in Tempest.
+     Filling that gap while implementing the new integration test cases is not efficient due to
+     many reasons:
+
+     * Hard to review
+     * Sync between multiple integration tests patches which try to fill the same schema gap at same
+       time
+     * Might delay the microversion change on project side where project team wants Tempest
+       tests to verify the results.
+
+     Tempest will allow to fill the schema gaps at the end of each cycle, or more
+     often if required.
+     Schema gap can be filled with testing those with a minimal set of tests. Those
+     tests might not be integration tests and might be already covered on project
+     side also.
+     This exception is needed because:
+
+     * Allow to create microversion response schema in Tempest at the same time that projects are
+       implementing their API microversions. This will make implementation easier for adding
+       required tests before a new microversion change can be merged in the corresponding project
+       and hence accelerate the development of microversions.
+     * New schema must be verified by at least one test case which exercises such schema.
+
+     For example:
+        If any projects implemented 4 API microversion say- v2.3, v2.4, v2.5, v2.6
+        Assume microversion v2.3, v2.4, v2.6 change the API Response which means Tempest
+        needs to add JSON schema for v2.3, v2.4, v2.6.
+        In that case if only 1 or 2 tests can verify all new schemas then we do not need
+        separate tests for each new schemas. In worst case, we have to add 3 separate tests.
+  #. Test covers service behavior at large scale with involvement of more deep layer like hypervisor
+     etc not just API/DB layer. This type of tests will be added case by case basis and
+     with project team consultation about why it cannot be covered on project side and worth to test
+     in Tempest.
+
+Project Scope For Microversion Testing
+""""""""""""""""""""""""""""""""""""""
+All microversions testing which are not covered under Tempest as per above section, should be
+tested on project side as functional tests or as Tempest plugin as per project decision.
 
 
 Configuration options for Microversion
@@ -36,6 +106,14 @@
 How To Implement Microversion Tests
 """""""""""""""""""""""""""""""""""
 
+Tempest provides stable interfaces to test API Microversion.
+For Details, see: `API Microversion testing Framework`_
+This document explains how to implement Microversion tests using those
+interfaces.
+
+.. _API Microversion testing Framework: http://docs.openstack.org/developer/tempest/library/api_microversion_testing.html
+
+
 Step1: Add skip logic based on configured Microversion range
 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
 
diff --git a/doc/source/write_tests.rst b/doc/source/write_tests.rst
index 67b55aa..8488fb1 100644
--- a/doc/source/write_tests.rst
+++ b/doc/source/write_tests.rst
@@ -112,6 +112,7 @@
         super(TestExampleCase, cls).resource_setup()
         cls.shared_server = cls.servers_client.create_server(...)
 
+.. _credentials:
 
 Allocating Credentials
 ''''''''''''''''''''''
@@ -187,7 +188,7 @@
 +-------------------+---------------------+
 
 By default cls.os is available since it is allocated in the base tempest test
-class. (located in tempest/test.py) If your TestCase inherits from a different
+class (located in tempest/test.py). If your TestCase inherits from a different
 direct parent class (it'll still inherit from the BaseTestCase, just not
 directly) be sure to check if that class overrides allocated credentials.
 
@@ -198,10 +199,10 @@
 automatically setup when a tenant is created. Since tempest needs isolated
 tenants to function properly it also needs to handle network allocation. By
 default the base test class will allocate a network, subnet, and router
-automatically. (this depends on the configured credential provider, for more
-details see: :ref:`tempest_conf_network_allocation`) However, there are
-situations where you do no need all of these resources allocated. (or your
-TestCase inherits from a class that overrides the default in tempest/test.py)
+automatically (this depends on the configured credential provider, for more
+details see: :ref:`tempest_conf_network_allocation`). However, there are
+situations where you do no need all of these resources allocated (or your
+TestCase inherits from a class that overrides the default in tempest/test.py).
 There is a class level mechanism to override this allocation and specify which
 resources you need. To do this you need to call `cls.set_network_resources()`
 in the `setup_credentials()` method before the `super()`. For example::
@@ -241,3 +242,70 @@
 inheriting from classes other than the base TestCase in tempest/test.py it is
 worth checking the immediate parent for what is set to determine if your
 class needs to override that setting.
+
+Interacting with Credentials and Clients
+========================================
+
+Once you have your basic TestCase setup you'll want to start writing tests. To
+do that you need to interact with an OpenStack deployment. This section will
+cover how credentials and clients are used inside of Tempest tests.
+
+
+Manager Objects
+---------------
+
+The primary interface with which you interact with both credentials and
+API clients is the client manager object. These objects are created
+automatically by the base test class as part of credential setup (for more
+details see the previous :ref:`credentials` section). Each manager object is
+initialized with a set of credentials and has each client object already setup
+to use that set of credentials for making all the API requests. Each client is
+accessible as a top level attribute on the manager object. So to start making
+API requests you just access the client's method for making that call and the
+credentials are already setup for you. For example if you wanted to make an API
+call to create a server in Nova::
+
+  from tempest import test
+
+
+  class TestExampleCase(test.BaseTestCase):
+    def test_example_create_server(self):
+      self.os.servers_client.create_server(...)
+
+is all you need to do. As described previously, in the above example the ``self.os``
+is created automatically because the base test class sets the ``credentials``
+attribute to allocate a primary credential set and initializes the client
+manager as ``self.os``. This same access pattern can be used for all of the
+clients in Tempest.
+
+Credentials Objects
+-------------------
+
+In certain cases you need direct access to the credentials (the most common
+use case would be an API request that takes a user or project id in the request
+body). If you're in a situation where you need to access this you'll need to
+access the ``credentials`` object which is allocated from the configured
+credential provider in the base test class. This is accessible from the manager
+object via the manager's ``credentials`` attribute. For example::
+
+  from tempest import test
+
+
+  class TestExampleCase(test.BaseTestCase):
+    def test_example_create_server(self):
+      credentials = self.os.credentials
+
+The credentials object provides access to all of the credential information you
+would need to make API requests. For example, building off the previous
+example::
+
+  from tempest import test
+
+
+  class TestExampleCase(test.BaseTestCase):
+    def test_example_create_server(self):
+      credentials = self.os.credentials
+      username = credentials.username
+      user_id = credentials.user_id
+      password = credentials.password
+      tenant_id = credentials.tenant_id
diff --git a/releasenotes/notes/add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml b/releasenotes/notes/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
similarity index 100%
rename from releasenotes/notes/add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
rename to releasenotes/notes/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
diff --git a/releasenotes/notes/add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml b/releasenotes/notes/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
similarity index 100%
rename from releasenotes/notes/add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
rename to releasenotes/notes/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
diff --git a/releasenotes/notes/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-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-list-volume-transfers-with-detail-to-transfers-client-80169bf78cf4fa66.yaml b/releasenotes/notes/add-list-volume-transfers-with-detail-to-transfers-client-80169bf78cf4fa66.yaml
new file mode 100644
index 0000000..8e85d3a
--- /dev/null
+++ b/releasenotes/notes/add-list-volume-transfers-with-detail-to-transfers-client-80169bf78cf4fa66.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Add list volume transfers with details API to v2 transfers_client library.
+    This feature enables the possibility to list volume transfers with details.
diff --git a/releasenotes/notes/add-volume-backup-force-delete-af0156651a0cbf7f.yaml b/releasenotes/notes/add-volume-backup-force-delete-af0156651a0cbf7f.yaml
new file mode 100644
index 0000000..71bbfcb
--- /dev/null
+++ b/releasenotes/notes/add-volume-backup-force-delete-af0156651a0cbf7f.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    As in the [doc]:
+    https://developer.openstack.org/api-ref/block-storage/v3/
+    #force-delete-a-backup.
+
+      * Force-deletes a backup(v2)
+
diff --git a/releasenotes/notes/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-config-forbid_global_implied_dsr-e64cfa66e6e3ded5.yaml b/releasenotes/notes/deprecate-config-forbid_global_implied_dsr-e64cfa66e6e3ded5.yaml
new file mode 100644
index 0000000..2b63402
--- /dev/null
+++ b/releasenotes/notes/deprecate-config-forbid_global_implied_dsr-e64cfa66e6e3ded5.yaml
@@ -0,0 +1,5 @@
+---
+deprecations:
+  - The config option ``forbid_global_implied_dsr`` from the ``IdentityFeature``
+    group is now deprecated. This feature flag was introduced to support
+    testing of old OpenStack versions which are not supported anymore.
diff --git a/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/identity/admin/v2/test_tenant_negative.py b/tempest/api/identity/admin/v2/test_tenant_negative.py
index 750f9f9..49bb949 100644
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ b/tempest/api/identity/admin/v2/test_tenant_negative.py
@@ -42,9 +42,7 @@
     @decorators.idempotent_id('162ba316-f18b-4987-8c0c-fd9140cd63ed')
     def test_tenant_delete_by_unauthorized_user(self):
         # Non-administrator user should not be able to delete a tenant
-        tenant_name = data_utils.rand_name(name='tenant')
-        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
-        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant()
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_tenants_client.delete_tenant,
                           tenant['id'])
@@ -53,9 +51,7 @@
     @decorators.idempotent_id('e450db62-2e9d-418f-893a-54772d6386b1')
     def test_tenant_delete_request_without_token(self):
         # Request to delete a tenant without a valid token should fail
-        tenant_name = data_utils.rand_name(name='tenant')
-        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
-        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant()
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
         self.assertRaises(lib_exc.Unauthorized,
@@ -75,10 +71,7 @@
     def test_tenant_create_duplicate(self):
         # Tenant names should be unique
         tenant_name = data_utils.rand_name(name='tenant')
-        body = self.tenants_client.create_tenant(name=tenant_name)['tenant']
-        tenant1_id = body.get('id')
-
-        self.addCleanup(self.tenants_client.delete_tenant, tenant1_id)
+        self.setup_test_tenant(name=tenant_name)
         self.assertRaises(lib_exc.Conflict, self.tenants_client.create_tenant,
                           name=tenant_name)
 
@@ -131,9 +124,7 @@
     @decorators.idempotent_id('41704dc5-c5f7-4f79-abfa-76e6fedc570b')
     def test_tenant_update_by_unauthorized_user(self):
         # Non-administrator user should not be able to update a tenant
-        tenant_name = data_utils.rand_name(name='tenant')
-        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
-        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant()
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_tenants_client.update_tenant,
                           tenant['id'])
@@ -142,9 +133,7 @@
     @decorators.idempotent_id('7a421573-72c7-4c22-a98e-ce539219c657')
     def test_tenant_update_request_without_token(self):
         # Request to update a tenant without a valid token should fail
-        tenant_name = data_utils.rand_name(name='tenant')
-        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
-        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant()
         token = self.client.auth_provider.get_token()
         self.client.delete_token(token)
         self.assertRaises(lib_exc.Unauthorized,
diff --git a/tempest/api/identity/admin/v2/test_tenants.py b/tempest/api/identity/admin/v2/test_tenants.py
index 6b7413c..ad9b983 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -15,7 +15,6 @@
 
 from tempest.api.identity import base
 from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 
@@ -26,12 +25,7 @@
         # Create several tenants and delete them
         tenants = []
         for _ in range(3):
-            tenant_name = data_utils.rand_name(name='tenant-new')
-            tenant = self.tenants_client.create_tenant(
-                name=tenant_name)['tenant']
-            # Add the tenant to the cleanup list
-            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                            self.tenants_client.delete_tenant, tenant['id'])
+            tenant = self.setup_test_tenant()
             tenants.append(tenant)
         tenant_ids = [tn['id'] for tn in tenants]
         body = self.tenants_client.list_tenants()['tenants']
@@ -48,14 +42,8 @@
     @decorators.idempotent_id('d25e9f24-1310-4d29-b61b-d91299c21d6d')
     def test_tenant_create_with_description(self):
         # Create tenant with a description
-        tenant_name = data_utils.rand_name(name='tenant')
         tenant_desc = data_utils.rand_name(name='desc')
-        body = self.tenants_client.create_tenant(name=tenant_name,
-                                                 description=tenant_desc)
-        tenant = body['tenant']
-        # Add the tenant to the cleanup list
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant(description=tenant_desc)
         tenant_id = tenant['id']
         desc1 = tenant['description']
         self.assertEqual(desc1, tenant_desc, 'Description should have '
@@ -69,13 +57,7 @@
     @decorators.idempotent_id('670bdddc-1cd7-41c7-b8e2-751cfb67df50')
     def test_tenant_create_enabled(self):
         # Create a tenant that is enabled
-        tenant_name = data_utils.rand_name(name='tenant')
-        body = self.tenants_client.create_tenant(name=tenant_name,
-                                                 enabled=True)
-        tenant = body['tenant']
-        # Add the tenant to the cleanup list
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant(enabled=True)
         tenant_id = tenant['id']
         en1 = tenant['enabled']
         self.assertTrue(en1, 'Enable should be True in response')
@@ -87,13 +69,7 @@
     @decorators.idempotent_id('3be22093-b30f-499d-b772-38340e5e16fb')
     def test_tenant_create_not_enabled(self):
         # Create a tenant that is not enabled
-        tenant_name = data_utils.rand_name(name='tenant')
-        body = self.tenants_client.create_tenant(name=tenant_name,
-                                                 enabled=False)
-        tenant = body['tenant']
-        # Add the tenant to the cleanup list
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant(enabled=False)
         tenant_id = tenant['id']
         en1 = tenant['enabled']
         self.assertEqual('false', str(en1).lower(),
@@ -108,14 +84,9 @@
     def test_tenant_update_name(self):
         # Update name attribute of a tenant
         t_name1 = data_utils.rand_name(name='tenant')
-        body = self.tenants_client.create_tenant(name=t_name1)['tenant']
-        tenant = body
-        # Add the tenant to the cleanup list
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant, tenant['id'])
-
-        t_id = body['id']
-        resp1_name = body['name']
+        tenant = self.setup_test_tenant(name=t_name1)
+        t_id = tenant['id']
+        resp1_name = tenant['name']
 
         t_name2 = data_utils.rand_name(name='tenant2')
         body = self.tenants_client.update_tenant(t_id, name=t_name2)['tenant']
@@ -134,15 +105,8 @@
     @decorators.idempotent_id('859fcfe1-3a03-41ef-86f9-b19a47d1cd87')
     def test_tenant_update_desc(self):
         # Update description attribute of a tenant
-        t_name = data_utils.rand_name(name='tenant')
         t_desc = data_utils.rand_name(name='desc')
-        body = self.tenants_client.create_tenant(name=t_name,
-                                                 description=t_desc)
-        tenant = body['tenant']
-        # Add the tenant to the cleanup list
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant, tenant['id'])
-
+        tenant = self.setup_test_tenant(description=t_desc)
         t_id = tenant['id']
         resp1_desc = tenant['description']
 
@@ -164,14 +128,8 @@
     @decorators.idempotent_id('8fc8981f-f12d-4c66-9972-2bdcf2bc2e1a')
     def test_tenant_update_enable(self):
         # Update the enabled attribute of a tenant
-        t_name = data_utils.rand_name(name='tenant')
         t_en = False
-        body = self.tenants_client.create_tenant(name=t_name, enabled=t_en)
-        tenant = body['tenant']
-        # Add the tenant to the cleanup list
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant, tenant['id'])
-
+        tenant = self.setup_test_tenant(enabled=t_en)
         t_id = tenant['id']
         resp1_en = tenant['enabled']
 
diff --git a/tempest/api/identity/admin/v2/test_tokens.py b/tempest/api/identity/admin/v2/test_tokens.py
index b288705..b4c9389 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -26,10 +26,7 @@
         user_name = data_utils.rand_name(name='user')
         user_password = data_utils.rand_password()
         # first:create a tenant
-        tenant_name = data_utils.rand_name(name='tenant')
-        tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
-        # Delete the tenant at the end of the test
-        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+        tenant = self.setup_test_tenant()
         # second:create a user
         user = self.create_test_user(name=user_name,
                                      password=user_password,
@@ -70,16 +67,10 @@
 
         # Create a couple tenants.
         tenant1_name = data_utils.rand_name(name='tenant')
-        tenant1 = self.tenants_client.create_tenant(
-            name=tenant1_name)['tenant']
-        # Delete the tenant at the end of the test
-        self.addCleanup(self.tenants_client.delete_tenant, tenant1['id'])
+        tenant1 = self.setup_test_tenant(name=tenant1_name)
 
         tenant2_name = data_utils.rand_name(name='tenant')
-        tenant2 = self.tenants_client.create_tenant(
-            name=tenant2_name)['tenant']
-        # Delete the tenant at the end of the test
-        self.addCleanup(self.tenants_client.delete_tenant, tenant2['id'])
+        tenant2 = self.setup_test_tenant(name=tenant2_name)
 
         # Create a role
         role = self.setup_test_role()
diff --git a/tempest/api/identity/admin/v3/test_default_project_id.py b/tempest/api/identity/admin/v3/test_default_project_id.py
index c2ab488..ac2faa9 100644
--- a/tempest/api/identity/admin/v3/test_default_project_id.py
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -42,13 +42,10 @@
         self.addCleanup(self._delete_domain, dom_id)
 
         # create a project in the domain
-        proj_name = data_utils.rand_name('proj')
-        proj_body = self.projects_client.create_project(
-            proj_name, domain_id=dom_id)['project']
+        proj_body = self.setup_test_project(domain_id=dom_id)
         proj_id = proj_body['id']
-        self.addCleanup(self.projects_client.delete_project, proj_id)
         self.assertEqual(proj_body['domain_id'], dom_id,
-                         "project " + proj_name +
+                         "project " + proj_body['name'] +
                          "doesn't have domain id " + dom_id)
 
         # create a user in the domain, with the previous project as his
diff --git a/tempest/api/identity/admin/v3/test_domain_configuration.py b/tempest/api/identity/admin/v3/test_domain_configuration.py
new file mode 100644
index 0000000..f731697
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_domain_configuration.py
@@ -0,0 +1,184 @@
+# Copyright 2017 AT&T Corporation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+
+class DomainConfigurationTestJSON(base.BaseIdentityV3AdminTest):
+
+    custom_config = {
+        "identity": {
+            "driver": "ldap"
+        },
+        "ldap": {
+            "url": "ldap://myldap.com:389/",
+            "user_tree_dn": "ou=Users,dc=my_new_root,dc=org"
+        }
+    }
+
+    @classmethod
+    def setup_clients(cls):
+        super(DomainConfigurationTestJSON, cls).setup_clients()
+        cls.client = cls.domain_config_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(DomainConfigurationTestJSON, cls).resource_setup()
+        cls.group = cls.groups_client.create_group(
+            name=data_utils.rand_name('group'),
+            description=data_utils.rand_name('group-desc'))['group']
+
+    @classmethod
+    def resource_cleanup(cls):
+        cls.groups_client.delete_group(cls.group['id'])
+        super(DomainConfigurationTestJSON, cls).resource_cleanup()
+
+    def _create_domain_and_config(self, config):
+        domain = self.setup_test_domain()
+        config = self.client.create_domain_config(domain['id'], **config)[
+            'config']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_domain_config, domain['id'])
+        return domain, config
+
+    @decorators.idempotent_id('11a02bf0-6f94-4380-b3b0-c8dc18fc0d22')
+    def test_show_default_group_config_and_options(self):
+        # The API supports only the identity and ldap groups. For the ldap
+        # group, a valid value is url or user_tree_dn. For the identity group,
+        # a valid value is driver.
+
+        # Check that the default config has the identity and ldap groups.
+        config = self.client.show_default_config_settings()['config']
+        self.assertIsInstance(config, dict)
+        self.assertIn('identity', config)
+        self.assertIn('ldap', config)
+
+        # Check that the identity group is correct.
+        identity_config = self.client.show_default_group_config('identity')[
+            'config']
+
+        self.assertIsInstance(identity_config, dict)
+        self.assertIn('identity', identity_config)
+        self.assertIn('driver', identity_config['identity'])
+        self.assertIn('list_limit', identity_config['identity'])
+
+        # Show each option for the default domain and identity group.
+        for config_opt_name in ['driver', 'list_limit']:
+            retrieved_config_opt = self.client.show_default_group_option(
+                'identity', config_opt_name)['config']
+            self.assertIn(config_opt_name, retrieved_config_opt)
+
+        # Check that the ldap group is correct.
+        ldap_config = self.client.show_default_group_config('ldap')['config']
+
+        self.assertIsInstance(ldap_config, dict)
+        self.assertIn('ldap', ldap_config)
+
+        # Several valid options exist for ldap group.
+        valid_options = ldap_config['ldap'].keys()
+
+        # Show each option for the default domain and ldap group.
+        for config_opt_name in valid_options:
+            retrieved_config_opt = self.client.show_default_group_option(
+                'ldap', config_opt_name)['config']
+            self.assertIn(config_opt_name, retrieved_config_opt)
+
+    @decorators.idempotent_id('9e3ff13c-f597-4f01-9377-d6c06c2a1477')
+    def test_create_domain_config_and_show_config_groups_and_options(self):
+        domain, created_config = self._create_domain_and_config(
+            self.custom_config)
+
+        # Check that the entire configuration is correct.
+        self.assertEqual(self.custom_config, created_config)
+
+        # Check that each configuration group is correct.
+        for group_name in self.custom_config.keys():
+            group_cfg = self.client.show_domain_group_config(
+                domain['id'], group_name)['config']
+            self.assertIn(group_name, group_cfg)
+            self.assertEqual(self.custom_config[group_name],
+                             group_cfg[group_name])
+
+            # Check that each configuration option is correct.
+            for opt_name in self.custom_config[group_name].keys():
+                group_opt = self.client.show_domain_group_option_config(
+                    domain['id'], group_name, opt_name)['config']
+                self.assertIn(opt_name, group_opt)
+                self.assertEqual(self.custom_config[group_name][opt_name],
+                                 group_opt[opt_name])
+
+    @decorators.idempotent_id('7161023e-5dd0-4612-9da0-1bac6ac30b63')
+    def test_create_update_and_delete_domain_config(self):
+        domain, created_config = self._create_domain_and_config(
+            self.custom_config)
+
+        new_config = created_config
+        new_config['ldap']['url'] = data_utils.rand_url()
+
+        # Check that the altered configuration is reflected in updated_config.
+        updated_config = self.client.update_domain_config(
+            domain['id'], **new_config)['config']
+        self.assertEqual(new_config, updated_config)
+
+        # Check that showing the domain config shows the altered configuration.
+        retrieved_config = self.client.show_domain_config(domain['id'])[
+            'config']
+        self.assertEqual(new_config, retrieved_config)
+
+        # Check that deleting a configuration works.
+        self.client.delete_domain_config(domain['id'])
+        self.assertRaises(lib_exc.NotFound, self.client.show_domain_config,
+                          domain['id'])
+
+    @decorators.idempotent_id('c7510fa2-6661-4170-9c6b-4783a80651e9')
+    def test_create_update_and_delete_domain_config_groups_and_opts(self):
+        domain, _ = self._create_domain_and_config(self.custom_config)
+
+        # Check that updating configuration groups work.
+        new_driver = data_utils.rand_name('driver')
+        new_limit = data_utils.rand_int_id(0, 100)
+        new_group_config = {'identity': {'driver': new_driver,
+                                         'list_limit': new_limit}}
+
+        updated_config = self.client.update_domain_group_config(
+            domain['id'], 'identity', **new_group_config)['config']
+
+        self.assertEqual(new_driver, updated_config['identity']['driver'])
+        self.assertEqual(new_limit, updated_config['identity']['list_limit'])
+
+        # Check that updating individual configuration group options work.
+        new_driver = data_utils.rand_name('driver')
+
+        updated_config = self.client.update_domain_group_option_config(
+            domain['id'], 'identity', 'driver', driver=new_driver)['config']
+
+        self.assertEqual(new_driver, updated_config['identity']['driver'])
+
+        # Check that deleting individual configuration group options work.
+        self.client.delete_domain_group_option_config(
+            domain['id'], 'identity', 'driver')
+        self.assertRaises(lib_exc.NotFound,
+                          self.client.show_domain_group_option_config,
+                          domain['id'], 'identity', 'driver')
+
+        # Check that deleting configuration groups work.
+        self.client.delete_domain_group_config(domain['id'], 'identity')
+        self.assertRaises(lib_exc.NotFound,
+                          self.client.show_domain_group_config,
+                          domain['id'], 'identity')
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index 4de0f5b..f630f74 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -147,12 +147,8 @@
         src_role = self.setup_test_role()
 
         # Create a project hierarchy
-        leaf_project_name = data_utils.rand_name('project')
-        leaf_project = self.projects_client.create_project(
-            leaf_project_name, domain_id=self.domain['id'],
-            parent_id=self.project['id'])['project']
-        self.addCleanup(
-            self.projects_client.delete_project, leaf_project['id'])
+        leaf_project = self.setup_test_project(domain_id=self.domain['id'],
+                                               parent_id=self.project['id'])
 
         # Assign role on domain
         self.inherited_roles_client.create_inherited_role_on_domains_user(
@@ -195,12 +191,8 @@
         src_role = self.setup_test_role()
 
         # Create a project hierarchy
-        leaf_project_name = data_utils.rand_name('project')
-        leaf_project = self.projects_client.create_project(
-            leaf_project_name, domain_id=self.domain['id'],
-            parent_id=self.project['id'])['project']
-        self.addCleanup(
-            self.projects_client.delete_project, leaf_project['id'])
+        leaf_project = self.setup_test_project(domain_id=self.domain['id'],
+                                               parent_id=self.project['id'])
 
         # Assign role on parent project
         self.inherited_roles_client.create_inherited_role_on_projects_user(
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index 258581b..1b1d3f7 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -26,11 +26,8 @@
     @decorators.idempotent_id('0ecf465c-0dc4-4532-ab53-91ffeb74d12d')
     def test_project_create_with_description(self):
         # Create project with a description
-        project_name = data_utils.rand_name('project')
         project_desc = data_utils.rand_name('desc')
-        project = self.projects_client.create_project(
-            project_name, description=project_desc)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(description=project_desc)
         project_id = project['id']
         desc1 = project['description']
         self.assertEqual(desc1, project_desc, 'Description should have '
@@ -45,9 +42,8 @@
         # Create project with a domain
         domain = self.setup_test_domain()
         project_name = data_utils.rand_name('project')
-        project = self.projects_client.create_project(
-            project_name, domain_id=domain['id'])['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(
+            name=project_name, domain_id=domain['id'])
         project_id = project['id']
         self.assertEqual(project_name, project['name'])
         self.assertEqual(domain['id'], project['domain_id'])
@@ -62,10 +58,8 @@
         domain_id = domain['id']
 
         root_project_name = data_utils.rand_name('root_project')
-        root_project = self.projects_client.create_project(
-            root_project_name, domain_id=domain_id)['project']
-        self.addCleanup(
-            self.projects_client.delete_project, root_project['id'])
+        root_project = self.setup_test_project(
+            name=root_project_name, domain_id=domain_id)
 
         root_project_id = root_project['id']
         parent_id = root_project['parent_id']
@@ -76,21 +70,16 @@
 
         # Create a project using root_project_id as parent_id
         project_name = data_utils.rand_name('project')
-        project = self.projects_client.create_project(
-            project_name, domain_id=domain_id,
-            parent_id=root_project_id)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(
+            name=project_name, domain_id=domain_id, parent_id=root_project_id)
         parent_id = project['parent_id']
         self.assertEqual(project_name, project['name'])
         self.assertEqual(root_project_id, parent_id)
 
     @decorators.idempotent_id('a7eb9416-6f9b-4dbb-b71b-7f73aaef59d5')
     def test_create_is_domain_project(self):
-        project_name = data_utils.rand_name('is_domain_project')
-        project = self.projects_client.create_project(
-            project_name, domain_id=None, is_domain=True)['project']
+        project = self.setup_test_project(domain_id=None, is_domain=True)
         # To delete a domain, we need to disable it first
-        self.addCleanup(self.projects_client.delete_project, project['id'])
         self.addCleanup(self.projects_client.update_project, project['id'],
                         enabled=False)
 
@@ -109,10 +98,7 @@
     @decorators.idempotent_id('1f66dc76-50cc-4741-a200-af984509e480')
     def test_project_create_enabled(self):
         # Create a project that is enabled
-        project_name = data_utils.rand_name('project')
-        project = self.projects_client.create_project(
-            project_name, enabled=True)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(enabled=True)
         project_id = project['id']
         en1 = project['enabled']
         self.assertTrue(en1, 'Enable should be True in response')
@@ -123,10 +109,7 @@
     @decorators.idempotent_id('78f96a9c-e0e0-4ee6-a3ba-fbf6dfd03207')
     def test_project_create_not_enabled(self):
         # Create a project that is not enabled
-        project_name = data_utils.rand_name('project')
-        project = self.projects_client.create_project(
-            project_name, enabled=False)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(enabled=False)
         en1 = project['enabled']
         self.assertEqual('false', str(en1).lower(),
                          'Enable should be False in response')
@@ -139,8 +122,7 @@
     def test_project_update_name(self):
         # Update name attribute of a project
         p_name1 = data_utils.rand_name('project')
-        project = self.projects_client.create_project(p_name1)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(name=p_name1)
 
         resp1_name = project['name']
 
@@ -160,11 +142,8 @@
     @decorators.idempotent_id('f138b715-255e-4a7d-871d-351e1ef2e153')
     def test_project_update_desc(self):
         # Update description attribute of a project
-        p_name = data_utils.rand_name('project')
         p_desc = data_utils.rand_name('desc')
-        project = self.projects_client.create_project(
-            p_name, description=p_desc)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(description=p_desc)
         resp1_desc = project['description']
 
         p_desc2 = data_utils.rand_name('desc2')
@@ -183,11 +162,8 @@
     @decorators.idempotent_id('b6b25683-c97f-474d-a595-55d410b68100')
     def test_project_update_enable(self):
         # Update the enabled attribute of a project
-        p_name = data_utils.rand_name('project')
         p_en = False
-        project = self.projects_client.create_project(p_name,
-                                                      enabled=p_en)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project(enabled=p_en)
 
         resp1_en = project['enabled']
 
@@ -208,9 +184,7 @@
     def test_associate_user_to_project(self):
         # Associate a user to a project
         # Create a Project
-        p_name = data_utils.rand_name('project')
-        project = self.projects_client.create_project(p_name)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project()
 
         # Create a User
         u_name = data_utils.rand_name('user')
diff --git a/tempest/api/identity/admin/v3/test_projects_negative.py b/tempest/api/identity/admin/v3/test_projects_negative.py
index 6277fda..33a9c8c 100644
--- a/tempest/api/identity/admin/v3/test_projects_negative.py
+++ b/tempest/api/identity/admin/v3/test_projects_negative.py
@@ -33,8 +33,7 @@
     def test_project_create_duplicate(self):
         # Project names should be unique
         project_name = data_utils.rand_name('project-dup')
-        project = self.projects_client.create_project(project_name)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        self.setup_test_project(name=project_name)
 
         self.assertRaises(lib_exc.Conflict,
                           self.projects_client.create_project, project_name)
@@ -67,9 +66,7 @@
     @decorators.idempotent_id('8d68c012-89e0-4394-8d6b-ccd7196def97')
     def test_project_delete_by_unauthorized_user(self):
         # Non-admin user should not be able to delete a project
-        project_name = data_utils.rand_name('project')
-        project = self.projects_client.create_project(project_name)['project']
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project()
         self.assertRaises(
             lib_exc.Forbidden, self.non_admin_projects_client.delete_project,
             project['id'])
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index de4f580..1a9502a 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -65,13 +65,10 @@
 
         # Create a couple projects
         project1_name = data_utils.rand_name(name='project')
-        project1 = self.projects_client.create_project(
-            project1_name)['project']
-        self.addCleanup(self.projects_client.delete_project, project1['id'])
+        project1 = self.setup_test_project(name=project1_name)
 
         project2_name = data_utils.rand_name(name='project')
-        project2 = self.projects_client.create_project(
-            project2_name)['project']
+        project2 = self.setup_test_project(name=project2_name)
         self.addCleanup(self.projects_client.delete_project, project2['id'])
 
         # Create a role
diff --git a/tempest/api/identity/admin/v3/test_users.py b/tempest/api/identity/admin/v3/test_users.py
index 28137ad..4f271cb 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -42,11 +42,7 @@
         # Delete the User at the end of this method
         self.addCleanup(self.users_client.delete_user, user['id'])
         # Creating second project for updation
-        project = self.projects_client.create_project(
-            data_utils.rand_name('project'),
-            description=data_utils.rand_name('project-desc'))['project']
-        # Delete the Project at the end of this method
-        self.addCleanup(self.projects_client.delete_project, project['id'])
+        project = self.setup_test_project()
         # Updating user details with new values
         u_name2 = data_utils.rand_name('user2')
         u_email2 = u_name2 + '@testmail.tm'
@@ -102,11 +98,7 @@
         # List the projects that a user has access upon
         assigned_project_ids = list()
         fetched_project_ids = list()
-        u_project = self.projects_client.create_project(
-            data_utils.rand_name('project'),
-            description=data_utils.rand_name('project-desc'))['project']
-        # Delete the Project at the end of this method
-        self.addCleanup(self.projects_client.delete_project, u_project['id'])
+        u_project = self.setup_test_project()
         # Create a user.
         u_name = data_utils.rand_name('user')
         u_desc = u_name + 'description'
@@ -124,14 +116,9 @@
         role = self.roles_client.show_role(role_body['id'])['role']
         for _ in range(2):
             # Creating project so as to assign role
-            project_body = self.projects_client.create_project(
-                data_utils.rand_name('project'),
-                description=data_utils.rand_name('project-desc'))['project']
+            project_body = self.setup_test_project()
             project = self.projects_client.show_project(
                 project_body['id'])['project']
-            # Delete the Project at the end of this method
-            self.addCleanup(
-                self.projects_client.delete_project, project_body['id'])
             # Assigning roles to user on project
             self.roles_client.create_user_role_on_project(project['id'],
                                                           user['id'],
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 06cc120..5dc88b8 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -127,6 +127,12 @@
     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
@@ -152,11 +158,13 @@
         user = self.create_test_user(tenantId=tenant['id'], password=password)
         return user
 
-    def setup_test_tenant(self):
+    def setup_test_tenant(self, **kwargs):
         """Set up a test tenant."""
-        tenant = self.projects_client.create_tenant(
-            name=data_utils.rand_name('test_tenant'),
-            description=data_utils.rand_name('desc'))['tenant']
+        if 'name' not in kwargs:
+            kwargs['name'] = data_utils.rand_name('test_tenant')
+        if 'description' not in kwargs:
+            kwargs['description'] = data_utils.rand_name('desc')
+        tenant = self.projects_client.create_tenant(**kwargs)['tenant']
         # Delete the tenant at the end of the test
         self.addCleanup(
             test_utils.call_and_ignore_notfound_exc,
@@ -214,6 +222,7 @@
         cls.projects_client = cls.os_adm.projects_client
         cls.role_assignments = cls.os_admin.role_assignments_client
         cls.oauth_consumers_client = cls.os_adm.oauth_consumers_client
+        cls.domain_config_client = cls.os_adm.domain_config_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.
@@ -247,11 +256,13 @@
                                      password=password)
         return user
 
-    def setup_test_project(self):
+    def setup_test_project(self, **kwargs):
         """Set up a test project."""
-        project = self.projects_client.create_project(
-            name=data_utils.rand_name('test_project'),
-            description=data_utils.rand_name('desc'))['project']
+        if 'name' not in kwargs:
+            kwargs['name'] = data_utils.rand_name('test_project')
+        if 'description' not in kwargs:
+            kwargs['description'] = data_utils.rand_name('test_description')
+        project = self.projects_client.create_project(**kwargs)['project']
         # Delete the project at the end of the test
         self.addCleanup(
             test_utils.call_and_ignore_notfound_exc,
diff --git a/tempest/api/image/admin/__init__.py b/tempest/api/image/admin/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/image/admin/__init__.py
+++ /dev/null
diff --git a/tempest/api/image/admin/v2/__init__.py b/tempest/api/image/admin/v2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/image/admin/v2/__init__.py
+++ /dev/null
diff --git a/tempest/api/image/admin/v2/test_images.py b/tempest/api/image/admin/v2/test_images.py
deleted file mode 100644
index fc5ed79..0000000
--- a/tempest/api/image/admin/v2/test_images.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright 2015 Red Hat, Inc.
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import six
-import testtools
-
-from tempest.api.image import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-CONF = config.CONF
-
-
-class BasicAdminOperationsImagesTest(base.BaseV2ImageAdminTest):
-    """Here we test admin operations of images"""
-
-    @testtools.skipUnless(CONF.image_feature_enabled.deactivate_image,
-                          'deactivate-image is not available.')
-    @decorators.idempotent_id('951ebe01-969f-4ea9-9898-8a3f1f442ab0')
-    def test_admin_deactivate_reactivate_image(self):
-        # Create image by non-admin tenant
-        image_name = data_utils.rand_name('image')
-        image = self.create_image(name=image_name,
-                                  container_format='bare',
-                                  disk_format='raw',
-                                  visibility='private')
-        # upload an image file
-        content = data_utils.random_bytes()
-        image_file = six.BytesIO(content)
-        self.client.store_image_file(image['id'], image_file)
-        # deactivate image
-        self.admin_client.deactivate_image(image['id'])
-        body = self.client.show_image(image['id'])
-        self.assertEqual("deactivated", body['status'])
-        # non-admin user unable to download deactivated image
-        self.assertRaises(lib_exc.Forbidden, self.client.show_image_file,
-                          image['id'])
-        # reactivate image
-        self.admin_client.reactivate_image(image['id'])
-        body = self.client.show_image(image['id'])
-        self.assertEqual("active", body['status'])
-        # non-admin user able to download image after reactivation by admin
-        body = self.client.show_image_file(image['id'])
-        self.assertEqual(content, body.data)
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 8310001..a5d9773 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -18,11 +18,14 @@
 
 import six
 
+import testtools
+
 from oslo_log import log as logging
 from tempest.api.image import base
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
@@ -125,6 +128,40 @@
         self.assertEqual(image['id'], body['id'])
         self.assertEqual(new_image_name, body['name'])
 
+    @testtools.skipUnless(CONF.image_feature_enabled.deactivate_image,
+                          'deactivate-image is not available.')
+    @decorators.idempotent_id('951ebe01-969f-4ea9-9898-8a3f1f442ab0')
+    def test_deactivate_reactivate_image(self):
+        # Create image
+        image_name = data_utils.rand_name('image')
+        image = self.create_image(name=image_name,
+                                  container_format='bare',
+                                  disk_format='raw',
+                                  visibility='private')
+
+        # Upload an image file
+        content = data_utils.random_bytes()
+        image_file = six.BytesIO(content)
+        self.client.store_image_file(image['id'], image_file)
+
+        # Deactivate image
+        self.client.deactivate_image(image['id'])
+        body = self.client.show_image(image['id'])
+        self.assertEqual("deactivated", body['status'])
+
+        # User unable to download deactivated image
+        self.assertRaises(lib_exc.Forbidden, self.client.show_image_file,
+                          image['id'])
+
+        # Reactivate image
+        self.client.reactivate_image(image['id'])
+        body = self.client.show_image(image['id'])
+        self.assertEqual("active", body['status'])
+
+        # User able to download image after reactivation
+        body = self.client.show_image_file(image['id'])
+        self.assertEqual(content, body.data)
+
 
 class ListUserImagesTest(base.BaseV2ImageTest):
     """Here we test the listing of image information"""
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index e7460af..85b2472 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -27,11 +27,7 @@
 
 
 class L3AgentSchedulerTestJSON(base.BaseAdminNetworkTest):
-    _agent_mode = 'legacy'
-
-    """
-    Tests the following operations in the Neutron API using the REST client for
-    Neutron:
+    """Tests the following operations in the Neutron API:
 
         List routers that the given L3 agent is hosting.
         List L3 agents hosting the given router.
@@ -52,14 +48,10 @@
     @classmethod
     def resource_setup(cls):
         super(L3AgentSchedulerTestJSON, cls).resource_setup()
-        body = cls.admin_agents_client.list_agents()
-        agents = body['agents']
+        agents = cls.admin_agents_client.list_agents(
+            agent_type=AGENT_TYPE)['agents']
         for agent in agents:
-            # TODO(armax): falling back on default _agent_mode can be
-            # dropped as soon as Icehouse is dropped.
-            agent_mode = (
-                agent['configurations'].get('agent_mode', cls._agent_mode))
-            if agent['agent_type'] == AGENT_TYPE and agent_mode in AGENT_MODES:
+            if agent['configurations']['agent_mode'] in AGENT_MODES:
                 cls.agent = agent
                 break
         else:
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index 6784a55..f955d34 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -82,11 +82,13 @@
         """Test that a user cannot modify or remove a quota on its account."""
 
         # Not able to remove quota
-        self.assertRaises(lib_exc.Forbidden,
-                          self.account_client.create_account_metadata,
-                          {"Quota-Bytes": ""})
+        self.assertRaises(
+            lib_exc.Forbidden,
+            self.account_client.create_update_or_delete_account_metadata,
+            create_update_metadata={"Quota-Bytes": ""})
 
         # Not able to modify quota
-        self.assertRaises(lib_exc.Forbidden,
-                          self.account_client.create_account_metadata,
-                          {"Quota-Bytes": "100"})
+        self.assertRaises(
+            lib_exc.Forbidden,
+            self.account_client.create_update_or_delete_account_metadata,
+            create_update_metadata={"Quota-Bytes": "100"})
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index d0a3f10..e54b6e7 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -272,13 +272,15 @@
         # set metadata to account
         metadata = {'test-account-meta1': 'Meta1',
                     'test-account-meta2': 'Meta2'}
-        resp, _ = self.account_client.create_account_metadata(metadata)
+        resp, _ = self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata)
 
         resp, _ = self.account_client.list_account_metadata()
         self.assertHeaders(resp, 'Account', 'HEAD')
         self.assertIn('x-account-meta-test-account-meta1', resp)
         self.assertIn('x-account-meta-test-account-meta2', resp)
-        self.account_client.delete_account_metadata(metadata)
+        self.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=metadata)
 
     @decorators.idempotent_id('b904c2e3-24c2-4dba-ad7d-04e90a761be5')
     def test_list_no_account_metadata(self):
@@ -291,7 +293,8 @@
     def test_update_account_metadata_with_create_metadata(self):
         # add metadata to account
         metadata = {'test-account-meta1': 'Meta1'}
-        resp, _ = self.account_client.create_account_metadata(metadata)
+        resp, _ = self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata)
         self.assertHeaders(resp, 'Account', 'POST')
 
         resp, body = self.account_client.list_account_metadata()
@@ -299,14 +302,17 @@
         self.assertEqual(resp['x-account-meta-test-account-meta1'],
                          metadata['test-account-meta1'])
 
-        self.account_client.delete_account_metadata(metadata)
+        self.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=metadata)
 
     @decorators.idempotent_id('9f60348d-c46f-4465-ae06-d51dbd470953')
     def test_update_account_metadata_with_delete_metadata(self):
         # delete metadata from account
         metadata = {'test-account-meta1': 'Meta1'}
-        self.account_client.create_account_metadata(metadata)
-        resp, _ = self.account_client.delete_account_metadata(metadata)
+        self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata)
+        resp, _ = self.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=metadata)
         self.assertHeaders(resp, 'Account', 'POST')
 
         resp, _ = self.account_client.list_account_metadata()
@@ -317,7 +323,8 @@
         # if the value of metadata is not set, the metadata is not
         # registered at a server
         metadata = {'test-account-meta1': ''}
-        resp, _ = self.account_client.create_account_metadata(metadata)
+        resp, _ = self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata)
         self.assertHeaders(resp, 'Account', 'POST')
 
         resp, _ = self.account_client.list_account_metadata()
@@ -328,9 +335,11 @@
         # Although the value of metadata is not set, the feature of
         # deleting metadata is valid
         metadata_1 = {'test-account-meta1': 'Meta1'}
-        self.account_client.create_account_metadata(metadata_1)
+        self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata_1)
         metadata_2 = {'test-account-meta1': ''}
-        resp, _ = self.account_client.delete_account_metadata(metadata_2)
+        resp, _ = self.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=metadata_2)
         self.assertHeaders(resp, 'Account', 'POST')
 
         resp, _ = self.account_client.list_account_metadata()
@@ -340,11 +349,13 @@
     def test_update_account_metadata_with_create_and_delete_metadata(self):
         # Send a request adding and deleting metadata requests simultaneously
         metadata_1 = {'test-account-meta1': 'Meta1'}
-        self.account_client.create_account_metadata(metadata_1)
+        self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata_1)
         metadata_2 = {'test-account-meta2': 'Meta2'}
-        resp, body = self.account_client.create_and_delete_account_metadata(
-            metadata_2,
-            metadata_1)
+        resp, body = (
+            self.account_client.create_update_or_delete_account_metadata(
+                create_update_metadata=metadata_2,
+                delete_metadata=metadata_1))
         self.assertHeaders(resp, 'Account', 'POST')
 
         resp, _ = self.account_client.list_account_metadata()
@@ -353,4 +364,5 @@
         self.assertEqual(resp['x-account-meta-test-account-meta2'],
                          metadata_2['test-account-meta2'])
 
-        self.account_client.delete_account_metadata(metadata_2)
+        self.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=metadata_2)
diff --git a/tempest/api/object_storage/test_object_formpost.py b/tempest/api/object_storage/test_object_formpost.py
index c100629..3a2233a 100644
--- a/tempest/api/object_storage/test_object_formpost.py
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -37,7 +37,8 @@
 
         cls.key = 'Meta'
         cls.metadata = {'Temp-URL-Key': cls.key}
-        cls.account_client.create_account_metadata(metadata=cls.metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=cls.metadata)
 
     def setUp(self):
         super(ObjectFormPostTest, self).setUp()
@@ -53,7 +54,8 @@
 
     @classmethod
     def resource_cleanup(cls):
-        cls.account_client.delete_account_metadata(metadata=cls.metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=cls.metadata)
         cls.delete_containers()
         super(ObjectFormPostTest, cls).resource_cleanup()
 
diff --git a/tempest/api/object_storage/test_object_formpost_negative.py b/tempest/api/object_storage/test_object_formpost_negative.py
index cfa1875..c56d91a 100644
--- a/tempest/api/object_storage/test_object_formpost_negative.py
+++ b/tempest/api/object_storage/test_object_formpost_negative.py
@@ -38,7 +38,8 @@
 
         cls.key = 'Meta'
         cls.metadata = {'Temp-URL-Key': cls.key}
-        cls.account_client.create_account_metadata(metadata=cls.metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=cls.metadata)
 
     def setUp(self):
         super(ObjectFormPostNegativeTest, self).setUp()
@@ -54,7 +55,8 @@
 
     @classmethod
     def resource_cleanup(cls):
-        cls.account_client.delete_account_metadata(metadata=cls.metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=cls.metadata)
         cls.delete_containers()
         super(ObjectFormPostNegativeTest, cls).resource_cleanup()
 
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 912deab..4b506f8 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -37,7 +37,8 @@
         cls.metadatas = []
         metadata = {'Temp-URL-Key': cls.key}
         cls.metadatas.append(metadata)
-        cls.account_client.create_account_metadata(metadata=metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata)
 
         # create an object
         cls.object_name, cls.content = cls.create_object(cls.container_name)
@@ -45,8 +46,8 @@
     @classmethod
     def resource_cleanup(cls):
         for metadata in cls.metadatas:
-            cls.account_client.delete_account_metadata(
-                metadata=metadata)
+            cls.account_client.create_update_or_delete_account_metadata(
+                delete_metadata=metadata)
 
         cls.delete_containers()
 
@@ -110,7 +111,8 @@
     def test_get_object_using_temp_url_key_2(self):
         key2 = 'Meta2-'
         metadata = {'Temp-URL-Key-2': key2}
-        self.account_client.create_account_metadata(metadata=metadata)
+        self.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=metadata)
         self.metadatas.append(metadata)
 
         # make sure the metadata has been set
diff --git a/tempest/api/object_storage/test_object_temp_url_negative.py b/tempest/api/object_storage/test_object_temp_url_negative.py
index 578249b..f4d63fd 100644
--- a/tempest/api/object_storage/test_object_temp_url_negative.py
+++ b/tempest/api/object_storage/test_object_temp_url_negative.py
@@ -39,14 +39,15 @@
         # update account metadata
         cls.key = 'Meta'
         cls.metadata = {'Temp-URL-Key': cls.key}
-        cls.account_client.create_account_metadata(metadata=cls.metadata)
+        cls.account_client.create_update_or_delete_account_metadata(
+            create_update_metadata=cls.metadata)
         cls.account_client_metadata, _ = \
             cls.account_client.list_account_metadata()
 
     @classmethod
     def resource_cleanup(cls):
-        resp, _ = cls.account_client.delete_account_metadata(
-            metadata=cls.metadata)
+        resp, _ = cls.account_client.create_update_or_delete_account_metadata(
+            delete_metadata=cls.metadata)
 
         cls.delete_containers()
 
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 a19af5d..52ec423 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -65,9 +65,11 @@
     def setup_clients(cls):
         super(BaseVolumeTest, cls).setup_clients()
         cls.servers_client = cls.os.servers_client
-        cls.compute_networks_client = cls.os.compute_networks_client
         cls.compute_images_client = cls.os.compute_images_client
 
+        if CONF.service_available.glance:
+            cls.images_client = cls.os.image_client_v2
+
         cls.snapshots_client = cls.os.snapshots_v2_client
         cls.volumes_client = cls.os.volumes_v2_client
         cls.backups_client = cls.os.backups_v2_client
@@ -143,7 +145,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
@@ -153,9 +156,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
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index afcffc2..75f2a73 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -73,19 +73,20 @@
                         volume['id'])
 
         # Create a volume transfer
-        body = self.client.create_volume_transfer(
-            volume_id=volume['id'])['transfer']
-        transfer_id = body['id']
+        transfer_id = self.client.create_volume_transfer(
+            volume_id=volume['id'])['transfer']['id']
         waiters.wait_for_volume_resource_status(
             self.volumes_client, volume['id'], 'awaiting-transfer')
 
-        # List all volume transfers (looking for the one we created)
-        body = self.client.list_volume_transfers()['transfers']
-        for transfer in body:
-            if volume['id'] == transfer['volume_id']:
-                break
-        else:
-            self.fail('Transfer not found for volume %s' % volume['id'])
+        # List all volume transfers with details, check the detail-specific
+        # elements, and look for the created transfer.
+        transfers = self.client.list_volume_transfers(detail=True)['transfers']
+        self.assertNotEmpty(transfers)
+        for transfer in transfers:
+            self.assertIn('created_at', transfer)
+        volume_list = [transfer['volume_id'] for transfer in transfers]
+        self.assertIn(volume['id'], volume_list,
+                      'Transfer not found for volume %s' % volume['id'])
 
         # Delete a volume transfer
         self.client.delete_volume_transfer(transfer_id)
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 5ba72c3..0516c69 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):
 
@@ -263,3 +270,32 @@
             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_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')
+
+        # 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'])
diff --git a/tempest/clients.py b/tempest/clients.py
index dbaacda..4ef6872 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -229,6 +229,8 @@
             **params_v3)
         self.oauth_consumers_client = self.identity_v3.OAUTHConsumerClient(
             **params_v3)
+        self.domain_config_client = self.identity_v3.DomainConfigurationClient(
+            **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
diff --git a/tempest/config.py b/tempest/config.py
index 00c69b0..b8a91b7 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'),
@@ -223,7 +227,11 @@
     cfg.BoolOpt('forbid_global_implied_dsr',
                 default=False,
                 help='Does the environment forbid global roles implying '
-                     'domain specific ones?'),
+                     'domain specific ones?',
+                deprecated_for_removal=True,
+                deprecated_reason="This feature flag was introduced to "
+                                  "support testing of old OpenStack versions, "
+                                  "which are not supported anymore"),
     cfg.BoolOpt('security_compliance',
                 default=False,
                 help='Does the environment have the security compliance '
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/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py
index 1489b50..f2f3391 100644
--- a/tempest/lib/services/identity/v3/__init__.py
+++ b/tempest/lib/services/identity/v3/__init__.py
@@ -14,6 +14,8 @@
 
 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.endpoints_client import EndPointsClient
 from tempest.lib.services.identity.v3.groups_client import GroupsClient
@@ -34,9 +36,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',
-           'PoliciesClient', 'ProjectsClient', 'RegionsClient',
-           'RoleAssignmentsClient', 'RolesClient', 'ServicesClient',
-           'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient',
-           'OAUTHConsumerClient']
+__all__ = ['CredentialsClient', 'DomainsClient', 'DomainConfigurationClient',
+           'EndPointsClient', 'GroupsClient', 'IdentityClient',
+           'InheritedRolesClient', 'OAUTHConsumerClient', 'PoliciesClient',
+           'ProjectsClient', 'RegionsClient', 'RoleAssignmentsClient',
+           'RolesClient', 'ServicesClient', '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/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/transfers_client.py b/tempest/lib/services/volume/v2/transfers_client.py
index 853948e..2dfbe7b 100644
--- a/tempest/lib/services/volume/v2/transfers_client.py
+++ b/tempest/lib/services/volume/v2/transfers_client.py
@@ -44,14 +44,17 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def list_volume_transfers(self, **params):
+    def list_volume_transfers(self, detail=False, **params):
         """List all the volume transfers created.
 
         For a full list of available parameters, please refer to the official
         API reference:
         https://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers
+        https://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers-with-details
         """
         url = 'os-volume-transfer'
+        if detail:
+            url += '/detail'
         if params:
             url += '?%s' % urllib.urlencode(params)
         resp, body = self.get(url)
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 469e58d..5a1737e 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -23,6 +23,32 @@
 
 class AccountClient(rest_client.RestClient):
 
+    def create_update_or_delete_account_metadata(
+            self,
+            create_update_metadata=None,
+            delete_metadata=None,
+            create_update_metadata_prefix='X-Account-Meta-',
+            delete_metadata_prefix='X-Remove-Account-Meta-'):
+        """Creates, Updates or deletes an account metadata entry.
+
+        Account Metadata can be created, updated or deleted based on
+        metadata header or value. For detailed info, please refer to the
+        official API reference:
+        http://developer.openstack.org/api-ref/object-storage/?expanded=create-update-or-delete-account-metadata-detail
+        """
+        headers = {}
+        if create_update_metadata:
+            for key in create_update_metadata:
+                metadata_header_name = create_update_metadata_prefix + key
+                headers[metadata_header_name] = create_update_metadata[key]
+        if delete_metadata:
+            for key in delete_metadata:
+                headers[delete_metadata_prefix + key] = delete_metadata[key]
+
+        resp, body = self.post('', headers=headers, body=None)
+        self.expected_success([200, 204], resp.status)
+        return resp, body
+
     def list_account_metadata(self):
         """HEAD on the storage URL
 
@@ -32,46 +58,6 @@
         self.expected_success(204, resp.status)
         return resp, body
 
-    def create_account_metadata(self, metadata,
-                                metadata_prefix='X-Account-Meta-'):
-        """Creates an account metadata entry."""
-        headers = {}
-        if metadata:
-            for key in metadata:
-                headers[metadata_prefix + key] = metadata[key]
-
-        resp, body = self.post('', headers=headers, body=None)
-        self.expected_success([200, 204], resp.status)
-        return resp, body
-
-    def delete_account_metadata(self, metadata,
-                                metadata_prefix='X-Remove-Account-Meta-'):
-        """Deletes an account metadata entry."""
-
-        headers = {}
-        for item in metadata:
-            headers[metadata_prefix + item] = metadata[item]
-        resp, body = self.post('', headers=headers, body=None)
-        self.expected_success(204, resp.status)
-        return resp, body
-
-    def create_and_delete_account_metadata(
-            self,
-            create_metadata=None,
-            delete_metadata=None,
-            create_metadata_prefix='X-Account-Meta-',
-            delete_metadata_prefix='X-Remove-Account-Meta-'):
-        """Creates and deletes an account metadata entry."""
-        headers = {}
-        for key in create_metadata:
-            headers[create_metadata_prefix + key] = create_metadata[key]
-        for key in delete_metadata:
-            headers[delete_metadata_prefix + key] = delete_metadata[key]
-
-        resp, body = self.post('', headers=headers, body=None)
-        self.expected_success(204, resp.status)
-        return resp, body
-
     def list_account_containers(self, params=None):
         """GET on the (base) storage URL
 
diff --git a/tempest/test.py b/tempest/test.py
index 2ebdc60..e63e08f 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -455,7 +455,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 +466,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/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/volume/v2/test_transfers_client.py b/tempest/tests/lib/services/volume/v2/test_transfers_client.py
new file mode 100644
index 0000000..0c59bf2
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_transfers_client.py
@@ -0,0 +1,61 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v2 import transfers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestTransfersClient(base.BaseServiceTest):
+
+    FAKE_LIST_VOLUME_TRANSFERS_WITH_DETAIL = {
+        "transfers": [{
+            "created_at": "2017-04-18T09:10:03.000000",
+            "volume_id": "47bf04ef-1ea5-4c5f-a375-430a086d6747",
+            "id": "0e89cdd1-6249-421b-96d8-25fac0623d42",
+            "links": [
+                {
+                    "href": "fake-url-1",
+                    "rel": "self"
+                },
+                {
+                    "href": "fake-url-2",
+                    "rel": "bookmark"
+                }
+            ],
+            "name": "fake-volume-transfer"
+        }]
+    }
+
+    def setUp(self):
+        super(TestTransfersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = transfers_client.TransfersClient(fake_auth,
+                                                       'volume',
+                                                       'regionOne')
+
+    def _test_list_volume_transfers_with_detail(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_volume_transfers,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_VOLUME_TRANSFERS_WITH_DETAIL,
+            bytes_body,
+            detail=True)
+
+    def test_list_volume_transfers_with_detail_with_str_body(self):
+        self._test_list_volume_transfers_with_detail()
+
+    def test_list_volume_transfers_with_detail_with_bytes_body(self):
+        self._test_list_volume_transfers_with_detail(bytes_body=True)
diff --git a/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()