Merge "Fixed manager.py to support multinode test on vnic_port"
diff --git a/doc/source/library.rst b/doc/source/library.rst
index 6a2fb83..29248d1 100644
--- a/doc/source/library.rst
+++ b/doc/source/library.rst
@@ -67,3 +67,4 @@
    library/utils
    library/api_microversion_testing
    library/auth
+   library/clients
diff --git a/doc/source/library/clients.rst b/doc/source/library/clients.rst
new file mode 100644
index 0000000..086cfc9
--- /dev/null
+++ b/doc/source/library/clients.rst
@@ -0,0 +1,24 @@
+.. _clients:
+
+Service Clients Usage
+=====================
+
+Tests make requests against APIs using service clients. Service clients are
+specializations of the ``RestClient`` class. The service clients that cover the
+APIs exposed by a service should be grouped in a service clients module.
+A service clients module is python module where all service clients are
+defined. If major API versions are available, submodules should be defined,
+one for each version.
+
+The ``ClientsFactory`` class helps initializing all clients of a specific
+service client module from a set of shared parameters.
+
+The ``ServiceClients`` class provides a convenient way to get access to all
+available service clients initialized with a provided set of credentials.
+
+------------------
+The clients module
+------------------
+
+.. automodule:: tempest.lib.services.clients
+   :members:
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index 9640469..d34023f 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -111,8 +111,9 @@
 
   class MyPlugin(plugins.TempestPlugin):
 
-Then you need to ensure you locally define all of the methods in the abstract
-class, you can refer to the api doc below for a reference of what that entails.
+Then you need to ensure you locally define all of the mandatory methods in the
+abstract class, you can refer to the api doc below for a reference of what that
+entails.
 
 Abstract Plugin Class
 ---------------------
@@ -164,6 +165,142 @@
 CONF object from tempest. This enables the plugin to add options to both
 existing sections and also create new configuration sections for new options.
 
+Service Clients
+---------------
+
+If a plugin defines a service client, it is beneficial for it to implement the
+``get_service_clients`` method in the plugin class. All service clients which
+are exposed via this interface will be automatically configured and be
+available in any instance of the service clients class, defined in
+``tempest.lib.services.clients.ServiceClients``. In case multiple plugins are
+installed, all service clients from all plugins will be registered, making it
+easy to write tests which rely on multiple APIs whose service clients are in
+different plugins.
+
+Example implementation of ``get_service_clients``::
+
+    def get_service_clients(self):
+        # Example implementation with two service clients
+        my_service1_config = config.service_client_config('my_service')
+        params_my_service1 = {
+            'name': 'my_service_v1',
+            'service_version': 'my_service.v1',
+            'module_path': 'plugin_tempest_tests.services.my_service.v1',
+            'client_names': ['API1Client', 'API2Client'],
+        }
+        params_my_service1.update(my_service_config)
+        my_service2_config = config.service_client_config('my_service')
+        params_my_service2 = {
+            'name': 'my_service_v2',
+            'service_version': 'my_service.v2',
+            'module_path': 'plugin_tempest_tests.services.my_service.v2',
+            'client_names': ['API1Client', 'API2Client'],
+        }
+        params_my_service2.update(my_service2_config)
+        return [params_my_service1, params_my_service2]
+
+Parameters:
+
+* **name**: Name of the attribute used to access the ``ClientsFactory`` from
+  the ``ServiceClients`` instance. See example below.
+* **service_version**: Tempest enforces a single implementation for each
+  service client. Available service clients are held in a ``ClientsRegistry``
+  singleton, and registered with ``service_version``, which means that
+  ``service_version`` must be unique and it should represent the service API
+  and version implemented by the service client.
+* **module_path**: Relative to the service client module, from the root of the
+  plugin.
+* **client_names**: Name of the classes that implement service clients in the
+  service clients module.
+
+Example usage of the the service clients in tests::
+
+   # my_creds is instance of tempest.lib.auth.Credentials
+   # identity_uri is v2 or v3 depending on the configuration
+   from tempest.lib.services import clients
+
+   my_clients = clients.ServiceClients(my_creds, identity_uri)
+   my_service1_api1_client = my_clients.my_service_v1.API1Client()
+   my_service2_api1_client = my_clients.my_service_v2.API1Client(my_args='any')
+
+Automatic configuration and registration of service clients imposes some extra
+constraints on the structure of the configuration options exposed by the
+plugin.
+
+First ``service_version`` should be in the format `service_config[.version]`.
+The `.version` part is optional, and should only be used if there are multiple
+versions of the same API available. The `service_config` must match the name of
+a configuration options group defined by the plugin. Different versions of one
+API must share the same configuration group.
+
+Second the configuration options group `service_config` must contain the
+following options:
+
+* `catalog_type`: corresponds to `service` in the catalog
+* `endpoint_type`
+
+The following options will be honoured if defined, but they are not mandatory,
+as they do not necessarily apply to all service clients.
+
+* `region`: default to identity.region
+* `build_timeout` : default to compute.build_timeout
+* `build_interval`: default to compute.build_interval
+
+Third the service client classes should inherit from ``RestClient``, should
+accept generic keyword arguments, and should pass those arguments to the
+``__init__`` method of ``RestClient``. Extra arguments can be added. For
+instance::
+
+   class MyAPIClient(rest_client.RestClient):
+
+    def __init__(self, auth_provider, service, region,
+                 my_arg, my_arg2=True, **kwargs):
+        super(MyAPIClient, self).__init__(
+            auth_provider, service, region, **kwargs)
+        self.my_arg = my_arg
+        self.my_args2 = my_arg
+
+Finally the service client should be structured in a python module, so that all
+service client classes are importable from it. Each major API version should
+have its own module.
+
+The following folder and module structure is recommended for a single major
+API version::
+
+    plugin_dir/
+      services/
+        __init__.py
+        client_api_1.py
+        client_api_2.py
+
+The content of __init__.py module should be::
+
+   from client_api_1.py import API1Client
+   from client_api_2.py import API2Client
+
+   __all__ = ['API1Client', 'API2Client']
+
+The following folder and module structure is recommended for multiple major
+API version::
+
+    plugin_dir/
+      services/
+        v1/
+           __init__.py
+           client_api_1.py
+           client_api_2.py
+        v2/
+           __init__.py
+           client_api_1.py
+           client_api_2.py
+
+The content each of __init__.py module under vN should be::
+
+   from client_api_1.py import API1Client
+   from client_api_2.py import API2Client
+
+   __all__ = ['API1Client', 'API2Client']
+
 Using Plugins
 =============
 
diff --git a/releasenotes/notes/clients_module-16f3025f515bf9ec.yaml b/releasenotes/notes/clients_module-16f3025f515bf9ec.yaml
new file mode 100644
index 0000000..53741da
--- /dev/null
+++ b/releasenotes/notes/clients_module-16f3025f515bf9ec.yaml
@@ -0,0 +1,18 @@
+---
+features:
+  - The Tempest plugin interface contains a new optional method, which allows
+    plugins to declare and automatically register any service client defined
+    in the plugin.
+  - tempest.lib exposes a new stable interface, the clients module and
+    ServiceClients class, which provides a convinient way for plugin tests to
+    access service clients defined in Tempest as well as service clients
+    defined in all loaded plugins.
+    The new ServiceClients class only exposes for now the service clients
+    which are in tempest.lib, i.e. compute, network and image. The remaing
+    service clients (identity, volume and object-storage) will be added in
+    future updates.
+deprecations:
+  - The new clients module provides a stable alternative to tempest classes
+    manager.Manager and clients.Manager. manager.Manager only exists now
+    to smoothen the transition of plugins to the new interface, but it will
+    be removed shortly without further notice.
diff --git a/releasenotes/notes/nova_cert_default-90eb7c1e3cde624a.yaml b/releasenotes/notes/nova_cert_default-90eb7c1e3cde624a.yaml
new file mode 100644
index 0000000..cfe97c5
--- /dev/null
+++ b/releasenotes/notes/nova_cert_default-90eb7c1e3cde624a.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+
+  - The ``nova_cert`` option default is changed to ``False``. The nova
+    certification management APIs were a hold over from ec2, and are
+    not used by any other parts of nova. They are deprecated for
+    removal in nova after the newton release. This makes false a more
+    sensible default going forward.
\ No newline at end of file
diff --git a/releasenotes/notes/volume-clients-as-library-9a3444dd63c134b3.yaml b/releasenotes/notes/volume-clients-as-library-9a3444dd63c134b3.yaml
new file mode 100644
index 0000000..6a4fc2b
--- /dev/null
+++ b/releasenotes/notes/volume-clients-as-library-9a3444dd63c134b3.yaml
@@ -0,0 +1,16 @@
+---
+features:
+  - |
+    Define volume service clients as libraries
+    The following volume service clients are defined as library interface,
+    so the other projects can use these modules as stable libraries
+    without any maintenance changes.
+
+      * availability_zone_client(v1)
+      * availability_zone_client(v2)
+      * extensions_client(v1)
+      * extensions_client(v2)
+      * hosts_client(v1)
+      * hosts_client(v2)
+      * services_client(v1)
+      * services_client(v2)
diff --git a/requirements.txt b/requirements.txt
index 0f8e94d..d698cda 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,7 +9,7 @@
 netaddr!=0.7.16,>=0.7.12 # BSD
 testrepository>=0.0.18 # Apache-2.0/BSD
 oslo.concurrency>=3.8.0 # Apache-2.0
-oslo.config>=3.12.0 # Apache-2.0
+oslo.config>=3.14.0 # Apache-2.0
 oslo.i18n>=2.1.0 # Apache-2.0
 oslo.log>=1.14.0 # Apache-2.0
 oslo.serialization>=1.10.0 # Apache-2.0
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 05c23ee..a5c303c 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -84,19 +84,21 @@
                                        self.volume['id'], 'available')
 
         if shelve_server:
-            # NOTE(andreaf) If we are going to shelve a server, we should
-            # check first whether the server is ssh-able. Otherwise we won't
-            # be able to distinguish failures introduced by shelve from
-            # pre-existing ones. Also it's good to wait for cloud-init to be
-            # done and sshd server to be running before shelving to avoid
-            # breaking the VM
-            linux_client = remote_client.RemoteClient(
-                self.get_server_ip(self.server),
-                self.image_ssh_user,
-                self.admin_pass,
-                self.validation_resources['keypair']['private_key'])
-            linux_client.validate_authentication()
-            # If validation went ok, shelve the server
+            if CONF.validation.run_validation:
+                # NOTE(andreaf) If we are going to shelve a server, we should
+                # check first whether the server is ssh-able. Otherwise we
+                # won't be able to distinguish failures introduced by shelve
+                # from pre-existing ones. Also it's good to wait for cloud-init
+                # to be done and sshd server to be running before shelving to
+                # avoid breaking the VM
+                linux_client = remote_client.RemoteClient(
+                    self.get_server_ip(self.server),
+                    self.image_ssh_user,
+                    self.admin_pass,
+                    self.validation_resources['keypair']['private_key'])
+                linux_client.validate_authentication()
+
+            # If validation went ok, or it was skipped, shelve the server
             compute.shelve_server(self.servers_client, self.server['id'])
 
         # Attach the volume to the server
@@ -110,8 +112,6 @@
         self.addCleanup(self._detach, self.server['id'], self.volume['id'])
 
     @test.idempotent_id('52e9045a-e90d-4c0d-9087-79d657faffff')
-    @testtools.skipUnless(CONF.validation.run_validation,
-                          'SSH required for this test')
     def test_attach_detach_volume(self):
         # Stop and Start a server with an attached volume, ensuring that
         # the volume remains attached.
@@ -125,16 +125,17 @@
         waiters.wait_for_server_status(self.servers_client, self.server['id'],
                                        'ACTIVE')
 
-        linux_client = remote_client.RemoteClient(
-            self.get_server_ip(self.server),
-            self.image_ssh_user,
-            self.admin_pass,
-            self.validation_resources['keypair']['private_key'],
-            server=self.server,
-            servers_client=self.servers_client)
+        if CONF.validation.run_validation:
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(self.server),
+                self.image_ssh_user,
+                self.admin_pass,
+                self.validation_resources['keypair']['private_key'],
+                server=self.server,
+                servers_client=self.servers_client)
 
-        partitions = linux_client.get_partitions()
-        self.assertIn(self.device, partitions)
+            partitions = linux_client.get_partitions()
+            self.assertIn(self.device, partitions)
 
         self._detach(self.server['id'], self.volume['id'])
         self.attachment = None
@@ -146,16 +147,17 @@
         waiters.wait_for_server_status(self.servers_client, self.server['id'],
                                        'ACTIVE')
 
-        linux_client = remote_client.RemoteClient(
-            self.get_server_ip(self.server),
-            self.image_ssh_user,
-            self.admin_pass,
-            self.validation_resources['keypair']['private_key'],
-            server=self.server,
-            servers_client=self.servers_client)
+        if CONF.validation.run_validation:
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(self.server),
+                self.image_ssh_user,
+                self.admin_pass,
+                self.validation_resources['keypair']['private_key'],
+                server=self.server,
+                servers_client=self.servers_client)
 
-        partitions = linux_client.get_partitions()
-        self.assertNotIn(self.device, partitions)
+            partitions = linux_client.get_partitions()
+            self.assertNotIn(self.device, partitions)
 
     @test.idempotent_id('7fa563fe-f0f7-43eb-9e22-a1ece036b513')
     def test_list_get_volume_attachments(self):
@@ -192,23 +194,22 @@
         waiters.wait_for_server_status(self.servers_client,
                                        self.server['id'],
                                        'ACTIVE')
-        linux_client = remote_client.RemoteClient(
-            self.get_server_ip(self.server['id']),
-            self.image_ssh_user,
-            self.admin_pass,
-            self.validation_resources['keypair']['private_key'],
-            server=self.server,
-            servers_client=self.servers_client)
+        if CONF.validation.run_validation:
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(self.server['id']),
+                self.image_ssh_user,
+                self.admin_pass,
+                self.validation_resources['keypair']['private_key'],
+                server=self.server,
+                servers_client=self.servers_client)
 
-        command = 'grep [vs]d /proc/partitions | wc -l'
-        nb_partitions = linux_client.exec_command(command).strip()
-        self.assertEqual(number_of_partition, nb_partitions)
+            command = 'grep [vs]d /proc/partitions | wc -l'
+            nb_partitions = linux_client.exec_command(command).strip()
+            self.assertEqual(number_of_partition, nb_partitions)
 
     @test.idempotent_id('13a940b6-3474-4c3c-b03f-29b89112bfee')
     @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
                           'Shelve is not available.')
-    @testtools.skipUnless(CONF.validation.run_validation,
-                          'SSH required for this test')
     def test_attach_volume_shelved_or_offload_server(self):
         self._create_and_attach(shelve_server=True)
 
@@ -228,8 +229,6 @@
     @test.idempotent_id('b54e86dd-a070-49c4-9c07-59ae6dae15aa')
     @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
                           'Shelve is not available.')
-    @testtools.skipUnless(CONF.validation.run_validation,
-                          'SSH required for this test')
     def test_detach_volume_shelved_or_offload_server(self):
         self._create_and_attach(shelve_server=True)
 
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 97d9eed..85026af 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -15,6 +15,7 @@
 
 from tempest.common import custom_matchers
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
 import tempest.test
@@ -57,16 +58,41 @@
         cls.container_client.auth_provider.clear_auth()
         cls.account_client.auth_provider.clear_auth()
 
+        cls.containers = []
+
     @classmethod
-    def delete_containers(cls, containers, container_client=None,
+    def create_container(cls):
+        # wrapper that returns a test container
+        container_name = data_utils.rand_name(name='TestContainer')
+        cls.container_client.create_container(container_name)
+        cls.containers.append(container_name)
+
+        return container_name
+
+    @classmethod
+    def create_object(cls, container_name, object_name=None,
+                      data=None, metadata=None):
+        # wrapper that returns a test object
+        if object_name is None:
+            object_name = data_utils.rand_name(name='TestObject')
+        if data is None:
+            data = data_utils.arbitrary_string()
+        cls.object_client.create_object(container_name,
+                                        object_name,
+                                        data,
+                                        metadata=metadata)
+
+        return object_name, data
+
+    @classmethod
+    def delete_containers(cls, container_client=None,
                           object_client=None):
-        """Remove given containers and all objects in them.
+        """Remove containers and all objects in them.
 
         The containers should be visible from the container_client given.
         Will not throw any error if the containers don't exist.
         Will not check that object and container deletions succeed.
 
-        :param containers: list of container names to remove
         :param container_client: if None, use cls.container_client, this means
             that the default testing user will be used (see 'username' in
             'etc/tempest.conf')
@@ -76,7 +102,7 @@
             container_client = cls.container_client
         if object_client is None:
             object_client = cls.object_client
-        for cont in containers:
+        for cont in cls.containers:
             try:
                 objlist = container_client.list_all_container_objects(cont)
                 # delete every object in the container
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index da4c80c..7292ee9 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -27,7 +27,7 @@
         self.containers = []
 
     def tearDown(self):
-        self.delete_containers(self.containers)
+        self.delete_containers()
         super(BulkTest, self).tearDown()
 
     def _create_archive(self):
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 0f6a330..fcbd6eb 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -34,8 +34,7 @@
     @classmethod
     def resource_setup(cls):
         super(AccountQuotasTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name="TestContainer")
-        cls.container_client.create_container(cls.container_name)
+        cls.container_name = cls.create_container()
 
         # Retrieve a ResellerAdmin auth data and use it to set a quota
         # on the client's account
@@ -73,8 +72,7 @@
 
     @classmethod
     def resource_cleanup(cls):
-        if hasattr(cls, "container_name"):
-            cls.delete_containers([cls.container_name])
+        cls.delete_containers()
         super(AccountQuotasTest, cls).resource_cleanup()
 
     @test.attr(type="smoke")
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index 546bb06..0eec387 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -36,8 +36,7 @@
     @classmethod
     def resource_setup(cls):
         super(AccountQuotasNegativeTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name="TestContainer")
-        cls.container_client.create_container(cls.container_name)
+        cls.container_name = cls.create_container()
 
         # Retrieve a ResellerAdmin auth data and use it to set a quota
         # on the client's account
@@ -74,8 +73,7 @@
 
     @classmethod
     def resource_cleanup(cls):
-        if hasattr(cls, "container_name"):
-            cls.delete_containers([cls.container_name])
+        cls.delete_containers()
         super(AccountQuotasNegativeTest, cls).resource_cleanup()
 
     @test.attr(type=["negative"])
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 5983c1f..723b870 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -50,7 +50,7 @@
 
     @classmethod
     def resource_cleanup(cls):
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
         super(AccountTest, cls).resource_cleanup()
 
     @test.attr(type='smoke')
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index c1b6711..ffdd1de 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -39,11 +39,10 @@
 
     def setUp(self):
         super(ObjectTestACLs, self).setUp()
-        self.container_name = data_utils.rand_name(name='TestContainer')
-        self.container_client.create_container(self.container_name)
+        self.container_name = self.create_container()
 
     def tearDown(self):
-        self.delete_containers([self.container_name])
+        self.delete_containers()
         super(ObjectTestACLs, self).tearDown()
 
     @test.idempotent_id('a3270f3f-7640-4944-8448-c7ea783ea5b6')
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index 01e5389..c26e49b 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -38,8 +38,7 @@
                      Maximum object count of the container.
         """
         super(ContainerQuotasTest, self).setUp()
-        self.container_name = data_utils.rand_name(name="TestContainer")
-        self.container_client.create_container(self.container_name)
+        self.container_name = self.create_container()
         metadata = {"quota-bytes": str(QUOTA_BYTES),
                     "quota-count": str(QUOTA_COUNT), }
         self.container_client.update_container_metadata(
@@ -47,7 +46,7 @@
 
     def tearDown(self):
         """Cleans the container of any object after each test."""
-        self.delete_containers([self.container_name])
+        self.delete_containers()
         super(ContainerQuotasTest, self).tearDown()
 
     @test.idempotent_id('9a0fb034-86af-4df0-86fa-f8bd7db21ae0')
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index 9d043e5..296d8ee 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -21,31 +21,11 @@
 class ContainerTest(base.BaseObjectTest):
     def setUp(self):
         super(ContainerTest, self).setUp()
-        self.containers = []
 
     def tearDown(self):
-        self.delete_containers(self.containers)
+        self.delete_containers()
         super(ContainerTest, self).tearDown()
 
-    def _create_container(self):
-        # setup container
-        container_name = data_utils.rand_name(name='TestContainer')
-        self.container_client.create_container(container_name)
-        self.containers.append(container_name)
-
-        return container_name
-
-    def _create_object(self, container_name, object_name=None):
-        # setup object
-        if object_name is None:
-            object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
-        self.object_client.create_object(container_name,
-                                         object_name,
-                                         data)
-
-        return object_name
-
     @test.attr(type='smoke')
     @test.idempotent_id('92139d73-7819-4db1-85f8-3f2f22a8d91f')
     def test_create_container(self):
@@ -140,7 +120,7 @@
     @test.idempotent_id('95d3a249-b702-4082-a2c4-14bb860cf06a')
     def test_delete_container(self):
         # create a container
-        container_name = self._create_container()
+        container_name = self.create_container()
         # delete container, success asserted within
         resp, _ = self.container_client.delete_container(container_name)
         self.assertHeaders(resp, 'Container', 'DELETE')
@@ -150,8 +130,8 @@
     @test.idempotent_id('312ff6bd-5290-497f-bda1-7c5fec6697ab')
     def test_list_container_contents(self):
         # get container contents list
-        container_name = self._create_container()
-        object_name = self._create_object(container_name)
+        container_name = self.create_container()
+        object_name, _ = self.create_object(container_name)
 
         resp, object_list = self.container_client.list_container_contents(
             container_name)
@@ -161,7 +141,7 @@
     @test.idempotent_id('4646ac2d-9bfb-4c7d-a3c5-0f527402b3df')
     def test_list_container_contents_with_no_object(self):
         # get empty container contents list
-        container_name = self._create_container()
+        container_name = self.create_container()
 
         resp, object_list = self.container_client.list_container_contents(
             container_name)
@@ -171,9 +151,9 @@
     @test.idempotent_id('fe323a32-57b9-4704-a996-2e68f83b09bc')
     def test_list_container_contents_with_delimiter(self):
         # get container contents list using delimiter param
-        container_name = self._create_container()
+        container_name = self.create_container()
         object_name = data_utils.rand_name(name='TestObject/')
-        self._create_object(container_name, object_name)
+        self.create_object(container_name, object_name)
 
         params = {'delimiter': '/'}
         resp, object_list = self.container_client.list_container_contents(
@@ -185,8 +165,8 @@
     @test.idempotent_id('55b4fa5c-e12e-4ca9-8fcf-a79afe118522')
     def test_list_container_contents_with_end_marker(self):
         # get container contents list using end_marker param
-        container_name = self._create_container()
-        object_name = self._create_object(container_name)
+        container_name = self.create_container()
+        object_name, _ = self.create_object(container_name)
 
         params = {'end_marker': 'ZzzzObject1234567890'}
         resp, object_list = self.container_client.list_container_contents(
@@ -198,8 +178,8 @@
     @test.idempotent_id('196f5034-6ab0-4032-9da9-a937bbb9fba9')
     def test_list_container_contents_with_format_json(self):
         # get container contents list using format_json param
-        container_name = self._create_container()
-        self._create_object(container_name)
+        container_name = self.create_container()
+        self.create_object(container_name)
 
         params = {'format': 'json'}
         resp, object_list = self.container_client.list_container_contents(
@@ -217,8 +197,8 @@
     @test.idempotent_id('655a53ca-4d15-408c-a377-f4c6dbd0a1fa')
     def test_list_container_contents_with_format_xml(self):
         # get container contents list using format_xml param
-        container_name = self._create_container()
-        self._create_object(container_name)
+        container_name = self.create_container()
+        self.create_object(container_name)
 
         params = {'format': 'xml'}
         resp, object_list = self.container_client.list_container_contents(
@@ -241,8 +221,8 @@
     @test.idempotent_id('297ec38b-2b61-4ff4-bcd1-7fa055e97b61')
     def test_list_container_contents_with_limit(self):
         # get container contents list using limit param
-        container_name = self._create_container()
-        object_name = self._create_object(container_name)
+        container_name = self.create_container()
+        object_name, _ = self.create_object(container_name)
 
         params = {'limit': data_utils.rand_int_id(1, 10000)}
         resp, object_list = self.container_client.list_container_contents(
@@ -254,8 +234,8 @@
     @test.idempotent_id('c31ddc63-2a58-4f6b-b25c-94d2937e6867')
     def test_list_container_contents_with_marker(self):
         # get container contents list using marker param
-        container_name = self._create_container()
-        object_name = self._create_object(container_name)
+        container_name = self.create_container()
+        object_name, _ = self.create_object(container_name)
 
         params = {'marker': 'AaaaObject1234567890'}
         resp, object_list = self.container_client.list_container_contents(
@@ -267,9 +247,9 @@
     @test.idempotent_id('58ca6cc9-6af0-408d-aaec-2a6a7b2f0df9')
     def test_list_container_contents_with_path(self):
         # get container contents list using path param
-        container_name = self._create_container()
+        container_name = self.create_container()
         object_name = data_utils.rand_name(name='Swift/TestObject')
-        self._create_object(container_name, object_name)
+        self.create_object(container_name, object_name)
 
         params = {'path': 'Swift'}
         resp, object_list = self.container_client.list_container_contents(
@@ -281,8 +261,8 @@
     @test.idempotent_id('77e742c7-caf2-4ec9-8aa4-f7d509a3344c')
     def test_list_container_contents_with_prefix(self):
         # get container contents list using prefix param
-        container_name = self._create_container()
-        object_name = self._create_object(container_name)
+        container_name = self.create_container()
+        object_name, _ = self.create_object(container_name)
 
         prefix_key = object_name[0:8]
         params = {'prefix': prefix_key}
@@ -296,7 +276,7 @@
     @test.idempotent_id('96e68f0e-19ec-4aa2-86f3-adc6a45e14dd')
     def test_list_container_metadata(self):
         # List container metadata
-        container_name = self._create_container()
+        container_name = self.create_container()
 
         metadata = {'name': 'Pictures'}
         self.container_client.update_container_metadata(
@@ -312,7 +292,7 @@
     @test.idempotent_id('a2faf936-6b13-4f8d-92a2-c2278355821e')
     def test_list_no_container_metadata(self):
         # HEAD container without metadata
-        container_name = self._create_container()
+        container_name = self.create_container()
 
         resp, _ = self.container_client.list_container_metadata(
             container_name)
@@ -345,7 +325,7 @@
     @test.idempotent_id('2ae5f295-4bf1-4e04-bfad-21e54b62cec5')
     def test_update_container_metadata_with_create_metadata(self):
         # update container metadata using add metadata
-        container_name = self._create_container()
+        container_name = self.create_container()
 
         metadata = {'test-container-meta1': 'Meta1'}
         resp, _ = self.container_client.update_container_metadata(
@@ -380,7 +360,7 @@
     @test.idempotent_id('31f40a5f-6a52-4314-8794-cd89baed3040')
     def test_update_container_metadata_with_create_metadata_key(self):
         # update container metadata with a blank value of metadata
-        container_name = self._create_container()
+        container_name = self.create_container()
 
         metadata = {'test-container-meta1': ''}
         resp, _ = self.container_client.update_container_metadata(
diff --git a/tempest/api/object_storage/test_container_staticweb.py b/tempest/api/object_storage/test_container_staticweb.py
index 5b3ce79..47ef0d3 100644
--- a/tempest/api/object_storage/test_container_staticweb.py
+++ b/tempest/api/object_storage/test_container_staticweb.py
@@ -24,18 +24,14 @@
     @classmethod
     def resource_setup(cls):
         super(StaticWebTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name="TestContainer")
 
         # This header should be posted on the container before every test
         cls.headers_public_read_acl = {'Read': '.r:*,.rlistings'}
 
         # Create test container and create one object in it
-        cls.container_client.create_container(cls.container_name)
-        cls.object_name = data_utils.rand_name(name="TestObject")
-        cls.object_data = data_utils.arbitrary_string()
-        cls.object_client.create_object(cls.container_name,
-                                        cls.object_name,
-                                        cls.object_data)
+        cls.container_name = cls.create_container()
+        cls.object_name, cls.object_data = cls.create_object(
+            cls.container_name)
 
         cls.container_client.update_container_metadata(
             cls.container_name,
@@ -44,8 +40,7 @@
 
     @classmethod
     def resource_cleanup(cls):
-        if hasattr(cls, "container_name"):
-            cls.delete_containers([cls.container_name])
+        cls.delete_containers()
         super(StaticWebTest, cls).resource_cleanup()
 
     @test.idempotent_id('c1f055ab-621d-4a6a-831f-846fcb578b8b')
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 2a5cec6..e10b900 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -80,7 +80,7 @@
     @classmethod
     def resource_cleanup(cls):
         for client in cls.clients.values():
-            cls.delete_containers(cls.containers, client[0], client[1])
+            cls.delete_containers(client[0], client[1])
         super(ContainerSyncTest, cls).resource_cleanup()
 
     def _test_container_synchronization(self, make_headers):
diff --git a/tempest/api/object_storage/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index 9db8bde..11acb31 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -16,7 +16,6 @@
 import time
 
 from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
 from tempest.lib import exceptions as lib_exc
 from tempest import test
 
@@ -25,19 +24,17 @@
     @classmethod
     def resource_setup(cls):
         super(ObjectExpiryTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name='TestContainer')
-        cls.container_client.create_container(cls.container_name)
+        cls.container_name = cls.create_container()
 
     def setUp(self):
         super(ObjectExpiryTest, self).setUp()
         # create object
-        self.object_name = data_utils.rand_name(name='TestObject')
-        resp, _ = self.object_client.create_object(self.container_name,
-                                                   self.object_name, '')
+        self.object_name, _ = self.create_object(
+            self.container_name)
 
     @classmethod
     def resource_cleanup(cls):
-        cls.delete_containers([cls.container_name])
+        cls.delete_containers()
         super(ObjectExpiryTest, cls).resource_cleanup()
 
     def _test_object_expiry(self, metadata):
diff --git a/tempest/api/object_storage/test_object_formpost.py b/tempest/api/object_storage/test_object_formpost.py
index 356f560..102ec2f 100644
--- a/tempest/api/object_storage/test_object_formpost.py
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -31,12 +31,9 @@
     @classmethod
     def resource_setup(cls):
         super(ObjectFormPostTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name='TestContainer')
+        cls.container_name = cls.create_container()
         cls.object_name = data_utils.rand_name(name='ObjectTemp')
 
-        cls.container_client.create_container(cls.container_name)
-        cls.containers = [cls.container_name]
-
         cls.key = 'Meta'
         cls.metadata = {'Temp-URL-Key': cls.key}
         cls.account_client.create_account_metadata(metadata=cls.metadata)
@@ -56,7 +53,7 @@
     @classmethod
     def resource_cleanup(cls):
         cls.account_client.delete_account_metadata(metadata=cls.metadata)
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
         super(ObjectFormPostTest, cls).resource_cleanup()
 
     def get_multipart_form(self, expires=600):
diff --git a/tempest/api/object_storage/test_object_formpost_negative.py b/tempest/api/object_storage/test_object_formpost_negative.py
index cb13271..8ff5d82 100644
--- a/tempest/api/object_storage/test_object_formpost_negative.py
+++ b/tempest/api/object_storage/test_object_formpost_negative.py
@@ -32,12 +32,9 @@
     @classmethod
     def resource_setup(cls):
         super(ObjectFormPostNegativeTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name='TestContainer')
+        cls.container_name = cls.create_container()
         cls.object_name = data_utils.rand_name(name='ObjectTemp')
 
-        cls.container_client.create_container(cls.container_name)
-        cls.containers = [cls.container_name]
-
         cls.key = 'Meta'
         cls.metadata = {'Temp-URL-Key': cls.key}
         cls.account_client.create_account_metadata(metadata=cls.metadata)
@@ -57,7 +54,7 @@
     @classmethod
     def resource_cleanup(cls):
         cls.account_client.delete_account_metadata(metadata=cls.metadata)
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
         super(ObjectFormPostNegativeTest, cls).resource_cleanup()
 
     def get_multipart_form(self, expires=600):
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index a88e4f4..a707ebb 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -35,24 +35,13 @@
     @classmethod
     def resource_setup(cls):
         super(ObjectTest, cls).resource_setup()
-        cls.container_name = data_utils.rand_name(name='TestContainer')
-        cls.container_client.create_container(cls.container_name)
-        cls.containers = [cls.container_name]
+        cls.container_name = cls.create_container()
 
     @classmethod
     def resource_cleanup(cls):
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
         super(ObjectTest, cls).resource_cleanup()
 
-    def _create_object(self, metadata=None):
-        # setup object
-        object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
-        self.object_client.create_object(self.container_name,
-                                         object_name, data, metadata=metadata)
-
-        return object_name, data
-
     def _upload_segments(self):
         # create object
         object_name = data_utils.rand_name(name='LObject')
@@ -335,7 +324,7 @@
     @test.idempotent_id('7a94c25d-66e6-434c-9c38-97d4e2c29945')
     def test_update_object_metadata(self):
         # update object metadata
-        object_name, data = self._create_object()
+        object_name, _ = self.create_object(self.container_name)
 
         metadata = {'X-Object-Meta-test-meta': 'Meta'}
         resp, _ = self.object_client.update_object_metadata(
@@ -431,8 +420,8 @@
 
     @test.idempotent_id('0dbbe89c-6811-4d84-a2df-eca2bdd40c0e')
     def test_update_object_metadata_with_x_object_metakey(self):
-        # update object metadata with a blenk value of metadata
-        object_name, data = self._create_object()
+        # update object metadata with a blank value of metadata
+        object_name, _ = self.create_object(self.container_name)
 
         update_metadata = {'X-Object-Meta-test-meta': ''}
         resp, _ = self.object_client.update_object_metadata(
@@ -494,7 +483,7 @@
     @test.idempotent_id('170fb90e-f5c3-4b1f-ae1b-a18810821172')
     def test_list_no_object_metadata(self):
         # get empty list of object metadata
-        object_name, data = self._create_object()
+        object_name, _ = self.create_object(self.container_name)
 
         resp, _ = self.object_client.list_object_metadata(
             self.container_name,
@@ -548,7 +537,7 @@
         # retrieve object's data (in response body)
 
         # create object
-        object_name, data = self._create_object()
+        object_name, data = self.create_object(self.container_name)
         # get object
         resp, body = self.object_client.get_object(self.container_name,
                                                    object_name)
@@ -701,7 +690,7 @@
     @test.idempotent_id('0aa1201c-10aa-467a-bee7-63cbdd463152')
     def test_get_object_with_if_unmodified_since(self):
         # get object with if_unmodified_since
-        object_name, data = self._create_object()
+        object_name, data = self.create_object(self.container_name)
 
         time_now = time.time()
         http_date = time.ctime(time_now + 86400)
@@ -716,7 +705,7 @@
     @test.idempotent_id('94587078-475f-48f9-a40f-389c246e31cd')
     def test_get_object_with_x_newest(self):
         # get object with x_newest
-        object_name, data = self._create_object()
+        object_name, data = self.create_object(self.container_name)
 
         list_metadata = {'X-Newest': 'true'}
         resp, body = self.object_client.get_object(
@@ -757,7 +746,7 @@
         # change the content type of an existing object
 
         # create object
-        object_name, data = self._create_object()
+        object_name, _ = self.create_object(self.container_name)
         # get the old content type
         resp_tmp, _ = self.object_client.list_object_metadata(
             self.container_name, object_name)
@@ -843,7 +832,8 @@
     def test_copy_object_with_x_fresh_metadata(self):
         # create source object
         metadata = {'x-object-meta-src': 'src_value'}
-        src_object_name, data = self._create_object(metadata)
+        src_object_name, data = self.create_object(self.container_name,
+                                                   metadata=metadata)
 
         # copy source object with x_fresh_metadata header
         metadata = {'X-Fresh-Metadata': 'true'}
@@ -863,7 +853,8 @@
     def test_copy_object_with_x_object_metakey(self):
         # create source object
         metadata = {'x-object-meta-src': 'src_value'}
-        src_obj_name, data = self._create_object(metadata)
+        src_obj_name, data = self.create_object(self.container_name,
+                                                metadata=metadata)
 
         # copy source object to destination with x-object-meta-key
         metadata = {'x-object-meta-test': ''}
@@ -885,7 +876,8 @@
     def test_copy_object_with_x_object_meta(self):
         # create source object
         metadata = {'x-object-meta-src': 'src_value'}
-        src_obj_name, data = self._create_object(metadata)
+        src_obj_name, data = self.create_object(self.container_name,
+                                                metadata=metadata)
 
         # copy source object to destination with object metadata
         metadata = {'x-object-meta-test': 'value'}
@@ -951,7 +943,7 @@
         # Make a conditional request for an object using the If-None-Match
         # header, it should get downloaded only if the local file is different,
         # otherwise the response code should be 304 Not Modified
-        object_name, data = self._create_object()
+        object_name, data = self.create_object(self.container_name)
         # local copy is identical, no download
         md5 = hashlib.md5(data).hexdigest()
         headers = {'If-None-Match': md5}
diff --git a/tempest/api/object_storage/test_object_slo.py b/tempest/api/object_storage/test_object_slo.py
index 867159b..e00bbab 100644
--- a/tempest/api/object_storage/test_object_slo.py
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -30,8 +30,7 @@
 
     def setUp(self):
         super(ObjectSloTest, self).setUp()
-        self.container_name = data_utils.rand_name(name='TestContainer')
-        self.container_client.create_container(self.container_name)
+        self.container_name = self.create_container()
         self.objects = []
 
     def tearDown(self):
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 3d28f6e..c2d3b69 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -32,9 +32,7 @@
     def resource_setup(cls):
         super(ObjectTempUrlTest, cls).resource_setup()
         # create a container
-        cls.container_name = data_utils.rand_name(name='TestContainer')
-        cls.container_client.create_container(cls.container_name)
-        cls.containers = [cls.container_name]
+        cls.container_name = cls.create_container()
 
         # update account metadata
         cls.key = 'Meta'
@@ -44,11 +42,7 @@
         cls.account_client.create_account_metadata(metadata=metadata)
 
         # create an object
-        cls.object_name = data_utils.rand_name(name='ObjectTemp')
-        cls.content = data_utils.arbitrary_string(size=len(cls.object_name),
-                                                  base_text=cls.object_name)
-        cls.object_client.create_object(cls.container_name,
-                                        cls.object_name, cls.content)
+        cls.object_name, cls.content = cls.create_object(cls.container_name)
 
     @classmethod
     def resource_cleanup(cls):
@@ -56,7 +50,7 @@
             cls.account_client.delete_account_metadata(
                 metadata=metadata)
 
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
 
         super(ObjectTempUrlTest, cls).resource_cleanup()
 
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 38fe697..577f3bd 100644
--- a/tempest/api/object_storage/test_object_temp_url_negative.py
+++ b/tempest/api/object_storage/test_object_temp_url_negative.py
@@ -33,9 +33,7 @@
     def resource_setup(cls):
         super(ObjectTempUrlNegativeTest, cls).resource_setup()
 
-        cls.container_name = data_utils.rand_name(name='TestContainer')
-        cls.container_client.create_container(cls.container_name)
-        cls.containers = [cls.container_name]
+        cls.container_name = cls.create_container()
 
         # update account metadata
         cls.key = 'Meta'
@@ -49,7 +47,7 @@
         resp, _ = cls.account_client.delete_account_metadata(
             metadata=cls.metadata)
 
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
 
         super(ObjectTempUrlNegativeTest, cls).resource_cleanup()
 
diff --git a/tempest/api/object_storage/test_object_version.py b/tempest/api/object_storage/test_object_version.py
index 24ec3f5..3f6623b 100644
--- a/tempest/api/object_storage/test_object_version.py
+++ b/tempest/api/object_storage/test_object_version.py
@@ -31,7 +31,7 @@
 
     @classmethod
     def resource_cleanup(cls):
-        cls.delete_containers(cls.containers)
+        cls.delete_containers()
         super(ContainerTest, cls).resource_cleanup()
 
     def assertContainer(self, container, count, byte, versioned):
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index b144c7c..ff28b50 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -43,7 +43,7 @@
 
     def _delete_backup(self, backup_id):
         self.admin_backups_client.delete_backup(backup_id)
-        self.admin_backups_client.wait_for_backup_deletion(backup_id)
+        self.admin_backups_client.wait_for_resource_deletion(backup_id)
 
     def _decode_url(self, backup_url):
         return json.loads(base64.decodestring(backup_url))
@@ -59,44 +59,6 @@
         backup.update(changes)
         return self._encode_backup(backup)
 
-    @test.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
-    def test_volume_backup_create_get_detailed_list_restore_delete(self):
-        # Create backup
-        backup_name = data_utils.rand_name('Backup')
-        create_backup = self.admin_backups_client.create_backup
-        backup = create_backup(volume_id=self.volume['id'],
-                               name=backup_name)['backup']
-        self.addCleanup(self.admin_backups_client.delete_backup,
-                        backup['id'])
-        self.assertEqual(backup_name, backup['name'])
-        waiters.wait_for_volume_status(self.admin_volume_client,
-                                       self.volume['id'], 'available')
-        self.admin_backups_client.wait_for_backup_status(backup['id'],
-                                                         'available')
-
-        # Get a given backup
-        backup = self.admin_backups_client.show_backup(backup['id'])['backup']
-        self.assertEqual(backup_name, backup['name'])
-
-        # Get all backups with detail
-        backups = self.admin_backups_client.list_backups(
-            detail=True)['backups']
-        self.assertIn((backup['name'], backup['id']),
-                      [(m['name'], m['id']) for m in backups])
-
-        # Restore backup
-        restore = self.admin_backups_client.restore_backup(
-            backup['id'])['restore']
-
-        # Delete backup
-        self.addCleanup(self.admin_volume_client.delete_volume,
-                        restore['volume_id'])
-        self.assertEqual(backup['id'], restore['backup_id'])
-        self.admin_backups_client.wait_for_backup_status(backup['id'],
-                                                         'available')
-        waiters.wait_for_volume_status(self.admin_volume_client,
-                                       restore['volume_id'], 'available')
-
     @test.idempotent_id('a99c54a1-dd80-4724-8a13-13bf58d4068d')
     def test_volume_backup_export_import(self):
         """Test backup export import functionality.
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index 87146db..d83c308 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -36,6 +36,44 @@
 
         cls.volume = cls.create_volume()
 
+    @test.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
+    def test_volume_backup_create_get_detailed_list_restore_delete(self):
+        # Create backup
+        backup_name = data_utils.rand_name('Backup')
+        create_backup = self.backups_client.create_backup
+        backup = create_backup(volume_id=self.volume['id'],
+                               name=backup_name)['backup']
+        self.addCleanup(self.backups_client.delete_backup,
+                        backup['id'])
+        self.assertEqual(backup_name, backup['name'])
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       self.volume['id'], 'available')
+        self.backups_client.wait_for_backup_status(backup['id'],
+                                                   'available')
+
+        # Get a given backup
+        backup = self.backups_client.show_backup(backup['id'])['backup']
+        self.assertEqual(backup_name, backup['name'])
+
+        # Get all backups with detail
+        backups = self.backups_client.list_backups(
+            detail=True)['backups']
+        self.assertIn((backup['name'], backup['id']),
+                      [(m['name'], m['id']) for m in backups])
+
+        # Restore backup
+        restore = self.backups_client.restore_backup(
+            backup['id'])['restore']
+
+        # Delete backup
+        self.addCleanup(self.volumes_client.delete_volume,
+                        restore['volume_id'])
+        self.assertEqual(backup['id'], restore['backup_id'])
+        self.backups_client.wait_for_backup_status(backup['id'],
+                                                   'available')
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       restore['volume_id'], 'available')
+
     @test.idempotent_id('07af8f6d-80af-44c9-a5dc-c8427b1b62e6')
     @test.services('compute')
     def test_backup_create_attached_volume(self):
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 1fdcb49..c501ffc 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -42,22 +42,13 @@
         super(VolumesV2ListTestJSON, cls).resource_setup()
 
         # Create 3 test volumes
-        cls.volume_list = []
         cls.volume_id_list = []
         cls.metadata = {'Type': 'work'}
         for i in range(3):
             volume = cls.create_volume(metadata=cls.metadata)
             volume = cls.client.show_volume(volume['id'])['volume']
-            cls.volume_list.append(volume)
             cls.volume_id_list.append(volume['id'])
 
-    @classmethod
-    def resource_cleanup(cls):
-        # Delete the created volumes
-        for volid in cls.volume_id_list:
-            cls.delete_volume(cls.client, volid)
-        super(VolumesV2ListTestJSON, cls).resource_cleanup()
-
     @test.idempotent_id('2a7064eb-b9c3-429b-b888-33928fc5edd3')
     def test_volume_list_details_with_multiple_params(self):
         # List volumes detail using combined condition
diff --git a/tempest/clients.py b/tempest/clients.py
index e070637..e14f6f8 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -14,15 +14,13 @@
 #    under the License.
 
 import copy
-
 from oslo_log import log as logging
-
 from tempest.common import negative_rest_client
 from tempest import config
 from tempest import exceptions
 from tempest.lib import auth
 from tempest.lib import exceptions as lib_exc
-from tempest import service_clients
+from tempest.lib.services import clients
 from tempest.services import baremetal
 from tempest.services import data_processing
 from tempest.services import identity
@@ -34,7 +32,7 @@
 LOG = logging.getLogger(__name__)
 
 
-class Manager(service_clients.ServiceClients):
+class Manager(clients.ServiceClients):
     """Top level manager for OpenStack tempest clients"""
 
     default_params = config.service_client_config()
@@ -99,17 +97,16 @@
         This uses `config.service_client_config` for all services to collect
         most configuration items needed to init the clients.
         """
-        # NOTE(andreaf) Configuration items will be passed in future patches
-        # into ClientFactory objects, but for now we update all the
-        # _set_*_client methods to consume them so we can verify that the
-        # configuration collected is correct
+        # NOTE(andreaf) Once all service clients in Tempest are migrated
+        # to tempest.lib, their configuration will be picked up from the
+        # registry, and this method will become redundant.
 
         configuration = {}
 
-        # Setup the parameters for all Tempest services.
+        # Setup the parameters for all Tempest services which are not in lib.
         # NOTE(andreaf) Since client.py is an internal module of Tempest,
         # it doesn't have to consider plugin configuration.
-        for service in service_clients.tempest_modules():
+        for service in clients._tempest_internal_modules():
             try:
                 # NOTE(andreaf) Use the unversioned service name to fetch
                 # the configuration since configuration is not versioned.
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 073481c..9d307ee 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -53,9 +53,7 @@
                     return
                 # NOTE(afazekas): The instance is in "ready for action state"
                 # when no task in progress
-                # NOTE(afazekas): Converted to string because of the XML
-                # responses
-                if str(task_state) == "None":
+                if task_state is None:
                     # without state api extension 3 sec usually enough
                     time.sleep(CONF.compute.ready_wait)
                     return
diff --git a/tempest/config.py b/tempest/config.py
index 6bae021..3fd20ab 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -26,6 +26,7 @@
 import testtools
 
 from tempest.lib import exceptions
+from tempest.lib.services import clients
 from tempest.test_discover import plugins
 
 
@@ -379,8 +380,9 @@
                 help='Does the test environment support creating snapshot '
                      'images of running instances?'),
     cfg.BoolOpt('nova_cert',
-                default=True,
-                help='Does the test environment have the nova cert running?'),
+                default=False,
+                help='Does the test environment have the nova cert running?',
+                deprecated_for_removal=True),
     cfg.BoolOpt('personality',
                 default=False,
                 help='Does the test environment support server personality'),
@@ -501,7 +503,7 @@
                 default=False,
                 help="Whether project networks can be reached directly from "
                      "the test client. This must be set to True when the "
-                     "'fixed' ssh_connect_method is selected."),
+                     "'fixed' connect_method is selected."),
     cfg.StrOpt('public_network_id',
                default="",
                help="Id of the public network that provides external "
@@ -652,7 +654,7 @@
     cfg.StrOpt('network_for_ssh',
                default='public',
                help="Network used for SSH connections. Ignored if "
-                    "connect_method=floating or run_validation=false.",
+                    "connect_method=floating.",
                deprecated_opts=[cfg.DeprecatedOpt('network_for_ssh',
                                                   group='compute')]),
 ]
@@ -1287,6 +1289,15 @@
             lockutils.set_defaults(lock_dir)
             self._config = TempestConfigPrivate(config_path=self._path)
 
+            # Pushing tempest internal service client configuration to the
+            # service clients register. Doing this in the config module ensures
+            # that the configuration is available by the time we register the
+            # service clients.
+            # NOTE(andreaf) This has to be done at the time the first
+            # attribute is accessed, to ensure all plugins have been already
+            # loaded, options registered, and _config is set.
+            _register_tempest_service_clients()
+
         return getattr(self._config, attr)
 
     def set_config_path(self, path):
@@ -1446,3 +1457,29 @@
     # Set service
     _parameters['service'] = getattr(options, 'catalog_type')
     return _parameters
+
+
+def _register_tempest_service_clients():
+    # Register tempest own service clients using the same mechanism used
+    # for external plugins.
+    # The configuration data is pushed to the registry so that automatic
+    # configuration of tempest own service clients is possible both for
+    # tempest as well as for the plugins.
+    service_clients = clients.tempest_modules()
+    registry = clients.ClientsRegistry()
+    all_clients = []
+    for service_client in service_clients:
+        module = service_clients[service_client]
+        configs = service_client.split('.')[0]
+        service_client_data = dict(
+            name=service_client.replace('.', '_'),
+            service_version=service_client,
+            module_path=module.__name__,
+            client_names=module.__all__,
+            **service_client_config(configs)
+        )
+        all_clients.append(service_client_data)
+    # NOTE(andreaf) Internal service clients do not actually belong
+    # to a plugin, so using '__tempest__' to indicate a virtual plugin
+    # which holds internal service clients.
+    registry.register_service_client('__tempest__', all_clients)
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 7e1a442..4e851b0 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -64,7 +64,7 @@
                                                     certificate validation
     :param str ca_certs: File containing the CA Bundle to use in verifying a
                          TLS server cert
-    :param str trace_request: Regex to use for specifying logging the entirety
+    :param str trace_requests: Regex to use for specifying logging the entirety
                               of the request and response payload
     :param str http_timeout: Timeout in seconds to wait for the http request to
                              return
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index 8054e62..adf666b 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -1,3 +1,4 @@
+# Copyright 2012 OpenStack Foundation
 # Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
 # All Rights Reserved.
 #
@@ -13,8 +14,94 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import copy
+import importlib
+import inspect
+import logging
+
+from tempest.lib import auth
 from tempest.lib.common.utils import misc
 from tempest.lib import exceptions
+from tempest.lib.services import compute
+from tempest.lib.services import image
+from tempest.lib.services import network
+
+
+LOG = logging.getLogger(__name__)
+
+
+def tempest_modules():
+    """Dict of service client modules available in Tempest.
+
+    Provides a dict of stable service modules available in Tempest, with
+    ``service_version`` as key, and the module object as value.
+    """
+    return {
+        'compute': compute,
+        'image.v1': image.v1,
+        'image.v2': image.v2,
+        'network': network
+    }
+
+
+def _tempest_internal_modules():
+    # Set of unstable service clients available in Tempest
+    # NOTE(andreaf) This list will exists only as long the remain clients
+    # are migrated to tempest.lib, and it will then be deleted without
+    # deprecation or advance notice
+    return set(['identity.v2', 'identity.v3', 'object-storage', 'volume.v1',
+                'volume.v2', 'volume.v3'])
+
+
+def available_modules():
+    """Set of service client modules available in Tempest and plugins
+
+    Set of stable service clients from Tempest and service clients exposed
+    by plugins. This set of available modules can be used for automatic
+    configuration.
+
+    :raise PluginRegistrationException: if a plugin exposes a service_version
+        already defined by Tempest or another plugin.
+
+    Examples:
+
+        >>> from tempest import config
+        >>> params = {}
+        >>> for service_version in available_modules():
+        >>>     service = service_version.split('.')[0]
+        >>>     params[service] = config.service_client_config(service)
+        >>> service_clients = ServiceClients(creds, identity_uri,
+        >>>                                  client_parameters=params)
+    """
+    extra_service_versions = set([])
+    _tempest_modules = set(tempest_modules())
+    plugin_services = ClientsRegistry().get_service_clients()
+    for plugin_name in plugin_services:
+        plug_service_versions = set([x['service_version'] for x in
+                                     plugin_services[plugin_name]])
+        # If a plugin exposes a duplicate service_version raise an exception
+        if plug_service_versions:
+            if not plug_service_versions.isdisjoint(extra_service_versions):
+                detailed_error = (
+                    'Plugin %s is trying to register a service %s already '
+                    'claimed by another one' % (plugin_name,
+                                                extra_service_versions &
+                                                plug_service_versions))
+                raise exceptions.PluginRegistrationException(
+                    name=plugin_name, detailed_error=detailed_error)
+            # NOTE(andreaf) Once all tempest clients are stable, the following
+            # if will have to be removed.
+            if not plug_service_versions.isdisjoint(
+                    _tempest_internal_modules()):
+                detailed_error = (
+                    'Plugin %s is trying to register a service %s already '
+                    'claimed by a Tempest one' % (plugin_name,
+                                                  _tempest_internal_modules() &
+                                                  plug_service_versions))
+                raise exceptions.PluginRegistrationException(
+                    name=plugin_name, detailed_error=detailed_error)
+        extra_service_versions |= plug_service_versions
+    return _tempest_modules | extra_service_versions
 
 
 @misc.singleton
@@ -34,3 +121,327 @@
 
     def get_service_clients(self):
         return self._service_clients
+
+
+class ClientsFactory(object):
+    """Builds service clients for a service client module
+
+    This class implements the logic of feeding service client parameters
+    to service clients from a specific module. It allows setting the
+    parameters once and obtaining new instances of the clients without the
+    need of passing any parameter.
+
+    ClientsFactory can be used directly, or consumed via the `ServiceClients`
+    class, which manages the authorization part.
+    """
+
+    def __init__(self, module_path, client_names, auth_provider, **kwargs):
+        """Initialises the client factory
+
+        :param module_path: Path to module that includes all service clients.
+            All service client classes must be exposed by a single module.
+            If they are separated in different modules, defining __all__
+            in the root module can help, similar to what is done by service
+            clients in tempest.
+        :param client_names: List or set of names of the service client
+            classes.
+        :param auth_provider: The auth provider used to initialise client.
+        :param kwargs: Parameters to be passed to all clients. Parameters
+            values can be overwritten when clients are initialised, but
+            parameters cannot be deleted.
+        :raise ImportError if the specified module_path cannot be imported
+
+        Example:
+
+            >>> # Get credentials and an auth_provider
+            >>> clients = ClientsFactory(
+            >>>     module_path='my_service.my_service_clients',
+            >>>     client_names=['ServiceClient1', 'ServiceClient2'],
+            >>>     auth_provider=auth_provider,
+            >>>     service='my_service',
+            >>>     region='region1')
+            >>> my_api_client = clients.MyApiClient()
+            >>> my_api_client_region2 = clients.MyApiClient(region='region2')
+
+        """
+        # Import the module. If it's not importable, the raised exception
+        # provides good enough information about what happened
+        _module = importlib.import_module(module_path)
+        # If any of the classes is not in the module we fail
+        for class_name in client_names:
+            # TODO(andreaf) This always passes all parameters to all clients.
+            # In future to allow clients to specify the list of parameters
+            # that they accept based out of a list of standard ones.
+
+            # Obtain the class
+            klass = self._get_class(_module, class_name)
+            final_kwargs = copy.copy(kwargs)
+
+            # Set the function as an attribute of the factory
+            setattr(self, class_name, self._get_partial_class(
+                klass, auth_provider, final_kwargs))
+
+    def _get_partial_class(self, klass, auth_provider, kwargs):
+
+        # Define a function that returns a new class instance by
+        # combining default kwargs with extra ones
+        def partial_class(alias=None, **later_kwargs):
+            """Returns a callable the initialises a service client
+
+            Builds a callable that accepts kwargs, which are passed through
+            to the __init__ of the service client, along with a set of defaults
+            set in factory at factory __init__ time.
+            Original args in the service client can only be passed as kwargs.
+
+            It accepts one extra parameter 'alias' compared to the original
+            service client. When alias is provided, the returned callable will
+            also set an attribute called with a name defined in 'alias', which
+            contains the instance of the service client.
+
+            :param alias: str Name of the attribute set on the factory once
+                the callable is invoked which contains the initialised
+                service client. If None, no attribute is set.
+            :param later_kwargs: kwargs passed through to the service client
+                __init__ on top of defaults set at factory level.
+            """
+            kwargs.update(later_kwargs)
+            _client = klass(auth_provider=auth_provider, **kwargs)
+            if alias:
+                setattr(self, alias, _client)
+            return _client
+
+        return partial_class
+
+    @classmethod
+    def _get_class(cls, module, class_name):
+        klass = getattr(module, class_name, None)
+        if not klass:
+            msg = 'Invalid class name, %s is not found in %s'
+            raise AttributeError(msg % (class_name, module))
+        if not inspect.isclass(klass):
+            msg = 'Expected a class, got %s of type %s instead'
+            raise TypeError(msg % (klass, type(klass)))
+        return klass
+
+
+class ServiceClients(object):
+    """Service client provider class
+
+    The ServiceClients object provides a useful means for tests to access
+    service clients configured for a specified set of credentials.
+    It hides some of the complexity from the authorization and configuration
+    layers.
+
+    Examples:
+
+        >>> from tempest.lib.services import clients
+        >>> johndoe = cred_provider.get_creds_by_role(['johndoe'])
+        >>> johndoe_clients = clients.ServiceClients(johndoe,
+        >>>                                                  identity_uri)
+        >>> johndoe_servers = johndoe_clients.servers_client.list_servers()
+
+    """
+    # NOTE(andreaf) This class does not depend on tempest configuration
+    # and its meant for direct consumption by external clients such as tempest
+    # plugins. Tempest provides a wrapper class, `clients.Manager`, that
+    # initialises this class using values from tempest CONF object. The wrapper
+    # class should only be used by tests hosted in Tempest.
+
+    def __init__(self, credentials, identity_uri, region=None, scope='project',
+                 disable_ssl_certificate_validation=True, ca_certs=None,
+                 trace_requests='', client_parameters=None):
+        """Service Clients provider
+
+        Instantiate a `ServiceClients` object, from a set of credentials and an
+        identity URI. The identity version is inferred from the credentials
+        object. Optionally auth scope can be provided.
+
+        A few parameters can be given a value which is applied as default
+        for all service clients: region, dscv, ca_certs, trace_requests.
+
+        Parameters dscv, ca_certs and trace_requests all apply to the auth
+        provider as well as any service clients provided by this manager.
+
+        Any other client parameter must be set via client_parameters.
+        The list of available parameters is defined in the service clients
+        interfaces. For reference, most clients will accept 'region',
+        'service', 'endpoint_type', 'build_timeout' and 'build_interval', which
+        are all inherited from RestClient.
+
+        The `config` module in Tempest exposes an helper function
+        `service_client_config` that can be used to extract from configuration
+        a dictionary ready to be injected in kwargs.
+
+        Exceptions are:
+        - Token clients for 'identity' have a very different interface
+        - Volume client for 'volume' accepts 'default_volume_size'
+        - Servers client from 'compute' accepts 'enable_instance_password'
+
+        Examples:
+
+            >>> identity_params = config.service_client_config('identity')
+            >>> params = {
+            >>>     'identity': identity_params,
+            >>>     'compute': {'region': 'region2'}}
+            >>> manager = lib_manager.Manager(
+            >>>     my_creds, identity_uri, client_parameters=params)
+
+        :param credentials: An instance of `auth.Credentials`
+        :param identity_uri: URI of the identity API. This should be a
+                             mandatory parameter, and it will so soon.
+        :param region: Default value of region for service clients.
+        :param scope: default scope for tokens produced by the auth provider
+        :param disable_ssl_certificate_validation: Applies to auth and to all
+                                                  service clients.
+        :param ca_certs: Applies to auth and to all service clients.
+        :param trace_requests: Applies to auth and to all service clients.
+        :param client_parameters: Dictionary with parameters for service
+            clients. Keys of the dictionary are the service client service
+            name, as declared in `service_clients.available_modules()` except
+            for the version. Values are dictionaries of parameters that are
+            going to be passed to all clients in the service client module.
+
+        Examples:
+
+            >>> params_service_x = {'param_name': 'param_value'}
+            >>> client_parameters = { 'service_x': params_service_x }
+
+            >>> params_service_y = config.service_client_config('service_y')
+            >>> client_parameters['service_y'] = params_service_y
+
+        """
+        self._registered_services = set([])
+        self.credentials = credentials
+        self.identity_uri = identity_uri
+        if not identity_uri:
+            raise exceptions.InvalidCredentials(
+                'ServiceClients requires a non-empty identity_uri.')
+        self.region = region
+        # Check if passed or default credentials are valid
+        if not self.credentials.is_valid():
+            raise exceptions.InvalidCredentials()
+        # Get the identity classes matching the provided credentials
+        # TODO(andreaf) Define a new interface in Credentials to get
+        # the API version from an instance
+        identity = [(k, auth.IDENTITY_VERSION[k][1]) for k in
+                    auth.IDENTITY_VERSION.keys() if
+                    isinstance(self.credentials, auth.IDENTITY_VERSION[k][0])]
+        # Zero matches or more than one are both not valid.
+        if len(identity) != 1:
+            raise exceptions.InvalidCredentials()
+        self.auth_version, auth_provider_class = identity[0]
+        self.dscv = disable_ssl_certificate_validation
+        self.ca_certs = ca_certs
+        self.trace_requests = trace_requests
+        # Creates an auth provider for the credentials
+        self.auth_provider = auth_provider_class(
+            self.credentials, self.identity_uri, scope=scope,
+            disable_ssl_certificate_validation=self.dscv,
+            ca_certs=self.ca_certs, trace_requests=self.trace_requests)
+        # Setup some defaults for client parameters of registered services
+        client_parameters = client_parameters or {}
+        self.parameters = {}
+        # Parameters are provided for unversioned services
+        all_modules = available_modules() | _tempest_internal_modules()
+        unversioned_services = set(
+            [x.split('.')[0] for x in all_modules])
+        for service in unversioned_services:
+            self.parameters[service] = self._setup_parameters(
+                client_parameters.pop(service, {}))
+        # Check that no client parameters was supplied for unregistered clients
+        if client_parameters:
+            raise exceptions.UnknownServiceClient(
+                services=list(client_parameters.keys()))
+
+        # Register service clients from the registry (__tempest__ and plugins)
+        clients_registry = ClientsRegistry()
+        plugin_service_clients = clients_registry.get_service_clients()
+        for plugin in plugin_service_clients:
+            service_clients = plugin_service_clients[plugin]
+            # Each plugin returns a list of service client parameters
+            for service_client in service_clients:
+                # NOTE(andreaf) If a plugin cannot register, stop the
+                # registration process, log some details to help
+                # troubleshooting, and re-raise
+                try:
+                    self.register_service_client_module(**service_client)
+                except Exception:
+                    LOG.exception(
+                        'Failed to register service client from plugin %s '
+                        'with parameters %s' % (plugin, service_client))
+                    raise
+
+    def register_service_client_module(self, name, service_version,
+                                       module_path, client_names, **kwargs):
+        """Register a service client module
+
+        Initiates a client factory for the specified module, using this
+        class auth_provider, and accessible via a `name` attribute in the
+        service client.
+
+        :param name: Name used to access the client
+        :param service_version: Name of the service complete with version.
+            Used to track registered services. When a plugin implements it,
+            it can be used by other plugins to obtain their configuration.
+        :param module_path: Path to module that includes all service clients.
+            All service client classes must be exposed by a single module.
+            If they are separated in different modules, defining __all__
+            in the root module can help, similar to what is done by service
+            clients in tempest.
+        :param client_names: List or set of names of service client classes.
+        :param kwargs: Extra optional parameters to be passed to all clients.
+            ServiceClient provides defaults for region, dscv, ca_certs and
+            trace_requests.
+        :raise ServiceClientRegistrationException: if the provided name is
+            already in use or if service_version is already registered.
+        :raise ImportError: if module_path cannot be imported.
+        """
+        if hasattr(self, name):
+            using_name = getattr(self, name)
+            detailed_error = 'Module name already in use: %s' % using_name
+            raise exceptions.ServiceClientRegistrationException(
+                name=name, service_version=service_version,
+                module_path=module_path, client_names=client_names,
+                detailed_error=detailed_error)
+        if service_version in self.registered_services:
+            detailed_error = 'Service %s already registered.' % service_version
+            raise exceptions.ServiceClientRegistrationException(
+                name=name, service_version=service_version,
+                module_path=module_path, client_names=client_names,
+                detailed_error=detailed_error)
+        params = dict(region=self.region,
+                      disable_ssl_certificate_validation=self.dscv,
+                      ca_certs=self.ca_certs,
+                      trace_requests=self.trace_requests)
+        params.update(kwargs)
+        # Instantiate the client factory
+        _factory = ClientsFactory(module_path=module_path,
+                                  client_names=client_names,
+                                  auth_provider=self.auth_provider,
+                                  **params)
+        # Adds the client factory to the service_client
+        setattr(self, name, _factory)
+        # Add the name of the new service in self.SERVICES for discovery
+        self._registered_services.add(service_version)
+
+    @property
+    def registered_services(self):
+        # NOTE(andreaf) Once all tempest modules are stable this needs to
+        # be updated to remove _tempest_internal_modules
+        return self._registered_services | _tempest_internal_modules()
+
+    def _setup_parameters(self, parameters):
+        """Setup default values for client parameters
+
+        Region by default is the region passed as an __init__ parameter.
+        Checks that no parameter for an unknown service is provided.
+        """
+        _parameters = {}
+        # Use region from __init__
+        if self.region:
+            _parameters['region'] = self.region
+        # Update defaults with specified parameters
+        _parameters.update(parameters)
+        # If any parameter is left, parameters for an unknown service were
+        # provided as input. Fail rather than ignore silently.
+        return _parameters
diff --git a/tempest/lib/services/compute/agents_client.py b/tempest/lib/services/compute/agents_client.py
old mode 100644
new mode 100755
index 6d3a817..3f05d3b
--- a/tempest/lib/services/compute/agents_client.py
+++ b/tempest/lib/services/compute/agents_client.py
@@ -24,7 +24,11 @@
     """Tests Agents API"""
 
     def list_agents(self, **params):
-        """List all agent builds."""
+        """List all agent builds.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listbuilds
+        """
         url = 'os-agents'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -46,7 +50,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def delete_agent(self, agent_id):
-        """Delete an existing agent build."""
+        """Delete an existing agent build.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#deleteBuild
+        """
         resp, body = self.delete("os-agents/%s" % agent_id)
         self.validate_response(schema.delete_agent, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
old mode 100644
new mode 100755
index 5be8272..ae1700c
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -28,6 +28,11 @@
 class FlavorsClient(base_compute_client.BaseComputeClient):
 
     def list_flavors(self, detail=False, **params):
+        """Lists flavors.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listFlavors
+        """
         url = 'flavors'
         _schema = schema.list_flavors
 
@@ -43,6 +48,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def show_flavor(self, flavor_id):
+        """Shows details for a flavor.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#showFlavor
+        """
         resp, body = self.get("flavors/%s" % flavor_id)
         body = json.loads(body)
         self.validate_response(schema.create_get_flavor_details, resp, body)
@@ -67,7 +77,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def delete_flavor(self, flavor_id):
-        """Delete the given flavor."""
+        """Delete the given flavor.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#deleteFlavor
+        """
         resp, body = self.delete("flavors/{0}".format(flavor_id))
         self.validate_response(schema.delete_flavor, resp, body)
         return rest_client.ResponseBody(resp, body)
@@ -102,7 +116,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def list_flavor_extra_specs(self, flavor_id):
-        """Get extra Specs details of the mentioned flavor."""
+        """Get extra Specs details of the mentioned flavor.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listFlavorExtraSpecs
+        """
         resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id)
         body = json.loads(body)
         self.validate_response(schema_extra_specs.set_get_flavor_extra_specs,
@@ -110,7 +128,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def show_flavor_extra_spec(self, flavor_id, key):
-        """Get extra Specs key-value of the mentioned flavor and key."""
+        """Get extra Specs key-value of the mentioned flavor and key.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#showFlavorExtraSpec
+        """
         resp, body = self.get('flavors/%s/os-extra_specs/%s' % (flavor_id,
                               key))
         body = json.loads(body)
@@ -136,14 +158,22 @@
     def unset_flavor_extra_spec(self, flavor_id, key):  # noqa
         # NOTE: This noqa is for passing T111 check and we cannot rename
         #       to keep backwards compatibility.
-        """Unset extra Specs from the mentioned flavor."""
+        """Unset extra Specs from the mentioned flavor.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#deleteFlavorExtraSpec
+        """
         resp, body = self.delete('flavors/%s/os-extra_specs/%s' %
                                  (flavor_id, key))
         self.validate_response(schema.unset_flavor_extra_specs, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def list_flavor_access(self, flavor_id):
-        """Get flavor access information given the flavor id."""
+        """Get flavor access information given the flavor id.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listFlavorAccess
+        """
         resp, body = self.get('flavors/%s/os-flavor-access' % flavor_id)
         body = json.loads(body)
         self.validate_response(schema_access.add_remove_list_flavor_access,
@@ -151,7 +181,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def add_flavor_access(self, flavor_id, tenant_id):
-        """Add flavor access for the specified tenant."""
+        """Add flavor access for the specified tenant.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#addFlavorAccess
+        """
         post_body = {
             'addTenantAccess': {
                 'tenant': tenant_id
@@ -165,7 +199,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def remove_flavor_access(self, flavor_id, tenant_id):
-        """Remove flavor access from the specified tenant."""
+        """Remove flavor access from the specified tenant.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#removeFlavorAccess
+        """
         post_body = {
             'removeTenantAccess': {
                 'tenant': tenant_id
diff --git a/tempest/lib/services/compute/floating_ips_client.py b/tempest/lib/services/compute/floating_ips_client.py
old mode 100644
new mode 100755
index 03e4894..6922c48
--- a/tempest/lib/services/compute/floating_ips_client.py
+++ b/tempest/lib/services/compute/floating_ips_client.py
@@ -25,7 +25,11 @@
 class FloatingIPsClient(base_compute_client.BaseComputeClient):
 
     def list_floating_ips(self, **params):
-        """Returns a list of all floating IPs filtered by any parameters."""
+        """Returns a list of all floating IPs filtered by any parameters.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listfloatingipsObject
+        """
         url = 'os-floating-ips'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -36,7 +40,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def show_floating_ip(self, floating_ip_id):
-        """Get the details of a floating IP."""
+        """Get the details of a floating IP.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#showFloatingIP
+        """
         url = "os-floating-ips/%s" % floating_ip_id
         resp, body = self.get(url)
         body = json.loads(body)
@@ -57,7 +65,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def delete_floating_ip(self, floating_ip_id):
-        """Deletes the provided floating IP from the project."""
+        """Deletes the provided floating IP from the project.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#deleteFloatingIP
+        """
         url = "os-floating-ips/%s" % floating_ip_id
         resp, body = self.delete(url)
         self.validate_response(schema.add_remove_floating_ip, resp, body)
diff --git a/tempest/lib/services/compute/keypairs_client.py b/tempest/lib/services/compute/keypairs_client.py
old mode 100644
new mode 100755
index 7b8e6b2..2246739
--- a/tempest/lib/services/compute/keypairs_client.py
+++ b/tempest/lib/services/compute/keypairs_client.py
@@ -28,6 +28,11 @@
                             {'min': '2.2', 'max': None, 'schema': schemav22}]
 
     def list_keypairs(self, **params):
+        """Lists keypairs that are associated with the account.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listKeypairs
+        """
         url = 'os-keypairs'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -38,6 +43,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def show_keypair(self, keypair_name, **params):
+        """Shows details for a keypair that is associated with the account.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#showKeypair
+        """
         url = "os-keypairs/%s" % keypair_name
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -61,6 +71,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def delete_keypair(self, keypair_name, **params):
+        """Deletes a keypair.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#deleteKeypair
+        """
         url = "os-keypairs/%s" % keypair_name
         if params:
             url += '?%s' % urllib.urlencode(params)
diff --git a/tempest/lib/services/compute/security_groups_client.py b/tempest/lib/services/compute/security_groups_client.py
old mode 100644
new mode 100755
index 6b9c7e1..386c214
--- a/tempest/lib/services/compute/security_groups_client.py
+++ b/tempest/lib/services/compute/security_groups_client.py
@@ -26,7 +26,11 @@
 class SecurityGroupsClient(base_compute_client.BaseComputeClient):
 
     def list_security_groups(self, **params):
-        """List all security groups for a user."""
+        """List all security groups for a user.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listSecGroups
+        """
 
         url = 'os-security-groups'
         if params:
@@ -38,7 +42,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def show_security_group(self, security_group_id):
-        """Get the details of a Security Group."""
+        """Get the details of a Security Group.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#showSecGroup
+        """
         url = "os-security-groups/%s" % security_group_id
         resp, body = self.get(url)
         body = json.loads(body)
@@ -71,7 +79,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def delete_security_group(self, security_group_id):
-        """Delete the provided Security Group."""
+        """Delete the provided Security Group.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#deleteSecGroup
+        """
         resp, body = self.delete(
             'os-security-groups/%s' % security_group_id)
         self.validate_response(schema.delete_security_group, resp, body)
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
old mode 100644
new mode 100755
index 9444e20..24c0be9
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -103,7 +103,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def show_server(self, server_id):
-        """Get server details."""
+        """Get server details.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#showServer
+        """
         resp, body = self.get("servers/%s" % server_id)
         body = json.loads(body)
         schema = self.get_schema(self.schema_versions_info)
@@ -111,7 +115,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def delete_server(self, server_id):
-        """Delete server."""
+        """Delete server.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#deleteServer
+        """
         resp, body = self.delete("servers/%s" % server_id)
         self.validate_response(schema.delete_server, resp, body)
         return rest_client.ResponseBody(resp, body)
@@ -141,7 +149,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def list_addresses(self, server_id):
-        """Lists all addresses for a server."""
+        """Lists all addresses for a server.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#list-ips
+        """
         resp, body = self.get("servers/%s/ips" % server_id)
         body = json.loads(body)
         self.validate_response(schema.list_addresses, resp, body)
@@ -264,12 +276,22 @@
         return self.action(server_id, 'revertResize', **kwargs)
 
     def list_server_metadata(self, server_id):
+        """Lists all metadata for a server.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listServerMetadata
+        """
         resp, body = self.get("servers/%s/metadata" % server_id)
         body = json.loads(body)
         self.validate_response(schema.list_server_metadata, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def set_server_metadata(self, server_id, meta, no_metadata_field=False):
+        """Sets one or more metadata items for a server.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createServerMetadata
+        """
         if no_metadata_field:
             post_body = ""
         else:
@@ -281,6 +303,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def update_server_metadata(self, server_id, meta):
+        """Updates one or more metadata items for a server.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#updateServerMetadata
+        """
         post_body = json.dumps({'metadata': meta})
         resp, body = self.post('servers/%s/metadata' % server_id,
                                post_body)
@@ -290,6 +317,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def show_server_metadata_item(self, server_id, key):
+        """Shows details for a metadata item, by key, for a server.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#showServerMetadataItem
+        """
         resp, body = self.get("servers/%s/metadata/%s" % (server_id, key))
         body = json.loads(body)
         self.validate_response(schema.set_show_server_metadata_item,
@@ -297,6 +329,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def set_server_metadata_item(self, server_id, key, meta):
+        """Sets a metadata item, by key, for a server.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#setServerMetadataItem
+        """
         post_body = json.dumps({'meta': meta})
         resp, body = self.put('servers/%s/metadata/%s' % (server_id, key),
                               post_body)
@@ -306,6 +343,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def delete_server_metadata_item(self, server_id, key):
+        """Deletes a metadata item, by key, from a server.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#deleteServerMetadataItem
+        """
         resp, body = self.delete("servers/%s/metadata/%s" %
                                  (server_id, key))
         self.validate_response(schema.delete_server_metadata_item,
@@ -313,9 +355,19 @@
         return rest_client.ResponseBody(resp, body)
 
     def stop_server(self, server_id, **kwargs):
+        """Stops a running server and changes its status to SHUTOFF.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#stop
+        """
         return self.action(server_id, 'os-stop', **kwargs)
 
     def start_server(self, server_id, **kwargs):
+        """Starts a stopped server and changes its status to ACTIVE.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#start
+        """
         return self.action(server_id, 'os-start', **kwargs)
 
     def attach_volume(self, server_id, **kwargs):
@@ -341,14 +393,23 @@
         return rest_client.ResponseBody(resp, body)
 
     def detach_volume(self, server_id, volume_id):  # noqa
-        """Detaches a volume from a server instance."""
+        """Detaches a volume from a server instance.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#deleteVolumeAttachment
+        """
         resp, body = self.delete('servers/%s/os-volume_attachments/%s' %
                                  (server_id, volume_id))
         self.validate_response(schema.detach_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_volume_attachment(self, server_id, volume_id):
-        """Return details about the given volume attachment."""
+        """Return details about the given volume attachment.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#
+                              getVolumeAttachmentDetails
+        """
         resp, body = self.get('servers/%s/os-volume_attachments/%s' % (
             server_id, volume_id))
         body = json.loads(body)
@@ -356,7 +417,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def list_volume_attachments(self, server_id):
-        """Returns the list of volume attachments for a given instance."""
+        """Returns the list of volume attachments for a given instance.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listVolumeAttachments
+        """
         resp, body = self.get('servers/%s/os-volume_attachments' % (
             server_id))
         body = json.loads(body)
@@ -366,7 +431,8 @@
     def add_security_group(self, server_id, **kwargs):
         """Add a security group to the server.
 
-        Available params: TODO
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#addSecurityGroup
         """
         # TODO(oomichi): The api-site doesn't contain this API description.
         # So the above should be changed to the api-site link after
@@ -377,7 +443,8 @@
     def remove_security_group(self, server_id, **kwargs):
         """Remove a security group from the server.
 
-        Available params: TODO
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#removeSecurityGroup
         """
         # TODO(oomichi): The api-site doesn't contain this API description.
         # So the above should be changed to the api-site link after
@@ -507,7 +574,11 @@
         return self.action(server_id, 'rescue', schema.rescue_server, **kwargs)
 
     def unrescue_server(self, server_id):
-        """Unrescue the provided server."""
+        """Unrescue the provided server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#unrescue
+        """
         return self.action(server_id, 'unrescue')
 
     def show_server_diagnostics(self, server_id):
diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py
old mode 100644
new mode 100755
index a190e5f..b6dbe28
--- a/tempest/lib/services/compute/services_client.py
+++ b/tempest/lib/services/compute/services_client.py
@@ -25,6 +25,11 @@
 class ServicesClient(base_compute_client.BaseComputeClient):
 
     def list_services(self, **params):
+        """Lists all running Compute services for a tenant.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listServices
+        """
         url = 'os-services'
         if params:
             url += '?%s' % urllib.urlencode(params)
diff --git a/tempest/lib/services/compute/volumes_client.py b/tempest/lib/services/compute/volumes_client.py
old mode 100644
new mode 100755
index 41d9af2..2787779
--- a/tempest/lib/services/compute/volumes_client.py
+++ b/tempest/lib/services/compute/volumes_client.py
@@ -25,7 +25,11 @@
 class VolumesClient(base_compute_client.BaseComputeClient):
 
     def list_volumes(self, detail=False, **params):
-        """List all the volumes created."""
+        """List all the volumes created.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listVolumes
+        """
         url = 'os-volumes'
 
         if detail:
@@ -39,7 +43,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def show_volume(self, volume_id):
-        """Return the details of a single volume."""
+        """Return the details of a single volume.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#showVolume
+        """
         url = "os-volumes/%s" % volume_id
         resp, body = self.get(url)
         body = json.loads(body)
@@ -59,7 +67,11 @@
         return rest_client.ResponseBody(resp, body)
 
     def delete_volume(self, volume_id):
-        """Delete the Specified Volume."""
+        """Delete the Specified Volume.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#deleteVolume
+        """
         resp, body = self.delete("os-volumes/%s" % volume_id)
         self.validate_response(schema.delete_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/network/base.py b/tempest/lib/services/network/base.py
index a6ada04..620e0f1 100644
--- a/tempest/lib/services/network/base.py
+++ b/tempest/lib/services/network/base.py
@@ -54,18 +54,26 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def create_resource(self, uri, post_data):
+    def create_resource(self, uri, post_data, expect_empty_body=False):
         req_uri = self.uri_prefix + uri
         req_post_data = json.dumps(post_data)
         resp, body = self.post(req_uri, req_post_data)
-        body = json.loads(body)
+        # NOTE: RFC allows both a valid non-empty body and an empty body for
+        # response of POST API. If a body is expected not empty, we decode the
+        # body. Otherwise we returns the body as it is.
+        if not expect_empty_body:
+            body = json.loads(body)
         self.expected_success(201, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def update_resource(self, uri, post_data):
+    def update_resource(self, uri, post_data, expect_empty_body=False):
         req_uri = self.uri_prefix + uri
         req_post_data = json.dumps(post_data)
         resp, body = self.put(req_uri, req_post_data)
-        body = json.loads(body)
+        # NOTE: RFC allows both a valid non-empty body and an empty body for
+        # response of PUT API. If a body is expected not empty, we decode the
+        # body. Otherwise we returns the body as it is.
+        if not expect_empty_body:
+            body = json.loads(body)
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/network/networks_client.py b/tempest/lib/services/network/networks_client.py
index 7d75bf7..6b601ee 100755
--- a/tempest/lib/services/network/networks_client.py
+++ b/tempest/lib/services/network/networks_client.py
@@ -19,7 +19,7 @@
         """Creates a network.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2.html#createNetwork
+                              api-ref/networking/v2/index.html#create-network
         """
         uri = '/networks'
         post_data = {'network': kwargs}
@@ -29,7 +29,7 @@
         """Updates a network.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2.html#updateNetwork
+                              api-ref/networking/v2/index.html#update-network
         """
         uri = '/networks/%s' % network_id
         post_data = {'network': kwargs}
@@ -39,7 +39,7 @@
         """Shows details for a network.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2.html#showNetwork
+                              api-ref/networking/v2/index.html#show-network-details
         """
         uri = '/networks/%s' % network_id
         return self.show_resource(uri, **fields)
@@ -52,7 +52,7 @@
         """Lists networks to which the tenant has access.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2.html#listNetworks
+                              api-ref/networking/v2/index.html#list-networks
         """
         uri = '/networks'
         return self.list_resources(uri, **filters)
@@ -61,7 +61,7 @@
         """Create multiple networks in a single request.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2.html#bulkCreateNetwork
+                              api-ref/networking/v2/index.html#bulk-create-networks
         """
         uri = '/networks'
         return self.create_resource(uri, kwargs)
diff --git a/tempest/lib/services/volume/__init__.py b/tempest/lib/services/volume/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/volume/__init__.py
diff --git a/tempest/lib/services/volume/v1/__init__.py b/tempest/lib/services/volume/v1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/volume/v1/__init__.py
diff --git a/tempest/services/volume/v1/json/availability_zone_client.py b/tempest/lib/services/volume/v1/availability_zone_client.py
similarity index 100%
rename from tempest/services/volume/v1/json/availability_zone_client.py
rename to tempest/lib/services/volume/v1/availability_zone_client.py
diff --git a/tempest/services/volume/v1/json/extensions_client.py b/tempest/lib/services/volume/v1/extensions_client.py
similarity index 100%
rename from tempest/services/volume/v1/json/extensions_client.py
rename to tempest/lib/services/volume/v1/extensions_client.py
diff --git a/tempest/services/volume/v1/json/admin/hosts_client.py b/tempest/lib/services/volume/v1/hosts_client.py
similarity index 100%
rename from tempest/services/volume/v1/json/admin/hosts_client.py
rename to tempest/lib/services/volume/v1/hosts_client.py
diff --git a/tempest/services/volume/v1/json/admin/services_client.py b/tempest/lib/services/volume/v1/services_client.py
similarity index 100%
rename from tempest/services/volume/v1/json/admin/services_client.py
rename to tempest/lib/services/volume/v1/services_client.py
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/volume/v2/__init__.py
diff --git a/tempest/services/volume/v2/json/availability_zone_client.py b/tempest/lib/services/volume/v2/availability_zone_client.py
similarity index 100%
rename from tempest/services/volume/v2/json/availability_zone_client.py
rename to tempest/lib/services/volume/v2/availability_zone_client.py
diff --git a/tempest/services/volume/v2/json/extensions_client.py b/tempest/lib/services/volume/v2/extensions_client.py
similarity index 100%
rename from tempest/services/volume/v2/json/extensions_client.py
rename to tempest/lib/services/volume/v2/extensions_client.py
diff --git a/tempest/services/volume/v2/json/admin/hosts_client.py b/tempest/lib/services/volume/v2/hosts_client.py
similarity index 100%
rename from tempest/services/volume/v2/json/admin/hosts_client.py
rename to tempest/lib/services/volume/v2/hosts_client.py
diff --git a/tempest/services/volume/v2/json/admin/services_client.py b/tempest/lib/services/volume/v2/services_client.py
similarity index 100%
rename from tempest/services/volume/v2/json/admin/services_client.py
rename to tempest/lib/services/volume/v2/services_client.py
diff --git a/tempest/manager.py b/tempest/manager.py
index 3d495b6..e3174d4 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -15,15 +15,15 @@
 
 from oslo_log import log as logging
 
-from tempest import clients
+from tempest import clients as tempest_clients
 from tempest import config
-from tempest import service_clients
+from tempest.lib.services import clients
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
-class Manager(service_clients.ServiceClients):
+class Manager(clients.ServiceClients):
     """Service client manager class for backward compatibility
 
     The former manager.Manager is not a stable interface in Tempest,
@@ -37,7 +37,7 @@
                "soon as the client manager becomes available in tempest.lib.")
         LOG.warning(msg)
         dscv = CONF.identity.disable_ssl_certificate_validation
-        _, uri = clients.get_auth_provider_class(credentials)
+        _, uri = tempest_clients.get_auth_provider_class(credentials)
         super(Manager, self).__init__(
             credentials=credentials, scope=scope,
             identity_uri=uri,
@@ -58,5 +58,5 @@
            "as such it should not imported directly. It will be removed as "
            "the client manager becomes available in tempest.lib.")
     LOG.warning(msg)
-    return clients.get_auth_provider(credentials=credentials,
-                                     pre_auth=pre_auth, scope=scope)
+    return tempest_clients.get_auth_provider(credentials=credentials,
+                                             pre_auth=pre_auth, scope=scope)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 8fca8ca..6da0570 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -837,6 +837,7 @@
         # NOTE(vsaienko) With Ironic, instances live on separate hardware
         # servers. Neutron does not bind ports for Ironic instances, as a
         # result the port remains in the DOWN state.
+        # TODO(vsaienko) remove once bug: #1599836 is resolved.
         if CONF.service_available.ironic:
             p_status.append('DOWN')
         port_map = [(p["id"], fxip["ip_address"])
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 9c48080..e0e1204 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -24,6 +24,7 @@
 from tempest import config
 from tempest import exceptions
 from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
 from tempest.scenario import manager
 from tempest import test
 
@@ -410,6 +411,7 @@
     @test.idempotent_id('1546850e-fbaa-42f5-8b5f-03d8a6a95f15')
     @testtools.skipIf(CONF.baremetal.driver_enabled,
                       'Baremetal relies on a shared physical network.')
+    @decorators.skip_because(bug="1610994")
     @test.services('compute', 'network')
     def test_connectivity_between_vms_on_different_networks(self):
         """Test connectivity between VMs on different networks
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 59ebb7a..364b6f5 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -18,6 +18,7 @@
 
 from tempest import config
 from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
 from tempest.scenario import manager
 from tempest import test
 
@@ -254,6 +255,7 @@
         self._prepare_and_test(address6_mode='dhcpv6-stateless', n_subnets6=2,
                                dualnet=True)
 
+    @decorators.skip_because(bug="1540983")
     @test.idempotent_id('9178ad42-10e4-47e9-8987-e02b170cc5cd')
     @test.services('compute', 'network')
     def test_dualnet_multi_prefix_slaac(self):
diff --git a/tempest/service_clients.py b/tempest/service_clients.py
deleted file mode 100644
index a5bc86e..0000000
--- a/tempest/service_clients.py
+++ /dev/null
@@ -1,405 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import copy
-import importlib
-import inspect
-import logging
-
-from tempest.lib import auth
-from tempest.lib import exceptions
-from tempest.lib.services import clients
-from tempest.lib.services import compute
-from tempest.lib.services import image
-from tempest.lib.services import network
-
-LOG = logging.getLogger(__name__)
-
-client_modules_by_service_name = {
-    'compute': compute,
-    'image.v1': image.v1,
-    'image.v2': image.v2,
-    'network': network
-}
-
-
-def tempest_modules():
-    """List of service client modules available in Tempest.
-
-    Provides a list of service modules available Tempest.
-    """
-    return set(['compute', 'identity.v2', 'identity.v3', 'image.v1',
-                'image.v2', 'network', 'object-storage', 'volume.v1',
-                'volume.v2', 'volume.v3'])
-
-
-def available_modules():
-    """List of service client modules available in Tempest and plugins
-
-    The list of available modules can be used for automatic configuration.
-
-    :raise PluginRegistrationException: if a plugin exposes a service_version
-        already defined by Tempest or another plugin.
-
-    Examples:
-
-        >>> from tempest import config
-        >>> params = {}
-        >>> for service_version in available_modules():
-        >>>     service = service_version.split('.')[0]
-        >>>     params[service] = config.service_client_config(service)
-        >>> service_clients = ServiceClients(creds, identity_uri,
-        >>>                                  client_parameters=params)
-    """
-    extra_service_versions = set([])
-    plugin_services = clients.ClientsRegistry().get_service_clients()
-    for plugin_name in plugin_services:
-        plug_service_versions = set([x['service_version'] for x in
-                                     plugin_services[plugin_name]])
-        # If a plugin exposes a duplicate service_version raise an exception
-        if plug_service_versions:
-            if not plug_service_versions.isdisjoint(extra_service_versions):
-                detailed_error = (
-                    'Plugin %s is trying to register a service %s already '
-                    'claimed by another one' % (plugin_name,
-                                                extra_service_versions &
-                                                plug_service_versions))
-                raise exceptions.PluginRegistrationException(
-                    name=plugin_name, detailed_error=detailed_error)
-            if not plug_service_versions.isdisjoint(tempest_modules()):
-                detailed_error = (
-                    'Plugin %s is trying to register a service %s already '
-                    'claimed by a Tempest one' % (plugin_name,
-                                                  tempest_modules() &
-                                                  plug_service_versions))
-                raise exceptions.PluginRegistrationException(
-                    name=plugin_name, detailed_error=detailed_error)
-        extra_service_versions |= plug_service_versions
-    return tempest_modules() | extra_service_versions
-
-
-class ClientsFactory(object):
-    """Builds service clients for a service client module
-
-    This class implements the logic of feeding service client parameters
-    to service clients from a specific module. It allows setting the
-    parameters once and obtaining new instances of the clients without the
-    need of passing any parameter.
-
-    ClientsFactory can be used directly, or consumed via the `ServiceClients`
-    class, which manages the authorization part.
-    """
-
-    def __init__(self, module_path, client_names, auth_provider, **kwargs):
-        """Initialises the client factory
-
-        :param module_path: Path to module that includes all service clients.
-            All service client classes must be exposed by a single module.
-            If they are separated in different modules, defining __all__
-            in the root module can help, similar to what is done by service
-            clients in tempest.
-        :param client_names: List or set of names of the service client
-            classes.
-        :param auth_provider: The auth provider used to initialise client.
-        :param kwargs: Parameters to be passed to all clients. Parameters
-            values can be overwritten when clients are initialised, but
-            parameters cannot be deleted.
-        :raise ImportError if the specified module_path cannot be imported
-
-        Example:
-
-            >>> # Get credentials and an auth_provider
-            >>> clients = ClientsFactory(
-            >>>     module_path='my_service.my_service_clients',
-            >>>     client_names=['ServiceClient1', 'ServiceClient2'],
-            >>>     auth_provider=auth_provider,
-            >>>     service='my_service',
-            >>>     region='region1')
-            >>> my_api_client = clients.MyApiClient()
-            >>> my_api_client_region2 = clients.MyApiClient(region='region2')
-
-        """
-        # Import the module. If it's not importable, the raised exception
-        # provides good enough information about what happened
-        _module = importlib.import_module(module_path)
-        # If any of the classes is not in the module we fail
-        for class_name in client_names:
-            # TODO(andreaf) This always passes all parameters to all clients.
-            # In future to allow clients to specify the list of parameters
-            # that they accept based out of a list of standard ones.
-
-            # Obtain the class
-            klass = self._get_class(_module, class_name)
-            final_kwargs = copy.copy(kwargs)
-
-            # Set the function as an attribute of the factory
-            setattr(self, class_name, self._get_partial_class(
-                klass, auth_provider, final_kwargs))
-
-    @classmethod
-    def _get_partial_class(cls, klass, auth_provider, kwargs):
-
-        # Define a function that returns a new class instance by
-        # combining default kwargs with extra ones
-        def partial_class(**later_kwargs):
-            kwargs.update(later_kwargs)
-            return klass(auth_provider=auth_provider, **kwargs)
-
-        return partial_class
-
-    @classmethod
-    def _get_class(cls, module, class_name):
-        klass = getattr(module, class_name, None)
-        if not klass:
-            msg = 'Invalid class name, %s is not found in %s'
-            raise AttributeError(msg % (class_name, module))
-        if not inspect.isclass(klass):
-            msg = 'Expected a class, got %s of type %s instead'
-            raise TypeError(msg % (klass, type(klass)))
-        return klass
-
-
-class ServiceClients(object):
-    """Service client provider class
-
-    The ServiceClients object provides a useful means for tests to access
-    service clients configured for a specified set of credentials.
-    It hides some of the complexity from the authorization and configuration
-    layers.
-
-    Examples:
-
-        >>> from tempest import service_clients
-        >>> johndoe = cred_provider.get_creds_by_role(['johndoe'])
-        >>> johndoe_clients = service_clients.ServiceClients(johndoe,
-        >>>                                                  identity_uri)
-        >>> johndoe_servers = johndoe_clients.servers_client.list_servers()
-
-    """
-    # NOTE(andreaf) This class does not depend on tempest configuration
-    # and its meant for direct consumption by external clients such as tempest
-    # plugins. Tempest provides a wrapper class, `clients.Manager`, that
-    # initialises this class using values from tempest CONF object. The wrapper
-    # class should only be used by tests hosted in Tempest.
-
-    def __init__(self, credentials, identity_uri, region=None, scope='project',
-                 disable_ssl_certificate_validation=True, ca_certs=None,
-                 trace_requests='', client_parameters=None):
-        """Service Clients provider
-
-        Instantiate a `ServiceClients` object, from a set of credentials and an
-        identity URI. The identity version is inferred from the credentials
-        object. Optionally auth scope can be provided.
-
-        A few parameters can be given a value which is applied as default
-        for all service clients: region, dscv, ca_certs, trace_requests.
-
-        Parameters dscv, ca_certs and trace_requests all apply to the auth
-        provider as well as any service clients provided by this manager.
-
-        Any other client parameter must be set via client_parameters.
-        The list of available parameters is defined in the service clients
-        interfaces. For reference, most clients will accept 'region',
-        'service', 'endpoint_type', 'build_timeout' and 'build_interval', which
-        are all inherited from RestClient.
-
-        The `config` module in Tempest exposes an helper function
-        `service_client_config` that can be used to extract from configuration
-        a dictionary ready to be injected in kwargs.
-
-        Exceptions are:
-        - Token clients for 'identity' have a very different interface
-        - Volume client for 'volume' accepts 'default_volume_size'
-        - Servers client from 'compute' accepts 'enable_instance_password'
-
-        Examples:
-
-            >>> identity_params = config.service_client_config('identity')
-            >>> params = {
-            >>>     'identity': identity_params,
-            >>>     'compute': {'region': 'region2'}}
-            >>> manager = lib_manager.Manager(
-            >>>     my_creds, identity_uri, client_parameters=params)
-
-        :param credentials: An instance of `auth.Credentials`
-        :param identity_uri: URI of the identity API. This should be a
-                             mandatory parameter, and it will so soon.
-        :param region: Default value of region for service clients.
-        :param scope: default scope for tokens produced by the auth provider
-        :param disable_ssl_certificate_validation: Applies to auth and to all
-                                                  service clients.
-        :param ca_certs: Applies to auth and to all service clients.
-        :param trace_requests: Applies to auth and to all service clients.
-        :param client_parameters: Dictionary with parameters for service
-            clients. Keys of the dictionary are the service client service
-            name, as declared in `service_clients.available_modules()` except
-            for the version. Values are dictionaries of parameters that are
-            going to be passed to all clients in the service client module.
-
-        Examples:
-
-            >>> params_service_x = {'param_name': 'param_value'}
-            >>> client_parameters = { 'service_x': params_service_x }
-
-            >>> params_service_y = config.service_client_config('service_y')
-            >>> client_parameters['service_y'] = params_service_y
-
-        """
-        self._registered_services = set([])
-        self.credentials = credentials
-        self.identity_uri = identity_uri
-        if not identity_uri:
-            raise exceptions.InvalidCredentials(
-                'ServiceClients requires a non-empty identity_uri.')
-        self.region = region
-        # Check if passed or default credentials are valid
-        if not self.credentials.is_valid():
-            raise exceptions.InvalidCredentials()
-        # Get the identity classes matching the provided credentials
-        # TODO(andreaf) Define a new interface in Credentials to get
-        # the API version from an instance
-        identity = [(k, auth.IDENTITY_VERSION[k][1]) for k in
-                    auth.IDENTITY_VERSION.keys() if
-                    isinstance(self.credentials, auth.IDENTITY_VERSION[k][0])]
-        # Zero matches or more than one are both not valid.
-        if len(identity) != 1:
-            raise exceptions.InvalidCredentials()
-        self.auth_version, auth_provider_class = identity[0]
-        self.dscv = disable_ssl_certificate_validation
-        self.ca_certs = ca_certs
-        self.trace_requests = trace_requests
-        # Creates an auth provider for the credentials
-        self.auth_provider = auth_provider_class(
-            self.credentials, self.identity_uri, scope=scope,
-            disable_ssl_certificate_validation=self.dscv,
-            ca_certs=self.ca_certs, trace_requests=self.trace_requests)
-        # Setup some defaults for client parameters of registered services
-        client_parameters = client_parameters or {}
-        self.parameters = {}
-        # Parameters are provided for unversioned services
-        unversioned_services = set(
-            [x.split('.')[0] for x in available_modules()])
-        for service in unversioned_services:
-            self.parameters[service] = self._setup_parameters(
-                client_parameters.pop(service, {}))
-        # Check that no client parameters was supplied for unregistered clients
-        if client_parameters:
-            raise exceptions.UnknownServiceClient(
-                services=list(client_parameters.keys()))
-
-        # Register service clients owned by tempest
-        for service in tempest_modules():
-            if service in list(client_modules_by_service_name):
-                attribute = service.replace('.', '_')
-                configs = service.split('.')[0]
-                module = client_modules_by_service_name[service]
-                self.register_service_client_module(
-                    attribute, service, module.__name__,
-                    module.__all__, **self.parameters[configs])
-
-        # Register service clients from plugins
-        clients_registry = clients.ClientsRegistry()
-        plugin_service_clients = clients_registry.get_service_clients()
-        for plugin in plugin_service_clients:
-            service_clients = plugin_service_clients[plugin]
-            # Each plugin returns a list of service client parameters
-            for service_client in service_clients:
-                # NOTE(andreaf) If a plugin cannot register, stop the
-                # registration process, log some details to help
-                # troubleshooting, and re-raise
-                try:
-                    self.register_service_client_module(**service_client)
-                except Exception:
-                    LOG.exception(
-                        'Failed to register service client from plugin %s '
-                        'with parameters %s' % (plugin, service_client))
-                    raise
-
-    def register_service_client_module(self, name, service_version,
-                                       module_path, client_names, **kwargs):
-        """Register a service client module
-
-        Initiates a client factory for the specified module, using this
-        class auth_provider, and accessible via a `name` attribute in the
-        service client.
-
-        :param name: Name used to access the client
-        :param service_version: Name of the service complete with version.
-            Used to track registered services. When a plugin implements it,
-            it can be used by other plugins to obtain their configuration.
-        :param module_path: Path to module that includes all service clients.
-            All service client classes must be exposed by a single module.
-            If they are separated in different modules, defining __all__
-            in the root module can help, similar to what is done by service
-            clients in tempest.
-        :param client_names: List or set of names of service client classes.
-        :param kwargs: Extra optional parameters to be passed to all clients.
-            ServiceClient provides defaults for region, dscv, ca_certs and
-            trace_requests.
-        :raise ServiceClientRegistrationException: if the provided name is
-            already in use or if service_version is already registered.
-        :raise ImportError: if module_path cannot be imported.
-        """
-        if hasattr(self, name):
-            using_name = getattr(self, name)
-            detailed_error = 'Module name already in use: %s' % using_name
-            raise exceptions.ServiceClientRegistrationException(
-                name=name, service_version=service_version,
-                module_path=module_path, client_names=client_names,
-                detailed_error=detailed_error)
-        if service_version in self.registered_services:
-            detailed_error = 'Service %s already registered.' % service_version
-            raise exceptions.ServiceClientRegistrationException(
-                name=name, service_version=service_version,
-                module_path=module_path, client_names=client_names,
-                detailed_error=detailed_error)
-        params = dict(region=self.region,
-                      disable_ssl_certificate_validation=self.dscv,
-                      ca_certs=self.ca_certs,
-                      trace_requests=self.trace_requests)
-        params.update(kwargs)
-        # Instantiate the client factory
-        _factory = ClientsFactory(module_path=module_path,
-                                  client_names=client_names,
-                                  auth_provider=self.auth_provider,
-                                  **params)
-        # Adds the client factory to the service_client
-        setattr(self, name, _factory)
-        # Add the name of the new service in self.SERVICES for discovery
-        self._registered_services.add(service_version)
-
-    @property
-    def registered_services(self):
-        # TODO(andreaf) Temporary set needed until all services are migrated
-        _non_migrated_services = tempest_modules() - set(
-            client_modules_by_service_name)
-        return self._registered_services | _non_migrated_services
-
-    def _setup_parameters(self, parameters):
-        """Setup default values for client parameters
-
-        Region by default is the region passed as an __init__ parameter.
-        Checks that no parameter for an unknown service is provided.
-        """
-        _parameters = {}
-        # Use region from __init__
-        if self.region:
-            _parameters['region'] = self.region
-        # Update defaults with specified parameters
-        _parameters.update(parameters)
-        # If any parameter is left, parameters for an unknown service were
-        # provided as input. Fail rather than ignore silently.
-        return _parameters
diff --git a/tempest/services/identity/v3/json/services_client.py b/tempest/services/identity/v3/json/services_client.py
index e863016..95caf7d 100644
--- a/tempest/services/identity/v3/json/services_client.py
+++ b/tempest/services/identity/v3/json/services_client.py
@@ -18,6 +18,7 @@
 """
 
 from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
@@ -57,13 +58,21 @@
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
-    def delete_service(self, serv_id):
-        url = "services/" + serv_id
+    def delete_service(self, service_id):
+        url = "services/" + service_id
         resp, body = self.delete(url)
         self.expected_success(204, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def list_services(self):
+    def list_services(self, **params):
+        """List services.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref/identity/v3/#list-services
+        """
+        url = 'services'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
         resp, body = self.get('services')
         self.expected_success(200, resp.status)
         body = json.loads(body)
diff --git a/tempest/services/volume/base/base_backups_client.py b/tempest/services/volume/base/base_backups_client.py
index 63c5417..fc247a9 100644
--- a/tempest/services/volume/base/base_backups_client.py
+++ b/tempest/services/volume/base/base_backups_client.py
@@ -116,14 +116,9 @@
                             self.build_timeout))
                 raise exceptions.TimeoutException(message)
 
-    def wait_for_backup_deletion(self, backup_id):
-        """Waits for backup deletion"""
-        start_time = int(time.time())
-        while True:
-            try:
-                self.show_backup(backup_id)
-            except lib_exc.NotFound:
-                return
-            if int(time.time()) - start_time >= self.build_timeout:
-                raise exceptions.TimeoutException
-            time.sleep(self.build_interval)
+    def is_resource_deleted(self, id):
+        try:
+            self.show_backup(id)
+        except lib_exc.NotFound:
+            return True
+        return False
diff --git a/tempest/services/volume/v1/__init__.py b/tempest/services/volume/v1/__init__.py
index 6bdb8c4..52d2942 100644
--- a/tempest/services/volume/v1/__init__.py
+++ b/tempest/services/volume/v1/__init__.py
@@ -12,15 +12,14 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-from tempest.services.volume.v1.json.admin.hosts_client import HostsClient
-from tempest.services.volume.v1.json.admin.quotas_client import QuotasClient
-from tempest.services.volume.v1.json.admin.services_client import \
-    ServicesClient
-from tempest.services.volume.v1.json.admin.types_client import TypesClient
-from tempest.services.volume.v1.json.availability_zone_client import \
+from tempest.lib.services.volume.v1.availability_zone_client import \
     AvailabilityZoneClient
+from tempest.lib.services.volume.v1.extensions_client import ExtensionsClient
+from tempest.lib.services.volume.v1.hosts_client import HostsClient
+from tempest.lib.services.volume.v1.services_client import ServicesClient
+from tempest.services.volume.v1.json.admin.quotas_client import QuotasClient
+from tempest.services.volume.v1.json.admin.types_client import TypesClient
 from tempest.services.volume.v1.json.backups_client import BackupsClient
-from tempest.services.volume.v1.json.extensions_client import ExtensionsClient
 from tempest.services.volume.v1.json.qos_client import QosSpecsClient
 from tempest.services.volume.v1.json.snapshots_client import SnapshotsClient
 from tempest.services.volume.v1.json.volumes_client import VolumesClient
diff --git a/tempest/services/volume/v2/__init__.py b/tempest/services/volume/v2/__init__.py
index c75b0e5..26fbc2b 100644
--- a/tempest/services/volume/v2/__init__.py
+++ b/tempest/services/volume/v2/__init__.py
@@ -12,15 +12,14 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-from tempest.services.volume.v2.json.admin.hosts_client import HostsClient
-from tempest.services.volume.v2.json.admin.quotas_client import QuotasClient
-from tempest.services.volume.v2.json.admin.services_client import \
-    ServicesClient
-from tempest.services.volume.v2.json.admin.types_client import TypesClient
-from tempest.services.volume.v2.json.availability_zone_client import \
+from tempest.lib.services.volume.v2.availability_zone_client import \
     AvailabilityZoneClient
+from tempest.lib.services.volume.v2.extensions_client import ExtensionsClient
+from tempest.lib.services.volume.v2.hosts_client import HostsClient
+from tempest.lib.services.volume.v2.services_client import ServicesClient
+from tempest.services.volume.v2.json.admin.quotas_client import QuotasClient
+from tempest.services.volume.v2.json.admin.types_client import TypesClient
 from tempest.services.volume.v2.json.backups_client import BackupsClient
-from tempest.services.volume.v2.json.extensions_client import ExtensionsClient
 from tempest.services.volume.v2.json.qos_client import QosSpecsClient
 from tempest.services.volume.v2.json.snapshots_client import SnapshotsClient
 from tempest.services.volume.v2.json.volumes_client import VolumesClient
diff --git a/tempest/test_discover/plugins.py b/tempest/test_discover/plugins.py
index cfb0c7f..eb50126 100644
--- a/tempest/test_discover/plugins.py
+++ b/tempest/test_discover/plugins.py
@@ -88,7 +88,7 @@
             >>>     'client_names': ['API1Client', 'API2Client'],
             >>>  }
             >>>  params.update(myservice_config)
-            >>> return [params]
+            >>>  return [params]
 
             >>>  # Example implementation with two service clients
             >>>  foo1_config = config.service_client_config('foo')
@@ -107,7 +107,7 @@
             >>>     'client_names': ['API1Client', 'API2Client'],
             >>>  }
             >>>  params_foo2.update(foo2_config)
-            >>> return [params_foo1, params_foo2]
+            >>>  return [params_foo1, params_foo2]
         """
         return []
 
diff --git a/tempest/tests/lib/common/utils/test_data_utils.py b/tempest/tests/lib/common/utils/test_data_utils.py
index 399c4af..94a4847 100644
--- a/tempest/tests/lib/common/utils/test_data_utils.py
+++ b/tempest/tests/lib/common/utils/test_data_utils.py
@@ -59,7 +59,7 @@
     def test_rand_password(self):
         actual = data_utils.rand_password()
         self.assertIsInstance(actual, str)
-        self.assertRegex(actual, "[A-Za-z0-9~!@#$%^&*_=+]{15,}")
+        self.assertRegex(actual, "[A-Za-z0-9~!@#%^&*_=+]{15,}")
         actual2 = data_utils.rand_password()
         self.assertNotEqual(actual, actual2)
 
@@ -67,7 +67,7 @@
         actual = data_utils.rand_password(8)
         self.assertIsInstance(actual, str)
         self.assertEqual(len(actual), 8)
-        self.assertRegex(actual, "[A-Za-z0-9~!@#$%^&*_=+]{8}")
+        self.assertRegex(actual, "[A-Za-z0-9~!@#%^&*_=+]{8}")
         actual2 = data_utils.rand_password(8)
         self.assertNotEqual(actual, actual2)
 
@@ -75,7 +75,7 @@
         actual = data_utils.rand_password(2)
         self.assertIsInstance(actual, str)
         self.assertEqual(len(actual), 3)
-        self.assertRegex(actual, "[A-Za-z0-9~!@#$%^&*_=+]{3}")
+        self.assertRegex(actual, "[A-Za-z0-9~!@#%^&*_=+]{3}")
         actual2 = data_utils.rand_password(2)
         self.assertNotEqual(actual, actual2)
 
diff --git a/tempest/tests/test_service_clients.py b/tempest/tests/lib/services/test_clients.py
similarity index 81%
rename from tempest/tests/test_service_clients.py
rename to tempest/tests/lib/services/test_clients.py
index 3d8b360..5db932c 100644
--- a/tempest/tests/test_service_clients.py
+++ b/tempest/tests/lib/services/test_clients.py
@@ -19,7 +19,7 @@
 
 from tempest.lib import auth
 from tempest.lib import exceptions
-from tempest import service_clients
+from tempest.lib.services import clients
 from tempest.tests import base
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib import fake_credentials
@@ -54,14 +54,14 @@
     def test___init___one_class(self):
         fake_partial = 'fake_partial'
         partial_mock = self.useFixture(fixtures.MockPatch(
-            'tempest.service_clients.ClientsFactory._get_partial_class',
+            'tempest.lib.services.clients.ClientsFactory._get_partial_class',
             return_value=fake_partial)).mock
         class_names = ['FakeServiceClient1']
         mock_importlib = self._setup_fake_module(class_names=class_names)
         auth_provider = fake_auth_provider.FakeAuthProvider()
         params = {'k1': 'v1', 'k2': 'v2'}
-        factory = service_clients.ClientsFactory('fake_path', class_names,
-                                                 auth_provider, **params)
+        factory = clients.ClientsFactory('fake_path', class_names,
+                                         auth_provider, **params)
         # Assert module has been imported
         mock_importlib.assert_called_once_with('fake_path')
         # All attributes have been created
@@ -77,14 +77,14 @@
     def test___init___two_classes(self):
         fake_partial = 'fake_partial'
         partial_mock = self.useFixture(fixtures.MockPatch(
-            'tempest.service_clients.ClientsFactory._get_partial_class',
+            'tempest.lib.services.clients.ClientsFactory._get_partial_class',
             return_value=fake_partial)).mock
         class_names = ['FakeServiceClient1', 'FakeServiceClient2']
         mock_importlib = self._setup_fake_module(class_names=class_names)
         auth_provider = fake_auth_provider.FakeAuthProvider()
         params = {'k1': 'v1', 'k2': 'v2'}
-        factory = service_clients.ClientsFactory('fake_path', class_names,
-                                                 auth_provider, **params)
+        factory = clients.ClientsFactory('fake_path', class_names,
+                                         auth_provider, **params)
         # Assert module has been imported
         mock_importlib.assert_called_once_with('fake_path')
         # All attributes have been created
@@ -100,8 +100,8 @@
         auth_provider = fake_auth_provider.FakeAuthProvider()
         class_names = ['FakeServiceClient1', 'FakeServiceClient2']
         with testtools.ExpectedException(ImportError, '.*fake_module.*'):
-            service_clients.ClientsFactory('fake_module', class_names,
-                                           auth_provider)
+            clients.ClientsFactory('fake_module', class_names,
+                                   auth_provider)
 
     def test___init___not_a_class(self):
         class_names = ['FakeServiceClient1', 'FakeServiceClient2']
@@ -111,8 +111,8 @@
         auth_provider = fake_auth_provider.FakeAuthProvider()
         expected_msg = '.*not_really_a_class.*str.*'
         with testtools.ExpectedException(TypeError, expected_msg):
-            service_clients.ClientsFactory('fake_module', extended_class_names,
-                                           auth_provider)
+            clients.ClientsFactory('fake_module', extended_class_names,
+                                   auth_provider)
 
     def test___init___class_not_found(self):
         class_names = ['FakeServiceClient1', 'FakeServiceClient2']
@@ -121,15 +121,15 @@
         auth_provider = fake_auth_provider.FakeAuthProvider()
         expected_msg = '.*not_really_a_class.*fake_service_client.*'
         with testtools.ExpectedException(AttributeError, expected_msg):
-            service_clients.ClientsFactory('fake_module', extended_class_names,
-                                           auth_provider)
+            clients.ClientsFactory('fake_module', extended_class_names,
+                                   auth_provider)
 
     def test__get_partial_class_no_later_kwargs(self):
         expected_fake_client = 'not_really_a_client'
         self._setup_fake_module(class_names=[])
         auth_provider = fake_auth_provider.FakeAuthProvider()
         params = {'k1': 'v1', 'k2': 'v2'}
-        factory = service_clients.ClientsFactory(
+        factory = clients.ClientsFactory(
             'fake_path', [], auth_provider, **params)
         klass_mock = mock.Mock(return_value=expected_fake_client)
         partial = factory._get_partial_class(klass_mock, auth_provider, params)
@@ -147,7 +147,7 @@
         auth_provider = fake_auth_provider.FakeAuthProvider()
         params = {'k1': 'v1', 'k2': 'v2'}
         later_params = {'k2': 'v4', 'k3': 'v3'}
-        factory = service_clients.ClientsFactory(
+        factory = clients.ClientsFactory(
             'fake_path', [], auth_provider, **params)
         klass_mock = mock.Mock(return_value=expected_fake_client)
         partial = factory._get_partial_class(klass_mock, auth_provider, params)
@@ -160,21 +160,45 @@
         klass_mock.assert_called_once_with(auth_provider=auth_provider,
                                            **params)
 
+    def test__get_partial_class_with_alias(self):
+        expected_fake_client = 'not_really_a_client'
+        client_alias = 'fake_client'
+        self._setup_fake_module(class_names=[])
+        auth_provider = fake_auth_provider.FakeAuthProvider()
+        params = {'k1': 'v1', 'k2': 'v2'}
+        later_params = {'k2': 'v4', 'k3': 'v3'}
+        factory = clients.ClientsFactory(
+            'fake_path', [], auth_provider, **params)
+        klass_mock = mock.Mock(return_value=expected_fake_client)
+        partial = factory._get_partial_class(klass_mock, auth_provider, params)
+        # Class has not be initialised yet
+        klass_mock.assert_not_called()
+        # Use partial and assert on parameters
+        client = partial(alias=client_alias, **later_params)
+        params.update(later_params)
+        self.assertEqual(expected_fake_client, client)
+        klass_mock.assert_called_once_with(auth_provider=auth_provider,
+                                           **params)
+        self.assertThat(factory, has_attribute(client_alias))
+        self.assertEqual(expected_fake_client, getattr(factory, client_alias))
+
 
 class TestServiceClients(base.TestCase):
 
     def setUp(self):
         super(TestServiceClients, self).setUp()
         self.useFixture(fixtures.MockPatch(
-            'tempest.service_clients.tempest_modules',
-            return_value=set(['fake_service1', 'fake_service2'])))
+            'tempest.lib.services.clients.tempest_modules', return_value={}))
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.services.clients._tempest_internal_modules',
+            return_value=set(['fake_service1'])))
 
     def test___init___creds_v2_uri(self):
         # Verify that no API request is made, since no mock
         # is required to run the test successfully
         creds = fake_credentials.FakeKeystoneV2Credentials()
         uri = 'fake_uri'
-        _manager = service_clients.ServiceClients(creds, identity_uri=uri)
+        _manager = clients.ServiceClients(creds, identity_uri=uri)
         self.assertIsInstance(_manager.auth_provider,
                               auth.KeystoneV2AuthProvider)
 
@@ -183,7 +207,7 @@
         # is required to run the test successfully
         creds = fake_credentials.FakeKeystoneV3Credentials()
         uri = 'fake_uri'
-        _manager = service_clients.ServiceClients(creds, identity_uri=uri)
+        _manager = clients.ServiceClients(creds, identity_uri=uri)
         self.assertIsInstance(_manager.auth_provider,
                               auth.KeystoneV3AuthProvider)
 
@@ -191,14 +215,14 @@
         creds = fake_credentials.FakeCredentials()
         uri = 'fake_uri'
         with testtools.ExpectedException(exceptions.InvalidCredentials):
-            service_clients.ServiceClients(creds, identity_uri=uri)
+            clients.ServiceClients(creds, identity_uri=uri)
 
     def test___init___invalid_creds_uri(self):
         creds = fake_credentials.FakeKeystoneV2Credentials()
         delattr(creds, 'username')
         uri = 'fake_uri'
         with testtools.ExpectedException(exceptions.InvalidCredentials):
-            service_clients.ServiceClients(creds, identity_uri=uri)
+            clients.ServiceClients(creds, identity_uri=uri)
 
     def test___init___creds_uri_none(self):
         creds = fake_credentials.FakeKeystoneV2Credentials()
@@ -206,7 +230,7 @@
                "non-empty")
         with testtools.ExpectedException(exceptions.InvalidCredentials,
                                          value_re=msg):
-            service_clients.ServiceClients(creds, None)
+            clients.ServiceClients(creds, None)
 
     def test___init___creds_uri_params(self):
         creds = fake_credentials.FakeKeystoneV2Credentials()
@@ -214,8 +238,8 @@
                           'fake_param2': 'fake_value2'}
         params = {'fake_service1': expeted_params}
         uri = 'fake_uri'
-        _manager = service_clients.ServiceClients(creds, identity_uri=uri,
-                                                  client_parameters=params)
+        _manager = clients.ServiceClients(creds, identity_uri=uri,
+                                          client_parameters=params)
         self.assertIn('fake_service1', _manager.parameters)
         for _key in expeted_params:
             self.assertIn(_key, _manager.parameters['fake_service1'].keys())
@@ -231,14 +255,14 @@
         msg = "(?=.*{0})(?=.*{1})".format(*list(params.keys()))
         with testtools.ExpectedException(
                 exceptions.UnknownServiceClient, value_re=msg):
-            service_clients.ServiceClients(creds, identity_uri=uri,
-                                           client_parameters=params)
+            clients.ServiceClients(creds, identity_uri=uri,
+                                   client_parameters=params)
 
     def _get_manager(self, init_region='fake_region'):
         # Get a manager to invoke _setup_parameters on
         creds = fake_credentials.FakeKeystoneV2Credentials()
-        return service_clients.ServiceClients(creds, identity_uri='fake_uri',
-                                              region=init_region)
+        return clients.ServiceClients(creds, identity_uri='fake_uri',
+                                      region=init_region)
 
     def test__setup_parameters_none_no_region(self):
         kwargs = {}
@@ -270,7 +294,7 @@
         _manager = self._get_manager(init_region='fake_region_default')
         # Mock after the _manager is setup to preserve the call count
         factory_mock = self.useFixture(fixtures.MockPatch(
-            'tempest.service_clients.ClientsFactory')).mock
+            'tempest.lib.services.clients.ClientsFactory')).mock
         _manager.register_service_client_module(
             name='fake_module',
             service_version='fake_service',
@@ -299,7 +323,7 @@
         _manager = self._get_manager(init_region='fake_region_default')
         # Mock after the _manager is setup to preserve the call count
         factory_mock = self.useFixture(fixtures.MockPatch(
-            'tempest.service_clients.ClientsFactory')).mock
+            'tempest.lib.services.clients.ClientsFactory')).mock
         _manager.register_service_client_module(
             name='fake_module',
             service_version='fake_service',
@@ -322,7 +346,7 @@
 
     def test_register_service_client_module_duplicate_name(self):
         self.useFixture(fixtures.MockPatch(
-            'tempest.service_clients.ClientsFactory'))
+            'tempest.lib.services.clients.ClientsFactory')).mock
         _manager = self._get_manager()
         name_owner = 'this_is_a_string'
         setattr(_manager, 'fake_module', name_owner)
@@ -335,7 +359,7 @@
 
     def test_register_service_client_module_duplicate_service(self):
         self.useFixture(fixtures.MockPatch(
-            'tempest.service_clients.ClientsFactory'))
+            'tempest.lib.services.clients.ClientsFactory')).mock
         _manager = self._get_manager()
         duplicate_service = 'fake_service1'
         expected_error = '.*' + duplicate_service